aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.yml2
-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.yml210
-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.sh3
-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.txt454
-rw-r--r--CMakePresets.json110
-rw-r--r--CONTRIBUTING.md71
-rw-r--r--LICENSE.txt7
-rw-r--r--MAINTAIN.md123
-rw-r--r--Makefile18
-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.txt45
-rw-r--r--cmake.config/config.h.in4
-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.imp236
-rw-r--r--cmake.deps/CMakeLists.txt81
-rw-r--r--cmake.deps/cmake/BuildGettext.cmake36
-rw-r--r--cmake.deps/cmake/BuildLibiconv.cmake32
-rw-r--r--cmake.deps/cmake/BuildLibtermkey.cmake60
-rw-r--r--cmake.deps/cmake/BuildLibuv.cmake21
-rw-r--r--cmake.deps/cmake/BuildLibvterm.cmake66
-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.cmake92
-rw-r--r--cmake.deps/cmake/BuildMsgpack.cmake57
-rw-r--r--cmake.deps/cmake/BuildTreesitter.cmake67
-rw-r--r--cmake.deps/cmake/BuildTreesitterParsers.cmake51
-rw-r--r--cmake.deps/cmake/BuildUnibilium.cmake65
-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.txt2
-rw-r--r--cmake.deps/cmake/TreesitterParserCMakeLists.txt10
-rw-r--r--cmake.deps/cmake/UnibiliumCMakeLists.txt2
-rw-r--r--cmake.deps/cmake/libtermkeyCMakeLists.txt2
-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.cmake39
-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.cmake57
-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/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.txt1018
-rw-r--r--runtime/doc/arabic.txt22
-rw-r--r--runtime/doc/autocmd.txt83
-rw-r--r--runtime/doc/builtin.txt479
-rw-r--r--runtime/doc/change.txt24
-rw-r--r--runtime/doc/channel.txt40
-rw-r--r--runtime/doc/cmdline.txt50
-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.txt524
-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.txt74
-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.txt753
-rw-r--r--runtime/doc/lua-guide.txt757
-rw-r--r--runtime/doc/lua.txt1209
-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.txt108
-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.txt180
-rw-r--r--runtime/doc/nvim.txt53
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt102
-rw-r--r--runtime/doc/options.txt644
-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.txt166
-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.txt993
-rw-r--r--runtime/doc/uganda.txt96
-rw-r--r--runtime/doc/ui.txt230
-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.txt33
-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.txt158
-rw-r--r--runtime/doc/visual.txt6
-rw-r--r--runtime/doc/windows.txt77
-rw-r--r--runtime/filetype.lua13
-rw-r--r--runtime/filetype.vim2620
-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/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.lua622
-rw-r--r--runtime/lua/nvim/health.lua387
-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.lua332
-rw-r--r--runtime/lua/vim/filetype.lua151
-rw-r--r--runtime/lua/vim/filetype/detect.lua79
-rw-r--r--runtime/lua/vim/fs.lua175
-rw-r--r--runtime/lua/vim/highlight.lua3
-rw-r--r--runtime/lua/vim/keymap.lua39
-rw-r--r--runtime/lua/vim/lsp.lua227
-rw-r--r--runtime/lua/vim/lsp/buf.lua307
-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.lua138
-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.lua86
-rw-r--r--runtime/lua/vim/lsp/rpc.lua79
-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.lua138
-rw-r--r--runtime/lua/vim/treesitter.lua380
-rw-r--r--runtime/lua/vim/treesitter/health.lua21
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua66
-rw-r--r--runtime/lua/vim/treesitter/language.lua16
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua119
-rw-r--r--runtime/lua/vim/treesitter/playground.lua186
-rw-r--r--runtime/lua/vim/treesitter/query.lua169
-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.vim6
-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.vim68
-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/syntax.vim6
-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
-rwxr-xr-xscripts/bump-deps.sh104
-rwxr-xr-x[-rw-r--r--]scripts/bump_deps.lua601
-rw-r--r--scripts/cliff.toml73
-rwxr-xr-xscripts/download-unicode-files.sh19
-rw-r--r--scripts/gen_help_html.lua1111
-rw-r--r--scripts/gen_help_html.py389
-rwxr-xr-xscripts/gen_vimdoc.py94
-rwxr-xr-xscripts/genappimage.sh12
-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
-rwxr-xr-xsrc/nvim/CMakeLists.txt608
-rw-r--r--src/nvim/README.md10
-rw-r--r--src/nvim/api/autocmd.c122
-rw-r--r--src/nvim/api/buffer.c167
-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.c133
-rw-r--r--src/nvim/api/extmark.h3
-rw-r--r--src/nvim/api/keysets.lua18
-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.h10
-rw-r--r--src/nvim/api/private/helpers.c95
-rw-r--r--src/nvim/api/private/helpers.h8
-rw-r--r--src/nvim/api/tabpage.c16
-rw-r--r--src/nvim/api/ui.c222
-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.c181
-rw-r--r--src/nvim/api/vimscript.c29
-rw-r--r--src/nvim/api/win_config.c154
-rw-r--r--src/nvim/api/window.c31
-rw-r--r--src/nvim/arabic.c135
-rw-r--r--src/nvim/arglist.c609
-rw-r--r--src/nvim/ascii.h14
-rw-r--r--src/nvim/assert.h2
-rw-r--r--src/nvim/auevents.lua4
-rw-r--r--src/nvim/autocmd.c301
-rw-r--r--src/nvim/autocmd.h14
-rw-r--r--src/nvim/buffer.c311
-rw-r--r--src/nvim/buffer.h16
-rw-r--r--src/nvim/buffer_defs.h514
-rw-r--r--src/nvim/buffer_updates.c65
-rw-r--r--src/nvim/buffer_updates.h4
-rw-r--r--src/nvim/change.c129
-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.c113
-rw-r--r--src/nvim/charset.h3
-rw-r--r--src/nvim/cmdexpand.c1776
-rw-r--r--src/nvim/cmdexpand.h3
-rw-r--r--src/nvim/cmdhist.c297
-rw-r--r--src/nvim/cmdhist.h3
-rw-r--r--src/nvim/context.c24
-rw-r--r--src/nvim/context.h4
-rw-r--r--src/nvim/cursor.c55
-rw-r--r--src/nvim/cursor_shape.c15
-rw-r--r--src/nvim/debugger.c271
-rw-r--r--src/nvim/decoration.c43
-rw-r--r--src/nvim/decoration.h16
-rw-r--r--src/nvim/decoration_provider.c65
-rw-r--r--src/nvim/decoration_provider.h6
-rw-r--r--src/nvim/diff.c1433
-rw-r--r--src/nvim/diff.h3
-rw-r--r--src/nvim/digraph.c184
-rw-r--r--src/nvim/drawline.c431
-rw-r--r--src/nvim/drawline.h6
-rw-r--r--src/nvim/drawscreen.c530
-rw-r--r--src/nvim/drawscreen.h3
-rw-r--r--src/nvim/edit.c840
-rw-r--r--src/nvim/edit.h2
-rw-r--r--src/nvim/eval.c943
-rw-r--r--src/nvim/eval.h67
-rw-r--r--src/nvim/eval.lua15
-rw-r--r--src/nvim/eval/buffer.c733
-rw-r--r--src/nvim/eval/buffer.h10
-rw-r--r--src/nvim/eval/decode.c76
-rw-r--r--src/nvim/eval/encode.c57
-rw-r--r--src/nvim/eval/encode.h15
-rw-r--r--src/nvim/eval/executor.c14
-rw-r--r--src/nvim/eval/funcs.c1984
-rw-r--r--src/nvim/eval/funcs.h5
-rw-r--r--src/nvim/eval/gc.c5
-rw-r--r--src/nvim/eval/gc.h1
-rw-r--r--src/nvim/eval/typval.c226
-rw-r--r--src/nvim/eval/typval.h458
-rw-r--r--src/nvim/eval/typval_defs.h399
-rw-r--r--src/nvim/eval/typval_encode.c.h28
-rw-r--r--src/nvim/eval/typval_encode.h4
-rw-r--r--src/nvim/eval/userfunc.c966
-rw-r--r--src/nvim/eval/userfunc.h85
-rw-r--r--src/nvim/eval/vars.c230
-rw-r--r--src/nvim/eval/vars.h2
-rw-r--r--src/nvim/eval/window.c951
-rw-r--r--src/nvim/eval/window.h68
-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.c43
-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.c1200
-rw-r--r--src/nvim/ex_cmds.h1
-rw-r--r--src/nvim/ex_cmds.lua42
-rw-r--r--src/nvim/ex_cmds2.c178
-rw-r--r--src/nvim/ex_cmds_defs.h5
-rw-r--r--src/nvim/ex_docmd.c841
-rw-r--r--src/nvim/ex_docmd.h3
-rw-r--r--src/nvim/ex_eval.c876
-rw-r--r--src/nvim/ex_eval.h2
-rw-r--r--src/nvim/ex_eval_defs.h2
-rw-r--r--src/nvim/ex_getln.c1540
-rw-r--r--src/nvim/ex_getln.h8
-rw-r--r--src/nvim/ex_session.c56
-rw-r--r--src/nvim/extmark.c13
-rw-r--r--src/nvim/extmark.h7
-rw-r--r--src/nvim/extmark_defs.h4
-rw-r--r--src/nvim/file_search.c666
-rw-r--r--src/nvim/file_search.h6
-rw-r--r--src/nvim/fileio.c1138
-rw-r--r--src/nvim/fileio.h1
-rw-r--r--src/nvim/fold.c299
-rw-r--r--src/nvim/fold.h7
-rw-r--r--src/nvim/garray.c15
-rw-r--r--src/nvim/garray.h4
-rw-r--r--src/nvim/generators/c_grammar.lua3
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua103
-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.lua27
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua37
-rw-r--r--src/nvim/generators/gen_options.lua7
-rw-r--r--src/nvim/generators/gen_unicode_tables.lua3
-rw-r--r--src/nvim/getchar.c501
-rw-r--r--src/nvim/globals.h83
-rw-r--r--src/nvim/grid.c37
-rw-r--r--src/nvim/grid.h19
-rw-r--r--src/nvim/grid_defs.h20
-rw-r--r--src/nvim/hardcopy.c3178
-rw-r--r--src/nvim/hardcopy.h86
-rw-r--r--src/nvim/hashtab.c34
-rw-r--r--src/nvim/hashtab.h23
-rw-r--r--src/nvim/help.c199
-rw-r--r--src/nvim/highlight.c106
-rw-r--r--src/nvim/highlight.h3
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/highlight_group.c238
-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.c423
-rw-r--r--src/nvim/indent.h2
-rw-r--r--src/nvim/indent_c.c1568
-rw-r--r--src/nvim/input.c17
-rw-r--r--src/nvim/insexpand.c658
-rw-r--r--src/nvim/insexpand.h11
-rw-r--r--src/nvim/keycodes.c119
-rw-r--r--src/nvim/keycodes.h4
-rw-r--r--src/nvim/lib/ringbuf.h11
-rw-r--r--src/nvim/linematch.c385
-rw-r--r--src/nvim/linematch.h10
-rw-r--r--src/nvim/locale.c38
-rw-r--r--src/nvim/log.c10
-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.c384
-rw-r--r--src/nvim/lua/executor.h4
-rw-r--r--src/nvim/lua/spell.c16
-rw-r--r--src/nvim/lua/stdlib.c83
-rw-r--r--src/nvim/lua/treesitter.c35
-rw-r--r--src/nvim/lua/xdiff.c147
-rw-r--r--src/nvim/macros.h11
-rw-r--r--src/nvim/main.c732
-rw-r--r--src/nvim/main.h11
-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.c496
-rw-r--r--src/nvim/mapping.h14
-rw-r--r--src/nvim/mark.c359
-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.c383
-rw-r--r--src/nvim/math.c2
-rw-r--r--src/nvim/mbyte.c558
-rw-r--r--src/nvim/mbyte.h16
-rw-r--r--src/nvim/memfile.c37
-rw-r--r--src/nvim/memfile_defs.h4
-rw-r--r--src/nvim/memline.c1534
-rw-r--r--src/nvim/memline.h4
-rw-r--r--src/nvim/memline_defs.h4
-rw-r--r--src/nvim/memory.c115
-rw-r--r--src/nvim/memory.h12
-rw-r--r--src/nvim/menu.c230
-rw-r--r--src/nvim/menu.h6
-rw-r--r--src/nvim/menu_defs.h2
-rw-r--r--src/nvim/message.c652
-rw-r--r--src/nvim/message.h14
-rw-r--r--src/nvim/mouse.c942
-rw-r--r--src/nvim/mouse.h82
-rw-r--r--src/nvim/move.c635
-rw-r--r--src/nvim/msgpack_rpc/channel.c88
-rw-r--r--src/nvim/msgpack_rpc/channel.h2
-rw-r--r--src/nvim/msgpack_rpc/helpers.c42
-rw-r--r--src/nvim/msgpack_rpc/server.c17
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c38
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h4
-rw-r--r--src/nvim/normal.c1139
-rw-r--r--src/nvim/normal.h16
-rw-r--r--src/nvim/ops.c932
-rw-r--r--src/nvim/ops.h23
-rw-r--r--src/nvim/option.c1780
-rw-r--r--src/nvim/option.h4
-rw-r--r--src/nvim/option_defs.h126
-rw-r--r--src/nvim/options.lua207
-rw-r--r--src/nvim/optionstr.c270
-rw-r--r--src/nvim/optionstr.h2
-rw-r--r--src/nvim/os/dl.c3
-rw-r--r--src/nvim/os/env.c139
-rw-r--r--src/nvim/os/fileio.c45
-rw-r--r--src/nvim/os/fileio.h5
-rw-r--r--src/nvim/os/fs.c124
-rw-r--r--src/nvim/os/fs.h4
-rw-r--r--src/nvim/os/input.c94
-rw-r--r--src/nvim/os/lang.c10
-rw-r--r--src/nvim/os/mem.c1
-rw-r--r--src/nvim/os/os_defs.h4
-rw-r--r--src/nvim/os/os_win_console.c47
-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.c153
-rw-r--r--src/nvim/os/signal.c13
-rw-r--r--src/nvim/os/stdpaths.c12
-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.c731
-rw-r--r--src/nvim/path.h6
-rw-r--r--src/nvim/plines.c74
-rw-r--r--src/nvim/plines.h4
-rw-r--r--src/nvim/po/CMakeLists.txt39
-rw-r--r--src/nvim/po/af.po95
-rw-r--r--src/nvim/po/ca.po122
-rw-r--r--src/nvim/po/cleanup.vim2
-rw-r--r--src/nvim/po/cs.cp1250.po125
-rw-r--r--src/nvim/po/cs.po125
-rw-r--r--src/nvim/po/da.po93
-rw-r--r--src/nvim/po/de.po120
-rw-r--r--src/nvim/po/en_GB.po122
-rw-r--r--src/nvim/po/eo.po94
-rw-r--r--src/nvim/po/es.po123
-rw-r--r--src/nvim/po/fi.po93
-rw-r--r--src/nvim/po/fixfilenames.vim13
-rw-r--r--src/nvim/po/fr.po97
-rw-r--r--src/nvim/po/ga.po93
-rw-r--r--src/nvim/po/it.po120
-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.po120
-rw-r--r--src/nvim/po/nb.po120
-rw-r--r--src/nvim/po/nl.po120
-rw-r--r--src/nvim/po/no.po120
-rw-r--r--src/nvim/po/pl.UTF-8.po120
-rw-r--r--src/nvim/po/pt_BR.po123
-rw-r--r--src/nvim/po/ru.po120
-rw-r--r--src/nvim/po/sk.cp1250.po121
-rw-r--r--src/nvim/po/sk.po121
-rw-r--r--src/nvim/po/sr.po94
-rw-r--r--src/nvim/po/sv.po120
-rw-r--r--src/nvim/po/tojavascript.vim18
-rw-r--r--src/nvim/po/tr.po1391
-rw-r--r--src/nvim/po/uk.po93
-rw-r--r--src/nvim/po/vi.po121
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po11903
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po121
-rw-r--r--src/nvim/popupmenu.c103
-rw-r--r--src/nvim/popupmenu.h18
-rw-r--r--src/nvim/profile.c53
-rw-r--r--src/nvim/quickfix.c431
-rw-r--r--src/nvim/rbuffer.c8
-rw-r--r--src/nvim/rbuffer.h2
-rw-r--r--src/nvim/regexp.c620
-rw-r--r--src/nvim/regexp.h3
-rw-r--r--src/nvim/regexp_bt.c738
-rw-r--r--src/nvim/regexp_defs.h98
-rw-r--r--src/nvim/regexp_nfa.c564
-rw-r--r--src/nvim/runtime.c153
-rw-r--r--src/nvim/runtime.h6
-rw-r--r--src/nvim/screen.c588
-rw-r--r--src/nvim/screen.h7
-rw-r--r--src/nvim/search.c956
-rw-r--r--src/nvim/search.h5
-rw-r--r--src/nvim/sha256.c12
-rw-r--r--src/nvim/sha256.h4
-rw-r--r--src/nvim/shada.c378
-rw-r--r--src/nvim/sign.c470
-rw-r--r--src/nvim/sign_defs.h10
-rw-r--r--src/nvim/spell.c652
-rw-r--r--src/nvim/spell_defs.h13
-rw-r--r--src/nvim/spellfile.c754
-rw-r--r--src/nvim/spellsuggest.c684
-rw-r--r--src/nvim/state.c31
-rw-r--r--src/nvim/state.h2
-rw-r--r--src/nvim/statusline.c880
-rw-r--r--src/nvim/statusline.h9
-rw-r--r--src/nvim/statusline_defs.h26
-rw-r--r--src/nvim/strings.c267
-rw-r--r--src/nvim/strings.h2
-rw-r--r--src/nvim/syntax.c1520
-rw-r--r--src/nvim/syntax.h1
-rw-r--r--src/nvim/syntax_defs.h2
-rw-r--r--src/nvim/tag.c2719
-rw-r--r--src/nvim/tag.h4
-rw-r--r--src/nvim/terminal.c115
-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.vim30
-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.vim5
-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.vim425
-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.vim10
-rw-r--r--src/nvim/testdir/test_breakindent.vim162
-rw-r--r--src/nvim/testdir/test_buffer.vim46
-rw-r--r--src/nvim/testdir/test_bufline.vim94
-rw-r--r--src/nvim/testdir/test_cd.vim34
-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.vim26
-rw-r--r--src/nvim/testdir/test_cmdline.vim989
-rw-r--r--src/nvim/testdir/test_compiler.vim8
-rw-r--r--src/nvim/testdir/test_const.vim10
-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.vim139
-rw-r--r--src/nvim/testdir/test_cursorline.vim16
-rw-r--r--src/nvim/testdir/test_debugger.vim43
-rw-r--r--src/nvim/testdir/test_delete.vim14
-rw-r--r--src/nvim/testdir/test_diffmode.vim174
-rw-r--r--src/nvim/testdir/test_display.vim77
-rw-r--r--src/nvim/testdir/test_edit.vim246
-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.vim46
-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.vim18
-rw-r--r--src/nvim/testdir/test_expand_func.vim1
-rw-r--r--src/nvim/testdir/test_expr.vim189
-rw-r--r--src/nvim/testdir/test_fileformat.vim9
-rw-r--r--src/nvim/testdir/test_filetype.vim198
-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.vim606
-rw-r--r--src/nvim/testdir/test_functions.vim623
-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.vim24
-rw-r--r--src/nvim/testdir/test_increment.vim35
-rw-r--r--src/nvim/testdir/test_ins_complete.vim875
-rw-r--r--src/nvim/testdir/test_lambda.vim20
-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.vim174
-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.vim33
-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.vim654
-rw-r--r--src/nvim/testdir/test_options.vim270
-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_profile.vim47
-rw-r--r--src/nvim/testdir/test_prompt_buffer.vim4
-rw-r--r--src/nvim/testdir/test_quickfix.vim998
-rw-r--r--src/nvim/testdir/test_quotestar.vim2
-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.vim230
-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_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.vim96
-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.vim110
-rw-r--r--src/nvim/testdir/test_swap.vim159
-rw-r--r--src/nvim/testdir/test_syntax.vim95
-rw-r--r--src/nvim/testdir/test_system.vim38
-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.vim30
-rw-r--r--src/nvim/testdir/test_timers.vim45
-rw-r--r--src/nvim/testdir/test_trycatch.vim45
-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.vim330
-rw-r--r--src/nvim/testdir/test_usercommands.vim108
-rw-r--r--src/nvim/testdir/test_utf8.vim34
-rw-r--r--src/nvim/testdir/test_vartabs.vim18
-rw-r--r--src/nvim/testdir/test_viminfo.vim5
-rw-r--r--src/nvim/testdir/test_vimscript.vim5847
-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.c156
-rw-r--r--src/nvim/textformat.c76
-rw-r--r--src/nvim/textformat.h4
-rw-r--r--src/nvim/textobject.c94
-rw-r--r--src/nvim/textobject.h6
-rw-r--r--src/nvim/tui/input.c166
-rw-r--r--src/nvim/tui/input.h6
-rw-r--r--src/nvim/tui/terminfo.c81
-rw-r--r--src/nvim/tui/terminfo.h2
-rw-r--r--src/nvim/tui/tui.c1677
-rw-r--r--src/nvim/types.h3
-rw-r--r--src/nvim/ugrid.c8
-rw-r--r--src/nvim/ugrid.h4
-rw-r--r--src/nvim/ui.c262
-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.c132
-rw-r--r--src/nvim/ui_client.h21
-rw-r--r--src/nvim/ui_compositor.c80
-rw-r--r--src/nvim/undo.c112
-rw-r--r--src/nvim/undo_defs.h4
-rw-r--r--src/nvim/usercmd.c172
-rw-r--r--src/nvim/usercmd.h5
-rw-r--r--src/nvim/version.c1162
-rw-r--r--src/nvim/version.h2
-rw-r--r--src/nvim/vim.h52
-rw-r--r--src/nvim/viml/parser/expressions.c85
-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.h24
-rw-r--r--src/nvim/window.c2252
-rw-r--r--src/nvim/window.h79
-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/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.lua101
-rw-r--r--test/functional/api/extmark_spec.lua22
-rw-r--r--test/functional/api/highlight_spec.lua8
-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.lua11
-rw-r--r--test/functional/api/server_requests_spec.lua2
-rw-r--r--test/functional/api/vim_spec.lua247
-rw-r--r--test/functional/api/window_spec.lua183
-rw-r--r--test/functional/autocmd/cmdline_spec.lua9
-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.lua99
-rw-r--r--test/functional/core/channels_spec.lua9
-rw-r--r--test/functional/core/fileio_spec.lua124
-rw-r--r--test/functional/core/job_spec.lua49
-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.lua301
-rw-r--r--test/functional/editor/completion_spec.lua46
-rw-r--r--test/functional/editor/mark_spec.lua14
-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/quickfix_commands_spec.lua21
-rw-r--r--test/functional/ex_cmds/quit_spec.lua2
-rw-r--r--test/functional/ex_cmds/source_spec.lua8
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua79
-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.lua240
-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.lua2
-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/cmdline_spec.lua99
-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/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/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.lua177
-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/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.lua204
-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.lua82
-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.lua568
-rw-r--r--test/functional/plugin/man_spec.lua32
-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.lua567
-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.lua31
-rw-r--r--test/functional/treesitter/language_spec.lua20
-rw-r--r--test/functional/treesitter/node_spec.lua5
-rw-r--r--test/functional/treesitter/parser_spec.lua9
-rw-r--r--test/functional/treesitter/utils_spec.lua2
-rw-r--r--test/functional/ui/bufhl_spec.lua2
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua12
-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.lua66
-rw-r--r--test/functional/ui/highlight_spec.lua118
-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.lua141
-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.lua89
-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.lua820
-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.lua409
-rw-r--r--test/functional/ui/statusline_spec.lua127
-rw-r--r--test/functional/ui/syntax_conceal_spec.lua85
-rw-r--r--test/functional/ui/tabline_spec.lua36
-rw-r--r--test/functional/ui/wildmode_spec.lua48
-rw-r--r--test/functional/vimscript/container_functions_spec.lua2
-rw-r--r--test/functional/vimscript/eval_spec.lua125
-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/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.lua10
-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.lua116
-rw-r--r--test/includes/CMakeLists.txt1
-rw-r--r--test/unit/buffer_spec.lua10
-rw-r--r--test/unit/eval/typval_spec.lua8
-rw-r--r--test/unit/message_spec.lua2
-rw-r--r--test/unit/os/fs_spec.lua6
-rw-r--r--test/unit/path_spec.lua16
-rw-r--r--test/unit/search_spec.lua2
-rw-r--r--test/unit/tui_spec.lua2
1197 files changed, 104511 insertions, 80828 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..3789fb866b 100644
--- a/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
@@ -31,7 +31,7 @@ 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.
+ - Create a minimal_init.lua using vim.lsp.start - read `:h lsp-quickstart` and `:h vim.lsp.start` for more information.
- _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..48338527f4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,7 +9,7 @@ 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:
@@ -19,7 +19,7 @@ concurrency:
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 +31,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 +59,38 @@ 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()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: lintlua
run: make lintlua
- - if: "!cancelled()"
- name: lintpy
- run: make lintpy
-
- - 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 +98,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 +113,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 +126,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 +152,15 @@ jobs:
run: ./ci/run_tests.sh build_nvim
- 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: lintc
run: make lintc
- - if: "!cancelled()"
+ - if: success() || failure() && steps.abort_job.outputs.status == 'success'
name: check-single-includes
run: make check-single-includes
@@ -213,19 +174,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 +197,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 }}
@@ -244,6 +205,8 @@ jobs:
env:
CC: ${{ matrix.cc }}
CI_OS_NAME: ${{ matrix.os }}
+ # TEST_FILE: test/functional/core/startup_spec.lua
+ # TEST_FILTER: foo
steps:
- uses: actions/checkout@v3
@@ -252,9 +215,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 +235,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 +244,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 +252,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 +280,69 @@ 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
+ # TEST_FILE: test/functional/core/startup_spec.lua
+ # TEST_FILTER: foo
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"
+ 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..4a2217de89 100755
--- a/.github/workflows/env.sh
+++ b/.github/workflows/env.sh
@@ -30,7 +30,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 +56,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..cadab62ce6 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)
@@ -612,59 +265,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 +312,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
@@ -692,6 +331,11 @@ install_helper(
FILES ${CMAKE_SOURCE_DIR}/src/man/nvim.1
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
+option(ENABLE_LIBICONV "enable libiconv" ON)
+if(ENABLE_LIBICONV)
+ find_package(Iconv REQUIRED)
+endif()
+
#
# Go down the tree.
#
@@ -709,17 +353,17 @@ 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(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test pwsh-test streams-test tty-test ${GENERATED_HELP_TAGS})
@@ -736,9 +380,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 +411,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 +427,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 +445,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..76e38111ea
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,110 @@
+{
+ "version": 3,
+ "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"
+ }
+ ]
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 582d5fbdd4..d0af90d679 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
@@ -259,9 +246,15 @@ You can lint a single file (but this will _not_ exclude legacy errors):
For managing includes in C files, use [include-what-you-use].
- [Install include-what-you-use][include-what-you-use-install]
-- Run with:
+- To see which includes needs fixing just use the cmake preset `iwyu`:
+ ```
+ cmake --preset iwyu
+ cmake --build --preset iwyu
+ ```
+- There's also a make target that automatically fixes the suggestions from
+ IWYU:
```
- make CMAKE_EXTRA_FLAGS=-DCMAKE_C_INCLUDE_WHAT_YOU_USE=include-what-you-use | tee iwyu.txt
+ make iwyu
```
See [#549][549] for more details.
@@ -321,6 +314,7 @@ as context, use the `-W` argument as well.
[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
@@ -345,7 +339,6 @@ as context, use the `-W` argument as well.
[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
-[sourcehut]: https://builds.sr.ht/~jmk
[style-guide]: https://neovim.io/doc/user/dev_style.html#dev-style
[uncrustify]: http://uncrustify.sourceforge.net/
[wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp
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..110e9e3760 100644
--- a/Makefile
+++ b/Makefile
@@ -127,25 +127,23 @@ 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
$(BUILD_TOOL) -C build $@
-lintlua lintsh lintpy lintuncrustify lintc lintcfull check-single-includes generated-sources lintcommit lint formatc formatlua format: | build/.ran-cmake
+lintlua lintsh lintc check-single-includes generated-sources lintcommit lint formatc formatlua format: | build/.ran-cmake
$(CMAKE_PRG) --build build --target $@
test: functionaltest unittest
+iwyu: build/.ran-cmake
+ cmake --preset iwyu
+ cmake --build --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 +172,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 lintlua lintsh functionaltest unittest lint lintc clean distclean nvim libnvim cmake deps install appimage checkprefix lintcommit formatc formatlua format
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..0ea2124401 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)
@@ -51,6 +66,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..f946fa6124 100644
--- a/cmake.config/config.h.in
+++ b/cmake.config/config.h.in
@@ -54,10 +54,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..592c60b756
--- /dev/null
+++ b/cmake.config/iwyu/mapping.imp
@@ -0,0 +1,236 @@
+[
+ # 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 ] },
+
+ # 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.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt
index 27659b2a56..5f0f9378e6 100644
--- a/cmake.deps/CMakeLists.txt
+++ b/cmake.deps/CMakeLists.txt
@@ -2,22 +2,22 @@
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)
+
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)
-endif()
+set_default_buildtype()
set(DEFAULT_MAKE_CFLAGS CFLAGS+=-g)
@@ -105,11 +105,7 @@ 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()
+set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}")
if(CMAKE_CXX_COMPILER)
set(DEPS_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
@@ -147,23 +143,18 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
message("-- 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/a04480e311f93d3ceb2f92549cad3fffa38250ef.tar.gz)
+set(LUAJIT_SHA256 297e9c06d934753f9553fa7d1c1e43381e20094ed3289e0e145dd5f3c203a27e)
set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz)
set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333)
@@ -177,25 +168,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.tar.gz)
+set(LIBVTERM_SHA256 61eb0d6628c52bdf02900dfd4468aa86a1a7125228bab8a67328981887483358)
-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 +194,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_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_URL https://github.com/tree-sitter/tree-sitter/archive/1f1b1eb4501ed0a2d195d37f7de15f72aa10acd0.tar.gz)
-set(TREESITTER_SHA256 a324bdb7ff507cd5157a36b908224d60ba638f8e3363070dd51aa383c7cbd790)
+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/v0.20.7.tar.gz)
+set(TREESITTER_SHA256 b355e968ec2d0241bbd96748e00a9038f83968f85d822ecb9940cbe4c42e182e)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)
@@ -276,15 +274,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..4ba1f46d2e 100644
--- a/cmake.deps/cmake/BuildGettext.cmake
+++ b/cmake.deps/cmake/BuildGettext.cmake
@@ -1,30 +1,22 @@
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
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ ${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})
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..382aae3df7 100644
--- a/cmake.deps/cmake/BuildLibiconv.cmake
+++ b/cmake.deps/cmake/BuildLibiconv.cmake
@@ -1,28 +1,20 @@
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
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ ${BUILD_TYPE_STRING}
+ -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM})
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..1e0fb5d36a 100644
--- a/cmake.deps/cmake/BuildLibtermkey.cmake
+++ b/cmake.deps/cmake/BuildLibtermkey.cmake
@@ -1,57 +1,37 @@
if(WIN32)
-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 ${CMAKE_COMMAND} -E copy
- ${CMAKE_CURRENT_SOURCE_DIR}/cmake/libtermkeyCMakeLists.txt
+ set(LIBTERMKEY_CONFIGURE_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})
+ -DUNIBILIUM_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}unibilium${CMAKE_STATIC_LIBRARY_SUFFIX})
+ set(LIBTERMKEY_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>)
+ set(LIBTERMKEY_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config $<CONFIG>)
else()
-find_package(PkgConfig REQUIRED)
+ find_package(PkgConfig REQUIRED)
+ set(LIBTERMKEY_BUILD_COMMAND "" BUILD_IN_SOURCE 1)
+ set(LIBTERMKEY_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()
+
+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 ""
- 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()
+ CONFIGURE_COMMAND "${LIBTERMKEY_CONFIGURE_COMMAND}"
+ BUILD_COMMAND "${LIBTERMKEY_BUILD_COMMAND}"
+ INSTALL_COMMAND "${LIBTERMKEY_INSTALL_COMMAND}")
list(APPEND THIRD_PARTY_DEPS libtermkey)
diff --git a/cmake.deps/cmake/BuildLibuv.cmake b/cmake.deps/cmake/BuildLibuv.cmake
index aee3b9a43f..c21e166d95 100644
--- a/cmake.deps/cmake/BuildLibuv.cmake
+++ b/cmake.deps/cmake/BuildLibuv.cmake
@@ -1,26 +1,19 @@
+if(USE_EXISTING_SRC_DIR)
+ unset(LIBUV_URL)
+endif()
ExternalProject_Add(libuv
- PREFIX ${DEPS_BUILD_DIR}
URL ${LIBUV_URL}
+ URL_HASH SHA256=${LIBUV_SHA256}
+ DOWNLOAD_NO_PROGRESS TRUE
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
-DCMAKE_INSTALL_LIBDIR=lib
-DBUILD_TESTING=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DLIBUV_BUILD_SHARED=OFF
+ ${BUILD_TYPE_STRING}
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)
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libuv)
list(APPEND THIRD_PARTY_DEPS libuv)
diff --git a/cmake.deps/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake
index a905733abc..dffa545638 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
@@ -49,14 +10,11 @@ if(WIN32)
-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")
+ if(NOT MSVC)
+ list(APPEND LIBVTERM_CONFIGURE_COMMAND "-DCMAKE_C_FLAGS:STRING=-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})
+ 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 +24,17 @@ 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}")
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..1a599a9ee2 100644
--- a/cmake.deps/cmake/BuildLuv.cmake
+++ b/cmake.deps/cmake/BuildLuv.cmake
@@ -1,57 +1,3 @@
-# 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")
@@ -94,8 +40,7 @@ if(MSVC)
${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}"
+ "-DCMAKE_C_FLAGS:STRING=${LUV_INCLUDE_FLAGS}"
# Make sure we use the same generator, otherwise we may
# accidentally end up using different MSVC runtimes
-DCMAKE_GENERATOR=${CMAKE_GENERATOR})
@@ -103,22 +48,39 @@ 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")
+ "-DCMAKE_C_FLAGS:STRING=${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()
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
+ CONFIGURE_COMMAND "${LUV_CONFIGURE_COMMAND}"
+ BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>
+ INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config $<CONFIG>
+ LIST_SEPARATOR |)
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..3197ec45a1 100644
--- a/cmake.deps/cmake/BuildMsgpack.cmake
+++ b/cmake.deps/cmake/BuildMsgpack.cmake
@@ -1,65 +1,34 @@
-# 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
+set(MSGPACK_CMAKE_ARGS
-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_C_FLAGS:STRING=-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
+ set(MSGPACK_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})
+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 "${MSGPACK_CMAKE_ARGS}"
+ LIST_SEPARATOR |)
list(APPEND THIRD_PARTY_DEPS msgpack)
diff --git a/cmake.deps/cmake/BuildTreesitter.cmake b/cmake.deps/cmake/BuildTreesitter.cmake
index 6d98f6179c..c3ea02014f 100644
--- a/cmake.deps/cmake/BuildTreesitter.cmake
+++ b/cmake.deps/cmake/BuildTreesitter.cmake
@@ -1,42 +1,5 @@
-# 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
+ set(TREESITTER_CONFIGURE_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
@@ -44,16 +7,28 @@ if(MSVC)
-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}
- )
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR})
+ set(TREESITTER_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>)
+ set(TREESITTER_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config $<CONFIG>)
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)
+ set(TREESITTER_BUILD_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} CFLAGS=${TS_CFLAGS})
+ set(TREESITTER_INSTALL_COMMAND
+ ${MAKE_PRG} CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} install)
+endif()
+
+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}
+ BUILD_IN_SOURCE 1
+ CONFIGURE_COMMAND "${TREESITTER_CONFIGURE_COMMAND}"
+ BUILD_COMMAND "${TREESITTER_BUILD_COMMAND}"
+ INSTALL_COMMAND "${TREESITTER_INSTALL_COMMAND}")
list(APPEND THIRD_PARTY_DEPS tree-sitter)
diff --git a/cmake.deps/cmake/BuildTreesitterParsers.cmake b/cmake.deps/cmake/BuildTreesitterParsers.cmake
index 1ff86e89b9..ead039aae6 100644
--- a/cmake.deps/cmake/BuildTreesitterParsers.cmake
+++ b/cmake.deps/cmake/BuildTreesitterParsers.cmake
@@ -1,29 +1,24 @@
-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}
+ CMAKE_CACHE_ARGS
+ -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES}
+ PATCH_COMMAND ${CMAKE_COMMAND} -E copy
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${TS_CMAKE_FILE}
+ ${DEPS_BUILD_DIR}/src/${NAME}/CMakeLists.txt
+ CMAKE_ARGS
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ -DPARSERLANG=${LANG})
+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..cc56499edb 100644
--- a/cmake.deps/cmake/BuildUnibilium.cmake
+++ b/cmake.deps/cmake/BuildUnibilium.cmake
@@ -1,48 +1,31 @@
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
+ set(UNIBILIUM_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})
+ COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/unibilium
+ -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR}
+ -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
+ ${BUILD_TYPE_STRING}
+ -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
+ set(UNIBILIUM_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>)
+ set(UNIBILIUM_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config $<CONFIG>)
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)
+ set(UNIBILIUM_BUILD_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER}
+ PREFIX=${DEPS_INSTALL_DIR} CFLAGS=-fPIC LDFLAGS+=-static
+ BUILD_IN_SOURCE 1)
+ set(UNIBILIUM_INSTALL_COMMAND ${MAKE_PRG} PREFIX=${DEPS_INSTALL_DIR} install)
endif()
+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
+ CONFIGURE_COMMAND "${UNIBILIUM_CONFIGURE_COMMAND}"
+ BUILD_COMMAND "${UNIBILIUM_BUILD_COMMAND}"
+ INSTALL_COMMAND "${UNIBILIUM_INSTALL_COMMAND}")
+
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..69372bd2b0 100644
--- a/cmake.deps/cmake/TreesitterCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterCMakeLists.txt
@@ -19,3 +19,5 @@ install(FILES
include(GNUInstallDirs)
install(TARGETS tree-sitter
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/TreesitterParserCMakeLists.txt b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
index 54bc35fb8a..44e4ef160f 100644
--- a/cmake.deps/cmake/TreesitterParserCMakeLists.txt
+++ b/cmake.deps/cmake/TreesitterParserCMakeLists.txt
@@ -1,10 +1,12 @@
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
@@ -17,3 +19,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
index 9112b416fa..0a5d8481a7 100644
--- a/cmake.deps/cmake/UnibiliumCMakeLists.txt
+++ b/cmake.deps/cmake/UnibiliumCMakeLists.txt
@@ -23,3 +23,5 @@ install(TARGETS unibilium
PUBLIC_HEADER
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+# vim: set ft=cmake:
diff --git a/cmake.deps/cmake/libtermkeyCMakeLists.txt b/cmake.deps/cmake/libtermkeyCMakeLists.txt
index 6c02b7549d..b419d38d7b 100644
--- a/cmake.deps/cmake/libtermkeyCMakeLists.txt
+++ b/cmake.deps/cmake/libtermkeyCMakeLists.txt
@@ -32,3 +32,5 @@ foreach(f ${TESTSOURCES})
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..8cea39e4de 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 AND NOT RES EQUAL 0)
+ 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,6 @@ 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}")
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..a86ced89d6 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,7 +73,15 @@ 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()
+ foreach(exclude_pattern ${ARG_EXCLUDE})
+ list(FILTER globfiles EXCLUDE REGEX ${exclude_pattern})
+ endforeach()
list(APPEND ARG_FILES ${globfiles})
endforeach()
@@ -143,3 +152,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/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 2fbce09b67..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: Tue 23 Aug 2022 16:50:34 MSK
+" 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') && !has('gui_running') ? (&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 9e96a4638c..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: Tue 23 Aug 2022 16:50:35 MSK
+" 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') && !has('gui_running') ? (&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 e21adf2795..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: Tue 23 Aug 2022 16:50:36 MSK
+" 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') && !has('gui_running') ? (&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 510c2ee263..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: Tue 23 Aug 2022 16:50:37 MSK
+" 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') && !has('gui_running') ? (&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 dc0327b5fe..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: Tue 23 Aug 2022 16:50:37 MSK
+" 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') && !has('gui_running') ? (&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 978444d79f..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: Tue 23 Aug 2022 16:50:38 MSK
+" 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') && !has('gui_running') ? (&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 b356260102..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: Tue 23 Aug 2022 16:50:38 MSK
+" 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') && !has('gui_running') ? (&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 c627d8c826..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: Tue 23 Aug 2022 16:50:39 MSK
+" 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') && !has('gui_running') ? (&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 cc4eff6cdf..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: Tue 23 Aug 2022 16:50:39 MSK
+" 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') && !has('gui_running') ? (&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 2f2d4d949b..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: Tue 23 Aug 2022 16:50:40 MSK
+" 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') && !has('gui_running') ? (&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 6ffecef422..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: Tue 23 Aug 2022 16:50:41 MSK
+" 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') && !has('gui_running') ? (&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 7b5defd3d0..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: Tue 23 Aug 2022 16:50:41 MSK
+" 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') && !has('gui_running') ? (&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 eaf6deb22b..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: Tue 23 Aug 2022 16:50:42 MSK
+" 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') && !has('gui_running') ? (&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 ad47c31e19..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: Tue 23 Aug 2022 16:50:42 MSK
+" 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') && !has('gui_running') ? (&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 9f265726cc..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: Tue 23 Aug 2022 16:50:43 MSK
+" 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') && !has('gui_running') ? (&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 f622a6f991..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: Tue 23 Aug 2022 16:50:45 MSK
+" 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') && !has('gui_running') ? (&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 331ca9487c..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: Tue 23 Aug 2022 16:50:46 MSK
+" 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') && !has('gui_running') ? (&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 ecdaca1e2a..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 23 Aug 2022 16:50:46 MSK
+" 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') && !has('gui_running') ? (&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 dbce11c921..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: Tue 23 Aug 2022 16:50:47 MSK
+" 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') && !has('gui_running') ? (&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 db500c52f2..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: Tue 23 Aug 2022 16:50:48 MSK
+" 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') && !has('gui_running') ? (&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 89baf84c86..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.
@@ -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 a2e15142e7..3dc21c0d73 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,6 +170,7 @@ 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}
getchangelist([{buf}]) List list of change list items
@@ -222,6 +222,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 +280,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 +318,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 +351,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}
@@ -464,10 +468,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}
@@ -528,6 +533,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
@@ -632,6 +639,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),
@@ -650,9 +658,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.
@@ -913,7 +922,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()
@@ -946,7 +956,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
@@ -1081,8 +1091,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:
@@ -1129,13 +1139,14 @@ 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
@@ -1163,8 +1174,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
@@ -1179,6 +1190,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
@@ -1189,16 +1202,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()
@@ -1276,7 +1288,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:
@@ -1431,47 +1443,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|).
@@ -1514,9 +1485,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.
@@ -1535,8 +1507,8 @@ cursor({list})
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}
+ 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|.
@@ -1625,9 +1597,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:
@@ -2079,18 +2051,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()
<
@@ -2316,7 +2297,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
@@ -2507,10 +2488,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}
@@ -2529,30 +2511,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)
@@ -2598,7 +2606,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:
@@ -2678,11 +2686,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.
@@ -2705,6 +2715,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
@@ -2943,12 +2958,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
@@ -2960,7 +2975,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
@@ -3005,7 +3020,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
@@ -3193,7 +3208,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
@@ -3211,7 +3227,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
@@ -3260,18 +3276,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()*
@@ -3384,7 +3400,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
@@ -3470,7 +3486,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
@@ -3595,6 +3611,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.
@@ -3774,15 +3803,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
@@ -4386,7 +4415,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.
@@ -4510,6 +4539,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
@@ -4918,7 +4957,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)
@@ -4970,7 +5009,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
@@ -5028,8 +5067,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: >
@@ -5150,7 +5187,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'].
@@ -5170,12 +5207,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
@@ -5595,12 +5632,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.
@@ -6020,9 +6064,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:
@@ -6052,6 +6106,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
@@ -6063,8 +6118,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.
@@ -6081,6 +6134,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()|.
@@ -6121,7 +6177,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
@@ -6175,7 +6233,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
@@ -6193,7 +6252,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
@@ -6624,7 +6684,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|: >
@@ -6780,18 +6840,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
@@ -6856,29 +6922,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.
- An error is given if the argument is invalid, also when a
- range overlaps with another.
+ 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 0x100 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. *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
@@ -6999,6 +7074,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.
@@ -7041,8 +7118,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
@@ -7177,7 +7254,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}
@@ -7222,6 +7299,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
@@ -7669,7 +7747,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|: >
@@ -7730,7 +7808,7 @@ stdpath({what}) *stdpath()* *E6100*
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")
@@ -7795,6 +7873,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
@@ -7809,12 +7902,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.
@@ -7909,7 +8004,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
@@ -8053,7 +8148,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.
@@ -8201,7 +8296,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"
@@ -8257,12 +8352,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()*
@@ -8806,6 +8901,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
@@ -8859,7 +8974,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)
@@ -8940,6 +9060,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)
@@ -8954,6 +9075,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)
@@ -9040,12 +9162,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
@@ -9237,6 +9359,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 f19671e713..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>
<
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..6feb5cbb49 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.
@@ -2284,6 +2299,11 @@ v:version Vim version number: major version times 100 plus minor
v:vim_did_enter 0 during startup, 1 just before |VimEnter|.
Read-only.
+ *v:wrap* *wrap-variable*
+v:wrap Boolean indicating whether 'statuscolumn' is being evaluated
+ for the wrapped part of a line.
+ Read-only.
+
*v:warningmsg* *warningmsg-variable*
v:warningmsg Last given warning message.
Modifiable (can be set).
@@ -2309,397 +2329,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: >
-
- :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
+functions. The function takes arguments, executes a sequence of Ex commands
+and can return a value.
-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 +2548,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
@@ -3080,6 +2713,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 +2802,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 +3194,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 +4138,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 c5411b5f16..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,13 +969,16 @@ 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 dictionary values 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:python_indent.open_paren = 'shiftwidth() * 2'
@@ -1142,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: >
@@ -1161,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 3393a1a1fd..46cfa60529 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?
@@ -129,7 +129,7 @@ FAQ *lsp-faq*
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
" (async = false is the default for format)
@@ -162,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)))
<
@@ -190,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
@@ -245,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: >
@@ -290,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, {
@@ -300,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, {
@@ -310,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.
@@ -324,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 = {
@@ -338,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(
@@ -356,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.
@@ -375,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,
@@ -402,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"
@@ -425,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')
@@ -435,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')
@@ -486,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)
@@ -504,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)
@@ -514,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
@@ -524,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
<
@@ -539,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
@@ -548,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
@@ -576,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: ~
@@ -595,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
@@ -666,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.
@@ -676,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))
@@ -696,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.
@@ -705,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
@@ -720,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
@@ -745,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}:
@@ -768,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|
@@ -778,52 +780,51 @@ 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|nil) client_id
@@ -836,36 +837,37 @@ start_client({config}) *vim.lsp.start_client()*
The following parameters describe fields in the {config} table.
Parameters: ~
- {cmd} (table|string|fun(dispatchers: table):table)
- command string or list treated like |jobstart|.
+ • {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
+ 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
+ • {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
+ • {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
+ • {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
+ • {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
@@ -873,44 +875,44 @@ start_client({config}) *vim.lsp.start_client()*
• 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
+ • {handlers} Map of language server method names to
|lsp-handler|
- {settings} Map with language server specific settings. These
+ • {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} (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
+ • {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
+ • {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
+ • {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
+ • {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.
+ `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,
+ • {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
+ • {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
@@ -923,19 +925,19 @@ start_client({config}) *vim.lsp.start_client()*
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
+ • {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
+ • {on_attach} Callback (client, bufnr) invoked when client
attaches to a buffer.
- {trace} "off" | "messages" | "verbose" | nil passed
+ • {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
+ • {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
@@ -945,11 +947,11 @@ start_client({config}) *vim.lsp.start_client()*
debounce occurs if nil
• exit_timeout (number|boolean, default false):
Milliseconds to wait for server to exit cleanly
- after sending the 'shutdown' request before
+ after sending the "shutdown" request before
sending kill -15. If set to false, nvim exits
- immediately after sending the 'shutdown'
+ immediately after sending the "shutdown"
request to the server.
- {root_dir} (string) Directory where the LSP server will base
+ • {root_dir} (string) Directory where the LSP server will base
its workspaceFolders, rootUri, and rootPath on
initialization.
@@ -961,19 +963,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'.
@@ -984,8 +985,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
@@ -994,8 +995,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}
@@ -1014,7 +1015,7 @@ 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.
@@ -1041,13 +1042,13 @@ completion({context}) *vim.lsp.buf.completion()*
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.
@@ -1056,7 +1057,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
@@ -1066,7 +1067,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
@@ -1075,22 +1076,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|
@@ -1098,7 +1099,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
@@ -1108,7 +1109,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
@@ -1122,12 +1123,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
@@ -1136,59 +1137,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
@@ -1199,14 +1152,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.
@@ -1214,41 +1167,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|
@@ -1264,9 +1191,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.
@@ -1288,7 +1215,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
@@ -1302,8 +1229,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|
@@ -1316,14 +1243,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, {
@@ -1345,25 +1272,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[]`)
@@ -1376,8 +1310,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()*
@@ -1387,27 +1322,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()|
@@ -1415,21 +1412,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()|
==============================================================================
@@ -1441,8 +1438,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: ~
@@ -1453,9 +1450,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
@@ -1465,37 +1462,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: ~
@@ -1510,9 +1507,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.
@@ -1525,14 +1522,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
@@ -1542,7 +1539,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
@@ -1554,26 +1551,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})
@@ -1584,8 +1581,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: ~
@@ -1595,8 +1592,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
@@ -1607,14 +1604,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
@@ -1625,7 +1623,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
@@ -1639,13 +1637,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: ~
@@ -1658,9 +1656,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`
@@ -1678,9 +1676,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`
@@ -1693,7 +1691,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`
@@ -1706,18 +1704,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
@@ -1745,7 +1743,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
@@ -1759,7 +1757,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
@@ -1768,7 +1766,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.
@@ -1776,14 +1774,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
@@ -1797,8 +1811,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
@@ -1815,7 +1829,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})
@@ -1823,10 +1837,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 }
@@ -1838,7 +1852,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
@@ -1851,10 +1865,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.
==============================================================================
@@ -1876,20 +1890,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
@@ -1903,8 +1917,8 @@ connect({host}, {port}) *vim.lsp.rpc.connect()*
and port
Parameters: ~
- {host} (string)
- {port} (number)
+ • {host} (string)
+ • {port} (number)
Return: ~
(function)
@@ -1913,7 +1927,7 @@ 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
@@ -1922,8 +1936,8 @@ notify({method}, {params}) *vim.lsp.rpc.notify()*
Sends a notification to the LSP server.
Parameters: ~
- {method} (string) The invoked LSP method
- {params} (table|nil): 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
@@ -1933,11 +1947,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|nil) Parameters for the invoked LSP
+ • {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
+ • {callback} (function) Callback to invoke
+ • {notify_reply_callback} (function|nil) Callback to invoke as soon as
a request is no longer pending
Return: ~
@@ -1949,28 +1963,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
@@ -1994,17 +2009,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
==============================================================================
@@ -2020,7 +2035,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..71dc48b715
--- /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.
+ An empty string is equivalent to |<Nop>|, which disables a key.
+• {rhs} is either a string with a Vim command or a Lua function that should
+ be executed when the {lhs} is entered.
+
+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 499b3f9a6f..16d0bcb612 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...]
<
-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.
+ *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" }
+<
+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
@@ -48,8 +137,7 @@ documentation at https://www.lua.org/manual/5.1/manual.html#pdf-require.
For example, if 'runtimepath' is `foo,bar` and |package.cpath| was
`./?.so;./?.dll` at startup, `require('mod')` searches these paths in order
-and loads the first module found:
-
+and loads the first module found ("first wins"): >
foo/lua/mod.lua
foo/lua/mod/init.lua
bar/lua/mod.lua
@@ -58,10 +146,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 +159,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 +198,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 +207,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 +224,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 +239,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 +261,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 +275,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 +286,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 +300,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 +336,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 +353,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 +387,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 +404,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 +424,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 +436,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 +445,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 +464,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 +485,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 +508,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 +545,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 +557,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 +584,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 +602,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 +640,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 +670,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 +713,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 +739,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 +764,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*
@@ -890,9 +836,9 @@ vim.iconv({str}, {from}, {to}[, {opts}]) *vim.iconv()*
can accept, see ":Man 3 iconv".
Parameters: ~
- {str} (string) Text to convert
- {from} (string) Encoding of {str}
- {to} (string) Target encoding
+ • {str} (string) Text to convert
+ • {from} (string) Encoding of {str}
+ • {to} (string) Target encoding
Returns: ~
Converted string if conversion succeeds, `nil` otherwise.
@@ -906,12 +852,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
@@ -924,10 +870,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`.
@@ -943,7 +889,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
@@ -967,6 +913,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
@@ -975,7 +958,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,
@@ -1016,12 +999,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})
@@ -1029,7 +1013,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
@@ -1052,13 +1036,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`.
@@ -1086,7 +1084,7 @@ 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)
<
@@ -1102,76 +1100,65 @@ Vim options can be accessed through |vim.o|, which behaves like Vimscript
Examples: ~
To set a boolean toggle:
- In Vimscript:
- `set number`
-
- In Lua:
- `vim.o.number = true`
+ Vimscript: `set number`
+ Lua: `vim.o.number = true`
To set a string value:
- In Vimscript:
- `set wildignore=*.o,*.a,__pycache__`
-
- In Lua:
- `vim.o.wildignore = '*.o,*.a,__pycache__'`
-
-Similarly, there exist |vim.bo| and |vim.wo| for setting buffer-local and
-window-local options, respectively, similarly to |:setlocal|. There is also
-|vim.go| that only sets the global value of a |global-local| option, see
-|:setglobal|. The following table summarizes this relation.
-
- lua command global_value local_value ~
-vim.o :set set set
-vim.bo/vim.wo :setlocal - set
-vim.go :setglobal set -
+ Vimscript: `set wildignore=*.o,*.a,__pycache__`
+ Lua: `vim.o.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|.
vim.o *vim.o*
- Get or set editor options, like |:set|. Invalid key is an error.
+ Get or set |options|. Like `:set`. Invalid key is an error.
+
+ Note: this works on both buffer-scoped and window-scoped options using the
+ current buffer and window.
- Example: >
+ Example: >lua
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()|.
+ Get or set global |options|. Like `:setglobal`. Invalid key is
+ an error.
- 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.
+ 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.
- Example: >
+ Example: >lua
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.
+ 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.
- This is a wrapper around |nvim_set_option_value()| and
- |nvim_get_option_value()| with `opts = {scope = local, buf = bufnr}` .
+ Note: this is equivalent to both `:set` and `:setlocal`.
- Example: >
+ 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
<
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.
+ 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.
- This is a wrapper around |nvim_set_option_value()| and
- |nvim_get_option_value()| with `opts = {scope = local, win = winid}` .
-
- Example: >
+ 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)
@@ -1180,9 +1167,8 @@ vim.wo[{winid}] *
- *lua-vim-opt*
- *lua-vim-optlocal*
- *lua-vim-optglobal*
+ *vim.opt_local*
+ *vim.opt_global*
*vim.opt*
@@ -1193,56 +1179,56 @@ offers object-oriented method for adding and removing entries.
Examples: ~
The following methods of setting a list-style option are equivalent:
- In Vimscript:
- `set wildignore=*.o,*.a,__pycache__`
-
- In Lua using `vim.o`:
- `vim.o.wildignore = '*.o,*.a,__pycache__'`
-
- In Lua using `vim.opt`:
- `vim.opt.wildignore = { '*.o', '*.a', '__pycache__' }`
-
- To replicate the behavior of |:set+=|, use: >
+ 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 replicate the behavior of |:set^=|, use: >
+ To replicate the behavior of |:set^=|, use: >lua
vim.opt.wildignore:prepend { "new_first_value" }
<
- To replicate the behavior of |:set-=|, use: >
+ 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:
- `set listchars=space:_,tab:>~`
-
- In Lua using `vim.o`:
- `vim.o.listchars = 'space:_,tab:>~'`
-
- In Lua using `vim.opt`:
- `vim.opt.listchars = { space = '_', tab = '>~' }`
-
+ 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 = '>~' }
+<
Note that |vim.opt| returns an `Option` object, not the value of the option,
-which is accessed through |Option:get()|:
+which is accessed through |vim.opt:get()|:
Examples: ~
The following methods of getting a list-style option are equivalent:
- In Vimscript:
- `echo wildignore`
-
- In Lua using `vim.o`:
- `print(vim.o.wildignore)`
-
- In Lua using `vim.opt`:
- `vim.pretty_print(vim.opt.wildignore:get())`
-
+ 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
+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`.
@@ -1254,7 +1240,7 @@ 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]]
vim.pretty_print(vim.opt.wildignore:get())
@@ -1267,18 +1253,18 @@ 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:>~]]
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]]
vim.pretty_print(vim.opt.formatoptions:get())
@@ -1294,28 +1280,28 @@ 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'`
-
+ These are equivalent: >lua
+ vim.opt.wildignore:remove('*.pyc')
+ vim.opt.wildignore = vim.opt.wildignore - '*.pyc'
+<
==============================================================================
Lua module: vim *lua-vim*
@@ -1326,7 +1312,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([[
@@ -1355,7 +1341,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
@@ -1372,31 +1358,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
@@ -1410,9 +1396,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.
@@ -1421,9 +1407,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
@@ -1442,11 +1428,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: ~
@@ -1460,7 +1446,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)
@@ -1474,27 +1460,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()|
@@ -1504,25 +1491,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()*
@@ -1531,8 +1575,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`
@@ -1545,17 +1589,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`
@@ -1564,9 +1631,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: ~
@@ -1581,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`
@@ -1592,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
@@ -1608,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
@@ -1630,25 +1697,25 @@ pesc({s}) *vim.pesc()*
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()|
@@ -1657,8 +1724,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`
@@ -1670,7 +1737,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
@@ -1679,22 +1746,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
@@ -1706,29 +1774,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
@@ -1740,8 +1808,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
@@ -1751,7 +1819,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
@@ -1763,15 +1831,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: ~
@@ -1781,7 +1849,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
@@ -1797,7 +1865,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`
@@ -1807,10 +1875,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
@@ -1819,8 +1887,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
@@ -1830,16 +1898,16 @@ 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
@@ -1850,7 +1918,7 @@ trim({s}) *vim.trim()*
validate({opt}) *vim.validate()*
Validates a parameter specification (types and values).
- Usage example: >
+ Usage example: >lua
function user.new(name, age, hobbies)
vim.validate{
@@ -1862,29 +1930,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
@@ -1909,7 +1977,7 @@ uri_from_bufnr({bufnr}) *vim.uri_from_bufnr()*
Get a URI from a bufnr
Parameters: ~
- {bufnr} (number)
+ • {bufnr} (number)
Return: ~
(string) URI
@@ -1918,7 +1986,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
@@ -1928,7 +1996,7 @@ uri_to_bufnr({uri}) *vim.uri_to_bufnr()*
the uri already exists.
Parameters: ~
- {uri} (string)
+ • {uri} (string)
Return: ~
(number) bufnr
@@ -1937,7 +2005,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
@@ -1949,7 +2017,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)
@@ -1957,7 +2025,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
@@ -1966,14 +2034,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:',
@@ -1990,8 +2059,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
@@ -2001,7 +2070,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.
@@ -2029,14 +2098,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 = {
@@ -2059,6 +2128,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'
@@ -2070,7 +2141,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 = {
@@ -2090,7 +2161,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()*
@@ -2109,23 +2180,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}
@@ -2151,7 +2223,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')
@@ -2159,7 +2231,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.
@@ -2167,7 +2239,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)
@@ -2186,25 +2258,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`.
@@ -2230,18 +2302,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
@@ -2252,7 +2329,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}
@@ -2266,15 +2343,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).
@@ -2282,13 +2362,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
@@ -2296,20 +2377,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
@@ -2317,7 +2398,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
@@ -2333,9 +2414,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..ccd48a8959 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
@@ -1437,7 +1471,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 +1482,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 +1659,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 +1696,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 +1707,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 +1721,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 +1738,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 +1747,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..ce07c3035c
--- /dev/null
+++ b/runtime/doc/news.txt
@@ -0,0 +1,180 @@
+*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`
+
+==============================================================================
+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.
+
+==============================================================================
+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.
+
+==============================================================================
+REMOVED FEATURES *news-removed*
+
+The following deprecated functions or APIs were removed.
+
+• `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 9e396dd3e8..4498dda300 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
@@ -2478,6 +2501,7 @@ A jump table for the options with a short description can be found at |Q_op|.
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 '='
@@ -2515,16 +2539,18 @@ A jump table for the options with a short description can be found at |Q_op|.
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 "")
@@ -2712,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.
@@ -3325,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.
@@ -3382,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).
@@ -3749,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|
@@ -3913,9 +3969,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'*
@@ -4067,7 +4123,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
@@ -4231,6 +4288,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
@@ -4291,7 +4357,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 +
@@ -4368,12 +4434,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
@@ -4394,7 +4460,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
@@ -4557,7 +4625,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: >
@@ -4613,58 +4681,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
@@ -4922,8 +4938,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|
@@ -4932,10 +4947,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|
@@ -4956,20 +4973,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
@@ -4979,18 +4996,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)
@@ -5086,19 +5098,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
@@ -5343,7 +5342,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=
@@ -5511,42 +5510,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
@@ -5583,7 +5588,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"
@@ -5591,6 +5595,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)
@@ -5694,9 +5715,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
@@ -5724,11 +5745,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
@@ -5862,10 +5883,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")
@@ -5941,6 +5966,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
@@ -5960,6 +6001,52 @@ 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
+
+ To draw the sign and fold columns, they must be included in
+ 'statuscolumn'.
+
+ 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:wrap| variable holds true for the wrapped part of a line.
+
+ 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:wrap?"":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|
@@ -5984,6 +6071,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|.
@@ -5991,12 +6080,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.
@@ -6032,7 +6121,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).
@@ -6042,24 +6130,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.
@@ -6254,12 +6343,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: >
@@ -6312,7 +6401,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:
@@ -6405,7 +6494,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)
@@ -6467,7 +6558,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'*
@@ -6528,6 +6619,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.
@@ -6734,11 +6827,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.
@@ -6759,28 +6852,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)
@@ -7122,7 +7218,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 6fcf292513..b4afc3f233 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -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|
@@ -4935,7 +4980,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 +5001,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 +5042,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 +5061,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 +5109,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 +5144,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 +5168,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 +5219,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 +5232,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 +5333,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 +5442,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 +5492,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 +5502,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 2c6c9e4ed8..096cec6678 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:
+https://tree-sitter.github.io/tree-sitter/
-Nvim integrates the tree-sitter library for incremental parsing of buffers.
-
- *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.
+
+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|.
-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.
+See |lua-treesitter-query| for the list of available methods for working with
+treesitter queries from Lua.
-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.
-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 *treesitter-predicates*
-Treesitter Query Predicates *lua-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,191 +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.
+
+
+TREESITTER QUERY MODELINES *treesitter-query-modeline*
-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.
+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:
-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: >
+ `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`.
- local query = [[
- "for" @keyword
- "if" @keyword
- "return" @keyword
+ `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.
- (string_literal) @string
- (number_literal) @number
- (comment) @comment
+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: >
- (preproc_function_def name: (identifier) @function)
+ ;; inherits: foo,bar
+ ;; extends
- ; ... more definitions
- ]]
+ ;; extends
+ ;;
+ ;; inherits: baz
+<
+==============================================================================
+TREESITTER SYNTAX HIGHLIGHTING *treesitter-highlight*
- highlighter = vim.treesitter.TSHighlighter.new(query, bufnr, lang)
- -- alternatively, to use the current buffer and its filetype:
- -- highlighter = vim.treesitter.TSHighlighter.new(query)
+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 >
- -- Don't recreate the highlighter for the same buffer, instead
- -- modify the query like this:
- local query2 = [[ ... ]]
- highlighter:set_query(query2)
+ (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): >
+ "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()|.
- *lua-treesitter-highlight-groups*
+ *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.
+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, captures highlights can always be specialized by
+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: >
+instance, to highlight comments differently per language: >vim
hi @comment.c guifg=Blue
- hi @comment.lua @guifg=DarkBlue
+ hi @comment.lua guifg=DarkBlue
hi link @comment.doc.java String
<
-It is possible to use custom highlight groups. As an example, if we
-define the `@warning` group: >
+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: >
+
+ (comment) @spell
+<
+
+There is also `@nospell` which disables spellchecking regions with `@spell`.
- hi link @warning WarningMsg
+ *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: >
+
+ (fenced_code_block_delimiter) @conceal (#set! conceal "")
<
-the following query warns of a binary expression with two
-identical identifiers, highlighting both as |hl-WarningMsg|: >
+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) @warning.left right: (identifier) @warning.right)
- (eq? @warning.left @warning.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_captures_at_position()*
-get_captures_at_position({bufnr}, {row}, {col})
- Gets a list of captures for a given cursor position
+ *vim.treesitter.get_captures_at_cursor()*
+get_captures_at_cursor({winnr})
+ Returns a list of highlight capture names under the cursor
Parameters: ~
- {bufnr} (number) The buffer number
- {row} (number) The position row
- {col} (number) The position column
+ • {winnr} (number|nil) Window handle or 0 for current window (default)
Return: ~
- (table) A table of captures
+ string[] List of capture names
-get_node_range({node_or_range}) *get_node_range()*
- Get the node's range or unpack a range table
+ *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: ~
- {node_or_range} (table)
+ • {bufnr} (number) Buffer number (0 for current buffer)
+ • {row} (number) Position row
+ • {col} (number) Position column
Return: ~
- start_row, start_col, end_row, end_col
+ 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
-get_parser({bufnr}, {lang}, {opts}) *get_parser()*
- Gets the parser for this bufnr / ft combination.
+ Parameters: ~
+ • {winnr} (number|nil) Window handle or 0 for current window (default)
+
+ Return: ~
+ (string) Name of node under the cursor
- If needed this will create the parser. Unconditionally attach the provided
- callback
+ *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} The buffer the parser should be tied to
- {lang} The filetype of this parser
- {opts} Options object to pass to the created language tree
+ • {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: ~
- The parser
+ userdata|nil |tsnode| under the cursor
-get_string_parser({str}, {lang}, {opts}) *get_string_parser()*
- Gets a string parser
+get_node_range({node_or_range}) *vim.treesitter.get_node_range()*
+ Returns the node's range or an unpacked range table
Parameters: ~
- {str} The string to parse
- {lang} The language of this string
- {opts} Options to pass to the created language tree
+ • {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
-is_ancestor({dest}, {source}) *is_ancestor()*
+ 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
+
+ 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} (table) the possible ancestor
- {source} (table) the possible descendant node
+ • {dest} userdata Possible ancestor |tsnode|
+ • {source} userdata Possible descendant |tsnode|
Return: ~
- (boolean) True if dest is an ancestor of source
+ (boolean) True if {dest} is an ancestor of {source}
-is_in_node_range({node}, {line}, {col}) *is_in_node_range()*
+ *vim.treesitter.is_in_node_range()*
+is_in_node_range({node}, {line}, {col})
Determines whether (line, col) position is in node range
Parameters: ~
- {node} Node defining the range
- {line} A line (0-based)
- {col} A column (0-based)
+ • {node} userdata |tsnode| defining the range
+ • {line} (number) Line (0-based)
+ • {col} (number) Column (0-based)
-node_contains({node}, {range}) *node_contains()*
+ Return: ~
+ (boolean) True if the position is in node range
+
+node_contains({node}, {range}) *vim.treesitter.node_contains()*
Determines if a node contains a range
Parameters: ~
- {node} (table) The node
- {range} (table) The range
+ • {node} userdata |tsnode|
+ • {range} (table)
Return: ~
- (boolean) True if the node contains the range
+ (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
- *require_language()*
+ Return: ~
+ (table)
+
+ *vim.treesitter.language.require_language()*
require_language({lang}, {path}, {silent}, {symbol_name})
- 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}
Parameters: ~
- {lang} (string) The 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
+ • {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
+ • {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
@@ -482,65 +707,76 @@ 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)
+ • {name} (string) Name of the directive, without leading #
+ • {handler} function(match:string, pattern:string, bufnr:number,
+ predicate:function, metadata:table)
-add_predicate({name}, {handler}, {force}) *add_predicate()*
+ *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)
+ • {name} (string) Name of the predicate, without leading #
+ • {handler} function(match:string, pattern:string, bufnr:number,
+ predicate:function)
-get_node_text({node}, {source}, {opts}) *get_node_text()*
+ *vim.treesitter.query.get_node_text()*
+get_node_text({node}, {source}, {opts})
Gets the text corresponding to a given node
Parameters: ~
- {node} (table) The node
- {source} (table) The buffer or string from which the node is
+ • {node} userdata |tsnode|
+ • {source} (number|string) Buffer or string from which the {node} is
extracted
- {opts} (table) Optional parameters.
- • concat: (boolean default true) Concatenate result in a
- string
+ • {opts} (table|nil) Optional parameters.
+ • concat: (boolean) Concatenate result in a string (default
+ true)
-get_query({lang}, {query_name}) *get_query()*
+ Return: ~
+ (string[]|string)
+
+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`
+
+ Return: ~
+ string[] query_files List of files to load for given query and
+ language
-list_directives() *list_directives()*
+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}.
@@ -548,219 +784,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}
+ Gets the smallest named node that contains {range}.
Parameters: ~
- {range} (table) A text range
- {opts} (table) Options table
- {opts.ignore_injections} (boolean) (default true) Ignore injected
- languages.
- {self}
+ • {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
@@ -768,14 +999,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
@@ -785,72 +1020,50 @@ LanguageTree:register_cbs({self}, {cbs}) *LanguageTree:register_cbs()*
tree.
• `on_child_removed` : emitted when a child is removed from
the tree.
- {self}
-
-LanguageTree:remove_child({self}, {lang}) *LanguageTree:remove_child()*
- Removes a child language from this tree.
-
- 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.
-
- `{ { 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.
-
- Parameters: ~
- {regions} (table) list of regions this tree should manage and parse.
- {self}
+ • {self}
LanguageTree:source({self}) *LanguageTree:source()*
Returns the source content of the language tree (bufnr or string).
Parameters: ~
- {self}
+ • {self}
*LanguageTree:tree_for_range()*
LanguageTree:tree_for_range({self}, {range}, {opts})
- Gets the tree that contains {range}
+ Gets the tree that contains {range}.
Parameters: ~
- {range} (table) A text range
- {opts} (table) Options table
- {opts.ignore_injections} (boolean) (default true) Ignore injected
- languages.
- {self}
+ • {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 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..a2ae9f22ce 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.
@@ -345,7 +351,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 +360,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 +379,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 +444,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 +487,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 +581,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 +593,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 +644,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 +656,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 +669,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 +685,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 +708,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 +719,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 +732,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 +752,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 +786,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 76c2f8454f..226bd029a3 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,7 +619,8 @@ 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
@@ -637,6 +640,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 +749,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 +785,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 +826,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 +845,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 +859,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
@@ -1013,6 +1024,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
@@ -1047,7 +1059,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
@@ -1070,8 +1081,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
@@ -1707,7 +1718,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
@@ -1758,7 +1769,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
@@ -2620,7 +2631,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 0011cd9821..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'.
@@ -416,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
@@ -463,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.
@@ -478,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|
@@ -495,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.
@@ -525,6 +549,7 @@ Aliases:
Commands:
:fixdel
+ :hardcopy
:helpfind
:mode (no longer accepts an argument)
:open
@@ -536,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
@@ -543,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.
@@ -556,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'
@@ -565,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.)
@@ -585,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>
@@ -617,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'*
@@ -636,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
@@ -686,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 2627068a14..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.
@@ -235,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,
@@ -277,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.
@@ -345,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
@@ -530,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).
@@ -597,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*
@@ -636,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
@@ -740,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|.
@@ -767,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 98d9df8b5c..0000000000
--- a/runtime/filetype.vim
+++ /dev/null
@@ -1,2620 +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
-
-" Godot shader
-au BufRead,BufNewFile *.gdshader,*.shader setf gdshader
-
-" 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
-
-" VDM
-au BufRead,BufNewFile *.vdmpp,*.vpp setf vdmpp
-au BufRead,BufNewFile *.vdmrt setf vdmrt
-au BufRead,BufNewFile *.vdmsl,*.vdm setf vdmsl
-
-" 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/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..a644dd68b8 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,545 @@ 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
+
+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
-return { highlight_man_page = highlight_man_page }
+-- 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()
+ 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..c00b921d5c
--- /dev/null
+++ b/runtime/lua/nvim/health.lua
@@ -0,0 +1,387 @@
+local M = {}
+local health = require('vim.health')
+
+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 empty = function(o)
+ return 0 ~= vim.fn.empty(o)
+ end
+ local filereadable = function(o)
+ return 0 ~= vim.fn.filereadable(o)
+ end
+ local filewritable = function(o)
+ return 0 ~= vim.fn.filewritable(o)
+ end
+
+ 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 1 == vim.fn.exists('$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()
+ vim.api.nvim_exec([=[
+ func! s:check_performance() abort
+ let s:suggest_faq = ']=] .. suggest_faq .. [=['
+
+ call health#report_start('Performance')
+
+ " check buildtype
+ let s:buildtype = matchstr(execute('version'), '\v\cbuild type:?\s*[^\n\r\t ]+')
+ if empty(s:buildtype)
+ call health#report_error('failed to get build type from :version')
+ elseif s:buildtype =~# '\v(MinSizeRel|Release|RelWithDebInfo)'
+ call health#report_ok(s:buildtype)
+ else
+ call health#report_info(s: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 s:slow_cmd_time = 1.5
+ let s:start_time = reltime()
+ call system('echo')
+ let s:elapsed_time = reltimefloat(reltime(s:start_time))
+ if s:elapsed_time > s:slow_cmd_time
+ call health#report_warn(
+ \ 'Slow shell invocation (took '.printf('%.2f', s:elapsed_time).' seconds).')
+ endif
+ endf
+
+ call s:check_performance()
+ ]=], false)
+end
+
+-- Load the remote plugin manifest file and check for unregistered plugins
+local function check_rplugin_manifest()
+ vim.api.nvim_exec(
+ [=[
+ func! 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
+ endf
+
+ call s:check_rplugin_manifest()
+ ]=],
+ false
+ )
+end
+
+local function check_tmux()
+ vim.api.nvim_exec([=[
+ let s:suggest_faq = ']=] .. suggest_faq .. [=['
+
+ func! 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
+ endf
+
+ func! 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')
+ 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', 'show-messages', '-JT'])
+ 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
+ endf
+
+ call s:check_tmux()
+ ]=], false)
+end
+
+local function check_terminal()
+ vim.api.nvim_exec(
+ [=[
+ func! 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(printf('key_backspace (kbs) terminfo entry: `%s`', (empty(kbs_entry) ? '? (not found)' : kbs_entry)))
+ call health#report_info(printf('key_dc (kdch1) terminfo entry: `%s`', (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
+ endf
+
+ call s:check_terminal()
+ ]=],
+ false
+ )
+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 db92085423..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,
@@ -47,11 +48,11 @@ local bufnr_and_namespace_cacher_mt = {
local diagnostic_cache
do
- local group = vim.api.nvim_create_augroup('DiagnosticBufDelete', {})
+ local group = api.nvim_create_augroup('DiagnosticBufWipeout', {})
diagnostic_cache = setmetatable({}, {
__index = function(t, bufnr)
assert(bufnr > 0, 'Invalid buffer number')
- vim.api.nvim_create_autocmd('BufDelete', {
+ api.nvim_create_autocmd('BufWipeout', {
group = group,
buffer = bufnr,
callback = function()
@@ -245,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 {}
@@ -299,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
@@ -312,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
@@ -322,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,
@@ -333,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 = {}
@@ -357,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' }
@@ -379,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()
@@ -397,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
@@ -412,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,
})
@@ -420,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
@@ -441,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
@@ -484,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
@@ -496,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
@@ -505,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)
@@ -519,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)
@@ -555,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)
@@ -574,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,
}))
@@ -591,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>
---
@@ -626,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
@@ -679,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
@@ -720,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
@@ -738,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
@@ -763,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
@@ -771,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 },
@@ -783,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
@@ -809,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
@@ -915,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,
@@ -943,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
@@ -970,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,
}
@@ -1009,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
@@ -1021,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,
})
@@ -1033,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,
}
@@ -1051,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
@@ -1064,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],
})
@@ -1105,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
@@ -1148,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
@@ -1165,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
@@ -1232,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
@@ -1263,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
@@ -1285,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
@@ -1317,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'",
},
})
@@ -1327,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
@@ -1353,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'",
},
})
@@ -1366,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
@@ -1386,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
@@ -1422,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
@@ -1528,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.
@@ -1544,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' },
@@ -1591,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 = {
@@ -1616,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
@@ -1628,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 fcd697a7c1..b73519f1be 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',
@@ -201,6 +202,7 @@ local extension = {
return require('vim.filetype.detect').change(bufnr)
end,
chs = 'chaskell',
+ chatito = 'chatito',
chopro = 'chordpro',
crd = 'chordpro',
crdpro = 'chordpro',
@@ -255,6 +257,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 +327,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,6 +415,7 @@ local extension = {
fs = function(path, bufnr)
return require('vim.filetype.detect').fs(bufnr)
end,
+ fsh = 'fsh',
fsi = 'fsharp',
fsx = 'fsharp',
fusion = 'fusion',
@@ -446,6 +446,8 @@ local extension = {
gsp = 'gsp',
gjs = 'javascript.glimmer',
gts = 'typescript.glimmer',
+ gyp = 'gyp',
+ gypi = 'gyp',
hack = 'hack',
hackpartial = 'hack',
haml = 'haml',
@@ -471,6 +473,8 @@ local extension = {
hex = 'hex',
['h32'] = 'hex',
hjson = 'hjson',
+ m3u = 'hlsplaylist',
+ m3u8 = 'hlsplaylist',
hog = 'hog',
hws = 'hollywood',
hoon = 'hoon',
@@ -540,6 +544,7 @@ local extension = {
mjs = 'javascript',
javascript = 'javascript',
js = 'javascript',
+ jsm = 'javascript',
cjs = 'javascript',
jsx = 'javascriptreact',
clp = 'jess',
@@ -548,6 +553,7 @@ local extension = {
jov = 'jovial',
jovial = 'jovial',
properties = 'jproperties',
+ jq = 'jq',
slnf = 'json',
json = 'json',
jsonp = 'json',
@@ -556,6 +562,8 @@ local extension = {
['json-patch'] = 'json',
json5 = 'json5',
jsonc = 'jsonc',
+ jsonnet = 'jsonnet',
+ libsonnet = 'jsonnet',
jsp = 'jsp',
jl = 'julia',
kv = 'kivy',
@@ -602,11 +610,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,
@@ -646,6 +657,9 @@ local extension = {
dmt = 'maxima',
wxm = 'maxima',
mel = 'mel',
+ mmd = 'mermaid',
+ mmdc = 'mermaid',
+ mermaid = 'mermaid',
mf = 'mf',
mgl = 'mgl',
mgp = 'mgp',
@@ -664,6 +678,7 @@ local extension = {
DEF = 'modula2',
['m2'] = 'modula2',
mi = 'modula2',
+ lm3 = 'modula3',
ssc = 'monk',
monk = 'monk',
tsc = 'monk',
@@ -697,6 +712,9 @@ local extension = {
nanorc = 'nanorc',
ncf = 'ncf',
nginx = 'nginx',
+ nim = 'nim',
+ nims = 'nim',
+ nimble = 'nim',
ninja = 'ninja',
nix = 'nix',
nqc = 'nqc',
@@ -709,6 +727,10 @@ local extension = {
nsi = 'nsis',
nsh = 'nsis',
obj = 'obj',
+ obl = 'obse',
+ obse = 'obse',
+ oblivion = 'obse',
+ obscript = 'obse',
mlt = 'ocaml',
mly = 'ocaml',
mll = 'ocaml',
@@ -722,6 +744,7 @@ local extension = {
opam = 'opam',
['or'] = 'openroad',
scad = 'openscad',
+ ovpn = 'openvpn',
ora = 'ora',
org = 'org',
org_archive = 'org',
@@ -743,6 +766,7 @@ local extension = {
php = 'php',
phpt = 'php',
phtml = 'php',
+ theme = 'php',
pike = 'pike',
pmod = 'pike',
rcp = 'pilrc',
@@ -760,6 +784,7 @@ local extension = {
po = 'po',
pot = 'po',
pod = 'pod',
+ filter = 'poefilter',
pk = 'poke',
ps = 'postscr',
epsi = 'postscr',
@@ -902,7 +927,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',
@@ -918,12 +945,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',
@@ -952,6 +981,9 @@ local extension = {
srec = 'srec',
mot = 'srec',
['s19'] = 'srec',
+ srt = 'srt',
+ ssa = 'ssa',
+ ass = 'ssa',
st = 'st',
imata = 'stata',
['do'] = 'stata',
@@ -1004,6 +1036,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',
@@ -1013,6 +1047,7 @@ local extension = {
dsm = 'vb',
ctl = 'vb',
vbs = 'vb',
+ vdf = 'vdf',
vdmpp = 'vdmpp',
vpp = 'vdmpp',
vdmrt = 'vdmrt',
@@ -1030,6 +1065,7 @@ local extension = {
hdl = 'vhdl',
vho = 'vhdl',
vbe = 'vhdl',
+ tape = 'vhs',
vim = 'vim',
vba = 'vim',
mar = 'vmasm',
@@ -1039,6 +1075,7 @@ local extension = {
vue = 'vue',
wat = 'wast',
wast = 'wast',
+ wdl = 'wdl',
wm = 'webmacro',
wbt = 'winbatch',
wml = 'wml',
@@ -1085,6 +1122,7 @@ local extension = {
yang = 'yang',
['z8a'] = 'z8a',
zig = 'zig',
+ zir = 'zir',
zu = 'zimbu',
zut = 'zimbutempl',
zsh = 'zsh',
@@ -1283,9 +1321,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',
@@ -1339,13 +1377,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',
@@ -1378,6 +1416,8 @@ local filename = {
['EDIT_DESCRIPTION'] = 'gitcommit',
['.gitconfig'] = 'gitconfig',
['.gitmodules'] = 'gitconfig',
+ ['.gitattributes'] = 'gitattributes',
+ ['.gitignore'] = 'gitignore',
['gitolite.conf'] = 'gitolite',
['git-rebase-todo'] = 'gitrebase',
gkrellmrc = 'gkrellmrc',
@@ -1386,6 +1426,7 @@ local filename = {
gnashpluginrc = 'gnash',
gnashrc = 'gnash',
['.gnuplot'] = 'gnuplot',
+ ['go.sum'] = 'gosum',
['go.work'] = 'gowork',
['.gprc'] = 'gp',
['/.gnupg/gpg.conf'] = 'gpg',
@@ -1415,11 +1456,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',
@@ -1434,6 +1479,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',
@@ -1479,8 +1528,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',
@@ -1513,6 +1565,9 @@ local filename = {
['.pythonstartup'] = 'python',
['.pythonrc'] = 'python',
SConstruct = 'python',
+ ['.Rprofile'] = 'r',
+ ['Rprofile'] = 'r',
+ ['Rprofile.site'] = 'r',
ratpoisonrc = 'ratpoison',
['.ratpoisonrc'] = 'ratpoison',
inputrc = 'readline',
@@ -1639,6 +1694,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',
@@ -1691,6 +1748,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'),
@@ -1709,7 +1767,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',
@@ -1751,6 +1809,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',
@@ -1815,26 +1875,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',
@@ -1848,7 +1903,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',
@@ -1862,7 +1917,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'),
@@ -2016,6 +2073,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'),
@@ -2252,13 +2310,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',
@@ -2280,6 +2339,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'
@@ -2291,8 +2352,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 = {
--- ['.*'] = {
@@ -2353,8 +2414,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
@@ -2384,7 +2465,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..65e6ca677c 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/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/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 1dc1a045fd..cfd6c938f7 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)
@@ -814,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'},
@@ -823,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.
----
---- `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.
+--- See |vim.lsp.start_client()| for all available options. The most important are:
---
---- `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
@@ -849,17 +844,20 @@ 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.
+--- - 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 {}
@@ -871,7 +869,10 @@ function lsp.start(config, opts)
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 = api.nvim_get_current_buf()
+ 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
@@ -902,12 +903,12 @@ end
---
---
---@param cmd: (table|string|fun(dispatchers: table):table) command string or
---- list treated like |jobstart|. The command must launch the language server
+--- 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|
+--- See |vim.lsp.rpc.request()| and |vim.lsp.rpc.notify()|
+--- For TCP there is a built-in rpc client factory: |vim.lsp.rpc.connect()|
---
---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch
--- the `cmd` process. Not related to `root_dir`.
@@ -963,7 +964,7 @@ end
---@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.
+--- `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)
@@ -999,8 +1000,8 @@ end
--- 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.
+--- 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
@@ -1078,7 +1079,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()
@@ -1147,33 +1148,34 @@ 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.
@@ -1287,8 +1289,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.
@@ -1366,7 +1366,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
@@ -1377,8 +1377,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)
@@ -1396,7 +1398,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
@@ -1411,8 +1413,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
@@ -1435,7 +1437,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
@@ -1530,6 +1534,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
@@ -1615,9 +1629,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)
@@ -1644,6 +1686,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
@@ -1754,16 +1797,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
@@ -1777,10 +1819,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
@@ -1797,7 +1845,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
@@ -1928,7 +1977,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
@@ -1970,9 +2019,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
@@ -1997,9 +2046,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)
@@ -2040,8 +2089,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
@@ -2170,8 +2219,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(...)
@@ -2180,7 +2229,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
@@ -2189,7 +2238,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)
@@ -2218,7 +2267,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)
@@ -2239,7 +2288,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..8f4bd15eaa 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()
@@ -206,16 +205,32 @@ function M.format(options)
vim.notify('[LSP] Format request failed, no matching language servers.')
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
+
+ ---@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
+
+ local method = range and 'textDocument/rangeFormatting' or 'textDocument/formatting'
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
@@ -606,7 +489,7 @@ function M.add_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
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)
@@ -885,23 +767,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 +776,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..b383ca1c35 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
@@ -298,13 +313,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 +330,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 +401,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 +539,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..92cda0b34f 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.
@@ -619,14 +629,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 +696,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 +801,9 @@ function protocol.make_client_capabilities()
end)(),
},
},
+ callHierarchy = {
+ dynamicRegistration = false,
+ },
},
workspace = {
symbol = {
@@ -765,9 +827,9 @@ function protocol.make_client_capabilities()
workspaceEdit = {
resourceOperations = { 'rename', 'create', 'delete' },
},
- },
- callHierarchy = {
- dynamicRegistration = false,
+ semanticTokens = {
+ refreshSupport = true,
+ },
},
experimental = nil,
window = {
@@ -778,7 +840,7 @@ function protocol.make_client_capabilities()
},
},
showDocument = {
- support = false,
+ support = true,
},
},
}
@@ -861,8 +923,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 70f838f34d..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')
@@ -294,7 +293,7 @@ end
---@private
--- Sends a notification to the LSP server.
---@param method (string) The invoked LSP method
----@param params (table|nil): Parameters for 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({
@@ -392,45 +391,46 @@ function Client:handle_body(body)
-- Schedule here so that the users functions don't trigger an error and
-- we can still use the result.
schedule(function()
- local status, result
- status, result, err = self:try_call(
- client_errors.SERVER_REQUEST_HANDLER_ERROR,
- self.dispatchers.server_request,
- decoded.method,
- decoded.params
- )
- local _ = log.debug()
- and log.debug(
- 'server_request: callback result',
- { status = status, result = result, err = err }
+ coroutine.wrap(function()
+ local status, result
+ status, result, err = self:try_call(
+ client_errors.SERVER_REQUEST_HANDLER_ERROR,
+ self.dispatchers.server_request,
+ decoded.method,
+ decoded.params
)
- if status then
- if not (result or err) then
- -- TODO this can be a problem if `null` is sent for result. needs vim.NIL
- error(
- string.format(
- 'method %q: either a result or an error must be sent to the server in response',
- decoded.method
- )
- )
- end
- if err then
- assert(
- type(err) == 'table',
- 'err must be a table. Use rpc_response_error to help format errors.'
- )
- local code_name = assert(
- protocol.ErrorCodes[err.code],
- 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+ local _ = log.debug()
+ and log.debug(
+ 'server_request: callback result',
+ { status = status, result = result, err = err }
)
- err.message = err.message or code_name
+ if status then
+ 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',
+ decoded.method
+ )
+ )
+ end
+ if err then
+ assert(
+ type(err) == 'table',
+ 'err must be a table. Use rpc_response_error to help format errors.'
+ )
+ local code_name = assert(
+ protocol.ErrorCodes[err.code],
+ 'Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.'
+ )
+ err.message = err.message or code_name
+ end
+ else
+ -- On an exception, result will contain the error message.
+ err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
+ result = nil
end
- else
- -- On an exception, result will contain the error message.
- err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
- result = nil
- end
- self:send_response(decoded.id, err, result)
+ 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
@@ -635,7 +635,8 @@ local function connect(host, port)
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.
+--- 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}.
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..b53c66ba63 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
@@ -61,8 +65,8 @@ end)()
---
---@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 +102,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 +113,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 +160,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 +176,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 +191,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 +207,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 +310,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 +383,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 +395,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 +414,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' },
@@ -476,7 +486,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 +494,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 +514,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
@@ -533,7 +544,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 +570,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 +582,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 +726,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 6431162799..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' },
@@ -120,10 +120,10 @@ end
--- Determines whether a node is the ancestor of another
---
----@param dest table the possible ancestor
----@param source table the possible descendant node
+---@param dest userdata Possible ancestor |tsnode|
+---@param source userdata Possible descendant |tsnode|
---
----@returns (boolean) True if dest is an ancestor of source
+---@return boolean True if {dest} is an ancestor of {source}
function M.is_ancestor(dest, source)
if not (dest and source) then
return false
@@ -141,11 +141,11 @@ function M.is_ancestor(dest, source)
return false
end
---- Get the node's range or unpack a range table
+--- Returns the node's range or an unpacked range table
---
----@param node_or_range table
+---@param node_or_range (userdata|table) |tsnode| or table of positions
---
----@returns start_row, start_col, end_row, end_col
+---@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)
@@ -154,11 +154,13 @@ function M.get_node_range(node_or_range)
end
end
----Determines whether (line, col) position is in node range
+--- 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)
---
----@param node Node defining the range
----@param line A line (0-based)
----@param col A 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
@@ -176,11 +178,12 @@ function M.is_in_node_range(node, line, col)
end
end
----Determines if a node contains a range
----@param node table The node
----@param range table The range
+--- Determines if a node contains a range
---
----@returns (boolean) True if the node contains the 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])
@@ -189,13 +192,17 @@ function M.node_contains(node, range)
return start_fits and end_fits
end
----Gets a list of captures for a given cursor position
----@param bufnr number The buffer number
----@param row number The position row
----@param col number The position column
+--- 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
---
----@returns (table) A table of captures
-function M.get_captures_at_position(bufnr, row, col)
+---@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
@@ -233,7 +240,7 @@ function M.get_captures_at_position(bufnr, row, col)
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, priority = metadata.priority })
+ table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() })
end
end
end
@@ -241,4 +248,295 @@ function M.get_captures_at_position(bufnr, row, col)
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 1f242b0fdd..e99994c8a9 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
@@ -45,9 +46,10 @@ 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)
@@ -86,7 +88,11 @@ function TSHighlighter.new(tree, opts)
end
end
- a.nvim_buf_set_option(self.bufnr, 'syntax', '')
+ self.orig_syntax = vim.bo[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
@@ -95,9 +101,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
@@ -108,6 +118,11 @@ 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].syntax = self.orig_syntax
+ vim.bo[self.bufnr].spelloptions = self.orig_spelloptions
+ end
end
---@private
@@ -145,8 +160,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)
@@ -156,7 +173,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
@@ -193,14 +210,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
@@ -217,7 +246,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
@@ -244,6 +287,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 d14b825603..c92d63b8c4 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -2,14 +2,15 @@ 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 string The 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
+---@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
@@ -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 70317a9f94..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
@@ -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,11 +571,12 @@ function LanguageTree:contains(range)
return false
end
---- Gets the tree that contains {range}
+--- Gets the tree that contains {range}.
---
----@param range table A text range
----@param opts table Options table
----@param opts.ignore_injections boolean (default true) Ignore injected languages.
+---@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)
@@ -577,19 +600,23 @@ function LanguageTree:tree_for_range(range, opts)
return nil
end
---- Gets the smallest named node that contains {range}
+--- Gets the smallest named node that contains {range}.
---
----@param range table A text range
----@param opts table Options table
----@param opts.ignore_injections boolean (default true) Ignore injected languages.
+---@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)
- return tree:root():named_descendant_for_range(unpack(range))
+ if tree then
+ return tree:root():named_descendant_for_range(unpack(range))
+ end
end
---- Gets the appropriate language that contains {range}
+--- 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 697e2e7691..4bec5db527 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,10 +226,11 @@ end
--- Gets the text corresponding to a given node
---
----@param node table The node
----@param source table The buffer or string from which the node is extracted
----@param opts table Optional parameters.
---- - concat: (boolean default true) Concatenate result in a string
+---@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)
@@ -372,9 +418,8 @@ 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:string, pattern:string, bufnr:number, predicate:function)
function M.add_predicate(name, handler, force)
if predicate_handlers[name] and not force then
error(string.format('Overriding %s', name))
@@ -390,9 +435,8 @@ 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:string, pattern:string, bufnr:number, predicate:function, metadata:table)
function M.add_directive(name, handler, force)
if directive_handlers[name] and not force then
error(string.format('Overriding %s', name))
@@ -402,12 +446,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
@@ -494,18 +539,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:
@@ -515,13 +559,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()
@@ -551,15 +596,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]
@@ -572,13 +616,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 ce2225c5f8..3982489b92 100644
--- a/runtime/plugin/matchparen.vim
+++ b/runtime/plugin/matchparen.vim
@@ -1,6 +1,6 @@
" 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)
@@ -19,8 +19,8 @@ endif
augroup matchparen
" Replace all matchparen autocommands
- autocmd! CursorMoved,CursorMovedI,WinEnter,WinScrolled * 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 21681d59e4..9b4df09ac7 100644
--- a/runtime/syntax/plsql.vim
+++ b/runtime/syntax/plsql.vim
@@ -4,8 +4,11 @@
" 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: Aug 21, 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.
@@ -54,8 +57,13 @@ 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 get(g:,"plsql_space_errors",0) == 1
if get(g:,"plsql_no_trail_space_error",0) == 0
@@ -79,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\)\>"
@@ -150,7 +157,7 @@ syn keyword plsqlKeyword DATA_SECURITY_REWRITE_LIMIT DATA_VALIDATE DATE_MODE DAY
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
@@ -189,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
@@ -342,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
@@ -445,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
@@ -468,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
@@ -584,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
+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
@@ -618,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 get(g:,"plsql_fold",0) == 1
- 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
+ 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
@@ -682,6 +689,10 @@ syn match plsqlConditional "\<END\>\_s\+\<IF\>"
syn match plsqlCase "\<END\>\_s\+\<CASE\>"
syn match plsqlCase "\<CASE\>"
+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
@@ -826,6 +837,13 @@ 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 get(g:,"plsql_legacy_sql_keywords",0) == 1
hi link plsqlSQLKeyword Function
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/syntax.vim b/runtime/syntax/syntax.vim
index 55a2ee6d71..5ec99c7e05 100644
--- a/runtime/syntax/syntax.vim
+++ b/runtime/syntax/syntax.vim
@@ -27,13 +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 * 0verbose 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/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/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..fa7c14eaa3
--- /dev/null
+++ b/scripts/gen_help_html.lua
@@ -0,0 +1,1111 @@
+-- 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",
+ ["vim.treesitter.start()"] = "treesitter.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 23ed0e3f08..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', 'Arena'))
+ 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/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/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 635833748d..b95709526b 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -1,25 +1,341 @@
-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})
+
+if(Iconv_FOUND)
+ target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${Iconv_INCLUDE_DIRS})
+ if(Iconv_LIBRARIES)
+ target_link_libraries(main_lib INTERFACE ${Iconv_LIBRARIES})
+ endif()
+endif()
+
+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()
+
+# 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)
+target_link_libraries(main_lib INTERFACE ${CMAKE_THREAD_LIBS_INIT})
+
+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(nvim PRIVATE EXITFREE)
+endif()
+
+if(MSVC)
+ # XXX: /W4 gives too many warnings. #3241
+ target_compile_options(main_lib INTERFACE -W1 -wd4311)
+ target_compile_definitions(main_lib INTERFACE _CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE)
+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 +352,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 +382,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 +415,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 +424,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)
@@ -159,26 +458,26 @@ list(REMOVE_ITEM NVIM_SOURCES ${to_remove})
if(NOT MSVC)
# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306
set_source_files_properties(
- ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion")
+ ${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 +489,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 +497,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 +511,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 +529,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")
@@ -316,18 +609,14 @@ 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}
DEPENDS
@@ -399,73 +688,44 @@ 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 +797,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 +829,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 +847,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()
@@ -597,12 +858,13 @@ set_target_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 +874,46 @@ 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}
- )
+ 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})
set_target_properties(
nvim-test
PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
- target_compile_options(nvim-test PRIVATE -DUNIT_TESTING)
+ 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 +956,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 +967,17 @@ 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})
add_custom_target(uncrustify-version
COMMAND ${CMAKE_COMMAND}
@@ -767,13 +985,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 +1003,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 1cf0211f43..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)
{
@@ -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 199650fc55..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.
@@ -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 09b004637f..44e7ed3986 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()|.
@@ -57,7 +66,7 @@ Integer nvim_create_namespace(String name)
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
@@ -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);
@@ -997,37 +1015,32 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
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..8f909e937f 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -2,6 +2,15 @@ return {
context = {
"types";
};
+ set_decoration_provider = {
+ "on_start";
+ "on_buf";
+ "on_win";
+ "on_line";
+ "on_end";
+ "_on_hl_def";
+ "_on_spell_nav";
+ };
set_extmark = {
"id";
"end_line";
@@ -28,6 +37,7 @@ return {
"line_hl_group";
"cursorline_hl_group";
"conceal";
+ "spell";
"ui_watched";
};
keymap = {
@@ -71,6 +81,8 @@ return {
"focusable";
"zindex";
"border";
+ "title";
+ "title_pos";
"style";
"noautocmd";
};
@@ -114,6 +126,8 @@ return {
"global_link";
"fallback";
"blend";
+ "fg_indexed";
+ "bg_indexed";
};
highlight_cterm = {
"bold";
@@ -188,6 +202,7 @@ return {
"browse";
"confirm";
"hide";
+ "horizontal";
"keepalt";
"keepjumps";
"keepmarks";
@@ -206,5 +221,8 @@ return {
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..7770ba39d8 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 = (char *)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 f92b205531..4ae61b2bfb 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -1,12 +1,14 @@
#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,
- Arena *arena,
- 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.
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index ebcf6cca6d..4ff600618d 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -3,37 +3,37 @@
#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"
@@ -218,6 +218,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) {
@@ -225,6 +227,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);
@@ -241,11 +247,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);
@@ -255,6 +266,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);
}
@@ -373,7 +391,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
@@ -444,53 +468,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;
@@ -499,7 +485,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;
@@ -519,12 +505,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)
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 2157ad0ec2..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"
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 e34dcbdb46..32b294c0ce 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -2,62 +2,33 @@
// 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/channel.h"
-#include "nvim/cursor_shape.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
@@ -122,7 +93,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 +108,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 +167,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 +189,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 +204,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 +219,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 +249,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 +355,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 +430,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 +460,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 +624,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 +635,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 +648,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 +685,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 +716,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 +726,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 +742,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 +751,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 +776,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 +790,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 +799,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 +822,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 +893,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 +920,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 +933,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], NULL));
+ 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], NULL));
+ 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 +1001,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], NULL));
+ 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 +1026,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 b164106cef..13c8e162b6 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
@@ -204,7 +206,7 @@ void nvim_set_hl_ns(Integer ns_id, Error *err)
/// 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.
@@ -1682,7 +1695,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er
// error handled after loop
break;
}
- // TODO(bfredl): wastefull copy. It could be avoided to encoding to msgpack
+ // 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));
@@ -1715,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++) {
@@ -1733,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--;
@@ -1817,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)
{
@@ -1879,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;
@@ -1901,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)
@@ -1932,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;
@@ -1956,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;
}
@@ -2116,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);
@@ -2214,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));
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index f6d0e39327..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"
@@ -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 96c560efa1..f81d26b486 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.
@@ -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 5203003369..df8ad165ba 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,8 +119,13 @@ 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, 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
@@ -430,7 +443,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
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index b57019286f..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;
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index 70b2e71949..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,6 +203,11 @@ 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
@@ -163,6 +217,8 @@ void alist_add(alist_T *al, char *fname, int set_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)
@@ -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 = 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 **)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,28 +533,43 @@ 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 *) * (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 = xstrdup(GARGLIST[i].ae_fname);
@@ -548,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('\'');
}
}
@@ -623,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));
@@ -639,6 +733,8 @@ void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
j--;
}
}
+
+ xfree(firstFullname);
}
}
@@ -677,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) {
@@ -745,76 +845,32 @@ char *alist_name(aentry_T *aep)
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;
@@ -826,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;
@@ -851,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)) {
@@ -867,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
@@ -882,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;
}
@@ -892,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;
@@ -941,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;
@@ -956,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);
}
@@ -994,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".
@@ -1126,31 +1245,33 @@ 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..2c0cb771c3 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,7 +123,8 @@ 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',
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 1c1de214cd..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".
@@ -725,7 +762,7 @@ int check_ei(void)
char *au_event_disable(char *what)
{
char *save_ei = xstrdup(p_ei);
- char *new_ei = xstrnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
+ 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,14 +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_skipcol = curwin->w_skipcol;
- 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;
@@ -1193,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)
@@ -1275,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);
@@ -1286,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
@@ -1316,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;
}
@@ -1325,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
@@ -1348,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;
@@ -1366,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;
@@ -1418,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;
}
}
@@ -1444,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
@@ -1466,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;
@@ -1739,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);
@@ -2012,7 +2075,7 @@ 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);
char *const namep = xmalloc(sourcing_name_len);
snprintf(namep, sourcing_name_len, s, name, ap->pat);
@@ -2214,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
@@ -2396,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;
}
@@ -2404,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;
@@ -2426,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] == '>';
}
@@ -2613,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;
@@ -2669,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;
@@ -2716,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 75a8a7aaa1..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
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 514be4c56b..cd1059eb0d 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,14 @@
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
+#include "nvim/screen.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 +107,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,6 +175,22 @@ 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.
///
@@ -328,8 +359,9 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
}
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
@@ -337,7 +369,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
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);
@@ -401,6 +433,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.
@@ -458,8 +513,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;
}
@@ -666,6 +720,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;
@@ -1034,16 +1090,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);
}
}
@@ -1201,13 +1257,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);
@@ -1273,7 +1333,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;
}
@@ -1813,7 +1873,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
}
@@ -1937,12 +1997,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);
@@ -1951,6 +2015,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);
@@ -2170,7 +2235,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"
@@ -2188,7 +2253,7 @@ 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);
+ regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
if (regmatch.regprog == NULL) {
xfree(pat);
return -1;
@@ -2284,7 +2349,7 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)".
if (*pat == '^') {
- patc = xmalloc(STRLEN(pat) + 11);
+ patc = xmalloc(strlen(pat) + 11);
STRCPY(patc, "\\(^\\|[\\/]\\)");
STRCPY(patc + 11, pat + 1);
} else {
@@ -2409,19 +2474,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;
@@ -2528,17 +2596,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".
@@ -2561,25 +2630,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;
}
@@ -2696,9 +2767,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)) {
@@ -2714,7 +2785,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 ? '#' : ' '),
@@ -2728,18 +2799,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();
}
@@ -2841,18 +2912,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.
@@ -2965,7 +3038,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;
}
{
@@ -3037,14 +3110,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;
@@ -3087,7 +3160,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,
@@ -3096,7 +3169,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);
}
@@ -3169,17 +3242,9 @@ 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),
- 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 = p_titlestring;
}
@@ -3283,16 +3348,8 @@ 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),
- 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 = p_iconstring;
}
@@ -3305,7 +3362,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;
@@ -3335,7 +3392,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;
@@ -3386,9 +3443,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))
@@ -3412,7 +3469,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) {
@@ -3448,7 +3505,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));
@@ -3701,10 +3758,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".
@@ -3720,7 +3777,7 @@ 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 && s[3] == '<')
@@ -3769,8 +3826,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;
}
@@ -3929,30 +3986,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
@@ -4116,13 +4149,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);
}
}
@@ -4147,7 +4182,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);
@@ -4159,7 +4194,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;
}
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 748e94f0a1..de0d969f3b 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,21 +86,14 @@ 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 *tagname; // tag name
fmark_T fmark; // cursor position BEFORE ":tag"
@@ -126,17 +105,13 @@ typedef struct taggy {
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 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,11 +124,9 @@ 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'
@@ -231,6 +204,8 @@ typedef struct {
#define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn'
char *wo_sbr;
#define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak'
+ 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;
@@ -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
@@ -321,9 +292,7 @@ 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 {
uint8_t *tb_buf; // buffer for typed characters
uint8_t *tb_noremap; // mapping flags for characters in tb_buf[]
@@ -347,13 +316,11 @@ 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
- uint8_t *m_keys; // mapped from, lhs
+ 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
@@ -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
@@ -484,6 +447,9 @@ typedef struct {
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
@@ -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
@@ -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,7 +603,7 @@ 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
@@ -673,11 +627,9 @@ 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
@@ -706,9 +658,13 @@ struct file_buffer {
char *b_p_csl; ///< 'completeslash'
#endif
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
char *b_p_umf; ///< 'usermarkfunc'
+ int b_p_eof; ///< 'endoffile'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
int b_p_et; ///< 'expandtab'
@@ -733,6 +689,7 @@ struct file_buffer {
uint32_t b_p_fex_flags; ///< flags for 'formatexpr'
char *b_p_kp; ///< 'keywordprg'
int b_p_lisp; ///< 'lisp'
+ char *b_p_lop; ///< 'lispoptions'
char *b_p_menc; ///< 'makeencoding'
char *b_p_mps; ///< 'matchpairs'
int b_p_ml; ///< 'modeline'
@@ -778,6 +735,7 @@ struct file_buffer {
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 *b_p_lw; ///< 'lispwords' local value
@@ -823,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
@@ -835,19 +794,17 @@ 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_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
@@ -907,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
@@ -951,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;
@@ -965,18 +921,16 @@ 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
@@ -985,10 +939,8 @@ typedef struct w_line {
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;
@@ -1002,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)
@@ -1030,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
@@ -1040,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;
@@ -1084,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;
@@ -1106,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;
@@ -1220,14 +1176,13 @@ 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 */
+ // "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
@@ -1241,8 +1196,9 @@ struct window_S {
colnr_T w_skipcol; // starting column when a single line
// doesn't fit in the window
- // five 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
@@ -1255,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.
@@ -1280,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
@@ -1312,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
@@ -1325,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;
@@ -1347,9 +1295,7 @@ 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
@@ -1358,6 +1304,7 @@ struct window_S {
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
@@ -1398,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))
@@ -1407,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
@@ -1427,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
@@ -1443,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
@@ -1470,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 85ab92e49b..e8c4af9879 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,22 +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"
@@ -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);
@@ -539,12 +560,13 @@ void unchanged(buf_T *buf, int ff, bool always_inc_changedtick)
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) {
+ || strcmp(buf->b_start_fenc, buf->b_p_fenc) != 0) {
xfree(buf->b_start_fenc);
buf->b_start_fenc = xstrdup(buf->b_p_fenc);
}
@@ -573,7 +595,8 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty)
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) {
+ 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) {
@@ -582,14 +605,14 @@ bool file_ff_differs(buf_T *buf, bool ignore_empty)
if (buf->b_start_fenc == NULL) {
return *buf->b_p_fenc != NUL;
}
- return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
+ 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 *p)
{
- ins_bytes_len(p, STRLEN(p));
+ ins_bytes_len(p, strlen(p));
}
/// Insert string "p" with length "len" at the cursor position.
@@ -599,7 +622,7 @@ void ins_bytes_len(char *p, size_t len)
size_t n;
for (size_t i = 0; i < len; i += n) {
// avoid reading past p[len]
- n = (size_t)utfc_ptr2len_len((char_u *)p + i, (int)(len - i));
+ n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i));
ins_char_bytes(p + i, n);
}
}
@@ -631,8 +654,8 @@ void ins_char_bytes(char *buf, size_t charlen)
size_t col = (size_t)curwin->w_cursor.col;
linenr_T lnum = curwin->w_cursor.lnum;
- char *oldp = (char *)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
@@ -731,7 +754,7 @@ void ins_char_bytes(char *buf, size_t charlen)
/// Caller must have prepared for undo.
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) {
@@ -739,8 +762,8 @@ void ins_str(char *s)
}
colnr_T col = curwin->w_cursor.col;
- char *oldp = (char *)ml_get(lnum);
- int oldlen = (int)STRLEN(oldp);
+ char *oldp = ml_get(lnum);
+ int oldlen = (int)strlen(oldp);
char *newp = xmalloc((size_t)oldlen + (size_t)newlen + 1);
if (col > 0) {
@@ -774,7 +797,7 @@ int del_char(bool fixpos)
int del_chars(long count, int fixpos)
{
int bytes = 0;
- char *p = (char *)get_cursor_pos_ptr();
+ char *p = get_cursor_pos_ptr();
for (long i = 0; i < count && *p != NUL; i++) {
int l = utfc_ptr2len(p);
bytes += l;
@@ -797,8 +820,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 *oldp = (char *)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) {
@@ -828,7 +851,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
col = n;
count = utf_ptr2len(oldp + n);
n += count;
- } while (utf_composinglike((char_u *)oldp + col, (char_u *)oldp + n));
+ } while (utf_composinglike(oldp + col, oldp + n));
fixpos = false;
}
}
@@ -843,7 +866,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
&& (get_ve_flags() & VE_ONEMORE) == 0) {
curwin->w_cursor.col--;
curwin->w_cursor.coladd = 0;
- curwin->w_cursor.col -= utf_head_off((char_u *)oldp, (char_u *)oldp + curwin->w_cursor.col);
+ curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
}
count = oldlen - col;
movelen = 1;
@@ -962,7 +985,7 @@ int copy_indent(int size, char *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);
@@ -999,7 +1022,7 @@ int copy_indent(int size, char *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
///
@@ -1036,7 +1059,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 *saved_line = (char *)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
@@ -1047,7 +1070,7 @@ 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 = (char *)vim_strsave(ml_get(curwin->w_cursor.lnum + 1));
+ next_line = xstrdup(ml_get(curwin->w_cursor.lnum + 1));
} else {
next_line = xstrdup("");
}
@@ -1072,7 +1095,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
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;
}
@@ -1119,7 +1142,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();
}
@@ -1155,7 +1178,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--;
}
@@ -1181,7 +1204,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
@@ -1205,12 +1228,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
@@ -1319,7 +1342,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// the comment leader.
if (dir == FORWARD) {
for (p = saved_line + lead_len; *p; p++) {
- if (STRNCMP(p, lead_end, n) == 0) {
+ if (strncmp(p, lead_end, n) == 0) {
comment_end = p;
lead_len = 0;
break;
@@ -1331,7 +1354,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
@@ -1410,7 +1433,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
leader = xmalloc((size_t)bytes);
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++) {
@@ -1465,7 +1488,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;
@@ -1641,7 +1664,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
@@ -1755,7 +1778,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
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;
@@ -1795,7 +1818,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);
}
@@ -1814,19 +1837,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) {
@@ -1838,7 +1861,7 @@ 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 = (char *)vim_strsave(get_cursor_line_ptr());
+ p_extra = xstrdup(get_cursor_line_ptr());
// Put back original line
ml_replace(curwin->w_cursor.lnum, next_line, false);
@@ -1872,7 +1895,7 @@ void truncate_line(int fixpos)
if (col == 0) {
newp = xstrdup("");
} else {
- newp = (char *)vim_strnsave(ml_get(lnum), (size_t)col);
+ newp = xstrnsave(ml_get(lnum), (size_t)col);
}
ml_replace(lnum, newp, false);
@@ -2084,7 +2107,7 @@ 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;
@@ -2171,7 +2194,7 @@ 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 = curbuf->b_p_com; *list;) {
char *flags_save = list;
@@ -2185,7 +2208,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;
}
@@ -2194,7 +2217,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 4efd8a4ad1..4115743e1c 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -6,28 +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 "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"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -267,8 +274,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 +284,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;
@@ -411,12 +418,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 +447,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 +475,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 +510,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;
}
@@ -650,7 +673,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
{
@@ -786,7 +809,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 +822,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 +852,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 +928,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
@@ -944,7 +967,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
&& !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) {
@@ -960,7 +983,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;
}
@@ -970,7 +993,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;
@@ -999,7 +1022,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
break;
}
- if ((posptr != NULL) && ((char_u *)cts.cts_ptr >= posptr)) {
+ if ((posptr != NULL) && (cts.cts_ptr >= posptr)) {
// character at pos->col
break;
}
@@ -1008,7 +1031,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
MB_PTR_ADV(cts.cts_ptr);
}
vcol = cts.cts_vcol;
- ptr = (char_u *)cts.cts_ptr;
+ ptr = cts.cts_ptr;
}
clear_chartabsize_arg(&cts);
@@ -1076,10 +1099,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) {
@@ -1157,7 +1180,7 @@ char *skipwhite(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- return skipwhite_len(p, STRLEN(p));
+ return skipwhite_len(p, strlen(p));
}
/// Like `skipwhite`, but skip up to `len` characters.
@@ -1182,7 +1205,7 @@ char *skipwhite_len(const char *p, size_t len)
// columns (bytes) at the start of a given line
intptr_t getwhitecols_curline(void)
{
- return getwhitecols((char *)get_cursor_line_ptr());
+ return getwhitecols(get_cursor_line_ptr());
}
intptr_t getwhitecols(const char *p)
@@ -1338,7 +1361,7 @@ char *skip_to_newline(const char *const p)
/// 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.
///
@@ -1355,7 +1378,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.
@@ -1463,14 +1486,14 @@ bool vim_isblankline(char *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;
@@ -1522,7 +1545,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)
@@ -1610,7 +1633,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;
}
@@ -1619,7 +1642,7 @@ vim_str2nr_proceed:
}
if (len != NULL) {
- *len = (int)(ptr - (const char *)start);
+ *len = (int)(ptr - start);
}
if (nptr != NULL) {
@@ -1698,7 +1721,7 @@ bool rem_backslash(const char *str)
|| (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;
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 5c54404aab..ca19d6de95 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,28 +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"
@@ -48,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"
@@ -64,10 +89,14 @@ 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])
+
+/// 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;
@@ -75,71 +104,76 @@ 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);
}
}
@@ -154,8 +188,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) {
@@ -172,34 +206,38 @@ 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 {
// Translate string into pattern and expand it.
- p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ p1 = addstar(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.
@@ -210,23 +248,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;
}
@@ -251,19 +289,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);
@@ -276,9 +349,238 @@ void cmdline_pum_cleanup(CmdlineInfo *cclp)
wildmenu_cleanup(cclp);
}
+/// 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_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char_u *orig_save)
+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;
@@ -291,8 +593,49 @@ static char_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, cha
findex = xp->xp_numfiles;
}
findex--;
- } else { // mode == WILD_NEXT
+ } 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.
@@ -318,17 +661,17 @@ static char_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, cha
}
*p_findex = findex;
- return vim_strsave(findex == -1 ? orig_save : (char_u *)xp->xp_files[findex]);
+ return xstrdup(findex == -1 ? orig_save : xp->xp_files[findex]);
}
/// Start the command-line expansion and get the matches.
-static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
+static char *ExpandOne_start(int mode, expand_T *xp, char *str, int options)
{
int non_suf_match; // number without matching suffix
- char_u *ss = NULL;
+ char *ss = NULL;
// Do the expansion.
- if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) {
+ 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,
@@ -346,8 +689,7 @@ static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
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 (mode != WILD_ALL && mode != WILD_ALL_KEEP && mode != WILD_LONGEST) {
if (xp->xp_numfiles) {
non_suf_match = xp->xp_numfiles;
} else {
@@ -361,7 +703,7 @@ static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
// expand_wildcards, only need to check the first two.
non_suf_match = 0;
for (int i = 0; i < 2; i++) {
- if (match_suffix((char_u *)xp->xp_files[i])) {
+ if (match_suffix(xp->xp_files[i])) {
non_suf_match++;
}
}
@@ -378,7 +720,7 @@ static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
}
}
if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
- ss = vim_strsave((char_u *)xp->xp_files[0]);
+ ss = xstrdup(xp->xp_files[0]);
}
}
}
@@ -420,7 +762,7 @@ static char *find_longest_match(expand_T *xp, int options)
return xstrndup(xp->xp_files[0], len);
}
-/// Do wildcard expansion on the string 'str'.
+/// 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.
@@ -444,6 +786,7 @@ static char *find_longest_match(expand_T *xp, int options)
/// 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
@@ -458,24 +801,27 @@ static char *find_longest_match(expand_T *xp, int options)
/// 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;
// first handle the case of using an old match
- if (mode == WILD_NEXT || mode == WILD_PREV) {
+ 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
@@ -483,6 +829,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;
@@ -500,7 +851,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode
// Find longest common part
if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
- ss = (char_u *)find_longest_match(xp, options);
+ ss = find_longest_match(xp, options);
findex = -1; // next p_wc gets first one
}
@@ -510,7 +861,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;
@@ -552,36 +903,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 = (char *)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;
}
@@ -591,26 +1005,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) {
@@ -626,18 +1022,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;
@@ -645,7 +1041,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
@@ -653,7 +1049,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
@@ -667,57 +1063,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(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 = (char_u *)backslash_halve_save((char *)path);
- j = os_isdir((char *)halved_slash);
- xfree(exp_path);
- if (halved_slash != path) {
- xfree(halved_slash);
- }
- } else {
- // Expansion was done here, file names are literal.
- j = os_isdir(files_found[k]);
- }
- if (showtail) {
- p = (char_u *)L_SHOWFILE(k);
- } else {
- home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true);
- p = (char_u *)NameBuff;
- }
- } else {
- j = false;
- p = (char_u *)L_SHOWFILE(k);
- }
- lastlen = msg_outtrans_attr((char *)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;
@@ -730,21 +1076,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)
@@ -761,7 +1107,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.
@@ -769,8 +1115,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
@@ -779,15 +1125,15 @@ 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((char *)s)) {
+ if (rem_backslash(s)) {
s++;
} else if (vim_strchr("*?[", *s) != NULL) {
return false;
@@ -804,13 +1150,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
@@ -832,7 +1178,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++) {
@@ -894,7 +1240,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.
@@ -902,7 +1248,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--) {
@@ -914,8 +1260,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--;
@@ -1148,24 +1494,235 @@ static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool use
// 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) {
+ && match_user(xp->xp_pattern + 1) >= 1) {
xp->xp_context = EXPAND_USER;
xp->xp_pattern++;
}
}
}
+/// 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);
+}
+
+/// 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);
+}
+
+/// 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.
+ }
+
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ 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++;
+ }
+ }
+ }
+ while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
+ arg++;
+ }
+ if (arg[0] != NUL) {
+ return arg;
+ }
+
+ 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;
+ }
+
+ // 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 == NUL || strchr("|\"\n", *arg) == NULL) {
+ xp->xp_context = EXPAND_NOTHING;
+ } else {
+ return arg;
+ }
+ }
+
+ return NULL;
+}
+
+/// 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;
+ }
+ }
+
+ 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);
+ }
+ 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;
+}
+
/// 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, const char *arg,
- uint32_t argt, int context, expand_T *xp, bool forceit)
+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)
{
- const char *p;
-
switch (cmdidx) {
case CMD_find:
case CMD_sfind:
@@ -1204,6 +1761,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_folddoclosed:
case CMD_folddoopen:
case CMD_hide:
+ case CMD_horizontal:
case CMD_keepalt:
case CMD_keepjumps:
case CMD_keepmarks:
@@ -1226,26 +1784,10 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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(skiptowhite(arg));
- if (*arg != NUL) {
- xp->xp_context = EXPAND_NOTHING;
- arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic, NULL);
- }
- }
- return (const char *)find_nextcmd(arg);
+ return set_context_in_match_cmd(xp, arg);
// All completion for the +cmdline_compl feature goes here.
@@ -1258,49 +1800,11 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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 *)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:
@@ -1310,26 +1814,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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);
@@ -1337,13 +1822,13 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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:
@@ -1392,20 +1877,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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;
@@ -1418,13 +1890,8 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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, 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:
@@ -1449,34 +1916,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
case CMD_USER:
case CMD_USER_BUF:
- if (context != EXPAND_NOTHING) {
- // EX_XFILE: file names are handled above.
- if (!(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:
@@ -1496,7 +1936,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, false,
false, cmdidx);
case CMD_unmap:
case CMD_nunmap:
@@ -1507,7 +1947,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, false,
true, cmdidx);
case CMD_mapclear:
case CMD_nmapclear:
@@ -1528,12 +1968,12 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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,
+ 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,
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char *)arg, forceit, true,
true, cmdidx);
case CMD_menu:
case CMD_noremenu:
@@ -1592,22 +2032,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
#ifdef HAVE_WORKING_LIBINTL
case CMD_language:
- 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;
- }
- }
- break;
+ return set_context_in_lang_cmd(xp, arg);
#endif
case CMD_profile:
set_context_in_profile_cmd(xp, arg);
@@ -1643,6 +2068,11 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons
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_lua:
xp->xp_context = EXPAND_LUA;
break;
@@ -1826,17 +2256,19 @@ static const char *set_one_cmd_context(expand_T *xp, const char *buff)
}
// Switch on command name.
- return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, context, xp, forceit);
+ 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.
@@ -1848,10 +2280,10 @@ 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_pattern = ccline->cmdbuff;
xp->xp_arg = (char *)ccline->xp_arg;
} else {
while (nextcomm != NULL) {
@@ -1861,7 +2293,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;
@@ -1881,9 +2313,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) {
@@ -1896,16 +2328,16 @@ 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);
+ 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;
}
@@ -1915,15 +2347,15 @@ int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***
}
/// Expand file or directory names.
-static int expand_files_and_dirs(expand_T *xp, char_u *pat, char ***file, int *num_file, int flags,
- int options)
+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 = vim_strsave(pat);
+ pat = xstrdup(pat);
for (int i = 0; pat[i]; i++) {
if (pat[i] == '\\') {
if (xp->xp_backslash == XP_BS_THREE
@@ -1952,14 +2384,14 @@ static int expand_files_and_dirs(expand_T *xp, char_u *pat, char ***file, int *n
}
// Expand wildcards, supporting %:h and the like.
- int ret = expand_wildcards_eval(&pat, num_file, file, flags);
+ 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 < *num_file; j++) {
- char *ptr = (*file)[j];
+ for (int j = 0; j < *numMatches; j++) {
+ char *ptr = (*matches)[j];
while (*ptr != NUL) {
if (p_csl[0] == 's' && *ptr == '\\') {
*ptr = '/';
@@ -1988,6 +2420,32 @@ 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 of the
/// ":messages {clear}" command.
static char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
@@ -2032,7 +2490,7 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
}
/// Do the expansion based on xp->xp_context and "rmp".
-static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***file)
+static int ExpandOther(expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches)
{
typedef CompleteListItemGetter ExpandFunc;
static struct expgen {
@@ -2062,7 +2520,6 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
{ 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_CSCOPE, get_cscope_name, true, true },
{ EXPAND_SIGN, get_sign_name, true, true },
{ EXPAND_PROFILE, get_profile_name, true, true },
#ifdef HAVE_WORKING_LIBINTL
@@ -2072,6 +2529,7 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
{ 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_CHECKHEALTH, get_healthcheck_names, true, false },
};
int ret = FAIL;
@@ -2083,7 +2541,7 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
if (tab[i].ic) {
rmp->rm_ic = true;
}
- ExpandGeneric(xp, rmp, num_file, file, tab[i].func, tab[i].escaped);
+ ExpandGeneric(xp, rmp, matches, numMatches, tab[i].func, tab[i].escaped);
ret = OK;
break;
}
@@ -2092,16 +2550,10 @@ static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***fil
return ret;
}
-/// 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)
+/// Map wild expand options to flags for expand_wildcards()
+static int map_wildopts_to_ewflags(int options)
{
- regmatch_T regmatch;
- int ret;
- int flags;
-
- flags = EW_DIR; // include directories
+ int flags = EW_DIR; // include directories
if (options & WILD_LIST_NOTFOUND) {
flags |= EW_NOTFOUND;
}
@@ -2121,87 +2573,98 @@ 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;
+ int ret;
+ int flags = map_wildopts_to_ewflags(options);
+
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH) {
- return expand_files_and_dirs(xp, pat, file, num_file, flags, options);
+ 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((char *)pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_COMPILER) {
char *directories[] = { "compiler", NULL };
- return ExpandRTDir((char *)pat, DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_LUA, numMatches, matches, directories);
}
if (xp->xp_context == EXPAND_OWNSYNTAX) {
char *directories[] = { "syntax", NULL };
- return ExpandRTDir((char *)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((char *)pat, DIP_LUA, num_file, file, directories);
+ return ExpandRTDir(pat, DIP_LUA, 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((char *)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);
+ regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
if (regmatch.regprog == NULL) {
return FAIL;
}
@@ -2211,13 +2674,13 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***f
if (xp->xp_context == EXPAND_SETTINGS
|| xp->xp_context == EXPAND_BOOL_SETTINGS) {
- ret = ExpandSettings(xp, &regmatch, num_file, file);
+ ret = ExpandSettings(xp, &regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_MAPPINGS) {
- ret = ExpandMappings(&regmatch, num_file, file);
+ ret = ExpandMappings(&regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_USER_DEFINED) {
- ret = ExpandUserDefined(xp, &regmatch, num_file, file);
+ ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
} else {
- ret = ExpandOther(xp, &regmatch, num_file, file);
+ ret = ExpandOther(xp, &regmatch, matches, numMatches);
}
vim_regfree(regmatch.regprog);
@@ -2233,23 +2696,23 @@ 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,
+static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches,
CompleteListItemGetter func, int escaped)
{
int i;
size_t count = 0;
- char_u *str;
+ char *str;
// count the number of matching names
for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
+ 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 (vim_regexec(regmatch, str, (colnr_T)0)) {
count++;
}
}
@@ -2257,29 +2720,29 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
return;
}
assert(count < INT_MAX);
- *num_file = (int)count;
- *file = xmalloc(count * sizeof(char_u *));
+ *numMatches = (int)count;
+ *matches = xmalloc(count * sizeof(char *));
// copy the matching names into allocated memory
count = 0;
for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
+ 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 (vim_regexec(regmatch, str, (colnr_T)0)) {
if (escaped) {
- str = vim_strsave_escaped(str, (char_u *)" \t\\.");
+ str = vim_strsave_escaped(str, " \t\\.");
} else {
- str = vim_strsave(str);
+ str = xstrdup(str);
}
- (*file)[count++] = (char *)str;
+ (*matches)[count++] = str;
if (func == get_menu_names) {
// Test for separator added by get_menu_names().
- str += STRLEN(str) - 1;
+ str += strlen(str) - 1;
if (*str == '\001') {
*str = '.';
}
@@ -2293,10 +2756,9 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC) {
// <SNR> functions should be sorted to the end.
- qsort((void *)(*file), (size_t)(*num_file), sizeof(char_u *),
- sort_func_compare);
+ qsort((void *)(*matches), (size_t)(*numMatches), sizeof(char *), sort_func_compare);
} else {
- sort_strings(*file, *num_file);
+ sort_strings(*matches, *numMatches);
}
}
@@ -2305,30 +2767,67 @@ static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, cha
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((char_u *)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, (char_u *)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);
@@ -2340,29 +2839,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");
+ if (!path_is_absolute((char_u *)pat)) {
+ 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) {
@@ -2372,7 +2871,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 {
@@ -2384,44 +2883,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);
@@ -2433,39 +2901,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) {
@@ -2477,32 +2942,32 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
}
/// Expand names with a function defined by the user.
-static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
+static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char ***matches, int *numMatches)
{
- char_u *e;
+ char *e;
garray_T ga;
- char_u *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file,
- file);
+ *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');
+ for (char *s = retstr; *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;
+ const bool skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
*e = keep;
if (!skip) {
- GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
+ GA_APPEND(char *, &ga, xstrnsave(s, (size_t)(e - s)));
}
if (*e != NUL) {
@@ -2510,16 +2975,17 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file,
}
}
xfree(retstr);
- *file = ga.ga_data;
- *num_file = ga.ga_len;
+ *matches = ga.ga_data;
+ *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;
}
@@ -2537,8 +3003,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;
}
@@ -2579,19 +3045,19 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options)
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);
@@ -2646,145 +3112,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
@@ -2811,7 +3290,7 @@ void wildmenu_cleanup(CmdlineInfo *cclp)
p_ls = save_p_ls;
p_wmh = save_p_wmh;
last_status(false);
- update_screen(UPD_VALID); // redraw the screen NOW
+ update_screen(); // redraw the screen NOW
redrawcmd();
save_p_ls = -1;
wild_menu_showing = 0;
@@ -2832,7 +3311,7 @@ void wildmenu_cleanup(CmdlineInfo *cclp)
/// "getcompletion()" function
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
@@ -2865,14 +3344,14 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -2881,21 +3360,16 @@ void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
+ 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 1426054d0b..d12ac87bbb 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
@@ -91,7 +107,7 @@ static char *(history_names[]) = {
char *get_history_arg(expand_T *xp, int idx)
{
const char *short_names = ":=@>?/";
- const int short_names_count = (int)STRLEN(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) {
@@ -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
@@ -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 "";
}
}
@@ -417,47 +439,48 @@ int clr_history(const int histype)
/// @param histype may be one of the HIST_ values.
static int del_history_entry(int histype, char_u *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((char *)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;
}
@@ -504,16 +527,19 @@ void f_histadd(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -556,7 +582,7 @@ void f_histget(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -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 34692cdf64..b064a92b74 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -3,16 +3,28 @@
// Context: snapshot of the entire editor state as one big object/map
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.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"
+#include "nvim/types.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "context.c.generated.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),
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 ed0488cf76..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) {
@@ -146,7 +153,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
cts.cts_vcol += csize;
}
col = cts.cts_vcol;
- idx = (int)(cts.cts_ptr - (char *)line);
+ idx = (int)(cts.cts_ptr - line);
clear_chartabsize_arg(&cts);
// Handle all the special cases. The virtual_active() check
@@ -171,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;
@@ -201,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;
@@ -308,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;
}
@@ -346,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) {
@@ -412,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;
@@ -449,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,
@@ -481,7 +489,7 @@ bool leftcol_changed(void)
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.
@@ -489,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 73ad99876a..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 },
@@ -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 36df62b502..3dfbcd1f81 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
@@ -60,7 +76,7 @@ void do_debug(char *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 *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 *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 *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':
@@ -239,14 +261,14 @@ void do_debug(char *cmd)
last_cmd = CMD_STEP;
break;
case CMD_BACKTRACE:
- do_showbacktrace((char_u *)cmd);
+ do_showbacktrace(cmd);
continue;
case CMD_FRAME:
if (*p == NUL) {
- do_showbacktrace((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 *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;
@@ -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);
@@ -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 : (char_u *)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);
}
}
}
@@ -720,7 +749,7 @@ void ex_breaklist(exarg_T *eap)
/// @param after after this line number
linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
{
- return debuggy_find(file, (char_u *)fname, after, &dbg_breakp, NULL);
+ return debuggy_find(file, fname, after, &dbg_breakp, NULL);
}
/// @param file true for a file, false for a function
@@ -730,7 +759,7 @@ linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
/// @returns true if profiling is on for a function or sourced file.
bool has_profiling(bool file, char *fname, bool *fp)
{
- return debuggy_find(file, (char_u *)fname, (linenr_T)0, &prof_ga, 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 *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);
}
@@ -773,7 +802,7 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
// while matching should abort it.
prev_got_int = got_int;
got_int = false;
- if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) {
+ if (vim_regexec_prog(&bp->dbg_prog, false, (char_u *)name, (colnr_T)0)) {
lnum = bp->dbg_lnum;
if (fp != NULL) {
*fp = bp->dbg_forceit;
@@ -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;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 9f3e5a8638..037eb9f0d9 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.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
-#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/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 +70,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 +120,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 +174,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 +314,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 +348,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 +367,7 @@ next_mark:
state->conceal = conceal;
state->conceal_char = conceal_char;
state->conceal_attr = conceal_attr;
+ state->spell = spell;
return attr;
}
@@ -394,7 +407,7 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattr
}
if (j < SIGN_SHOW_MAX) {
sattrs[j] = (SignTextAttrs) {
- .text = decor->sign_text,
+ .text = (char *)decor->sign_text,
.hl_attr_id = decor->sign_hl_id == 0 ? 0 : syn_id2attr(decor->sign_hl_id),
.priority = decor->priority
};
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 8f28442d41..cee1eb2f94 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,6 +53,7 @@ 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
@@ -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 1cfb535fe8..45f00cb41e 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
@@ -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 *line_org = (char *)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++) {
@@ -592,9 +613,9 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
break;
}
- if (diff_cmp((char_u *)line_org, ml_get_buf(tp->tp_diffbuf[i_new],
- dp->df_lnum[i_new] + off_new,
- false)) != 0) {
+ if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new],
+ dp->df_lnum[i_new] + off_new,
+ false)) != 0) {
break;
}
}
@@ -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,7 +813,7 @@ 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".
@@ -803,7 +823,7 @@ static int diff_write(buf_T *buf, diffin_T *din)
// 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, 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);
+ esc_name = (char *)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.
@@ -1429,7 +1449,7 @@ void diff_win_options(win_T *wp, int addbuf)
free_string_option(wp->w_p_fdc);
wp->w_p_fdc = xstrdup("2");
assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9);
- snprintf(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);
@@ -1482,7 +1502,7 @@ void ex_diffoff(exarg_T *eap)
free_string_option(wp->w_p_fdm);
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 = xstrdup(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;
@@ -1526,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 linebuf[LBUFLEN]; // only need to hold the diff line
- char *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 **)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(*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((char_u *)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((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501
- && (STRNCMP(line, "+++ ", 4) == 0)
- && (vim_fgets((char_u *)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((char_u *)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((char_u *)line, hunk) == FAIL) {
- 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;
}
}
- // 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);
@@ -1739,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);
}
@@ -1782,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
@@ -1793,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) {
@@ -1828,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;
@@ -1838,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;
@@ -1892,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
@@ -1920,11 +2242,11 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
}
for (int i = 0; i < dp->df_count[idx1]; i++) {
- char *line = (char *)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((char_u *)line, ml_get_buf(curtab->tp_diffbuf[idx2],
- dp->df_lnum[idx2] + i, false));
+ int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
+ dp->df_lnum[idx2] + i, false));
xfree(line);
if (cmp != 0) {
@@ -1936,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;
@@ -1968,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)) {
+ && (*(char_u *)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) {
@@ -1996,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;
@@ -2023,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) {
@@ -2038,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;
@@ -2060,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;
+ }
}
}
}
@@ -2134,6 +2461,7 @@ 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;
@@ -2141,68 +2469,72 @@ int diffopt_changed(void)
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)) {
@@ -2231,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;
@@ -2281,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 *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 *line_org = (char *)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) {
@@ -2306,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);
@@ -2313,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++) {
@@ -2322,8 +2660,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
continue;
}
added = false;
- line_new = (char *)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;
@@ -2338,7 +2675,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
si_org = (int)(skipwhite(line_org + si_org) - line_org);
si_new = (int)(skipwhite(line_new + si_new) - line_new);
} else {
- if (!diff_equal_char((char_u *)line_org + si_org, (char_u *)line_new + si_new, &l)) {
+ int l;
+ if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
break;
}
si_org += l;
@@ -2348,8 +2686,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
// Move back to first byte of character in both lines (may
// have "nn^" in line_org and "n^ in line_new).
- si_org -= utf_head_off((char_u *)line_org, (char_u *)line_org + si_org);
- si_new -= utf_head_off((char_u *)line_new, (char_u *)line_new + si_new);
+ si_org -= utf_head_off(line_org, line_org + si_org);
+ si_new -= utf_head_off(line_new, line_new + si_new);
if (*startp > si_org) {
*startp = si_org;
@@ -2357,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
@@ -2378,12 +2716,13 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
ei_new--;
}
} else {
- const char_u *p1 = (char_u *)line_org + ei_org;
- const char_u *p2 = (char_u *)line_new + ei_new;
+ const char *p1 = line_org + ei_org;
+ const char *p2 = line_new + ei_new;
- p1 -= utf_head_off((char_u *)line_org, p1);
- p2 -= utf_head_off((char_u *)line_new, p2);
+ p1 -= utf_head_off(line_org, p1);
+ p2 -= utf_head_off(line_new, p2);
+ int l;
if (!diff_equal_char(p1, p2, &l)) {
break;
}
@@ -2413,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) {
@@ -2462,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 {
@@ -2504,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 *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);
@@ -2531,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)
@@ -2553,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)
@@ -2565,11 +2886,12 @@ void ex_diffgetput(exarg_T *eap)
}
} else {
// Buffer number or pattern given. Ignore trailing white space.
- p = eap->arg + STRLEN(eap->arg);
+ char *p = eap->arg + strlen(eap->arg);
while (p > eap->arg && ascii_iswhite(p[-1])) {
p--;
}
+ int i;
for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) {}
if (eap->arg + i == p) {
@@ -2583,7 +2905,7 @@ void ex_diffgetput(exarg_T *eap)
return;
}
}
- buf = buflist_findnr(i);
+ buf_T *buf = buflist_findnr(i);
if (buf == NULL) {
semsg(_("E102: Can't find buffer \"%s\""), eap->arg);
@@ -2618,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.
@@ -2643,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) {
@@ -2677,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;
@@ -2702,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) {
@@ -2713,12 +3091,12 @@ 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 = (char *)vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, 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++;
@@ -2729,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)
@@ -2746,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);
}
}
@@ -2772,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
@@ -2784,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;
}
@@ -2796,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".
@@ -3056,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;
@@ -3064,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++;
@@ -3108,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;
@@ -3164,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 e4528f9038..33038dfb9f 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)
{
@@ -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;
}
@@ -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'.
@@ -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;
@@ -1817,54 +1827,56 @@ static void printdigraph(const digr_T *dp, result_T *previous)
char_u 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_u *p = &buf[0];
+ *p++ = dp->char1;
+ *p++ = dp->char2;
+ *p++ = ' ';
+ *p = NUL;
+ msg_outtrans((char *)buf);
+ p = buf;
- // add a space to draw a composing char on
- if (utf_iscomposing(dp->result)) {
- *p++ = ' ';
- }
- p += utf_char2bytes(dp->result, (char *)p);
+ // add a space to draw a composing char on
+ if (utf_iscomposing(dp->result)) {
+ *p++ = ' ';
+ }
+ p += utf_char2bytes(dp->result, (char *)p);
- *p = NUL;
- msg_outtrans_attr((char *)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((char *)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);
}
/// Get the two digraph characters from a typval.
@@ -1926,15 +1938,15 @@ void f_digraph_get(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -2010,8 +2022,8 @@ void f_digraph_setlist(typval_T *argvars, typval_T *rettv, EvalFuncData 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 = (char_u *)skiptowhite((char *)p);
- kp->from = vim_strnsave(p, (size_t)(s - p));
- p = (char_u *)skipwhite((char *)s);
- s = (char_u *)skiptowhite((char *)p);
- kp->to = vim_strnsave(p, (size_t)(s - p));
+ s = skiptowhite(p);
+ kp->from = xstrnsave(p, (size_t)(s - p));
+ p = skipwhite(s);
+ s = skiptowhite(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 b6c4400c60..8fc1a4b029 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,39 +140,39 @@ 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;
}
- u8c = utfc_ptr2char((char *)p, u8cc);
+ u8c = utfc_ptr2char(p, u8cc);
if (*p == TAB) {
cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
for (int c = 0; c < cells; c++) {
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((char *)p + c_len, pcc);
+ pc = utfc_ptr2char(p + c_len, pcc);
nc = s->prev_c;
pc1 = pcc[0];
}
@@ -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,98 @@ 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.
+///
+/// @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, char_u *extra,
+ foldinfo_T foldinfo, SignTextAttrs *sattrs, statuscol_T *stcp)
+{
+ long relnum = 0;
+ bool wrapped = row != startrow + filler_lines;
+ bool use_cul = use_cursor_line_sign(wp, lnum);
+
+ // Set num, fold and sign text and attrs, empty when wrapped
+ if (row == startrow) {
+ relnum = labs(get_cursor_rel_lnum(wp, lnum));
+ stcp->num_attr = sign_num_attr ? sign_num_attr
+ : get_line_number_attr(wp, lnum, row, startrow, filler_lines);
+
+ 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);
+ }
+ }
+
+ int i = 0;
+ for (; i < wp->w_scwidth; i++) {
+ SignTextAttrs *sattr = wrapped ? 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, row == startrow, wrapped, 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(LineDrawState *draw_state, int *char_attr, int *n_extrap,
+ int *c_extrap, int *c_finalp, char_u *extra, char **pp_extra,
+ statuscol_T *stcp)
+{
+ *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
@@ -480,29 +603,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
// 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 +647,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 +659,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 +698,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 +710,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 +723,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 +768,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);
@@ -688,7 +802,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
nextline[SPWORDLEN] = NUL;
if (lnum < wp->w_buffer->b_ml.ml_line_count) {
line = ml_get_buf(wp->w_buffer, lnum + 1, false);
- spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
+ spell_cat_line(nextline + SPWORDLEN, (char_u *)line, SPWORDLEN);
}
// When a word wrapped from the previous line the start of the current
@@ -772,7 +886,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 +910,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,7 +930,9 @@ 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;
@@ -864,13 +981,13 @@ 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) {
// For checking first word with a capital skip white space.
if (cap_col == 0) {
- cap_col = (int)getwhitecols((char *)line);
+ cap_col = (int)getwhitecols(line);
}
// To be able to spell-check over line boundaries copy the end of the
@@ -881,7 +998,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 +1009,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 +1026,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,7 +1056,7 @@ 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;
+ char *prev_ptr = ptr;
chartabsize_T cts;
int charsize;
@@ -947,11 +1064,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
while (cts.cts_vcol < v && *cts.cts_ptr != NUL) {
charsize = win_lbr_chartabsize(&cts, NULL);
cts.cts_vcol += charsize;
- prev_ptr = (char_u *)cts.cts_ptr;
+ prev_ptr = cts.cts_ptr;
MB_PTR_ADV(cts.cts_ptr);
}
vcol = cts.cts_vcol;
- ptr = (char_u *)cts.cts_ptr;
+ ptr = cts.cts_ptr;
clear_chartabsize_arg(&cts);
// When:
@@ -974,7 +1091,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
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) >= charsize || *ptr == TAB) {
+ if (utf_ptr2cells(ptr) >= charsize || *ptr == TAB) {
n_skip = (int)(v - vcol);
}
}
@@ -1011,7 +1128,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// no bad word found at line start, don't check until end of a
// word
spell_hlf = HLF_COUNT;
- word_end = (int)(spell_to_word_end(ptr, wp) - line + 1);
+ word_end = (int)(spell_to_word_end((char_u *)ptr, wp) - (char_u *)line + 1);
} else {
// bad word found, use attributes until end of word
assert(len <= INT_MAX);
@@ -1075,6 +1192,13 @@ 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;
// Repeat for the whole displayed line.
for (;;) {
@@ -1102,9 +1226,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
}
}
+ // Skip fold, sign and number states if 'statuscolumn' is set.
+ if (draw_state == WL_FOLD - 1 && n_extra == 0 && statuscol.draw) {
+ 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
@@ -1131,7 +1259,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (wp->w_scwidth > 0) {
get_sign_display_info(false, wp, lnum, sattrs, row,
startrow, filler_lines, filler_todo,
- &c_extra, &c_final, extra, sizeof(extra),
+ &c_extra, &c_final, (char *)extra, sizeof(extra),
&p_extra, &n_extra, &char_attr, sign_idx,
sign_cul_attr);
sign_idx++;
@@ -1156,7 +1284,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) {
get_sign_display_info(true, wp, lnum, sattrs, row,
startrow, filler_lines, filler_todo,
- &c_extra, &c_final, extra, sizeof(extra),
+ &c_extra, &c_final, (char *)extra, sizeof(extra),
&p_extra, &n_extra, &char_attr, sign_idx,
sign_cul_attr);
} else {
@@ -1164,21 +1292,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (row == startrow + filler_lines) {
get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
if (wp->w_skipcol > 0) {
- for (p_extra = extra; *p_extra == ' '; p_extra++) {
+ for (p_extra = (char *)extra; *p_extra == ' '; p_extra++) {
*p_extra = '-';
}
}
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);
- p2 = (char_u *)skiptowhite((char *)p2) - 1;
- for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
- const char_u t = *p1;
+ char *p2 = skipwhite((char *)extra);
+ p2 = skiptowhite(p2) - 1;
+ for (char *p1 = skipwhite((char *)extra); p1 < p2; p1++, p2--) {
+ const char t = *p1;
*p1 = *p2;
*p2 = t;
}
}
- p_extra = extra;
+ p_extra = (char *)extra;
c_extra = NUL;
} else {
c_extra = ' ';
@@ -1194,7 +1322,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, extra, foldinfo, sattrs, &statuscol);
+ if (wp->w_redr_statuscol) {
+ return 0;
+ }
+ }
+ get_statuscol_display_info(&draw_state, &char_attr, &n_extra, &c_extra,
+ &c_final, extra, &p_extra, &statuscol);
+ }
+ }
+
+ if (draw_state == WL_STC && n_extra == 0) {
win_col_offset = off;
}
@@ -1269,13 +1413,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 = (char *)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;
@@ -1326,7 +1470,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);
@@ -1350,7 +1494,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
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);
@@ -1386,11 +1530,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;
}
@@ -1407,8 +1551,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
@@ -1480,16 +1624,16 @@ 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;
} else if (mb_l > 1) {
- mb_c = utfc_ptr2char((char *)p_extra, u8cc);
+ mb_c = utfc_ptr2char(p_extra, u8cc);
mb_utf8 = true;
c = 0xc0;
}
@@ -1533,14 +1677,14 @@ 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((char *)ptr, u8cc);
+ mb_c = utfc_ptr2char(ptr, u8cc);
// Overlong encoded ASCII or ASCII with composing char
// is displayed normally, except a NUL.
if (mb_c < 0x80) {
@@ -1568,14 +1712,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Non-BMP character : display as ? or fullwidth ?.
transchar_hex((char *)extra, mb_c);
if (wp->w_p_rl) { // reverse
- rl_mirror(extra);
+ rl_mirror((char *)extra);
}
- p_extra = extra;
- c = *p_extra;
+ p_extra = (char *)extra;
+ c = (uint8_t)(*p_extra);
mb_c = mb_ptr2char_adv((const char_u **)&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) {
@@ -1595,10 +1739,10 @@ 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((char *)ptr + mb_l, pcc);
+ pc = utfc_ptr2char(ptr + mb_l, pcc);
nc = prev_c;
pc1 = pcc[0];
}
@@ -1646,7 +1790,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).
@@ -1698,6 +1843,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
@@ -1706,11 +1872,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;
@@ -1724,7 +1890,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
p = prev_ptr;
}
cap_col -= (int)(prev_ptr - line);
- size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange);
+ size_t tmplen = spell_check(wp, (char_u *)p, &spell_hlf, &cap_col, nochange);
assert(tmplen <= INT_MAX);
len = (int)tmplen;
word_end = (int)v + len;
@@ -1781,30 +1947,11 @@ 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);
+ char *p = ptr - (mb_off + 1);
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, p);
@@ -1813,11 +1960,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// We have just drawn the showbreak value, no need to add
// space for it again.
if (vcol == vcol_sbr) {
- n_extra -= mb_charlen(get_showbreak_value(wp));
+ n_extra -= mb_charlen((char *)get_showbreak_value(wp));
if (n_extra < 0) {
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,
@@ -1908,7 +2061,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// Only adjust the tab_len, when at the first column after the
// showbreak value was drawn.
if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) {
- vcol_adjusted = vcol - mb_charlen(sbr);
+ vcol_adjusted = vcol - mb_charlen((char *)sbr);
}
// tab amount depends on current column
tab_len = tabstop_padding((colnr_T)vcol_adjusted,
@@ -1918,7 +2071,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;
@@ -1959,7 +2112,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;
@@ -2051,7 +2204,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;
}
@@ -2061,18 +2214,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);
@@ -2180,7 +2335,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;
@@ -2205,7 +2360,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
@@ -2241,7 +2396,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;
@@ -2471,7 +2629,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
@@ -2554,7 +2712,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++;
}
@@ -2564,7 +2722,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;
}
@@ -2600,7 +2758,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
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);
+ kHlModeReplace, grid->cols, 0);
}
} else {
draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
@@ -2631,6 +2789,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;
}
@@ -2659,6 +2818,16 @@ 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 + 1 || row == startrow + filler_lines) {
+ // Re-evaluate 'statuscolumn' for the first wrapped row and non filler line
+ statuscol.textp = NULL;
+ } else { // Otherwise just reset the text/hlrec pointers
+ 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--;
// When the filler lines are actually below the last line of the
// file, don't draw the line itself, break here.
@@ -2669,7 +2838,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 29ff261461..568dbe99c0 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -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;
@@ -115,7 +134,7 @@ void conceal_check_cursor_line(void)
}
}
-/// Resize the screen to Rows and Columns.
+/// Resize default_grid to Rows and Columns.
///
/// Allocate default_grid.chars[] and other grid arrays.
///
@@ -125,19 +144,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 +168,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 +182,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 +194,20 @@ retry:
default_grid.col_offset = 0;
default_grid.handle = DEFAULT_GRID_HANDLE;
- must_redraw = UPD_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;
@@ -281,13 +267,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 +280,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--;
- if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
- screenclear();
+ // 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;
+ }
+
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
}
+ resizing_autocmd = false;
+ redraw_all_later(UPD_CLEAR);
+
if (starting != NO_SCREEN) {
maketitle();
@@ -320,14 +325,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 +343,7 @@ void screen_resize(int width, int height)
}
if (State & MODE_CMDLINE) {
redraw_popupmenu = false;
- update_screen(UPD_NOT_VALID);
+ update_screen();
redrawcmdline();
if (pum_drawn()) {
cmdline_pum_display(false);
@@ -350,12 +352,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(UPD_NOT_VALID);
+ update_screen();
if (redrawing()) {
setcursor();
}
@@ -370,9 +372,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 UPD_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 +380,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 +389,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 < UPD_NOT_VALID) {
- type = UPD_NOT_VALID;
- }
-
// Postpone the redrawing when it's not needed and when being called
// recursively.
if (!redrawing() || updating_screen) {
- must_redraw = type;
- if (type > UPD_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.
@@ -443,70 +431,48 @@ int update_screen(int type)
msg_grid.cols, false);
}
}
- if (msg_use_msgsep()) {
- msg_grid.throttled = false;
- // UPD_CLEAR is already handled
- if (type == UPD_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);
+ 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);
+ }
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ continue;
}
- 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, UPD_NOT_VALID);
- }
- if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
- 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 && Rows - p_ch - 1 > valid) {
- curwin->w_redr_status = true;
+ if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
+ wp->w_redr_status = true;
}
}
- msg_grid_set_pos(Rows - (int)p_ch, false);
- msg_grid_invalid = false;
- } else if (type != UPD_CLEAR) {
- if (msg_scrolled > Rows - 5) { // redrawing is faster
- type = UPD_NOT_VALID;
- } else {
- 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 < UPD_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 = UPD_REDRAW_TOP;
- } else {
- wp->w_redr_type = UPD_NOT_VALID;
- if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
- wp->w_redr_status = true;
- }
- }
- }
- }
- if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
- curwin->w_redr_status = true;
- }
- redraw_cmdline = true;
- redraw_tabline = true;
+ if (is_stl_global && Rows - p_ch - 1 > valid) {
+ curwin->w_redr_status = 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)
@@ -520,7 +486,8 @@ int update_screen(int type)
}
if (type == UPD_CLEAR) { // first clear screen
- screenclear(); // will reset clear_cmdline
+ screenclear(); // will reset clear_cmdline
+ // and set UPD_NOT_VALID for each window
cmdline_screen_cleared(); // clear external cmdline state
type = UPD_NOT_VALID;
// must_redraw may be set indirectly, avoid another redraw later
@@ -530,16 +497,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 == UPD_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)) {
@@ -553,31 +519,14 @@ int update_screen(int type)
// Force redraw when width of 'number' or 'relativenumber' column
// changes.
+ // 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_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc)
? number_width(curwin) : 0)) {
curwin->w_redr_type = UPD_NOT_VALID;
}
- // Only start redrawing if there is really something to do.
- if (type == UPD_INVERTED) {
- update_curswant();
- }
- if (curwin->w_redr_type < type
- && !((type == UPD_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 == UPD_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;
- }
-
// Redraw the tab pages line if needed.
if (redraw_tabline || type >= UPD_NOT_VALID) {
update_window_hl(curwin, type >= UPD_NOT_VALID);
@@ -681,6 +630,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;
@@ -701,9 +664,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]);
}
@@ -859,12 +837,10 @@ 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);
+ 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);
}
@@ -873,12 +849,10 @@ static void draw_vsep_win(win_T *wp)
/// 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);
+ 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);
}
@@ -982,11 +956,6 @@ static void draw_sep_connectors_win(win_T *wp)
/// 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
@@ -998,31 +967,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 >= UPD_NOT_VALID) {
+ // TODO(bfredl): should only be implied for CLEAR, not NOT_VALID!
wp->w_redr_status = true;
+
wp->w_lines_valid = 0;
}
@@ -1044,16 +1010,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) {
+ 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 = i;
+ wp->w_nrwidth = nrwidth;
if (buf->terminal) {
terminal_check_size(buf->terminal);
@@ -1101,12 +1083,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;
}
}
}
@@ -1125,7 +1107,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;
@@ -1173,14 +1155,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 == UPD_REDRAW_TOP) {
- j = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
+ 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;
@@ -1216,6 +1199,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;
@@ -1233,7 +1217,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;
@@ -1243,6 +1227,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.
@@ -1254,6 +1239,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];
}
@@ -1273,9 +1259,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;
@@ -1303,6 +1289,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
@@ -1310,7 +1297,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
@@ -1448,7 +1435,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;
@@ -1507,9 +1494,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 {
@@ -1555,38 +1542,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)
@@ -1650,6 +1617,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
@@ -1680,10 +1648,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++;
@@ -1710,6 +1679,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
@@ -1719,6 +1689,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.
@@ -1797,7 +1768,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
@@ -1852,6 +1823,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;
@@ -1871,9 +1854,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;
@@ -1890,30 +1875,34 @@ 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, "@@", 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
@@ -1928,8 +1917,19 @@ win_update_start:
// Make sure the rest of the screen is blank.
// write the "eob" character from 'fillchars' 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,
+ // 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);
@@ -1972,11 +1972,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;
}
@@ -1988,7 +1988,7 @@ 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 UPD_CLEAR, type UPD_NOT_VALID will do nothing.
@@ -2041,12 +2041,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;
+ }
}
}
}
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
index ef99899581..c14703dfa9 100644
--- a/src/nvim/drawscreen.h
+++ b/src/nvim/drawscreen.h
@@ -1,7 +1,10 @@
#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
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index aee8389900..36a8674730 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,19 +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"
@@ -83,16 +86,18 @@ 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.
@@ -145,14 +150,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);
@@ -278,7 +283,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;
}
@@ -319,11 +324,11 @@ static void insert_enter(InsertState *s)
// Get the current length of the redo buffer, those characters have to be
// skipped if we want to get to the inserted characters.
- s->ptr = get_inserted();
+ s->ptr = (char *)get_inserted();
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);
}
@@ -441,8 +446,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
@@ -904,6 +908,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>
@@ -1230,9 +1240,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.
@@ -1288,7 +1297,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()
@@ -1336,8 +1345,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.
@@ -1350,7 +1358,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
}
@@ -1359,9 +1367,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;
@@ -1391,10 +1397,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
@@ -1438,25 +1442,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);
@@ -1466,17 +1470,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);
@@ -1484,9 +1488,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;
@@ -1496,8 +1500,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();
@@ -1508,12 +1512,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) {
@@ -1528,10 +1530,8 @@ void edit_unputchar(void)
}
}
-/*
- * 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;
@@ -1544,8 +1544,8 @@ 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();
- curwin->w_cursor.col -= utf_head_off(p, p + col);
+ char_u *p = (char_u *)get_cursor_line_ptr();
+ curwin->w_cursor.col -= utf_head_off((char *)p, (char *)p + col);
curs_columns(curwin, false); // Recompute w_wrow and w_wcol
if (curwin->w_wcol < curwin->w_grid.cols) {
edit_putchar('$', false);
@@ -1554,10 +1554,8 @@ 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.
- */
+// Call this function before moving the cursor from the normal insert position
+// in insert mode.
void undisplay_dollar(void)
{
if (dollar_vcol >= 0) {
@@ -1586,11 +1584,11 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
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;
}
@@ -1600,11 +1598,9 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
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
@@ -1614,10 +1610,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;
}
@@ -1626,9 +1620,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 {
@@ -1643,20 +1635,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;
}
@@ -1670,9 +1658,9 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
// Advance the cursor until we reach the right screen column.
last_vcol = 0;
- ptr = get_cursor_line_ptr();
+ ptr = (char_u *)get_cursor_line_ptr();
chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr);
+ init_chartabsize_arg(&cts, curwin, 0, 0, (char *)ptr, (char *)ptr);
while (cts.cts_vcol <= (int)curwin->w_virtcol) {
last_vcol = cts.cts_vcol;
if (cts.cts_vcol > 0) {
@@ -1687,10 +1675,8 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
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);
@@ -1701,10 +1687,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;
}
@@ -1718,9 +1702,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
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) {
@@ -1761,14 +1743,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++;
@@ -1777,7 +1759,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
backspace_until_column(0);
// Insert new stuff into line again
- ins_bytes((char *)new_line);
+ ins_bytes(new_line);
xfree(new_line);
@@ -1801,7 +1783,7 @@ 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
}
@@ -1841,7 +1823,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;
@@ -1968,7 +1950,7 @@ 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
@@ -1980,16 +1962,16 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
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((char *)p);
- AppendToRedobuffLit((char *)p, -1);
+ ins_str(p);
+ AppendToRedobuffLit(p, -1);
ctrlv = false;
}
}
@@ -1998,15 +1980,13 @@ 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) == '^')
///
@@ -2082,7 +2062,7 @@ 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();
+ char_u *line = (char_u *)get_cursor_line_ptr();
int i = get_leader_len((char *)line, &p, false, true);
if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) { // Just checking
// Skip middle-comment string
@@ -2144,11 +2124,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();
@@ -2164,27 +2144,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((char *)buf);
+ 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;
@@ -2192,7 +2172,7 @@ 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(buf, (size_t)cc);
AppendCharToRedobuff(c);
@@ -2207,9 +2187,7 @@ void insertchar(int c, int flags, int second_indent)
}
}
-/*
- * 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];
@@ -2259,10 +2237,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) {
@@ -2273,11 +2249,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) {
@@ -2322,21 +2296,19 @@ 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".
- */
- ptr = get_inserted();
+ // 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 = (char *)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 = (char_u *)ptr;
last_insert_skip = new_insert_skip;
} else {
xfree(ptr);
@@ -2368,8 +2340,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) {
@@ -2438,10 +2410,8 @@ 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;
@@ -2466,13 +2436,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) {
@@ -2484,7 +2452,7 @@ void beginline(int flags)
if (flags & (BL_WHITE | BL_SOL)) {
char_u *ptr;
- for (ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr)
+ for (ptr = (char_u *)get_cursor_line_ptr(); ascii_iswhite(*ptr)
&& !((flags & BL_FIX) && ptr[1] == NUL); ptr++) {
curwin->w_cursor.col++;
}
@@ -2493,13 +2461,11 @@ void beginline(int flags)
}
}
-/*
- * 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)
{
@@ -2510,7 +2476,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;
@@ -2519,7 +2485,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
}
@@ -2562,7 +2528,7 @@ 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;
}
@@ -2585,98 +2551,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
@@ -2721,7 +2701,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;
@@ -2761,25 +2741,23 @@ char_u *get_last_insert(void)
return last_insert + last_insert_skip;
}
-/*
- * Get last inserted string, and remove trailing <Esc>.
- * Returns pointer to allocated memory (must be freed) or NULL.
- */
+// 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)
{
- 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((char *)last_insert + last_insert_skip);
+ len = (int)strlen(s);
if (len > 0 && s[len - 1] == ESC) { // remove trailing ESC
s[len - 1] = NUL;
}
- return s;
+ return (char_u *)s;
}
/// Check the word in front of the cursor for an abbreviation.
@@ -2803,20 +2781,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
@@ -2901,10 +2877,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;
@@ -2951,10 +2925,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);
@@ -2962,15 +2934,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;
@@ -2978,7 +2948,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;
@@ -2989,11 +2959,11 @@ static void replace_do_bs(int limit_col)
// Get the number of screen cells used by the character we are
// going to delete.
getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
- orig_vcols = win_chartabsize(curwin, (char *)get_cursor_pos_ptr(), start_vcol);
+ orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol);
}
(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();
@@ -3001,11 +2971,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, (char *)p + i, vcol);
- i += utfc_ptr2len((char *)p) - 1;
+ vcol += win_chartabsize(curwin, p + i, vcol);
+ i += utfc_ptr2len(p) - 1;
}
vcol -= start_vcol;
@@ -3033,36 +3003,6 @@ 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)
@@ -3078,11 +3018,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) {
@@ -3091,15 +3031,13 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
}
if (*curbuf->b_p_inde != NUL) {
- look = (char_u *)curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
+ look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
} else {
- look = (char_u *)curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
+ 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;
@@ -3149,8 +3087,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;
}
}
@@ -3194,7 +3132,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
return true;
}
- if (keytyped == get_special_key_code(look + 1)) {
+ if (keytyped == get_special_key_code((char_u *)look + 1)) {
return true;
}
}
@@ -3213,19 +3151,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);
@@ -3237,28 +3175,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]))) {
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;
@@ -3272,7 +3210,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) {
@@ -3281,14 +3219,12 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
}
// Skip over ", ".
- look = (char_u *)skip_to_option_part((char *)look);
+ 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
{
@@ -3390,9 +3326,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
@@ -3474,9 +3408,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;
@@ -3530,9 +3462,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)) {
@@ -3576,10 +3506,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) {
@@ -3622,10 +3550,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))
@@ -3663,10 +3589,8 @@ 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) {
@@ -3733,9 +3657,7 @@ 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" :
@@ -3753,9 +3675,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) {
@@ -3772,13 +3692,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) {
@@ -3786,9 +3704,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--;
@@ -3805,7 +3721,7 @@ static void ins_shift(int c, int lastc)
change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, 0, true);
}
- if (did_ai && *skipwhite((char *)get_cursor_line_ptr()) != NUL) {
+ if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) {
did_ai = false;
}
did_si = false;
@@ -3844,9 +3760,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();
@@ -3939,13 +3853,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
@@ -3965,10 +3877,10 @@ 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;
}
@@ -4025,9 +3937,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
@@ -4061,7 +3971,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);
}
@@ -4120,7 +4030,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
} else {
const int l_p_deco = p_deco;
if (l_p_deco) {
- (void)utfc_ptr2char((char *)get_cursor_pos_ptr(), cpc);
+ (void)utfc_ptr2char(get_cursor_pos_ptr(), cpc);
}
(void)del_char(false);
// If there are combining characters and 'delcombine' is set
@@ -4248,7 +4158,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 {
@@ -4373,7 +4283,7 @@ 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++;
@@ -4589,14 +4499,12 @@ static bool ins_tab(void)
}
}
- /*
- * 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 *saved_line = NULL; // init for GCC
pos_T pos;
pos_T fpos;
pos_T *cursor;
@@ -4609,10 +4517,10 @@ static bool ins_tab(void)
if (State & VREPLACE_FLAG) {
pos = curwin->w_cursor;
cursor = &pos;
- saved_line = vim_strsave(get_cursor_line_ptr());
- ptr = saved_line + pos.col;
+ saved_line = xstrdup(get_cursor_line_ptr());
+ ptr = (char_u *)saved_line + pos.col;
} else {
- ptr = get_cursor_pos_ptr();
+ ptr = (char_u *)get_cursor_pos_ptr();
cursor = &curwin->w_cursor;
}
@@ -4642,7 +4550,7 @@ static bool ins_tab(void)
char_u *tab = (char_u *)"\t";
chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab);
+ init_chartabsize_arg(&cts, curwin, 0, vcol, (char *)tab, (char *)tab);
// Use as many TABs as possible. Beware of 'breakindent', 'showbreak'
// and 'linebreak' adding extra virtual columns.
@@ -4671,7 +4579,7 @@ static bool ins_tab(void)
if (change_col >= 0) {
int repl_off = 0;
// Skip over the spaces we need.
- init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr);
+ init_chartabsize_arg(&cts, curwin, 0, vcol, (char *)ptr, (char *)ptr);
while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') {
cts.cts_vcol += lbr_chartabsize(&cts);
cts.cts_ptr++;
@@ -4691,7 +4599,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)) {
@@ -4716,7 +4624,7 @@ static bool ins_tab(void)
// Insert each char in saved_line from changed_col to
// ptr-cursor
- ins_bytes_len((char *)saved_line + change_col, (size_t)(cursor->col - change_col));
+ ins_bytes_len(saved_line + change_col, (size_t)(cursor->col - change_col));
}
}
@@ -4742,11 +4650,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);
@@ -4765,7 +4671,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);
@@ -4780,11 +4686,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;
@@ -4853,10 +4757,8 @@ 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;
@@ -4869,12 +4771,12 @@ int ins_copychar(linenr_T lnum)
}
// try to advance to the cursor column
- line = ml_get(lnum);
+ line = (char_u *)ml_get(lnum);
prev_ptr = line;
validate_virtcol();
chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, lnum, 0, line, line);
+ init_chartabsize_arg(&cts, curwin, lnum, 0, (char *)line, (char *)line);
while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) {
prev_ptr = (char_u *)cts.cts_ptr;
cts.cts_vcol += lbr_chartabsize_adv(&cts);
@@ -4894,9 +4796,7 @@ int ins_copychar(linenr_T lnum)
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;
@@ -4933,10 +4833,8 @@ 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;
@@ -4944,21 +4842,17 @@ static void ins_try_si(int c)
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
- */
- ptr = ml_get(pos->lnum);
+ // 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 = (char_u *)ml_get(pos->lnum);
i = pos->col;
if (i > 0) { // skip blanks before '{'
while (--i > 0 && ascii_iswhite(ptr[i])) {}
@@ -4983,7 +4877,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 = (char_u *)skipwhite(ml_get(--(curwin->w_cursor.lnum)));
// ignore empty lines and lines starting with '#'.
if (*ptr != '#' && *ptr != NUL) {
@@ -5001,9 +4895,7 @@ static void ins_try_si(int c)
}
}
- /*
- * 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();
@@ -5016,10 +4908,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
@@ -5034,12 +4924,10 @@ 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".
- */
+// 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)
{
char buf[MB_MAXBYTES + 1];
@@ -5055,13 +4943,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));
}
}
@@ -5071,7 +4959,7 @@ static char_u *do_insert_char_pre(int c)
// Restore the State, it may have been changed.
State = save_State;
- return res;
+ return (char_u *)res;
}
bool get_can_cindent(void)
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 2dbaa2f8ac..f9825496a5 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -3,13 +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"
+#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"
@@ -22,37 +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
@@ -60,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";
@@ -132,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"
@@ -237,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_WRAP, "wrap", VAR_BOOL, VV_RO),
};
#undef VV
@@ -330,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;
}
@@ -358,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;
@@ -370,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;
@@ -485,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 = {
@@ -541,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;
@@ -552,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;
@@ -578,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
}
@@ -640,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(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;
@@ -679,7 +694,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- (void)eval_to_bool((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);
@@ -757,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;
}
@@ -767,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;
}
@@ -977,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);
}
}
@@ -986,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);
}
}
@@ -1069,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)) {
@@ -1093,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:
@@ -1106,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.
@@ -1147,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.
@@ -1215,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
@@ -1311,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)) {
@@ -1439,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;
}
}
@@ -1587,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)
{
@@ -1596,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.
@@ -1667,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)
@@ -1754,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;
}
@@ -1802,8 +1805,6 @@ notify:
}
}
-// TODO(ZyX-I): move to eval/ex_cmds
-
/// Evaluate the expression used in a ":for var in expr" command.
/// "arg" points to "var".
///
@@ -1879,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.
///
@@ -1921,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)
{
@@ -1960,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)) {
@@ -2059,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);
}
});
@@ -2077,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);
@@ -2117,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
@@ -2134,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
@@ -2148,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
@@ -2162,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
@@ -2175,12 +2171,10 @@ 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)
+int pattern_match(const char *pat, const char *text, bool ic)
{
int matches = 0;
regmatch_T regmatch;
@@ -2188,7 +2182,7 @@ int pattern_match(char *pat, char *text, bool ic)
// avoid 'l' flag in 'cpoptions'
char *save_cpo = p_cpo;
p_cpo = empty_option;
- regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ 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);
@@ -2219,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.
@@ -2227,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);
@@ -2256,8 +2250,6 @@ 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
/// executed. The function may return OK, but the rettv will be of type
/// VAR_UNKNOWN. The function still returns FAIL for a syntax error.
@@ -2274,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);
}
@@ -2287,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
///
@@ -2364,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
///
@@ -2423,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
///
@@ -2482,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
@@ -2587,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
@@ -2750,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
@@ -2867,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
@@ -2906,6 +2893,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
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.
@@ -2918,6 +2906,20 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
}
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.
case '0':
@@ -2995,9 +2997,11 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
*arg = bp;
} else {
// decimal, hex or octal number
- vim_str2nr((char_u *)(*arg), NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
+ vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true);
if (len == 0) {
- semsg(_(e_invexpr2), *arg);
+ if (evaluate) {
+ semsg(_(e_invexpr2), *arg);
+ }
ret = FAIL;
break;
}
@@ -3029,7 +3033,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;
}
@@ -3040,7 +3044,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;
@@ -3120,6 +3124,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;
}
@@ -3209,13 +3215,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
@@ -3288,7 +3294,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
@@ -3343,8 +3349,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 '.'.
///
@@ -3396,7 +3400,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;
}
@@ -3644,8 +3648,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
@@ -3657,11 +3659,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);
@@ -3681,7 +3683,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) {
@@ -3826,7 +3828,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), (char_u *)name, flags, false, NULL);
if (extra != 0) {
name += extra;
if (name >= rettv->vval.v_string + len) {
@@ -3909,13 +3911,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++) {
@@ -3924,7 +3924,7 @@ static void partial_free(partial_T *pt)
xfree(pt->pt_argv);
tv_dict_unref(pt->pt_dict);
if (pt->pt_name != NULL) {
- func_unref(pt->pt_name);
+ func_unref((char_u *)pt->pt_name);
xfree(pt->pt_name);
} else {
func_ptr_unref(pt->pt_func);
@@ -3932,8 +3932,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)
@@ -3976,7 +3974,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);
@@ -3996,13 +3994,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;
}
@@ -4010,7 +4006,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;
}
@@ -4143,10 +4139,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);
@@ -4155,8 +4164,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)
@@ -4226,11 +4238,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);
}
@@ -4478,7 +4490,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack
// A partial does not have a copyID, because it cannot contain itself.
if (pt != NULL) {
- abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
+ abort = set_ref_in_func((char_u *)pt->pt_name, pt->pt_func, copyID);
if (pt->pt_dict != NULL) {
typval_T dtv;
@@ -4578,25 +4590,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;
@@ -4760,19 +4771,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)
{
@@ -4787,20 +4785,20 @@ 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))) {
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))) {
return;
@@ -4846,7 +4844,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
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) {
@@ -4988,9 +4986,8 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
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;
}
@@ -5010,19 +5007,12 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
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);
}
@@ -5060,7 +5050,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
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;
}
@@ -5113,7 +5103,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
func_ptr_ref(pt->pt_func);
xfree(name);
} else {
- pt->pt_name = (char_u *)name;
+ pt->pt_name = name;
func_ref((char_u *)name);
}
@@ -5130,46 +5120,6 @@ 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.
@@ -5186,121 +5136,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_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;
-}
-
-/// 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
@@ -5522,126 +5364,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
@@ -5694,7 +5416,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;
@@ -5778,7 +5500,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;
@@ -5799,8 +5522,14 @@ 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((char_u *)callback->data.funcref);
callback->type = kCallbackFuncref;
}
} else if (nlua_is_table_from_lua(arg)) {
@@ -5828,6 +5557,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
@@ -5839,7 +5569,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);
@@ -5858,13 +5588,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;
@@ -5875,15 +5600,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) {
@@ -6186,9 +5911,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;
@@ -6213,7 +5939,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;
@@ -6231,7 +5957,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';
@@ -6245,20 +5971,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';
}
});
@@ -6280,7 +6009,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;
@@ -6318,7 +6047,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;
@@ -6371,14 +6100,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;
}
@@ -6457,7 +6186,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;
@@ -6465,11 +6194,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)
@@ -6509,13 +6241,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;
@@ -6540,7 +6275,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;
}
@@ -6627,7 +6362,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);
@@ -6670,7 +6405,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 == '\'') {
@@ -6753,7 +6489,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);
@@ -6783,18 +6519,25 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
/// @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
/// 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)
{
@@ -6927,12 +6670,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.
@@ -7021,11 +6765,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);
@@ -7043,22 +6790,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;
}
@@ -7113,9 +6865,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:
@@ -7639,7 +7390,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--;
@@ -7736,7 +7487,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".
@@ -7745,19 +7496,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)) {
@@ -7826,7 +7577,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;
}
}
@@ -7861,9 +7612,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
@@ -7889,7 +7639,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) {
@@ -7901,7 +7651,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;
}
}
@@ -7925,8 +7675,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';
@@ -8067,8 +7816,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) {
@@ -8079,7 +7828,7 @@ repeat:
// Append a path separator to a directory.
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);
@@ -8113,17 +7862,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)) {
@@ -8155,7 +7904,7 @@ 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:\"
@@ -8257,7 +8006,7 @@ repeat:
s++;
}
- int sep = (char_u)(*s++);
+ int sep = (uint8_t)(*s++);
if (sep) {
// find end of pattern
p = vim_strchr(s, sep);
@@ -8272,7 +8021,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;
@@ -8290,17 +8039,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;
}
@@ -8312,7 +8061,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;
@@ -8331,11 +8080,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);
+ char *end = str + strlen(str);
while (vim_regexec_nl(&regmatch, (char_u *)str, (colnr_T)(tail - str))) {
// Skip empty match except for first match.
if (regmatch.startp[0] == regmatch.endp[0]) {
- if ((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);
@@ -8343,7 +8092,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
@@ -8356,14 +8105,14 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
(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,
REGSUB_COPY | REGSUB_MAGIC);
ga.ga_len += i + sublen - 1;
- tail = (char *)regmatch.endp[0];
+ tail = regmatch.endp[0];
if (*tail == NUL) {
break;
}
@@ -8497,9 +8246,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);
@@ -8606,7 +8355,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);
@@ -8630,10 +8379,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);
@@ -8656,9 +8405,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".
@@ -8856,7 +8605,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;
}
@@ -8871,10 +8620,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..d67414f12f 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_WRAP,
} VimVarIndex;
/// All recognized msgpack types
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 3e89489459..14be6aba73 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -38,7 +38,7 @@ return {
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},
@@ -73,12 +73,12 @@ return {
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={},
@@ -88,7 +88,6 @@ return {
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}},
@@ -119,7 +118,7 @@ return {
exists={args=1, base=1},
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
@@ -147,6 +146,7 @@ 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},
getchangelist={args={0, 1}, base=1},
getchar={args={0, 1}},
@@ -186,6 +186,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 +237,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},
@@ -290,6 +292,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},
@@ -374,6 +377,7 @@ return {
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},
@@ -425,6 +429,7 @@ return {
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..72eb0f8d67
--- /dev/null
+++ b/src/nvim/eval/buffer.c
@@ -0,0 +1,733 @@
+// 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/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_u *name = (char_u *)tv_get_string(&argvars[0]);
+
+ rettv->vval.v_number = buflist_add(*name == NUL ? NULL : (char *)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..03d4862550 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;
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index 8755ff48ac..e66dab1cff 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -2,11 +2,14 @@
#define NVIM_EVAL_ENCODE_H
#include <msgpack.h>
+#include <msgpack/pack.h>
#include <stddef.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 +18,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 +27,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 +47,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 0e0d0fe696..86d6063b01 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);
@@ -122,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 aa328d50ef..d6b9ca20fc 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,21 +226,21 @@ 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().
@@ -213,13 +250,13 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T
{
const EvalFuncDef *const fdef = find_internal_func((const char *)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,7 +268,7 @@ 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.
@@ -361,26 +398,6 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
(void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL);
}
-/// "append(lnum, string/list)" function
-static void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData 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, EvalFuncData fptr)
{
@@ -408,166 +425,6 @@ static void f_browsedir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
f_browse(argvars, rettv, fptr);
}
-/// 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, EvalFuncData 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, EvalFuncData fptr)
-{
- rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL);
-}
-
-/// "buflisted(expr)" function
-static 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
-static void f_bufload(typval_T *argvars, typval_T *unused, EvalFuncData 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, 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, 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
-static 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
-static void f_bufwinid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- buf_win_common(argvars, rettv, false);
-}
-
-/// "bufwinnr(nr)" function
-static void f_bufwinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- buf_win_common(argvars, rettv, true);
-}
-
/// Get buffer by number or pattern.
buf_T *tv_get_buf(typval_T *tv, int curtab_only)
{
@@ -578,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;
@@ -593,7 +450,7 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
char *save_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;
@@ -691,19 +548,19 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData 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]);
+ func = (char *)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) {
@@ -715,16 +572,16 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (argvars[2].v_type != VAR_DICT) {
emsg(_(e_dictreq));
if (owned) {
- func_unref(func);
+ func_unref((char_u *)func);
}
return;
}
selfdict = argvars[2].vval.v_dict;
}
- func_call(func, &argvars[1], partial, selfdict, rettv);
+ func_call((char_u *)func, &argvars[1], partial, selfdict, rettv);
if (owned) {
- func_unref(func);
+ func_unref((char_u *)func);
}
}
@@ -791,6 +648,14 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -798,7 +663,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -806,7 +671,6 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -832,15 +696,38 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -849,7 +736,7 @@ 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 *p = (char *)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)) {
@@ -862,6 +749,10 @@ 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
@@ -894,7 +785,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -930,12 +821,12 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -968,14 +859,14 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -1034,7 +925,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
@@ -1057,15 +948,15 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData 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 {
@@ -1073,10 +964,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
}
@@ -1132,29 +1023,6 @@ static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData 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, EvalFuncData fptr)
{
@@ -1270,7 +1138,7 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
@@ -1284,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) {
@@ -1293,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);
@@ -1305,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;
@@ -1343,19 +1216,21 @@ static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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
@@ -1364,10 +1239,10 @@ 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()
@@ -1484,82 +1359,6 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp
callback_free(&callback);
}
-/// "deletebufline()" function
-static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData fptr)
{
@@ -1590,9 +1389,10 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData 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)) {
@@ -1695,7 +1495,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -1722,23 +1522,22 @@ 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, 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;
}
@@ -1772,7 +1571,7 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
/// "executable()" function
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;
}
@@ -1799,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, 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;
@@ -1883,25 +1682,10 @@ static void f_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
execute_common(argvars, rettv, 0);
}
-/// "win_execute(win_id, command)" function
-static 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) {
- WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, 1));
- }
-}
-
/// "exepath()" function
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;
}
@@ -1986,7 +1770,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
size_t len;
char *errormsg = NULL;
- char_u *result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL, false);
+ char *result = (char *)eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL, false);
if (p_verbose == 0) {
emsg_off--;
} else if (errormsg != NULL) {
@@ -1999,7 +1783,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -2016,10 +1800,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData 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,
@@ -2053,6 +1836,12 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData 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]));
@@ -2066,9 +1855,17 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -2224,8 +2021,8 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, EvalFuncData 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 : (char_u *)curbuf->b_p_path;
+ char *fresult = NULL;
+ char *path = *curbuf->b_p_path == NUL ? (char *)p_path : curbuf->b_p_path;
int count = 1;
bool first = true;
bool error = false;
@@ -2242,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) {
@@ -2260,13 +2057,13 @@ 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 *)""
- : (char_u *)curbuf->b_p_sua));
+ ? ""
+ : curbuf->b_p_sua));
first = false;
if (fresult != NULL && rettv->v_type == VAR_LIST) {
@@ -2276,7 +2073,7 @@ 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;
}
}
@@ -2303,14 +2100,16 @@ 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;
}
}
@@ -2338,7 +2137,7 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "fnamemodify({fname}, {mods})" function
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]);
@@ -2350,7 +2149,7 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (*mods != NUL) {
size_t usedlen = 0;
(void)modify_fname((char *)mods, false, &usedlen,
- (char **)&fname, (char **)&fbuf, &len);
+ (char **)&fname, &fbuf, &len);
}
}
@@ -2437,7 +2236,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -2445,13 +2244,17 @@ static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData 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_u *)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) {
@@ -2486,118 +2289,6 @@ static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "getbufinfo()" function
-static 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 =
- (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, EvalFuncData 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, EvalFuncData fptr)
{
@@ -2721,15 +2412,6 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until());
}
-/// "getcmdwintype()" function
-static 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;
-}
-
/// `getcwd([{win}[, {tab}]])` function
///
/// Every scope not specified implies the currently selected scope object.
@@ -2834,13 +2516,13 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -2862,7 +2544,7 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -2870,7 +2552,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, EvalFuncData 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];
}
}
}
@@ -2919,7 +2601,7 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "getftype({fname})" function
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]);
@@ -2945,9 +2627,9 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -2981,24 +2663,6 @@ static void f_getjumplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "getline(lnum, [end])" function
-static 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);
-}
-
/// "getmarklist()" function
static void f_getmarklist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -3086,19 +2750,19 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -3157,38 +2821,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = xstrdup(buf);
}
-/// "gettabinfo()" function
-static 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;
- }
- }
-}
-
/// "gettagstack()" function
static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -3206,41 +2838,6 @@ static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_tagstack(wp, rettv->vval.v_dict);
}
-/// "getwininfo()" function
-static 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;
- }
- }
- }
-}
-
/// Dummy timer callback. Used by f_wait().
static void dummy_timer_due_cb(TimeWatcher *tw, void *data)
{}
@@ -3305,111 +2902,6 @@ static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
time_watcher_close(tw, dummy_timer_close_cb);
}
-/// "win_screenpos()" function
-static 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 || 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, 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);
-}
-
-/// "getwinpos({timeout})" function
-static 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
-static void f_getwinposx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-}
-
-/// "getwinposy()" function
-static void f_getwinposy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = -1;
-}
-
/// "glob()" function
static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -3441,11 +2933,11 @@ static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData 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++) {
@@ -3518,6 +3010,19 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData fptr)
{
@@ -3534,7 +3039,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
#ifdef UNIX
"unix",
#endif
-#if defined(WIN32)
+#ifdef MSWIN
"win32",
#endif
#ifdef _WIN64
@@ -3557,7 +3062,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"cmdwin",
"comments",
"conceal",
- "cscope",
"cursorbind",
"cursorshape",
#ifdef DEBUG
@@ -3608,8 +3112,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"packages",
"path_extra",
"persistent_undo",
- "postscript",
- "printer",
"profile",
"pythonx",
"reltime",
@@ -3626,7 +3128,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
"spell",
"syntax",
#if !defined(UNIX)
- "system", // TODO(SplinterOfChaos): This IS defined for UNIX!
+ "system",
#endif
"tablineat",
"tag_binary",
@@ -3849,7 +3351,7 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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
@@ -3862,13 +3364,11 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const char *const str = tv_get_string(&argvars[0]);
char buf1[NUMBUFLEN];
- char_u *const from =
- (char_u *)enc_canonize(enc_skip((char *)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 =
- (char_u *)enc_canonize(enc_skip((char *)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, (char *)from, (char *)to);
+ convert_setup(&vimconv, from, to);
// If the encodings are equal, no conversion needed.
if (vimconv.vc_type == CONV_NONE) {
@@ -3934,33 +3434,36 @@ static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
}
}
@@ -4140,11 +3643,11 @@ 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);
@@ -4259,7 +3762,7 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
static const char *ignored_env_vars[] = {
-#ifndef WIN32
+#ifndef MSWIN
"COLUMNS",
"LINES",
"TERMCAP",
@@ -4271,7 +3774,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",
@@ -4310,7 +3813,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"));
@@ -4345,7 +3848,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);
@@ -4445,7 +3948,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
-#ifdef WIN32
+#ifdef MSWIN
if (pty && overlapped) {
semsg(_(e_invarg2),
"job cannot have both 'pty' and 'overlapped' options set");
@@ -4675,6 +4178,20 @@ static void f_json_encode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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, EvalFuncData fptr)
{
@@ -4858,9 +4375,9 @@ static void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
@@ -4868,7 +4385,7 @@ 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;
@@ -4905,8 +4422,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];
@@ -4965,14 +4482,13 @@ 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;
}
}
- match = vim_regexec_nl(&regmatch, str, startcol);
+ match = vim_regexec_nl(&regmatch, (char_u *)str, startcol);
if (match && --nth <= 0) {
break;
@@ -4987,7 +4503,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;
@@ -5006,7 +4522,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) {
@@ -5042,11 +4558,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);
}
@@ -5120,7 +4634,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;
@@ -5133,7 +4647,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;
@@ -5143,6 +4657,7 @@ 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;
}
@@ -5194,10 +4709,9 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -5385,7 +4899,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
lnum = 0;
break;
}
- if (*skipwhite((char *)ml_get(lnum)) != NUL) {
+ if (*skipwhite(ml_get(lnum)) != NUL) {
break;
}
}
@@ -5443,12 +4957,12 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
@@ -5473,7 +4987,7 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
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--;
}
}
@@ -5565,7 +5079,7 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData
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
@@ -5635,10 +5149,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
@@ -5798,13 +5313,16 @@ static void f_range(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
@@ -5863,17 +5381,17 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ga_clear_strings(&ga);
}
-/// "readfile()" function
-static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -5922,13 +5440,13 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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.
@@ -5945,7 +5463,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -5960,7 +5478,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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.
@@ -5980,18 +5498,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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.
@@ -6002,8 +5520,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -6028,17 +5546,17 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -6046,6 +5564,18 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData fptr)
{
@@ -6227,8 +5757,8 @@ static void f_rename(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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));
}
}
@@ -6274,7 +5804,7 @@ 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)) {
@@ -6435,7 +5965,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
# endif
#endif
- simplify_filename((char_u *)rettv->vval.v_string);
+ simplify_filename(rettv->vval.v_string);
}
/// "reverse({list})" function
@@ -6486,8 +6016,8 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData 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];
@@ -6576,55 +6106,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;
}
@@ -6789,12 +6321,15 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -7059,32 +6594,6 @@ 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, 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
- };
- 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, EvalFuncData fptr)
{
@@ -7107,7 +6616,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -7270,14 +6779,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) {
@@ -7294,14 +6803,14 @@ 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,
.sa_tm = &tm,
};
- int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
+ int n = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1L,
options, RE_SEARCH, &sia);
if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) {
// didn't find it or found the first match again: FAIL
@@ -7453,9 +6962,8 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
@@ -7502,21 +7010,6 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "setbufline()" function
-static void f_setbufline(typval_T *argvars, typval_T *rettv, EvalFuncData 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.
/// Otherwise use the column number as a byte offset.
@@ -7526,31 +7019,35 @@ 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));
}
}
@@ -7568,23 +7065,25 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
}
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((char *)csearch, pcc);
- set_last_csearch(c, csearch, utfc_ptr2len((char *)csearch));
- }
+ if (d == NULL) {
+ return;
+ }
- dictitem_T *di = tv_dict_find(d, S_LEN("forward"));
- if (di != NULL) {
- set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD);
- }
+ 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((char *)csearch, pcc);
+ set_last_csearch(c, csearch, utfc_ptr2len((char *)csearch));
+ }
- di = tv_dict_find(d, S_LEN("until"));
- if (di != NULL) {
- set_csearch_until(!!tv_get_number(&di->di_tv));
- }
+ dictitem_T *di = tv_dict_find(d, S_LEN("forward"));
+ if (di != NULL) {
+ set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD);
+ }
+
+ di = tv_dict_find(d, S_LEN("until"));
+ if (di != NULL) {
+ set_csearch_until(!!tv_get_number(&di->di_tv));
}
}
@@ -7640,13 +7139,6 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_number = os_setperm(fname, mode) == OK;
}
-/// "setline()" function
-static void f_setline(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData fptr)
{
@@ -7810,7 +7302,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) {
@@ -7892,8 +7384,7 @@ 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;
}
@@ -7918,7 +7409,7 @@ 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;
}
@@ -8052,7 +7543,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -8152,7 +7643,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -8195,11 +7686,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData 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];
}
@@ -8263,10 +7754,10 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData 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));
}
}
@@ -8282,15 +7773,15 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData 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:
@@ -8342,9 +7833,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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;
}
@@ -8379,7 +7868,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- const size_t len = STRLEN(str);
+ const size_t len = strlen(str);
size_t byteidx = 0;
while (charidx >= 0 && byteidx < len) {
@@ -8437,26 +7926,38 @@ 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, EvalFuncData 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);
+ 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;
+}
+
+/// "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);
}
}
@@ -8485,7 +7986,7 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
@@ -8625,7 +8126,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData 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.
@@ -8687,7 +8188,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -8740,7 +8241,7 @@ static void f_swapname(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
@@ -8756,7 +8257,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
@@ -8822,7 +8323,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -8870,7 +8371,7 @@ 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);
@@ -8881,7 +8382,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -8894,7 +8395,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
: curwin->w_p_lcs_chars.conceal;
}
if (cchar != NUL) {
- utf_char2bytes(cchar, (char *)str);
+ utf_char2bytes(cchar, str);
}
}
}
@@ -8902,7 +8403,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
@@ -8918,7 +8419,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -8963,105 +8464,6 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, EvalFuncData fp
}
}
-/// "tabpagenr()" function
-static 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;
-}
-
-/// 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, 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;
-}
-
/// "tagfiles()" function
static void f_tagfiles(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -9094,14 +8496,14 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData 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, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)vim_tempname();
+ rettv->vval.v_string = vim_tempname();
}
/// "termopen(cmd[, cwd])" function
@@ -9188,9 +8590,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData 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';
@@ -9203,14 +8605,14 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData 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}
@@ -9361,7 +8763,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -9416,10 +8818,10 @@ 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;
@@ -9434,7 +8836,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
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
@@ -9472,7 +8874,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -9495,7 +8897,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -9580,7 +8982,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -9595,12 +8997,12 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "visualmode()" function
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])) {
@@ -9616,276 +9018,6 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
}
}
-/// "win_findbuf()" function
-static 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
-static void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = win_getid(argvars);
-}
-
-/// "win_gettype(nr)" function
-static 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 = (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, 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
-static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- win_id2tabwin(argvars, rettv);
-}
-
-/// "win_id2win()" function
-static void f_win_id2win(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = win_id2win(argvars);
-}
-
-/// "win_move_separator()" function
-static 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;
- }
-
- 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, 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;
- }
-
- 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, 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
-static void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- validate_cursor();
- rettv->vval.v_number = curwin->w_wcol + 1;
-}
-
-/// "winheight(nr)" function
-static 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
-static 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
-static void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- validate_cursor();
- rettv->vval.v_number = curwin->w_wrow + 1;
-}
-
-/// "winnr()" function
-static void f_winnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->vval.v_number = get_winnr(curtab, &argvars[0]);
-}
-
-/// "winrestcmd()" function
-static void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData 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, 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
-static 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
-static 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;
- }
-}
-
/// "windowsversion()" function
static void f_windowsversion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -9924,6 +9056,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData 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) {
@@ -9939,6 +9072,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -9958,6 +9093,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData 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));
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index adff0b2441..1ae031a952 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -1,9 +1,14 @@
#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, EvalFuncData data);
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 d5b2b1f2ae..7c61a2f990 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
@@ -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) {
@@ -1242,17 +1255,15 @@ 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) {
li = tv_list_item_remove(l, li);
} else {
- idx++;
li = TV_LIST_ITEM_NEXT(l, li);
}
- if (info.item_compare_func_err) { // -V547
+ if (info.item_compare_func_err) {
emsg(_("E882: Uniq compare function failed"));
break;
}
@@ -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?
@@ -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);
@@ -2454,6 +2498,10 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
|| 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,7 +2588,7 @@ 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);
+ 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);
@@ -2777,7 +2833,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);
@@ -3114,6 +3170,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
@@ -3583,13 +3640,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 +3685,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 +3774,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 +3802,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 +3859,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 (tv->v_type != VAR_STRING) {
- emsg(_(e_stringreq));
+ 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 "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 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_check_for_string(tv) == FAIL) {
+ 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;
}
- if (tv->vval.v_string == NULL || *tv->vval.v_string == NUL) {
- emsg(_(e_non_empty_string_required));
+ return OK;
+}
+
+/// 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 (args[idx].v_type != VAR_NUMBER) {
+ semsg(_(e_number_required_for_argument_nr), idx + 1);
+ return FAIL;
+ }
+ 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 8177d01f90..1ee3b5bf69 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,402 +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;
-
-/// 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
@@ -439,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
@@ -486,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
@@ -502,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.
@@ -516,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
@@ -559,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
@@ -574,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
@@ -587,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
@@ -602,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
@@ -625,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
@@ -640,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
@@ -658,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
@@ -676,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
@@ -692,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
@@ -706,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
@@ -719,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.
@@ -737,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.
@@ -751,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 char_u 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.
@@ -765,7 +372,7 @@ static inline char_u tv_blob_get(const blob_T *const b, int idx)
return ((char_u *)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, char_u c)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL;
/// Store the byte `c` at index `idx` in the blob.
@@ -790,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
@@ -833,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
///
@@ -890,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.
@@ -932,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
@@ -967,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..939e5d0810
--- /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_u *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..6c931d3f88 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
///
@@ -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 4be922a055..c70d56cd25 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -3,45 +3,51 @@
// 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/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 +68,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)
{
@@ -85,10 +93,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) {
@@ -110,8 +118,8 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
p++;
}
if (arg == p || isdigit(*arg)
- || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
- || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
+ || (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 +133,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 +150,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 +171,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 {
@@ -220,12 +237,12 @@ char_u *get_lambda_name(void)
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 +259,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 +289,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 = (char *)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 +330,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 +383,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 +394,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 +405,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 = (char_u *)concat_str("<SNR>", (char *)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 +447,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 +457,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 +481,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 +491,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,39 +543,38 @@ 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;
}
@@ -576,9 +593,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 +610,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 +775,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 +845,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;
@@ -870,7 +887,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
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,7 +906,7 @@ 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;
@@ -915,7 +932,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 +984,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 +1010,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);
}
}
@@ -1208,11 +1225,39 @@ 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_u *name)
{
return isdigit(*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)
+{
+ 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;
/// Save the current function call pointer, and set it to NULL.
@@ -1252,7 +1297,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) {
@@ -1273,12 +1318,12 @@ void free_all_functions(void)
// Only free functions that are not refcounted, those are
// supposed to be freed when no longer referenced.
fp = HI2UF(hi);
- if (func_name_refcount(fp->uf_name)) {
+ if (func_name_refcount((char_u *)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;
}
@@ -1299,7 +1344,7 @@ void free_all_functions(void)
// Only free functions that are not refcounted, those are
// supposed to be freed when no longer referenced.
fp = HI2UF(hi);
- if (func_name_refcount(fp->uf_name)) {
+ if (func_name_refcount((char_u *)fp->uf_name)) {
skipped++;
} else {
func_free(fp);
@@ -1322,10 +1367,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;
}
@@ -1353,11 +1398,11 @@ 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;
+ 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((char *)name, -1, rettv, argc, argv, &funcexe);
func_call_skip_call:
@@ -1369,35 +1414,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)
FUNC_ATTR_NONNULL_ALL
{
switch (error) {
- case ERROR_UNKNOWN:
- emsg_funcname(N_("E117: Unknown function: %s"), name);
+ case FCERR_UNKNOWN:
+ emsg_funcname(N_("E117: Unknown function: %s"), (char *)name);
break;
- case ERROR_NOTMETHOD:
- emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
+ case FCERR_NOTMETHOD:
+ emsg_funcname(N_("E276: Cannot use function as a method: %s"), (char *)name);
break;
- case ERROR_DELETED:
- emsg_funcname(N_("E933: Function was deleted: %s"), name);
+ case FCERR_DELETED:
+ emsg_funcname(N_("E933: Function was deleted: %s"), (char *)name);
break;
- case ERROR_TOOMANY:
- emsg_funcname(_(e_toomanyarg), name);
+ case FCERR_TOOMANY:
+ emsg_funcname(_(e_toomanyarg), (char *)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"), (char *)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"), (char *)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"), (char *)name);
break;
}
}
@@ -1435,27 +1495,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 +1523,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 +1538,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 +1554,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 +1564,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
@@ -1519,76 +1579,55 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
} else if (fp != NULL || !builtin_function((const char *)rfname, -1)) {
// User defined function.
if (fp == NULL) {
- fp = find_func(rfname);
+ fp = find_func((char_u *)rfname);
}
// 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);
+ fp = find_func((char_u *)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);
+ fp = find_func((char_u *)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);
+ error = call_internal_method((char_u *)fname, argcount, argvars, rettv,
+ 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 +1635,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) ? (char_u *)name : (char_u *)funcname);
}
// clear the copies made from the partial
@@ -1610,6 +1649,11 @@ theend:
return ret;
}
+char *printable_func_name(ufunc_T *fp)
+{
+ return fp->uf_name_exp != NULL ? (char *)fp->uf_name_exp : fp->uf_name;
+}
+
/// List the head of the function: "name(arg1, arg2)".
///
/// @param[in] fp Function pointer.
@@ -1682,9 +1726,9 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
char_u *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 +1736,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).
@@ -1705,14 +1749,14 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
// 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 +1764,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 +1780,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 +1799,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 +1833,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 +1869,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 +1900,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 +1910,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);
@@ -1882,23 +1923,108 @@ char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, pa
theend:
clear_lval(&lv);
- return name;
+ return (char_u *)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 = (char *)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((char_u *)fp->uf_name))
+ : (!isdigit(*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;
@@ -1913,44 +2039,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((char *)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 = (char_u *)skip_regexp(eap->arg + 1, '/', true, NULL);
+ p = skip_regexp(eap->arg + 1, '/', true);
if (!eap->skip) {
regmatch_T regmatch;
@@ -1960,25 +2069,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;
}
@@ -1996,24 +2094,21 @@ 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
@@ -2028,16 +2123,16 @@ 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;
}
if (!eap->skip && !got_int) {
- fp = find_func(name);
+ fp = find_func((char_u *)name);
if (fp != NULL) {
list_func_head(fp, !eap->forceit, eap->forceit);
for (int j = 0; j < fp->uf_lines.ga_len && !got_int; j++) {
@@ -2054,7 +2149,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();
}
@@ -2069,24 +2164,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
@@ -2097,7 +2190,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++;
}
@@ -2111,7 +2204,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;
}
@@ -2123,23 +2216,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 {
@@ -2155,9 +2247,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
@@ -2165,7 +2255,7 @@ void ex_function(exarg_T *eap)
if (!eap->skip && !eap->forceit) {
if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) {
emsg(_(e_funcdict));
- } else if (name != NULL && find_func(name) != NULL) {
+ } else if (name != NULL && find_func((char_u *)name) != NULL) {
emsg_funcname(e_funcexts, name);
}
}
@@ -2195,9 +2285,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,9 +2295,9 @@ void ex_function(exarg_T *eap)
} else {
xfree(line_to_free);
if (eap->getline == NULL) {
- theline = getcmdline(':', 0L, indent, do_concat);
+ theline = (char *)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;
}
@@ -2237,18 +2327,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;
@@ -2260,27 +2350,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;
}
}
@@ -2289,23 +2378,23 @@ 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) == '(') {
+ 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 {
@@ -2316,23 +2405,23 @@ void ex_function(exarg_T *eap)
}
// 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(skiptowhite((char *)p));
+ arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] == '<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
@@ -2349,22 +2438,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(skiptowhite((char *)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(skiptowhite((char *)arg));
+ arg = skipwhite(skiptowhite(arg));
if (arg[0] == '='
&& arg[1] == '<'
&& arg[2] == '<'
@@ -2372,14 +2461,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)((char_u *)skiptowhite((char *)p) - p));
+ skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p));
do_concat = false;
is_heredoc = true;
}
@@ -2392,8 +2480,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.
@@ -2413,18 +2501,15 @@ 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;
}
- fp = find_func(name);
+ fp = find_func((char_u *)name);
if (fp != NULL) {
// Function can be replaced with "function!" and when sourcing the
// same script again, but only once.
@@ -2435,8 +2520,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) {
@@ -2480,23 +2564,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);
@@ -2508,7 +2592,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) {
@@ -2524,7 +2608,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;
@@ -2533,8 +2617,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;
@@ -2590,8 +2674,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] == ':') {
@@ -2616,7 +2700,7 @@ 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;
@@ -2624,7 +2708,7 @@ bool function_exists(const char *const name, bool 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);
+ nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
// "funcname(...", not "funcname!...".
@@ -2640,15 +2724,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++;
}
@@ -2658,11 +2744,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.
}
@@ -2673,7 +2759,7 @@ char *get_user_func_name(expand_T *xp, int idx)
STRCAT(IObuff, ")");
}
}
- return (char *)IObuff;
+ return IObuff;
}
return NULL;
}
@@ -2682,12 +2768,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 *p;
char_u *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) {
@@ -2695,16 +2781,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(*name) && fudi.fd_dict == NULL) {
+ if (!eap->skip) {
+ semsg(_(e_invarg2), eap->arg);
+ }
+ xfree(name);
+ return;
+ }
if (!eap->skip) {
fp = find_func(name);
}
@@ -2739,7 +2832,7 @@ void ex_delfunction(exarg_T *eap)
// it and the refcount is more than one, it should be kept.
// A numbered function or lambda should be kept if the refcount is
// one or more.
- if (fp->uf_refcount > (func_name_refcount(fp->uf_name) ? 0 : 1)) {
+ if (fp->uf_refcount > (func_name_refcount((char_u *)fp->uf_name) ? 0 : 1)) {
// Function is still referenced somewhere. Don't free it but
// do remove it from the hashtable.
if (func_remove(fp)) {
@@ -2851,7 +2944,7 @@ 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;
@@ -2866,7 +2959,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 {
@@ -2889,7 +2982,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) {
@@ -2897,15 +2990,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;
@@ -2926,7 +3017,7 @@ void ex_call(exarg_T *eap)
return;
}
- tofree = trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial);
+ tofree = (char *)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);
@@ -2945,13 +3036,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 != '(') {
@@ -2975,13 +3065,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;
}
@@ -3018,7 +3108,7 @@ void ex_call(exarg_T *eap)
semsg(_(e_trailing_arg), arg);
}
} else {
- eap->nextcmd = (char *)check_nextcmd(arg);
+ eap->nextcmd = check_nextcmd(arg);
}
}
@@ -3118,12 +3208,12 @@ char *get_return_cmd(void *rettv)
}
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 (char *)vim_strsave(IObuff);
+ return xstrdup(IObuff);
}
/// Get next function line.
@@ -3200,21 +3290,21 @@ 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);
- fp = find_func(fname);
+ fp = find_func((char_u *)fname);
xfree(tofree);
}
@@ -3227,7 +3317,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;
@@ -3236,8 +3326,8 @@ 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);
- func_ref(pt->pt_name);
+ pt->pt_name = xstrdup(ret_pt->pt_name);
+ func_ref((char_u *)pt->pt_name);
} else {
pt->pt_func = ret_pt->pt_func;
func_ptr_ref(pt->pt_func);
@@ -3260,7 +3350,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)
{
- return ((funccall_T *)cookie)->func->uf_name;
+ return (char_u *)((funccall_T *)cookie)->func->uf_name;
}
/// @return the address holding the next breakpoint line for a funccall cookie.
@@ -3519,7 +3609,7 @@ bool set_ref_in_functions(int copyID)
if (!HASHITEM_EMPTY(hi)) {
todo--;
fp = HI2UF(hi);
- if (!func_name_refcount(fp->uf_name)
+ if (!func_name_refcount((char_u *)fp->uf_name)
&& set_ref_in_func(NULL, fp, copyID)) {
return true;
}
@@ -3549,18 +3639,18 @@ bool set_ref_in_func(char_u *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;
}
if (fp_in == NULL) {
- fname = fname_trans_sid(name, fname_buf, &tofree, &error);
- fp = find_func(fname);
+ fname = fname_trans_sid((char *)name, fname_buf, &tofree, &error);
+ fp = find_func((char_u *)fname);
}
if (fp != NULL) {
for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) {
@@ -3571,24 +3661,22 @@ 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_u *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 = (char *)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));
// coverity[leaked_storage]
- return fp->uf_name;
+ return (char_u *)fp->uf_name;
}
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 75410d40d8..206df03381 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);
@@ -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;
}
@@ -190,7 +209,7 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
}
expr = skipwhite(argend);
if (*expr != '=' && !((vim_strchr("+-*/%.", *expr) != NULL
- && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) {
+ && 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);
@@ -231,11 +250,13 @@ static void ex_let_const(exarg_T *eap, const bool is_const)
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,
@@ -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 == '$') {
@@ -580,12 +596,12 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
&& vim_strchr(endchars, *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 = concat_str(s, p);
p = (const char *)tofree;
@@ -609,7 +625,8 @@ 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)) {
@@ -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);
}
@@ -672,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));
@@ -683,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 == '@') {
@@ -710,8 +735,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
}
}
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);
@@ -744,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)
{
@@ -762,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`.
@@ -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.
@@ -883,9 +901,8 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
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.
@@ -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.
@@ -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.
@@ -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;
}
@@ -1330,7 +1343,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 +1362,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) {
@@ -1443,37 +1452,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:.
+ // Allow autoload variable.
if (!(vim_strchr("wbst", name[0]) != NULL && name[1] == ':')
- && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':')
- ? name[2] : name[0])) {
+ && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])
+ && 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.
@@ -1648,25 +1654,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)
@@ -1749,21 +1757,23 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
@@ -1790,27 +1800,29 @@ void f_setbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData 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..4bcbc13534
--- /dev/null
+++ b/src/nvim/eval/window.c
@@ -0,0 +1,951 @@
+// 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/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
+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..682a794113
--- /dev/null
+++ b/src/nvim/eval/window.h
@@ -0,0 +1,68 @@
+#ifndef NVIM_EVAL_WINDOW_H
+#define NVIM_EVAL_WINDOW_H
+
+#include <stdbool.h>
+
+#include "nvim/buffer_defs.h"
+#include "nvim/eval/typval_defs.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..10a09275d9 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -1,14 +1,14 @@
// 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 <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"
@@ -25,7 +25,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 +40,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 +63,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 +72,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 +85,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 +124,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..9dfd6f329a 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -2,20 +2,24 @@
// 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"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "event/process.c.generated.h"
@@ -32,10 +36,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 +405,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 e672b80d69..c2af0e6986 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"
@@ -120,7 +131,7 @@ void do_ascii(const exarg_T *const eap)
{
char *dig;
int cc[MAX_MCO];
- int c = utfc_ptr2char((char *)get_cursor_pos_ptr(), cc);
+ int c = utfc_ptr2char(get_cursor_pos_ptr(), cc);
if (c == NUL) {
msg("NUL");
return;
@@ -149,12 +160,12 @@ void do_ascii(const exarg_T *const eap)
dig = (char *)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);
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();
+ 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)
@@ -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 = skip_regexp(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;
@@ -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;
@@ -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
@@ -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(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, 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(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();
-}
-
/// :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);
@@ -1142,8 +956,7 @@ 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;
@@ -1155,16 +968,16 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
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,16 +1138,14 @@ 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) {
@@ -1374,7 +1179,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
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;
@@ -1428,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 '[
@@ -1440,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;
}
@@ -1497,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
@@ -1526,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;
@@ -1546,7 +1344,7 @@ 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;
@@ -1569,93 +1367,82 @@ 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, (char *)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, p_srr, otmp);
}
@@ -1710,13 +1497,13 @@ void print_line(linenr_T lnum, int use_number, int list)
int save_silent = silent_mode;
// apply :filter /pat/
- if (message_filtered((char *)ml_get(lnum))) {
+ if (message_filtered(ml_get(lnum))) {
return;
}
msg_start();
silent_mode = false;
- info_message = true; // use mch_msg(), not mch_errmsg()
+ info_message = true; // use os_msg(), not os_errmsg()
print_line_no_prefix(lnum, use_number, list);
if (save_silent) {
msg_putchar('\n');
@@ -1740,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;
@@ -1822,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;
@@ -1860,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) {
@@ -1881,7 +1675,11 @@ int do_write(exarg_T *eap)
// Writing to the current file is not allowed in readonly mode
// and a file name is required.
// "nofile" and "nowrite" buffers cannot be written implicitly either.
- if (!other && (bt_dontwrite_msg(curbuf) || check_fname() == FAIL
+ 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,7 +1695,7 @@ 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;
@@ -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);
@@ -2007,7 +1812,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
&& 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.
@@ -2020,7 +1825,7 @@ 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;
@@ -2049,16 +1854,16 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
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;
@@ -2113,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;
@@ -2171,7 +1974,7 @@ 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];
@@ -2187,13 +1990,12 @@ 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;
}
+ return true;
} else if (buf->b_p_ro) {
emsg(_(e_readonly));
} else {
@@ -2405,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);
@@ -2426,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;
@@ -2507,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;
@@ -2663,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) {
@@ -2678,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;
}
@@ -2728,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;
@@ -2741,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
@@ -2765,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;
}
@@ -2808,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)) {
@@ -2868,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;
@@ -3001,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) {
@@ -3254,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)
{
@@ -3312,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'
@@ -3351,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;
@@ -3389,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;
@@ -3482,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 = {
@@ -3552,17 +3357,15 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
which_pat = RE_LAST; // use last used regexp
delimiter = (char_u)(*cmd++); // remember delimiter character
pat = cmd; // remember start of search pat
- cmd = skip_regexp(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]) {
@@ -3620,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;
@@ -3642,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((char_u *)pat, NULL, RE_SUBST, which_pat,
+ (cmdpreview ? 0 : SEARCH_HIS), &regmatch) == FAIL) {
if (subflags.do_error) {
emsg(_(e_invcmd));
}
@@ -3672,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;
@@ -3680,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;
@@ -3711,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;
@@ -3765,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 },
@@ -3803,7 +3593,7 @@ 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
@@ -3811,11 +3601,9 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
curwin->w_cursor.lnum = lnum;
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) {
@@ -3847,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;
}
@@ -3880,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;
@@ -3944,13 +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));
+ 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);
}
@@ -3959,11 +3745,16 @@ 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;
+ 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(UPD_SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
+ update_screen();
highlight_match = false;
redraw_later(curwin, UPD_SOME_VALID);
@@ -3982,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
@@ -4043,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;
@@ -4065,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.
@@ -4127,7 +3896,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
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));
+ REGSUB_BACKSLASH
+ | (magic_isset() ? REGSUB_MAGIC : 0));
textlock--;
// If getting the substitute string caused an error, don't do
@@ -4148,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
@@ -4169,7 +3939,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
(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));
+ REGSUB_COPY | REGSUB_BACKSLASH
+ | (magic_isset() ? REGSUB_MAGIC : 0));
textlock--;
sub_nsubs++;
did_sub = true;
@@ -4187,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;
@@ -4230,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;
@@ -4266,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,
@@ -4282,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) {
@@ -4300,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;
@@ -4338,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;
}
@@ -4348,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
@@ -4446,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 {
@@ -4461,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());
@@ -4494,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
@@ -4513,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()) {
@@ -4609,11 +4367,9 @@ 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) {
@@ -4636,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 = skip_regexp(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_u *used_pat;
+ if (search_regcomp((char_u *)pat, &used_pat, RE_BOTH, which_pat,
+ SEARCH_HIS, &regmatch) == FAIL) {
emsg(_(e_invcmd));
return;
}
@@ -4673,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);
@@ -4752,36 +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').
@@ -4870,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
@@ -4938,7 +4690,7 @@ 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;
@@ -4953,7 +4705,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
*s = p + 1;
}
c = (char_u)(*p);
- p = skip_regexp(p + 1, c, true, NULL);
+ p = skip_regexp(p + 1, c, true);
if (*p != c) {
return NULL;
}
@@ -4989,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 *)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..68db03bd7d 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',
@@ -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 11280eecbb..d08823bc30 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -6,9 +6,9 @@
/// 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"
@@ -16,30 +16,33 @@
#include "nvim/autocmd.h"
#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_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"
@@ -200,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) {
@@ -252,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;
}
@@ -702,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
+ return;
+ }
+
+ 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 {
- 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>");
+ // ":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_unlet(S_LEN("g:current_compiler"), true);
- do_unlet(S_LEN("b:current_compiler"), true);
+ 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);
+ 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);
}
}
}
@@ -844,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_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 71956b2246..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
@@ -202,6 +202,7 @@ struct exarg {
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
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 26b0c0f1ef..785b727cd3 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4,59 +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/locale.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"
@@ -66,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"
@@ -254,7 +249,7 @@ void do_exmode(void)
RedrawingDisabled--;
no_wait_return--;
redraw_all_later(UPD_NOT_VALID);
- update_screen(UPD_NOT_VALID);
+ update_screen();
need_wait_return = false;
msg_scroll = save_msg_scroll;
}
@@ -535,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 = (char *)vim_strsave((char_u *)next_cmdline);
+ repeat_cmdline = xstrdup(next_cmdline);
} else {
repeat_cmdline = NULL;
}
@@ -642,7 +637,7 @@ 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) {
+ 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;
@@ -727,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 && !did_throw
+ if (!got_int && !did_throw && !aborting()
&& ((getline_equal(fgetline, cookie, getsourceline)
&& !source_finished(fgetline, cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
@@ -771,52 +766,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// of interrupts or errors to exceptions, and ensure that no more
// commands are executed.
if (did_throw) {
- assert(current_exception != NULL);
- 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();
+ 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
@@ -906,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)
{
@@ -1060,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':
@@ -1300,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);
}
@@ -1339,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);
@@ -1430,7 +1445,7 @@ 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;
+ *errormsg = IObuff;
goto end;
}
@@ -1512,6 +1527,30 @@ end:
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)
{
// If filename expansion is enabled, expand filenames
@@ -1536,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--;
}
@@ -1551,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;
@@ -1608,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; \
@@ -1658,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);
@@ -1757,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:
@@ -1794,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:
@@ -1892,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);
@@ -1929,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
@@ -1997,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);
}
@@ -2266,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);
}
@@ -2438,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;
@@ -2798,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;
@@ -2819,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) {
+ d = IObuff + strlen(IObuff);
+ while (*s != NUL && d - IObuff + 5 < IOSIZE) {
if ((char_u)s[0] == 0xc2 && (char_u)s[1] == 0xa0) {
s += 2;
STRCPY(d, "<a0>");
d += 4;
- } else if ((char_u *)d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
+ } else if (d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
break;
} else {
mb_copy_char((const char **)&s, &d);
@@ -2936,7 +2981,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) {
@@ -3081,10 +3126,9 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData 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)
@@ -3313,7 +3357,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
goto error;
}
if (skip) { // skip "/pat/"
- cmd = skip_regexp(cmd, c, p_magic, NULL);
+ cmd = skip_regexp(cmd, c, magic_isset());
if (*cmd == c) {
cmd++;
}
@@ -3342,7 +3386,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;
@@ -3442,9 +3486,10 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
} else {
i = (char_u)(*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));
@@ -3459,8 +3504,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);
@@ -3653,7 +3698,7 @@ 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 : curbuf->b_p_gp)
+ const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp)
: (*curbuf->b_p_mp == NUL ? (char *)p_mp : curbuf->b_p_mp);
arg = skipwhite(arg);
@@ -3662,7 +3707,7 @@ 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);
@@ -3690,7 +3735,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] == '=') {
@@ -3739,7 +3784,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
@@ -3758,7 +3802,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
for (l = repl; *l; l++) {
if (vim_strchr((char *)ESCAPE_CHARS, *l) != NULL) {
- l = (char *)vim_strsave_escaped((char_u *)repl, ESCAPE_CHARS);
+ l = vim_strsave_escaped(repl, (char *)ESCAPE_CHARS);
xfree(repl);
repl = l;
break;
@@ -3773,7 +3817,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;
}
@@ -3794,14 +3838,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, (char_u *)NameBuff, MAXPATHL, true, true, NULL);
- has_wildcards = path_has_wildcard((char_u *)NameBuff);
- p = (char *)NameBuff;
+ expand_env_esc(eap->arg, NameBuff, MAXPATHL, true, true, NULL);
+ has_wildcards = path_has_wildcard(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);
}
}
@@ -3825,11 +3869,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);
}
}
@@ -3847,10 +3891,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);
@@ -3868,7 +3912,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;
}
@@ -3883,7 +3927,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));
}
}
@@ -3935,7 +3979,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;
}
@@ -3943,7 +3987,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);
}
}
@@ -4014,7 +4058,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;
@@ -4029,26 +4073,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;
}
@@ -4110,9 +4161,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 {
@@ -4179,13 +4230,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);
}
@@ -4312,15 +4362,14 @@ char *find_nextcmd(const char *p)
/// Check if *p is a separator between Ex commands, skipping over white space.
///
/// @return NULL if it isn't, the following character if it is.
-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
@@ -4343,14 +4392,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;
@@ -4383,7 +4432,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);
}
}
@@ -4578,7 +4627,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;
}
@@ -4615,23 +4664,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);
}
}
@@ -4640,32 +4695,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;
+ }
}
}
@@ -4692,7 +4753,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
@@ -4708,11 +4768,6 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
break;
}
}
-
- redraw_tabline = true;
- if (h != tabline_height()) {
- win_new_screen_rows();
- }
}
/// ":only".
@@ -4725,9 +4780,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;
@@ -4741,25 +4795,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);
}
}
@@ -4834,7 +4890,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
@@ -4910,8 +4965,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 = (char *)find_file_in_path(eap->arg, strlen(eap->arg),
+ FNAME_MESS, true, curbuf->b_ffname);
if (fname == NULL) {
goto theend;
}
@@ -5034,9 +5089,8 @@ static void ex_tabs(exarg_T *eap)
}
msg_putchar('\n');
- vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++);
- msg_outtrans_attr((char *)IObuff, HL_ATTR(HLF_T));
- ui_flush(); // output one line at a time
+ vim_snprintf(IObuff, IOSIZE, _("Tab page %d"), tabcount++);
+ msg_outtrans_attr(IObuff, HL_ATTR(HLF_T));
os_breakcheck();
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
@@ -5050,12 +5104,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();
}
}
@@ -5105,23 +5158,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 = (char *)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 = (char *)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".
@@ -5183,7 +5238,7 @@ 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't edit another file when "curbuf->b_ro_locked" is set. Only ":edit"
// can bring us here, others are stopped earlier.
if (*eap->arg != NUL && curbuf_locked()) {
return;
@@ -5273,7 +5328,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);
}
}
@@ -5346,50 +5401,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(UPD_VALID);
}
+ redraw_curbuf_later(UPD_VALID);
}
}
@@ -5441,7 +5497,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) {
@@ -5479,7 +5535,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"));
@@ -5488,7 +5544,7 @@ bool changedir_func(char *new_dir, CdScope scope)
new_dir = pdir;
}
- if (os_dirname((char_u *)NameBuff, MAXPATHL) == OK) {
+ if (os_dirname(NameBuff, MAXPATHL) == OK) {
pdir = xstrdup(NameBuff);
} else {
pdir = NULL;
@@ -5503,13 +5559,13 @@ bool changedir_func(char *new_dir, CdScope scope)
#endif
// Use NameBuff for home directory name.
expand_env("$HOME", NameBuff, MAXPATHL);
- new_dir = (char *)NameBuff;
+ 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;
@@ -5543,27 +5599,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);
}
}
}
@@ -5571,7 +5627,7 @@ void ex_cd(exarg_T *eap)
/// ":pwd".
static void ex_pwd(exarg_T *eap)
{
- if (os_dirname((char_u *)NameBuff, MAXPATHL) == OK) {
+ if (os_dirname(NameBuff, MAXPATHL) == OK) {
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(NameBuff);
#endif
@@ -5584,9 +5640,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"));
@@ -5621,15 +5677,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.
@@ -5675,7 +5727,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));
@@ -5791,21 +5843,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;
}
@@ -5846,20 +5898,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;
}
/// ":!".
@@ -5873,7 +5926,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
}
@@ -5900,7 +5953,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);
}
@@ -5989,7 +6042,7 @@ 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)
@@ -6007,7 +6060,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);
}
}
}
@@ -6061,9 +6114,10 @@ static void ex_redraw(exarg_T *eap)
if (eap->forceit) {
redraw_all_later(UPD_NOT_VALID);
redraw_cmdline = true;
+ } else if (VIsual_active) {
+ redraw_curbuf_later(UPD_INVERTED);
}
- update_screen(eap->forceit ? UPD_NOT_VALID
- : VIsual_active ? UPD_INVERTED : 0);
+ update_screen();
if (need_maketitle) {
maketitle();
}
@@ -6089,14 +6143,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 ? UPD_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();
@@ -6153,11 +6215,11 @@ 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
- if (os_isdir((char *)fname)) {
+ if (os_isdir(fname)) {
semsg(_(e_isadir2), fname);
return NULL;
}
@@ -6168,7 +6230,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);
}
@@ -6180,17 +6242,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.
@@ -6292,7 +6358,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;
@@ -6321,7 +6387,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);
}
@@ -6385,10 +6451,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);
}
@@ -6457,7 +6523,7 @@ static void ex_findpat(exarg_T *eap)
if (*eap->arg == '/') { // Match regexp, not just whole words
whole = false;
eap->arg++;
- char *p = skip_regexp(eap->arg, '/', p_magic, NULL);
+ char *p = skip_regexp(eap->arg, '/', magic_isset());
if (*p) {
*p++ = NUL;
p = skipwhite(p);
@@ -6466,12 +6532,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);
}
@@ -6522,7 +6588,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;
@@ -6551,10 +6617,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;
}
@@ -6563,7 +6625,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);
}
@@ -6589,7 +6651,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[]) = {
@@ -6612,8 +6674,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;
@@ -6652,8 +6714,8 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
/// @return an allocated string if a valid match was found.
/// Returns NULL if no match was found. "usedlen" then still contains the
/// number of characters to skip.
-char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg,
- int *escaped, bool empty_is_error)
+char_u *eval_vars(char_u *src, const char_u *srcstart, size_t *usedlen, linenr_T *lnump,
+ char **errormsg, int *escaped, bool empty_is_error)
{
char *result;
char *resultbuf = NULL;
@@ -6669,7 +6731,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
// Check if there is something to do.
- ssize_t spec_idx = find_cmdline_var(src, usedlen);
+ ssize_t spec_idx = find_cmdline_var((char *)src, usedlen);
if (spec_idx < 0) { // no match
*usedlen = 1;
return NULL;
@@ -6679,7 +6741,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;
}
@@ -6712,7 +6774,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;
@@ -6766,7 +6828,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 = buf->b_fname;
- tilde_file = STRCMP(result, "~") == 0;
+ tilde_file = strcmp(result, "~") == 0;
}
}
break;
@@ -6789,7 +6851,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
// postponed to avoid a delay when <afile> is not used.
result = 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;
@@ -6797,7 +6859,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
@@ -6879,7 +6941,7 @@ 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)++;
@@ -6923,7 +6985,7 @@ 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
@@ -6942,11 +7004,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);
@@ -6986,12 +7048,12 @@ 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) {
+ 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) {
+ } 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);
@@ -7029,19 +7091,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;
@@ -7058,7 +7120,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);
@@ -7108,17 +7170,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_give_err("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;
}
}
@@ -7200,7 +7263,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);
@@ -7233,7 +7296,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 6dddafcfcb..6450892e39 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
@@ -79,15 +88,13 @@ 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).
- */
+// 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
@@ -153,69 +160,57 @@ 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 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.
- */
+ // 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.
- */
+ // 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
@@ -228,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) {
@@ -263,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])
@@ -312,10 +303,8 @@ 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;
@@ -396,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;
}
@@ -430,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;
@@ -455,13 +444,11 @@ 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
+ if (strncmp(value, "Vim", 3) == 0
&& (((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':'
|| ((char_u *)value)[3] == '(')) {
emsg(_("E608: Cannot :throw exceptions with 'Vim' prefix"));
@@ -551,7 +538,7 @@ static void discard_exception(except_T *excp, bool was_finished)
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
- saved_IObuff = (char *)vim_strsave(IObuff);
+ saved_IObuff = xstrdup(IObuff);
if (debug_break_level > 0) {
msg_silent = false; // display messages
} else {
@@ -561,9 +548,7 @@ static void discard_exception(except_T *excp, bool was_finished)
if (debug_break_level > 0 || *p_vfile == NUL) {
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;
@@ -574,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) {
@@ -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);
@@ -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
@@ -731,9 +714,9 @@ static void report_pending(int action, int pending, void *value)
default:
if (pending & CSTP_THROW) {
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
mesg, _("Exception"));
- mesg = concat_str((char *)IObuff, ": %s");
+ mesg = concat_str(IObuff, ": %s");
s = ((except_T *)value)->value;
} else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) {
s = _("Error and interrupt");
@@ -877,7 +860,7 @@ void ex_endif(exarg_T *eap)
/// 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,11 +948,9 @@ 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++;
@@ -973,16 +961,12 @@ 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.
@@ -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,
@@ -1119,7 +1099,7 @@ void ex_endwhile(exarg_T *eap)
}
// Try to find the matching ":while" and report what's missing.
for (idx = cstack->cs_idx; idx > 0; idx--) {
- fl = cstack->cs_flags[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".
@@ -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;
}
}
@@ -1211,19 +1189,17 @@ void do_throw(cstack_T *cstack)
#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;
@@ -1263,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;
@@ -1338,24 +1312,23 @@ void ex_catch(exarg_T *eap)
eap->nextcmd = find_nextcmd(eap->arg);
} else {
pat = eap->arg + 1;
- end = skip_regexp(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).
- */
+ // 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))) {
@@ -1422,16 +1395,14 @@ void ex_catch(exarg_T *eap)
internal_error("ex_catch()");
}
} 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.
- */
+ // 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);
}
}
@@ -1449,110 +1420,107 @@ void ex_finally(exarg_T *eap)
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 (did_throw), 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, 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 (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()");
- }
+ 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 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;
}
+
+ // 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;
}
}
@@ -1565,186 +1533,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 || did_throw || !(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 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();
- }
+ // 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 (did_throw
- && (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 did_throw or
- // cstack->cs_pending[idx].
- rethrow = false;
- if (did_throw && !(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 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;
+ // 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
@@ -1829,18 +1791,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) {
@@ -1893,12 +1851,10 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
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:
@@ -1934,11 +1890,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)) {
@@ -1973,11 +1927,9 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
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;
@@ -2016,7 +1968,7 @@ 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]);
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 0998bdd867..62586598bf 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 {
@@ -107,7 +109,7 @@ typedef struct command_line_state {
int c;
int gotesc; // true when <ESC> just typed
int do_abbr; // when true check for abbr.
- char_u *lookfor; // string to match
+ 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().
@@ -208,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;
@@ -231,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;
@@ -255,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,
};
@@ -273,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);
@@ -293,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++;
@@ -313,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 = skip_regexp(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) {
@@ -325,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;
@@ -336,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
@@ -371,7 +380,7 @@ 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 next_char;
char_u use_last_pat;
int search_delim;
@@ -483,13 +492,13 @@ 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) {
+ if (empty_pattern(ccline.cmdbuff + skiplen, search_delim) && !no_hlsearch) {
redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(true);
}
@@ -502,7 +511,8 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
curwin->w_redr_status = true;
}
- update_screen(UPD_SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
+ update_screen();
highlight_match = false;
restore_last_search_pattern();
@@ -548,11 +558,11 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s)
// command line has no uppercase characters, convert
// the character to lowercase
if (p_ic && p_scs
- && !pat_has_uppercase(ccline.cmdbuff + skiplen)) {
+ && !pat_has_uppercase((char_u *)ccline.cmdbuff + skiplen)) {
*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 = '\\';
@@ -565,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(UPD_SOME_VALID);
- if (call_update_screen) {
- update_screen(UPD_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
+
+ assert(indent >= 0);
- if (cmdheight0) {
- const long save_so = lastwin->w_p_so;
+ // set some variables for redrawcmd()
+ ccline.cmdfirstc = (firstc == '@' ? 0 : firstc);
+ ccline.cmdindent = (firstc > 0 ? 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;
+ // alloc initial ccline.cmdbuff
+ alloc_cmdbuff(indent + 50);
+ ccline.cmdlen = ccline.cmdpos = 0;
+ ccline.cmdbuff[0] = NUL;
- lastwin->w_p_so = 0;
- set_option_value("ch", 1L, NULL, 0);
- update_screen(UPD_VALID); // redraw the screen NOW
+ ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL,
+ .colors = KV_INITIAL_VALUE };
+ sb_text_start_cmdline();
- made_cmdheight_nonzero = false;
- lastwin->w_p_so = save_so;
+ // 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++;
@@ -634,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((char_u *)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);
}
@@ -661,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.
@@ -724,7 +724,7 @@ 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_pattern = ccline.cmdbuff;
s->xpc.xp_arg = (char *)ccline.xp_arg;
}
@@ -753,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);
@@ -793,11 +794,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;
}
@@ -821,6 +822,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);
@@ -854,7 +858,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 = (char *)vim_strsave(ccline.cmdbuff);
+ new_last_cmdline = xstrdup(ccline.cmdbuff);
}
}
@@ -867,12 +871,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);
+ semsg(e_autocmd_err, err.msg);
+ did_emsg = false;
api_clear_error(&err);
}
@@ -881,7 +891,7 @@ 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
@@ -897,7 +907,7 @@ theend:
xfree(ccline.last_colors.cmdbuff);
kv_destroy(ccline.last_colors.colors);
- char_u *p = ccline.cmdbuff;
+ char_u *p = (char_u *)ccline.cmdbuff;
if (ui_has(kUICmdline)) {
ui_call_cmdline_hide(ccline.level);
@@ -912,17 +922,6 @@ 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(UPD_NOT_VALID);
-
- made_cmdheight_nonzero = false;
- }
-
return p;
}
@@ -942,6 +941,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) {
@@ -960,6 +1144,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();
}
@@ -1028,100 +1225,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()
}
}
@@ -1169,107 +1315,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);
}
}
@@ -1326,18 +1373,18 @@ 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();
+ pat = (char *)last_search_pattern();
if (pat == NULL) {
restore_last_search_pattern();
return FAIL;
}
skiplen = 0;
- patlen = (int)STRLEN(pat);
+ patlen = (int)strlen(pat);
} else {
pat = ccline.cmdbuff + skiplen;
}
@@ -1361,7 +1408,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
pat[patlen] = NUL;
int found = searchit(curwin, curbuf, &t, NULL,
next_match ? FORWARD : BACKWARD,
- pat, count, search_flags,
+ (char_u *)pat, count, search_flags,
RE_SEARCH, NULL);
emsg_off--;
pat[patlen] = save;
@@ -1400,7 +1447,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(UPD_NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
+ update_screen();
highlight_match = false;
redrawcmdline();
curwin->w_cursor = s->match_end;
@@ -1411,9 +1459,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((char_u *)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();
+ } else {
+ set_imsearch_global();
+ }
+ }
+ 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) {
@@ -1456,96 +1697,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:
@@ -1555,28 +1818,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: {
@@ -1612,58 +1854,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) {
@@ -1688,7 +1887,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] != ' ');
@@ -1744,26 +1943,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
@@ -1830,8 +2010,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);
@@ -1846,88 +2026,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
@@ -2003,9 +2122,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);
}
@@ -2027,16 +2146,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", 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)
@@ -2156,6 +2294,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;
@@ -2193,6 +2332,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.
@@ -2215,7 +2356,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);
@@ -2225,6 +2366,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);
}
@@ -2281,7 +2427,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)) {
@@ -2352,7 +2498,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
if (cmdpreview_type != 0) {
int save_rd = RedrawingDisabled;
RedrawingDisabled = 0;
- update_screen(UPD_SOME_VALID);
+ update_screen();
RedrawingDisabled = save_rd;
}
@@ -2410,6 +2556,7 @@ 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
@@ -2418,10 +2565,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(UPD_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);
}
@@ -2557,13 +2707,13 @@ int check_opt_wim(void)
if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
return FAIL;
}
- if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
+ if (i == 7 && strncmp(p, "longest", 7) == 0) {
new_wim_flags[idx] |= WIM_LONGEST;
- } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
+ } else if (i == 4 && strncmp(p, "full", 4) == 0) {
new_wim_flags[idx] |= WIM_FULL;
- } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
+ } else if (i == 4 && strncmp(p, "list", 4) == 0) {
new_wim_flags[idx] |= WIM_LIST;
- } else if (i == 8 && STRNCMP(p, "lastused", 8) == 0) {
+ } else if (i == 8 && strncmp(p, "lastused", 8) == 0) {
new_wim_flags[idx] |= WIM_BUFLASTUSED;
} else {
return FAIL;
@@ -2603,10 +2753,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()));
@@ -2659,7 +2807,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
@@ -2685,7 +2833,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);
@@ -2704,8 +2852,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)++;
}
@@ -2738,15 +2886,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 {
@@ -2764,7 +2908,7 @@ void realloc_cmdbuff(int len)
return; // no need to resize
}
- char_u *p = ccline.cmdbuff;
+ char_u *p = (char_u *)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.
@@ -2781,7 +2925,7 @@ void realloc_cmdbuff(int len)
// 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;
}
}
}
@@ -2811,7 +2955,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 },
@@ -2889,7 +3033,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;
}
@@ -2904,7 +3048,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 };
@@ -3088,10 +3232,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)) {
@@ -3107,16 +3249,16 @@ 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_u *p = (char_u *)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);
+ int u8c = utfc_ptr2char_len((char *)p, u8cc, start + len - i);
+ mb_l = utfc_ptr2len_len((char *)p, start + len - i);
if (ARABIC_CHAR(u8c)) {
do_arabicshape = true;
break;
@@ -3140,7 +3282,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;
@@ -3149,10 +3291,10 @@ 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_u *p = (char_u *)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);
+ int u8c = utfc_ptr2char_len((char *)p, u8cc, start + len - i);
+ mb_l = utfc_ptr2len_len((char *)p, start + len - i);
if (ARABIC_CHAR(u8c)) {
int pc;
int pc1 = 0;
@@ -3175,7 +3317,7 @@ static void draw_cmdline(int start, int len)
} else {
int pcc[MAX_MCO];
- pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l);
+ pc = utfc_ptr2char_len((char *)p + mb_l, pcc, start + len - i - mb_l);
pc1 = pcc[0];
}
nc = prev_c;
@@ -3213,7 +3355,7 @@ draw_cmdline_no_arabicshape:
chunk.attr);
}
} else {
- msg_outtrans_len((char *)ccline.cmdbuff + start, len);
+ msg_outtrans_len(ccline.cmdbuff + start, len);
}
}
}
@@ -3225,7 +3367,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
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_u *p = (char_u *)ccline.cmdbuff; *p; MB_PTR_ADV(p)) {
len++;
}
char *buf = arena_alloc(&arena, len, false);
@@ -3342,11 +3484,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) {
@@ -3380,7 +3520,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();
@@ -3388,22 +3528,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);
@@ -3416,13 +3554,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) {
@@ -3440,17 +3578,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 {
@@ -3459,7 +3597,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) {
@@ -3498,7 +3636,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;
}
@@ -3576,19 +3714,19 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
// part of the word.
p = (char_u *)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;
}
}
@@ -3603,18 +3741,16 @@ 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.
- */
+// 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)
{
int c, cv;
if (literally) {
- put_on_cmdline(s, -1, true);
+ put_on_cmdline((char *)s, -1, true);
} else {
while (*s != NUL) {
cv = *s;
@@ -3675,9 +3811,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) {
@@ -3714,10 +3848,8 @@ 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.
- */
+ // 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
@@ -3736,6 +3868,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;
}
@@ -3794,12 +3929,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;
@@ -3848,7 +3981,7 @@ char *vim_strsave_fnameescape(const char *const fname, const int what)
// ":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;
}
}
@@ -3856,18 +3989,16 @@ char *vim_strsave_fnameescape(const char *const fname, const int what)
char *p = (char *)vim_strsave_escaped((const char_u *)fname,
(const char_u *)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;
}
@@ -3885,7 +4016,7 @@ 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_u *p = xmalloc(strlen(*pp) + 2);
p[0] = '\\';
STRCPY(p + 1, *pp);
xfree(*pp);
@@ -3894,7 +4025,7 @@ void escape_fname(char **pp)
/// 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;
@@ -3952,8 +4083,9 @@ static int get_cmdline_type(void)
/// Get the current command line in allocated memory.
/// Only works when the command line is being edited.
-/// Returns NULL when something is wrong.
-static char_u *get_cmdline_str(void)
+///
+/// @return NULL when something is wrong.
+static char *get_cmdline_str(void)
{
if (cmdline_star > 0) {
return NULL;
@@ -3963,23 +4095,25 @@ static char_u *get_cmdline_str(void)
if (p == NULL) {
return NULL;
}
- return vim_strnsave(p->cmdbuff, (size_t)p->cmdlen);
+ return xstrnsave(p->cmdbuff, (size_t)p->cmdlen);
}
/// Get the current command-line completion type.
-static char_u *get_cmdline_completion(void)
+static char *get_cmdline_completion(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 || p->xpc == NULL) {
+ return NULL;
+ }
+
+ 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;
@@ -3989,14 +4123,14 @@ static char_u *get_cmdline_completion(void)
void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)get_cmdline_completion();
+ 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 = (char *)get_cmdline_str();
+ rettv->vval.v_string = get_cmdline_str();
}
/// "getcmdpos()" function
@@ -4031,7 +4165,7 @@ static int set_cmdline_str(const char *str, int pos)
return 1;
}
- int len = (int)STRLEN(str);
+ int len = (int)strlen(str);
realloc_cmdbuff(len + 1);
p->cmdlen = len;
STRCPY(p->cmdbuff, str);
@@ -4071,8 +4205,8 @@ static int set_cmdline_pos(int pos)
/// "setcmdline()" function
void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[0].v_type != VAR_STRING || argvars[0].vval.v_string == NULL) {
- emsg(_(e_stringreq));
+ if (tv_check_for_string_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_number_arg(argvars, 1) == FAIL) {
return;
}
@@ -4125,7 +4259,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;
@@ -4133,7 +4267,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);
@@ -4269,7 +4403,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));
}
@@ -4277,7 +4411,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();
@@ -4309,9 +4443,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;
@@ -4347,7 +4479,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.
@@ -4361,16 +4493,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) {
@@ -4387,6 +4519,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
@@ -4403,6 +4536,7 @@ static int open_cmdwin(void)
// Restore window sizes.
win_size_restore(&winsizes);
+ skip_win_fix_cursor = false;
}
ga_clear(&winsizes);
@@ -4443,7 +4577,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..916b695a35 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,7 +47,7 @@ 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
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index e907c45e79..cae9c18309 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -9,32 +9,36 @@
#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/types.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -211,22 +215,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 +244,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 +268,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 +303,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;
@@ -523,7 +527,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 +546,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 +585,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 +832,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;
}
@@ -956,11 +960,11 @@ void ex_mkrc(exarg_T *eap)
}
// When using 'viewdir' may have to create the directory.
- if (using_vdir && !os_isdir((char *)p_vdir)) {
+ if (using_vdir && !os_isdir(p_vdir)) {
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 +1001,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 +1028,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 +1099,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 8e780f4aaa..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 {
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 bbc6e53aa8..d0280b3571 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,97 +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;
+ 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
+ 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.
- */
+// '**' 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'
- */
+// 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;
@@ -245,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 {
@@ -271,9 +261,9 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
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) {
@@ -290,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++;
@@ -323,17 +313,17 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
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] == ':') {
@@ -342,45 +332,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++;
@@ -390,11 +374,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;
@@ -402,37 +385,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'."),
@@ -444,60 +425,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((char *)buf)) {
+ 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);
@@ -512,29 +492,27 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
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_u *vim_findfile_stopdir(char *buf)
{
- char_u *r_ptr = buf;
+ char_u *r_ptr = (char_u *)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'. */
- STRMOVE(r_ptr, r_ptr + 1);
+ // Overwrite the escape char,
+ // use strlen(r_ptr) to move the trailing '\0'.
+ STRMOVE(r_ptr, (char *)r_ptr + 1);
r_ptr++;
}
r_ptr++;
@@ -575,12 +553,12 @@ void vim_findfile_cleanup(void *ctx)
/// NULL if nothing found.
char_u *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;
@@ -590,16 +568,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
@@ -618,25 +593,23 @@ 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) {
@@ -651,16 +624,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) {
@@ -670,50 +642,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;
@@ -737,17 +705,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) {
@@ -763,18 +729,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,
@@ -784,39 +748,35 @@ 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(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 {
@@ -824,14 +784,14 @@ char_u *vim_findfile(void *search_ctx_arg)
}
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)
- == os_isdir((char *)file_path)))))
+ == 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
@@ -852,13 +812,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);
}
@@ -871,7 +829,7 @@ char_u *vim_findfile(void *search_ctx_arg)
verbose_leave_scroll();
}
#endif
- return file_path;
+ return (char_u *)file_path;
}
// Not found or found already, try next suffix.
@@ -879,7 +837,7 @@ 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 {
@@ -889,7 +847,7 @@ char_u *vim_findfile(void *search_ctx_arg)
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));
}
@@ -898,22 +856,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(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));
}
}
@@ -922,9 +878,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;
@@ -937,12 +892,10 @@ char_u *vim_findfile(void *search_ctx_arg)
}
// 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;
@@ -952,12 +905,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);
@@ -1021,7 +974,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;
@@ -1030,7 +983,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();
@@ -1054,13 +1007,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;
@@ -1074,7 +1025,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;
@@ -1091,8 +1042,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 != '*')) {
@@ -1101,8 +1052,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];
}
@@ -1111,7 +1062,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;
@@ -1119,19 +1070,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
@@ -1142,10 +1093,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;
@@ -1157,7 +1106,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;
}
@@ -1169,7 +1118,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));
@@ -1184,14 +1133,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;
}
@@ -1199,12 +1148,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.
@@ -1277,7 +1228,7 @@ 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)
+static int ff_path_in_stoplist(char *path, int path_len, char **stopdirs_v)
{
int i = 0;
@@ -1292,17 +1243,16 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
}
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;
}
} else {
- if (FNAMECMP(stopdirs_v[i], path) == 0) {
+ if (path_fnamecmp(stopdirs_v[i], path) == 0) {
return true;
}
}
@@ -1337,16 +1287,16 @@ 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_u *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
- ? p_path
- : (char_u *)curbuf->b_p_path),
- FINDFILE_BOTH, rel_fname, (char_u *)curbuf->b_p_sua);
+ return (char_u *)find_file_in_path_option(ptr, len, options, first,
+ (*curbuf->b_p_path == NUL
+ ? (char *)p_path
+ : curbuf->b_p_path),
+ 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)
@@ -1374,8 +1324,8 @@ void free_findfile(void)
/// @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)
{
- return find_file_in_path_option(ptr, len, options, true, p_cdpath,
- FINDFILE_DIR, rel_fname, (char_u *)"");
+ return (char_u *)find_file_in_path_option((char *)ptr, len, options, true, (char *)p_cdpath,
+ FINDFILE_DIR, (char *)rel_fname, "");
}
/// @param ptr file name
@@ -1385,14 +1335,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;
@@ -1409,16 +1358,16 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
// copy file name into NameBuff, expanding environment variables
save_char = ptr[len];
ptr[len] = NUL;
- expand_env_esc(ptr, (char_u *)NameBuff, MAXPATHL, false, true, NULL);
+ expand_env_esc(ptr, NameBuff, MAXPATHL, false, true, NULL);
ptr[len] = save_char;
xfree(ff_file_to_find);
- ff_file_to_find = vim_strsave((char_u *)NameBuff);
+ ff_file_to_find = xstrdup(NameBuff);
if (options & FNAME_UNESC) {
// Change all "\ " to " ".
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));
}
}
}
@@ -1433,88 +1382,81 @@ 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.
- */
+ // 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);
+ 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. */
+ // 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);
+ 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((char_u *)NameBuff)
+ if ((os_path_exists(NameBuff)
&& (find_what == FINDFILE_BOTH
|| ((find_what == FINDFILE_DIR)
== os_isdir(NameBuff))))) {
- file_name = vim_strsave((char_u *)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.
- */
+ // 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;
}
for (;;) {
if (did_findfile_init) {
- file_name = vim_findfile(fdip_search_ctx);
+ file_name = (char *)vim_findfile(fdip_search_ctx);
if (file_name != NULL) {
break;
}
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;
@@ -1527,8 +1469,8 @@ 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 = (char *)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) {
@@ -1638,14 +1580,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((char_u *)NameBuff, sizeof(NameBuff)) != OK) {
+ 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;
}
@@ -1666,9 +1608,9 @@ 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),
+ char *dir_name = (char *)find_directory_in_path((char_u *)new_dir, strlen(new_dir),
FNAME_MESS, (char_u *)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 d67cd36a85..0ee547d124 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,27 +82,27 @@
#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
@@ -118,17 +128,19 @@ 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().
@@ -143,7 +155,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((char *)msg_may_trunc(false, IObuff), attr);
+ msg_outtrans_attr(msg_may_trunc(false, IObuff), attr);
msg_clr_eos();
ui_flush();
msg_scrolled_ign = false;
@@ -257,12 +269,10 @@ 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
@@ -350,11 +360,11 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
// 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;
@@ -365,7 +375,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;
@@ -387,10 +397,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;
@@ -401,12 +411,10 @@ 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;
@@ -419,17 +427,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 {
@@ -482,10 +488,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
@@ -504,26 +510,23 @@ 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;
}
@@ -532,6 +535,8 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
// Don't change 'eol' if reading from buffer as it will already be
// correctly set when reading stdin.
if (!read_buffer) {
+ curbuf->b_p_eof = false;
+ curbuf->b_start_eof = false;
curbuf->b_p_eol = true;
curbuf->b_start_eol = true;
}
@@ -645,13 +650,11 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
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))
@@ -673,16 +676,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
- /*
- * 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.
@@ -695,9 +696,7 @@ 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 = enc_canonize(eap->cmd + eap->force_enc);
fenc_alloced = true;
@@ -719,28 +718,26 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
fenc_alloced = false;
} else {
fenc_next = 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_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) {
@@ -764,10 +761,8 @@ retry:
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 {
@@ -793,9 +788,7 @@ retry:
#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) {
@@ -813,7 +806,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;
@@ -825,16 +818,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
@@ -855,10 +846,8 @@ retry:
}
#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
@@ -922,12 +911,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
@@ -999,10 +986,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 {
@@ -1011,8 +996,8 @@ 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
@@ -1052,9 +1037,7 @@ retry:
}
}
} else {
- /*
- * Read bytes from the file.
- */
+ // Read bytes from the file.
size = read_eintr(fd, ptr, (size_t)size);
}
@@ -1062,10 +1045,8 @@ 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
@@ -1080,9 +1061,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;
}
@@ -1098,7 +1078,7 @@ retry:
#ifdef HAVE_ICONV
|| iconv_fd != (iconv_t)-1
#endif
- )) {
+ )) { // NOLINT(whitespace/parens)
while (conv_restlen > 0) {
*(--ptr) = (char)bad_char_behavior;
conv_restlen--;
@@ -1118,12 +1098,10 @@ 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
@@ -1172,9 +1150,7 @@ 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;
}
@@ -1188,11 +1164,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)
@@ -1371,7 +1345,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) {
@@ -1493,9 +1467,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) {
@@ -1560,10 +1532,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) {
@@ -1665,18 +1635,28 @@ 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;
@@ -1719,7 +1699,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
@@ -1727,7 +1707,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
}
}
@@ -1738,9 +1718,7 @@ failed:
}
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)) {
@@ -1767,18 +1745,9 @@ 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
}
@@ -1789,7 +1758,7 @@ 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
@@ -1832,12 +1801,12 @@ failed:
c = true;
}
if (conv_error != 0) {
- sprintf((char *)IObuff + STRLEN(IObuff),
- _("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error);
+ 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);
+ 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]"));
@@ -1854,7 +1823,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
@@ -1878,10 +1847,8 @@ failed:
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 {
@@ -1900,17 +1867,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
@@ -1919,9 +1882,7 @@ 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];
@@ -1939,11 +1900,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,
@@ -1984,7 +1943,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
@@ -1999,7 +1958,7 @@ 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_u *p, const char_u *endp)
{
char_u *s;
linenr_T lnum;
@@ -2018,7 +1977,7 @@ 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);
@@ -2055,11 +2014,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 *fenc = enc_canonize(eap->cmd + eap->force_enc);
- set_string_option_direct("fenc", -1, 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'.
@@ -2068,25 +2029,25 @@ 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 = (char_u *)enc_canonize(*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;
- p = (char_u *)enc_canonize((char *)r);
+ r = xstrnsave(*pp, (size_t)(p - *pp));
+ *pp = p + 1;
+ p = enc_canonize(r);
xfree(r);
r = p;
}
@@ -2109,7 +2070,7 @@ static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp)
char_u *tmpname;
char *errmsg = NULL;
- tmpname = vim_tempname();
+ tmpname = (char_u *)vim_tempname();
if (tmpname == NULL) {
errmsg = _("Can't find temp file for conversion");
} else {
@@ -2151,8 +2112,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;
}
@@ -2237,8 +2198,8 @@ 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;
context_sha256_T sha_ctx;
@@ -2250,22 +2211,19 @@ 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;
}
@@ -2279,17 +2237,15 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
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
@@ -2317,7 +2273,7 @@ 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;
@@ -2325,9 +2281,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
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;
@@ -2344,11 +2298,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
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;
}
@@ -2386,9 +2338,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);
}
@@ -2434,19 +2386,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) {
@@ -2471,12 +2423,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;
@@ -2493,10 +2443,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;
}
@@ -2527,9 +2475,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
#ifndef UNIX
(char_u *)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
@@ -2543,9 +2491,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;
@@ -2562,8 +2508,8 @@ 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. */
+ // 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;
@@ -2595,10 +2541,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
#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) {
@@ -2610,10 +2554,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;
@@ -2622,41 +2564,33 @@ 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((char_u *)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)) {
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;
// 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;
@@ -2666,57 +2600,51 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
} 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;
} 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;
} 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;
}
#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);
@@ -2752,33 +2680,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.
- */
+ // 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((char *)IObuff)) {
+ 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);
@@ -2786,14 +2710,14 @@ 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
goto nobackup;
@@ -2814,9 +2738,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
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)) {
//
@@ -2831,7 +2753,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;
}
@@ -2848,9 +2770,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);
@@ -2873,10 +2793,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
@@ -2885,8 +2806,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((char_u *)backup, acl);
#endif
+ SET_ERRMSG(NULL);
break;
}
}
@@ -2906,40 +2828,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.
- */
+ // 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((char *)IObuff)) {
+ 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);
@@ -2947,7 +2861,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);
@@ -2955,7 +2869,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 {
@@ -2965,18 +2879,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!
@@ -2994,7 +2906,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;
}
@@ -3062,7 +2974,7 @@ nobackup:
}
// 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().
@@ -3099,13 +3011,11 @@ nobackup:
} 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.
- */
+ // 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();
+ 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;
@@ -3156,9 +3066,11 @@ nobackup:
// 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
@@ -3211,21 +3123,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;
}
}
@@ -3282,9 +3194,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, (char_u *)ptr + 1, (uint32_t)(strlen(ptr + 1) + 1));
}
while ((c = *++ptr) != NUL) {
if (c == NL) {
@@ -3358,6 +3270,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;
@@ -3425,7 +3342,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((char_u *)wfname, acl);
}
#endif
@@ -3453,7 +3370,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);
@@ -3487,7 +3404,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;
}
}
@@ -3502,13 +3419,13 @@ restore_backup:
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;
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) {
@@ -3542,11 +3459,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)) {
@@ -3561,10 +3478,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) {
@@ -3574,22 +3489,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, 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,
@@ -3597,12 +3508,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
@@ -3620,9 +3528,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) {
@@ -3631,9 +3537,7 @@ 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
nofail:
@@ -3654,15 +3558,15 @@ nofail:
}
#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) {
@@ -3698,10 +3602,8 @@ 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];
@@ -3714,10 +3616,8 @@ 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) {
@@ -3843,22 +3743,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);
}
@@ -3918,18 +3818,14 @@ static int buf_write_bytes(struct bw_info *ip)
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;
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);
@@ -3937,10 +3833,8 @@ static int buf_write_bytes(struct bw_info *ip)
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 {
@@ -3950,9 +3844,9 @@ 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;
@@ -3960,9 +3854,9 @@ static int buf_write_bytes(struct bw_info *ip)
memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l);
n = utf_ptr2len_len(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;
}
@@ -3986,9 +3880,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;
}
@@ -4031,9 +3925,9 @@ 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;
memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen);
@@ -4053,9 +3947,8 @@ 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;
tolen = save_len;
@@ -4063,9 +3956,7 @@ static int buf_write_bytes(struct bw_info *ip)
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) {
@@ -4121,8 +4012,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;
@@ -4166,21 +4057,21 @@ 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) {
+ if (*fenc == NUL || strcmp(p_enc, fenc) == 0) {
same_encoding = true;
fenc_flags = 0;
} else {
// Ignore difference between "ansi" and "latin1", "ucs-4" and
// "ucs-4be", etc.
enc_flags = get_fio_flags((char_u *)p_enc);
- fenc_flags = get_fio_flags(fenc);
+ fenc_flags = get_fio_flags((char_u *)fenc);
same_encoding = (enc_flags != 0 && fenc_flags == enc_flags);
}
if (same_encoding) {
@@ -4205,7 +4096,7 @@ static int get_fio_flags(const char_u *name)
if (*name == NUL) {
name = (char_u *)p_enc;
}
- prop = enc_canon_props(name);
+ prop = enc_canon_props((char *)name);
if (prop & ENC_UNICODE) {
if (prop & ENC_2BYTE) {
if (prop & ENC_ENDIAN_L) {
@@ -4239,7 +4130,7 @@ 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_u *check_for_bom(const char_u *p, long size, int *lenp, int flags)
{
char *name = NULL;
int len = 2;
@@ -4327,7 +4218,7 @@ void shorten_buf_fname(buf_T *buf, char_u *dirname, int force)
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, (char *)dirname);
if (p != NULL) {
buf->b_sfname = xstrdup(p);
buf->b_fname = buf->b_sfname;
@@ -4343,7 +4234,7 @@ void shorten_fnames(int force)
{
char_u dirname[MAXPATHL];
- os_dirname(dirname, MAXPATHL);
+ os_dirname((char *)dirname, MAXPATHL);
FOR_ALL_BUFFERS(buf) {
shorten_buf_fname(buf, dirname, force);
@@ -4390,7 +4281,7 @@ 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;
@@ -4401,7 +4292,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
} 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 ':'.
@@ -4419,12 +4310,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.
@@ -4458,7 +4348,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;
@@ -4467,7 +4358,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') {
@@ -4619,7 +4510,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;
@@ -4633,13 +4524,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 {
@@ -4665,11 +4554,9 @@ int vim_rename(const char_u *from, const char_u *to)
if (use_tmp_file) {
char_u 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);
@@ -4677,14 +4564,14 @@ int vim_rename(const char_u *from, const char_u *to)
char *tail = path_tail((char *)tempname);
snprintf(tail, (size_t)((MAXPATHL + 1) - (tail - (char *)tempname - 1)), "%d", n);
- if (!os_path_exists(tempname)) {
- if (os_rename(from, tempname) == OK) {
- if (os_rename(tempname, to) == OK) {
+ if (!os_path_exists((char *)tempname)) {
+ if (os_rename((char_u *)from, tempname) == OK) {
+ if (os_rename(tempname, (char_u *)to) == OK) {
return 0;
}
// Strange, the second step failed. Try moving the
// file back and return failure.
- (void)os_rename(tempname, from);
+ (void)os_rename(tempname, (char_u *)from);
return -1;
}
// If it fails for one temp name it will most likely fail
@@ -4695,33 +4582,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.
- */
- if (os_rename(from, to) == OK) {
+ // First try a normal rename, return if it works.
+ if (os_rename((char_u *)from, (char_u *)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((char_u *)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;
}
@@ -4732,7 +4613,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;
}
@@ -4744,7 +4625,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;
}
@@ -4769,8 +4650,8 @@ int vim_rename(const char_u *from, const char_u *to)
os_setperm((const char *)to, perm);
#endif
#ifdef HAVE_ACL
- mch_set_acl(to, acl);
- mch_free_acl(acl);
+ os_set_acl((char_u *)to, acl);
+ os_free_acl(acl);
#endif
if (errmsg != NULL) {
semsg(errmsg, to);
@@ -4852,13 +4733,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;
@@ -4893,7 +4774,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;
@@ -4908,7 +4789,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;
@@ -4983,12 +4864,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;
@@ -5028,7 +4909,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;
@@ -5036,11 +4917,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
@@ -5051,8 +4932,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;
@@ -5077,7 +4958,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);
@@ -5128,7 +5009,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
@@ -5292,6 +5173,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.
@@ -5364,10 +5248,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);
}
@@ -5434,7 +5317,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_u **)ga.ga_data)[i]);
if (delete_recursive((const char *)NameBuff) != 0) {
// Remember the failure but continue deleting any further
// entries.
@@ -5457,15 +5340,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).
@@ -5490,12 +5408,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;
}
@@ -5506,7 +5428,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;
@@ -5518,10 +5440,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
@@ -5553,12 +5475,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)
@@ -5639,7 +5559,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
*allow_dirs = false;
}
if (pat_end == NULL) {
- pat_end = pat + STRLEN(pat);
+ pat_end = pat + strlen(pat);
}
if (pat_end == pat) {
@@ -5715,7 +5635,7 @@ 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++] = '\\';
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index ae3c51f1bc..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"
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index b4ddb3ec08..6cb9e5c38d 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
@@ -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();
}
}
@@ -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;
}
}
@@ -1556,7 +1575,7 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
}
parseMarker(wp);
- foldAddMarker(buf, start, (char_u *)wp->w_p_fmr, foldstartmarkerlen);
+ foldAddMarker(buf, start, wp->w_p_fmr, foldstartmarkerlen);
foldAddMarker(buf, end, foldendmarker, foldendmarkerlen);
// Update both changes here, to avoid all folds after the start are
@@ -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 = (char_u *)buf->b_p_cms;
- char_u *newline;
- char_u *p = (char_u *)strstr(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);
}
}
@@ -1621,7 +1642,7 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu
lnum_off + fp->fd_top);
}
}
- foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off, (char_u *)wp->w_p_fmr,
+ foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off, wp->w_p_fmr,
foldstartmarkerlen);
foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off + fp->fd_len - 1,
foldendmarker, foldendmarkerlen);
@@ -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 = (char_u *)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(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(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,7 +2869,7 @@ 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
@@ -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(wp->w_p_fmr, ',');
- foldstartmarkerlen = (size_t)(foldendmarker++ - (char_u *)wp->w_p_fmr);
- foldendmarkerlen = STRLEN(foldendmarker);
+ foldendmarker = vim_strchr(wp->w_p_fmr, ',');
+ foldstartmarkerlen = (size_t)(foldendmarker++ - wp->w_p_fmr);
+ 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 = (char_u *)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;
@@ -3240,7 +3259,7 @@ void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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, EvalFuncData 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, 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, EvalFuncData 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..9c3a34ab31 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
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 1afabe4e10..0f11868031 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)
@@ -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;
}
@@ -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
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 0281678925..3e2650b410 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -1,9 +1,11 @@
#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/memory.h"
#include "nvim/types.h"
/// Structure used for growing arrays.
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index d872ffd6a9..3e89b60b4a 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -27,6 +27,7 @@ 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))
)
@@ -48,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 67b8f5f0f5..96c9b21b8f 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -60,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
@@ -78,6 +84,10 @@ for i = 6, #arg do
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()
@@ -155,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]
@@ -185,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]+)%)")
@@ -231,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
@@ -317,18 +358,42 @@ 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
-- if the function can fail, also pass a pointer to the local error object
if #args > 0 then
@@ -497,6 +562,10 @@ local function process_function(fn)
]])
end
+ if fn.has_lua_imp then
+ cparams = cparams .. 'lstate, '
+ end
+
if fn.can_fail then
cparams = cparams .. '&err'
else
@@ -539,13 +608,27 @@ local function process_function(fn)
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);
+ ]], return_type))
+ end
+
+ write_shifted_output(output, string.format([[
%s
%s
%s
return 1;
- ]], fn.return_type, fn.name, cparams, return_type,
- free_retval, 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 f9e888c20d..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]..', NULL);\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]..', NULL);\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]..', NULL);\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 8e6d1f2634..a50e058e00 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -27,6 +27,33 @@ 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
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_options.lua b/src/nvim/generators/gen_options.lua
index 0454c54faf..af21d44eaf 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)
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 0a9457ef35..b9e3b6b902 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"
@@ -87,25 +99,23 @@ 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
@@ -124,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;
@@ -152,7 +160,7 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero)
// 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) {
@@ -174,31 +182,27 @@ 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 = (char *)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.
@@ -233,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) {
@@ -250,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;
@@ -267,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".
@@ -351,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) {
@@ -381,19 +385,15 @@ int readbuf1_empty(void)
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();
@@ -437,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.
@@ -476,10 +476,12 @@ void saveRedobuff(save_redo_T *save_redo)
// 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);
+ if (s == NULL) {
+ return;
}
+
+ add_buff(&redobuff, s, -1L);
+ xfree(s);
}
/// Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff.
@@ -527,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)) {
@@ -559,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) {
@@ -615,9 +615,7 @@ 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);
@@ -783,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;
@@ -818,21 +814,21 @@ void stop_redo_ins(void)
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.
@@ -871,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]
@@ -939,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) {
@@ -1016,18 +1010,14 @@ int typebuf_typed(void)
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;
@@ -1038,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);
@@ -1101,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
{
@@ -1155,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)
@@ -1177,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);
@@ -1195,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) {
@@ -1212,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)
@@ -1239,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;
@@ -1257,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) {
@@ -1304,7 +1275,7 @@ void openscript(char *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) {
@@ -1314,12 +1285,10 @@ void openscript(char *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;
@@ -1348,9 +1317,7 @@ void openscript(char *name, bool directly)
}
}
-/*
- * Close the currently active input script.
- */
+// Close the currently active input script.
static void closescript(void)
{
free_typebuf();
@@ -1452,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;
@@ -1628,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.
@@ -1641,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;
@@ -1656,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;
@@ -1672,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()) {
@@ -1686,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;
@@ -1702,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;
@@ -1726,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.
@@ -1736,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;
@@ -1766,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(UPD_NOT_VALID);
- }
-
set_vim_var_nr(VV_MOUSE_WIN, 0);
set_vim_var_nr(VV_MOUSE_WINID, 0);
set_vim_var_nr(VV_MOUSE_LNUM, 0);
@@ -1778,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;
@@ -1838,19 +1787,21 @@ 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
@@ -1961,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;
@@ -2031,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;
}
}
@@ -2053,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)) {
@@ -2071,8 +2034,8 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// mapping starts with K_SNR.
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;
}
@@ -2179,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) {
@@ -2205,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
@@ -2242,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
@@ -2263,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((char_u *)mp->m_str);
+ save_m_str = xstrdup(mp->m_str);
}
map_str = eval_map_expr(mp, NUL);
@@ -2277,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;
@@ -2297,7 +2260,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
vgetc_busy = save_vgetc_busy;
may_garbage_collect = save_may_garbage_collect;
} else {
- map_str = (char_u *)mp->m_str;
+ map_str = mp->m_str;
}
// Insert the 'to' part in the typebuf.tb_buf.
@@ -2312,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);
}
@@ -2393,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;
@@ -2566,10 +2531,10 @@ static int vgetorpeek(bool advance)
// white-space, so find the last non-white
// character -- webb
curwin->w_wcol = 0;
- ptr = get_cursor_line_ptr();
+ ptr = (char_u *)get_cursor_line_ptr();
chartabsize_T cts;
init_chartabsize_arg(&cts, curwin,
- curwin->w_cursor.lnum, 0, ptr, ptr);
+ 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;
@@ -2596,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--;
}
@@ -2676,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
}
@@ -2851,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
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 060db3b3b1..aa8dbf6331 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;
@@ -194,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
@@ -315,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
@@ -413,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.
@@ -475,17 +491,16 @@ 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);
/// 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
@@ -500,7 +515,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.
@@ -599,7 +614,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:
@@ -613,7 +628,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
@@ -621,7 +636,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);
@@ -661,6 +676,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
@@ -670,7 +687,7 @@ 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 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
@@ -824,9 +841,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()
@@ -857,7 +871,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"));
@@ -997,8 +1011,6 @@ 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"));
@@ -1009,17 +1021,18 @@ EXTERN char e_highlight_group_name_invalid_char[] INIT(= N_("E5248: Invalid char
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
@@ -1029,7 +1042,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
@@ -1069,9 +1082,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 329adfda12..4702e224c6 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;
@@ -130,7 +140,7 @@ void grid_putchar(ScreenGrid *grid, int c, int row, int col, int 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;
@@ -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;
@@ -240,10 +250,10 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col,
c = (unsigned char)(*ptr);
// check if this is the first byte of a multibyte
mbyte_blen = len > 0
- ? utfc_ptr2len_len((char_u *)ptr, (int)((text + len) - ptr))
+ ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
: utfc_ptr2len(ptr);
u8c = len >= 0
- ? utfc_ptr2char_len((char_u *)ptr, u8cc, (int)((text + len) - ptr))
+ ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
: utfc_ptr2char(ptr, u8cc);
mbyte_cells = utf_char2cells(u8c);
if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
@@ -254,7 +264,7 @@ void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col,
nc1 = NUL;
} else {
nc = len >= 0
- ? utfc_ptr2char_len((char_u *)ptr + mbyte_blen, pcc,
+ ? utfc_ptr2char_len(ptr + mbyte_blen, pcc,
(int)((text + len) - ptr - mbyte_blen))
: utfc_ptr2char(ptr + mbyte_blen, pcc);
nc1 = pcc[0];
@@ -280,7 +290,8 @@ void grid_puts_len(ScreenGrid *grid, char *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
@@ -412,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) {
@@ -605,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 602fa8b71d..0000000000
--- a/src/nvim/hardcopy.c
+++ /dev/null
@@ -1,3178 +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/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"
-#include "nvim/vim.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((char_u *)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((char_u *)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, (char *)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;
- 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.
- char_u *p_encoding = (char_u *)enc_skip(p_penc);
- if (*p_encoding == NUL) {
- p_encoding = (char_u *)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(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 = (char_u *)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 = 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 = 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 = enc_skip(p_penc);
- if (*p_encoding == NUL) {
- p_encoding = 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((char_u *)p_enc) & enc_canon_props((char_u *)p_encoding) & ENC_8BIT)) {
- // Set up encoding conversion if required
- if (convert_setup(&prt_conv, p_enc, p_encoding) == FAIL) {
- 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
- p = (char_u *)string_convert(&prt_conv, (char *)p, &len);
- tofree = p;
- 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 308f64f011..042cb43ce4 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -87,7 +87,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((char_u *)key));
}
/// Like hash_find, but key is not NUL-terminated
@@ -140,7 +140,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 +166,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,22 +194,22 @@ 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);
+ hash_T hash = hash_hash((char_u *)key);
+ hashitem_T *hi = hash_lookup(ht, key, strlen(key), hash);
if (!HASHITEM_EMPTY(hi)) {
internal_error("hash_add()");
return FAIL;
}
- hash_add_item(ht, hi, key, hash);
+ hash_add_item(ht, hi, (char_u *)key, hash);
return OK;
}
@@ -223,10 +223,11 @@ int hash_add(hashtab_T *ht, char_u *key)
void hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash)
{
ht->ht_used++;
+ ht->ht_changed++;
if (hi->hi_key == NULL) {
ht->ht_filled++;
}
- hi->hi_key = key;
+ hi->hi_key = (char *)key;
hi->hi_hash = hash;
// When the space gets low may resize the array.
@@ -242,6 +243,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 +267,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 +292,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 +305,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 +335,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;
@@ -384,6 +393,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) \
@@ -449,5 +459,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 245d80489f..0a695a0aa7 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;
@@ -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;
}
@@ -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) {
@@ -459,7 +472,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
if (*s < ' '
|| (*s == '^' && s[1]
&& (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
- if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
+ 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;
}
}
@@ -537,7 +550,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep
if (keep_lang) {
flags |= TAG_KEEP_LANG;
}
- if (find_tags((char *)IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
+ if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
&& *num_matches > 0) {
// Sort the matches found on the heuristic number that is after the
// tag name.
@@ -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,7 +663,7 @@ 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_give_err("ft", 0L, "help", OPT_LOCAL);
curbuf->b_ro_locked--;
@@ -658,13 +671,13 @@ void fix_help_buffer(void)
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,14 +700,14 @@ 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;
}
@@ -703,10 +716,10 @@ void fix_help_buffer(void)
// $VIMRUNTIME.
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 = strrchr(t1, '.');
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
@@ -807,19 +818,19 @@ void fix_help_buffer(void)
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 = string_convert(&vc, (char *)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);
@@ -915,7 +926,7 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
ga_init(&ga, (int)sizeof(char_u *), 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,12 +941,13 @@ 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++) {
+ for (s = IObuff; *s != NUL; s++) {
if ((char_u)(*s) >= 0x80) {
this_utf8 = kTrue;
const int l = utf_ptr2len(s);
@@ -960,6 +972,13 @@ static void helptags_one(char *dir, const char *ext, const char *tagfname, bool
}
firstline = false;
}
+ if (in_example) {
+ // skip over example; a non-white in the first column ends it
+ if (vim_strchr(" \t\n\r", IObuff[0])) {
+ continue;
+ }
+ in_example = false;
+ }
p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
while (p1 != NULL) {
p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
@@ -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')
+ && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
&& (vim_strchr(" \t\n\r", s[1]) != NULL
|| s[1] == '\0')) {
*p2 = '\0';
p1++;
- size_t s_len= (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,18 +1181,17 @@ 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);
+ 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 {
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index a78b933108..c3a259c87d 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)
@@ -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;
}
@@ -407,7 +415,7 @@ void update_ns_hl(int ns_id)
int *hl_attrs = **alloc;
for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
- int id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[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);
}
@@ -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,25 +809,21 @@ 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_BOLD) {
PUT_C(hl, "bold", BOOLEAN_OBJ(true));
@@ -899,14 +903,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, NULL);
- kv_destroy(hl);
- return allocated;
- }
+ *dict = hl;
}
HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err)
@@ -933,22 +930,26 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(dict, mask, italic, , HL_ITALIC);
CHECK_FLAG(dict, mask, reverse, , HL_INVERSE);
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
+ 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;
@@ -1035,11 +1036,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 +1050,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 +1087,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..2557a248c3 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -114,6 +114,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 +179,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 ed1f0185b7..404835c4a9 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
@@ -98,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",
@@ -133,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",
@@ -169,27 +192,35 @@ 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",
- "default link @error Error",
+ // 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 @todo Todo",
- "default link @debug Debug",
+ "default link @text.todo Todo",
// Miscs
"default link @comment Comment",
@@ -203,6 +234,7 @@ static const char *highlight_init_both[] = {
"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",
@@ -227,12 +259,27 @@ static const char *highlight_init_both[] = {
"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 @structure Structure",
+ "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
};
@@ -554,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) {
@@ -607,8 +654,9 @@ 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;
@@ -622,9 +670,9 @@ int load_colors(char_u *name)
}
recursive = true;
- size_t buflen = STRLEN(name) + 12;
+ size_t buflen = strlen(name) + 12;
buf = xmalloc(buflen);
- apply_autocmds(EVENT_COLORSCHEMEPRE, (char *)name, curbuf->b_fname, false, curbuf);
+ apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf);
snprintf((char *)buf, buflen, "colors/%s.vim", name);
retval = source_runtime((char *)buf, DIP_START + DIP_OPT);
if (retval == FAIL) {
@@ -632,7 +680,9 @@ int load_colors(char_u *name)
retval = source_runtime((char *)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;
@@ -790,7 +840,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;
@@ -843,7 +893,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
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);
@@ -987,7 +1037,7 @@ 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);
+ 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)) {
@@ -1083,7 +1133,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
int i;
while (arg[off] != NUL) {
for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) {
- len = (int)STRLEN(hl_name_table[i]);
+ len = (int)strlen(hl_name_table[i]);
if (STRNICMP(arg + off, hl_name_table[i], len) == 0) {
attr |= hl_attr_table[i];
off += len;
@@ -1118,9 +1168,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;
@@ -1238,7 +1288,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)) {
@@ -1249,7 +1299,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;
@@ -1466,19 +1516,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 = (char *)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;
@@ -1497,34 +1549,37 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg
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;
+ }
+
+ 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;
}
@@ -1719,9 +1774,8 @@ int syn_name2id(const char *name)
if (name[0] == '@') {
// if we look up @aaa.bbb, we have to consider @aaa as well
return syn_check_group(name, strlen(name));
- } else {
- return syn_name2id_len(name, STRLEN(name));
}
+ return syn_name2id_len(name, strlen(name));
}
/// Lookup a highlight group name and return its ID.
@@ -2004,7 +2058,7 @@ void highlight_changed(void)
/// 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]));
+ id = syn_check_group(hlf_names[hlf], strlen(hlf_names[hlf]));
if (id == 0) {
abort();
}
@@ -2031,7 +2085,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;
//
@@ -2061,6 +2115,8 @@ void highlight_changed(void)
}
}
highlight_ga.ga_len = hlcnt;
+
+ decor_provider_invalidate_hl();
}
/// Handle command line completion for :highlight command.
@@ -2072,36 +2128,44 @@ 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(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(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(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;
- }
- }
+ 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.
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
deleted file mode 100644
index 9413abb2e1..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(arg);
- if (*p != NUL) { // Past first word.
- xp->xp_pattern = skipwhite(p);
- if (*skiptowhite(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(arg1, 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(arg2, 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, 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(csinfo[i].ppath, 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(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(false);
- 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 792f4d93c4..be1dfb77cf 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,22 +13,34 @@
#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_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/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"
@@ -100,7 +114,7 @@ bool tabstop_set(char *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;
@@ -128,7 +142,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;
@@ -177,7 +191,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;
@@ -185,6 +199,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;
@@ -241,7 +256,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;
@@ -265,7 +280,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;
@@ -343,7 +358,7 @@ int get_sts_value(void)
// Count the size (in window cells) of the indent in the current line.
int get_indent(void)
{
- return get_indent_str_vtab((char *)get_cursor_line_ptr(),
+ return get_indent_str_vtab(get_cursor_line_ptr(),
curbuf->b_p_ts,
curbuf->b_p_vts_array,
false);
@@ -352,7 +367,7 @@ int get_indent(void)
// Count the size (in window cells) of the indent in line "lnum".
int get_indent_lnum(linenr_T lnum)
{
- return get_indent_str_vtab((char *)ml_get(lnum),
+ return get_indent_str_vtab(ml_get(lnum),
curbuf->b_p_ts,
curbuf->b_p_vts_array,
false);
@@ -362,8 +377,8 @@ int get_indent_lnum(linenr_T lnum)
// "buf".
int get_indent_buf(buf_T *buf, linenr_T lnum)
{
- return get_indent_str_vtab((char *)ml_get_buf(buf, lnum, false),
- curbuf->b_p_ts,
+ return get_indent_str_vtab(ml_get_buf(buf, lnum, false),
+ buf->b_p_ts,
buf->b_p_vts_array,
false);
}
@@ -434,10 +449,10 @@ int get_indent_str_vtab(const char *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;
@@ -549,9 +564,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
@@ -631,7 +646,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 (;;) {
@@ -659,7 +674,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,
@@ -710,7 +725,7 @@ 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(curbuf->b_p_flp, RE_MAGIC);
@@ -719,7 +734,7 @@ int get_number_indent(linenr_T lnum)
// 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;
@@ -742,22 +757,26 @@ bool briopt_check(win_T *wp)
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
+ 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])) {
+ } 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) {
+ } else if (strncmp(p, "sbr", 3) == 0) {
p += 3;
bri_sbr = true;
- } else if (STRNCMP(p, "list:", 5) == 0) {
+ } 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;
@@ -770,7 +789,8 @@ bool briopt_check(win_T *wp)
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_list = bri_list;
+ wp->w_briopt_vcol = bri_vcol;
return true;
}
@@ -778,55 +798,82 @@ bool briopt_check(win_T *wp)
// 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 long prev_ts = 0L; // Cached tabstop value.
static const char_u *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
- if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
+ // used cached indent, unless
+ // - line pointer changed
+ // - 'tabstop' changed
+ // - 'briopt_list changed' changed or
+ // - 'formatlistpattern' changed
+ if (prev_line != (char_u *)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_line = (char_u *)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((char *)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_list = (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(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;
+ } else {
+ bri = prev_list;
}
}
@@ -857,15 +904,14 @@ int inindent(int extra)
char_u *ptr;
colnr_T col;
- for (col = 0, ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr); col++) {
+ for (col = 0, ptr = (char_u *)get_cursor_line_ptr(); ascii_iswhite(*ptr); col++) {
ptr++;
}
if (col >= curwin->w_cursor.col + extra) {
return true;
- } else {
- return false;
}
+ return false;
}
/// @return true if the conditions are OK for smart indenting.
@@ -874,7 +920,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;
@@ -898,8 +1135,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((char_u *)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) {
@@ -918,6 +1155,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();
@@ -979,7 +1222,7 @@ int get_lisp_indent(void)
continue;
}
- for (that = get_cursor_line_ptr(); *that != NUL; that++) {
+ for (that = (char_u *)get_cursor_line_ptr(); *that != NUL; that++) {
if (*that == ';') {
while (*(that + 1) != NUL) {
that++;
@@ -1029,14 +1272,14 @@ int get_lisp_indent(void)
curwin->w_cursor.col = pos->col;
col = pos->col;
- that = get_cursor_line_ptr();
+ that = (char_u *)get_cursor_line_ptr();
if (vi_lisp && (get_indent() == 0)) {
amount = 2;
} else {
char_u *line = that;
chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
+ init_chartabsize_arg(&cts, curwin, pos->lnum, 0, (char *)line, (char *)line);
while (*cts.cts_ptr != NUL && col > 0) {
cts.cts_vcol += lbr_chartabsize_adv(&cts);
col--;
@@ -1050,7 +1293,7 @@ int get_lisp_indent(void)
// (let ((a 1)) instead (let ((a 1))
// (...)) of (...))
if (!vi_lisp && ((*that == '(') || (*that == '['))
- && lisp_match(that + 1)) {
+ && lisp_match((char *)that + 1)) {
amount += 2;
} else {
if (*that != NUL) {
@@ -1060,7 +1303,7 @@ int get_lisp_indent(void)
firsttry = amount;
init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line),
- amount, line, that);
+ amount, (char *)line, (char *)that);
while (ascii_iswhite(*cts.cts_ptr)) {
cts.cts_vcol += lbr_chartabsize(&cts);
cts.cts_ptr++;
@@ -1081,7 +1324,7 @@ int get_lisp_indent(void)
quotecount = 0;
init_chartabsize_arg(&cts, curwin,
- (colnr_T)(that - line), amount, line, that);
+ (colnr_T)(that - line), amount, (char *)line, (char *)that);
if (vi_lisp || ((*that != '"') && (*that != '\'')
&& (*that != '#') && ((*that < '0') || (*that > '9')))) {
while (*cts.cts_ptr
@@ -1128,19 +1371,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 ? (char_u *)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 166695ef5b..ccf4448e93 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -1,21 +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 <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/types.h"
#include "nvim/vim.h"
// Find result cache for cpp_baseclass
@@ -27,11 +35,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 +54,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 +101,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 +129,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 +165,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 +192,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,27 +202,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.
- */
+// 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 = 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 = 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]))) {
+ 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;
}
@@ -235,21 +230,19 @@ bool cin_is_cinword(const char *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 != '/') {
@@ -257,7 +250,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,24 +268,22 @@ static const char_u *cin_skipcomment(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_u *s)
+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;
@@ -305,21 +296,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) {
@@ -334,14 +325,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)++;
}
@@ -355,7 +346,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.
@@ -365,26 +356,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--;
- /*
- * 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;
@@ -411,14 +399,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());
@@ -447,7 +433,7 @@ static int cin_isinit(void)
return true;
}
- if (cin_ends_in(s, (char_u *)"=", (char_u *)"{")) {
+ if (cin_ends_in(s, "=", "{")) {
return true;
}
@@ -457,7 +443,7 @@ static int cin_isinit(void)
/// 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")) {
@@ -481,9 +467,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;
@@ -495,30 +480,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 = 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);
+ 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;
@@ -535,33 +518,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;
}
@@ -576,13 +559,11 @@ 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++) {
if (*l == ':') {
@@ -605,16 +586,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);
@@ -628,15 +607,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;
@@ -660,77 +637,74 @@ 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);
- len = (int)((char_u *)skiptowhite((char *)p) - p);
- if (len == 6 && STRNCMP(p, "static", 6) == 0) {
- p = (char_u *)skipwhite((char *)p + 6);
- len = (int)((char_u *)skiptowhite((char *)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]))) {
+ p = skipwhite(line);
+ len = (int)(skiptowhite(p) - p);
+ 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 = 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);
+ s = ml_get(lnum);
+ line = s;
while (*s != NUL && vim_strchr("=;{}\"'", *s) == NULL) {
if (cin_iscomment(s)) { // ignore comments
s = cin_skipcomment(s);
@@ -742,7 +716,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;
}
@@ -757,12 +731,10 @@ 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;
@@ -772,14 +744,14 @@ static int cin_ispreproc(const char_u *s)
/// continuation line of a preprocessor statement. Decrease "*lnump" to the
/// start and return the line in "*pp".
/// Put the amount of indent in "*amount".
-static int cin_ispreproc_cont(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);
}
@@ -793,7 +765,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;
}
}
@@ -807,18 +779,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] == '/';
}
@@ -834,9 +802,9 @@ 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;
@@ -885,9 +853,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;
@@ -948,7 +916,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;
@@ -956,10 +924,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;
@@ -974,7 +942,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;
}
@@ -995,30 +963,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;
@@ -1046,13 +1012,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;
@@ -1064,19 +1028,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;
}
}
@@ -1084,7 +1048,7 @@ 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;
}
@@ -1099,9 +1063,9 @@ probablyFound:
/// 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;
@@ -1113,10 +1077,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);
@@ -1143,31 +1107,29 @@ static int cin_iswhileofdo_end(int terminated)
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
@@ -1175,7 +1137,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;
}
@@ -1186,22 +1148,21 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
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)
- * {}
- */
+ // 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;
}
@@ -1248,13 +1209,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 */
+ // 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;
@@ -1262,8 +1223,8 @@ 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]))) {
+ } 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;
@@ -1276,14 +1237,14 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
if (s[0] == '{' || s[0] == '}' || s[0] == ';') {
cpp_base_class = lookfor_ctor_init = class_or_struct = false;
} else if (s[0] == ')') {
- /* Constructor-initialization is assumed if we come across
- * something like "):" */
+ // 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;
@@ -1325,7 +1286,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 {
@@ -1342,18 +1303,18 @@ static int get_baseclass_amount(int col)
/// 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)
+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;
@@ -1367,25 +1328,25 @@ static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore
}
/// 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)
+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] == '"') {
@@ -1410,17 +1371,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);
@@ -1436,11 +1396,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()
// {
// }
@@ -1478,7 +1437,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;
@@ -1488,7 +1447,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);
@@ -1540,12 +1499,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;
@@ -1556,11 +1513,9 @@ 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;
@@ -1585,10 +1540,8 @@ static int find_last_paren(const char_u *l, int start, int end)
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;
@@ -1597,39 +1550,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.
@@ -1641,8 +1592,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.
@@ -1654,32 +1605,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
@@ -1695,8 +1646,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.
@@ -1717,8 +1668,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
@@ -1764,8 +1715,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;
@@ -1885,10 +1836,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;
@@ -1896,22 +1845,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
@@ -1949,25 +1898,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
@@ -1975,10 +1922,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
@@ -1994,8 +1939,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;
}
@@ -2008,10 +1953,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;
@@ -2019,7 +1962,7 @@ int get_c_indent(void)
if (trypos == NULL && curwin->w_cursor.lnum > 1) {
// There may be a statement before the comment, search from the end
// of the line for a comment start.
- linecomment_pos.col = check_linecomment((char *)ml_get(curwin->w_cursor.lnum - 1));
+ linecomment_pos.col = check_linecomment(ml_get(curwin->w_cursor.lnum - 1));
if (linecomment_pos.col != MAXCOL) {
trypos = &linecomment_pos;
trypos->lnum = curwin->w_cursor.lnum - 1;
@@ -2032,10 +1975,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;
@@ -2077,31 +2018,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) {
+ // 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;
@@ -2115,10 +2056,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) {
@@ -2132,10 +2073,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] == '*') {
@@ -2159,7 +2099,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);
@@ -2172,7 +2112,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);
@@ -2185,8 +2125,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) {
@@ -2198,17 +2138,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;
}
@@ -2241,22 +2179,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 {
@@ -2276,14 +2212,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);
@@ -2303,22 +2239,20 @@ 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++) {
switch (l[col]) {
@@ -2353,10 +2287,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) {
@@ -2374,8 +2306,8 @@ 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--;
@@ -2391,8 +2323,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 {
@@ -2408,14 +2340,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;
}
@@ -2437,13 +2367,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;
@@ -2470,7 +2398,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);
@@ -2488,18 +2416,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;
@@ -2514,18 +2438,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;
@@ -2537,7 +2457,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)) {
@@ -2584,10 +2504,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
@@ -2601,9 +2519,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) {
@@ -2614,10 +2532,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;
@@ -2638,27 +2554,23 @@ int get_c_indent(void)
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;
}
@@ -2690,11 +2602,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 {
@@ -2718,10 +2629,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;
}
@@ -2734,8 +2643,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;
@@ -2777,20 +2686,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;
}
@@ -2852,7 +2759,7 @@ int get_c_indent(void)
if (n) {
amount = n;
l = after_label(get_cursor_line_ptr());
- if (l != NULL && cin_is_cinword((char *)l)) {
+ if (l != NULL && cin_is_cinword(l)) {
if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
} else {
@@ -2863,14 +2770,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);
@@ -2879,10 +2784,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) {
@@ -2892,9 +2795,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)) {
@@ -2902,12 +2803,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)) {
@@ -2940,9 +2839,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 {
@@ -2950,17 +2848,15 @@ 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).
- */
+ // 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) {
@@ -2989,21 +2885,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
@@ -3030,12 +2925,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)) {
@@ -3045,17 +2938,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--;
@@ -3063,50 +2954,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((char *)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)
@@ -3122,19 +3005,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;
@@ -3145,14 +3026,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;
@@ -3160,16 +3039,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;
@@ -3181,21 +3058,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 == ',') {
@@ -3205,11 +3078,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;
@@ -3232,9 +3105,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;
}
@@ -3261,7 +3134,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;
@@ -3281,7 +3154,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);
}
@@ -3294,19 +3167,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) {
@@ -3325,28 +3193,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)) {
@@ -3356,17 +3218,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) {
@@ -3377,34 +3237,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, '(', ')')
@@ -3422,41 +3276,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)
@@ -3469,10 +3318,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) {
@@ -3519,19 +3366,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;
@@ -3580,35 +3425,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--;
@@ -3626,20 +3468,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;
}
@@ -3648,7 +3486,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;
}
@@ -3656,17 +3494,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) {
@@ -3677,46 +3513,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) {
@@ -3738,7 +3568,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;
@@ -3764,9 +3594,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;
@@ -3792,38 +3622,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);
@@ -3833,10 +3655,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;
@@ -3845,11 +3665,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;
}
@@ -3860,11 +3678,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;
}
@@ -3872,9 +3688,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 0aa9feaca3..0f0af9d5f0 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -4,21 +4,29 @@
// 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'
@@ -83,7 +91,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 +117,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 ba0a36cafe..fa8e78f624 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -4,11 +4,15 @@
// insexpand.c: functions for Insert mode completion
#include <assert.h>
-#include <inttypes.h>
+#include <limits.h>
#include <stdbool.h>
+#include <stddef.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,36 +22,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[]
@@ -55,30 +71,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
@@ -132,10 +149,10 @@ 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
@@ -152,7 +169,7 @@ typedef struct {
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_u *dict; ///< dictionary file to search
+ char *dict; ///< dictionary file to search
int dict_f; ///< "dict" is an exact file name or not
} ins_compl_next_state_T;
@@ -191,7 +208,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
@@ -199,6 +216,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.
@@ -213,11 +232,11 @@ 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; ///< number of completion matches
@@ -231,14 +250,13 @@ static pos_T compl_startpos;
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
@@ -418,7 +436,7 @@ void compl_status_clear(void)
compl_cont_status = 0;
}
-// @return true if completion is using the forward direction matches
+/// @return true if completion is using the forward direction matches
static bool compl_dir_forward(void)
{
return compl_direction == FORWARD;
@@ -450,7 +468,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();
@@ -570,8 +588,8 @@ bool ins_compl_accept_char(int 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_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_char_len,
- int min_len, char **tofree)
+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;
@@ -579,7 +597,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
// Allocate wide character array for the completion and fill it.
int *const wca = xmalloc((size_t)char_len * sizeof(*wca));
{
- const char_u *p = str;
+ const char_u *p = (char_u *)str;
for (int i = 0; i < char_len; i++) {
wca[i] = mb_ptr2char_adv(&p);
}
@@ -587,7 +605,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
// Rule 1: Were any chars converted to lower?
{
- const char_u *p = compl_orig_text;
+ const char_u *p = (char_u *)compl_orig_text;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -606,7 +624,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
// Rule 2: No lower case, 2nd consecutive letter converted to
// upper case.
if (!has_lower) {
- const char_u *p = compl_orig_text;
+ const char_u *p = (char_u *)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])) {
@@ -622,7 +640,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
// Copy the original case of the part we typed.
{
- const char_u *p = compl_orig_text;
+ const char_u *p = (char_u *)compl_orig_text;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -635,7 +653,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
// Generate encoding specific output from wide character array.
garray_T gap;
- char *p = (char *)IObuff;
+ char *p = IObuff;
int i = 0;
ga_init(&gap, 1, 500);
while (i < char_len) {
@@ -644,7 +662,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
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 - (char *)IObuff) + 6 >= IOSIZE) {
+ } 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
@@ -652,7 +670,7 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
ga_grow(&gap, IOSIZE);
*p = NUL;
STRCPY(gap.ga_data, IObuff);
- gap.ga_len = (int)STRLEN(IObuff);
+ gap.ga_len = (int)strlen(IObuff);
} else {
p += utf_char2bytes(wca[i++], p);
}
@@ -674,11 +692,11 @@ static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_
/// 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;
+ char *str = str_arg;
int char_len; // count multi-byte characters
int compl_char_len;
int flags = 0;
@@ -689,7 +707,7 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
// Find actual length of completion.
{
- const char_u *p = str;
+ const char *p = str;
char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
@@ -699,7 +717,7 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
// Find actual length of original text.
{
- const char_u *p = compl_orig_text;
+ const char *p = compl_orig_text;
compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
@@ -748,9 +766,9 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
/// @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;
@@ -775,7 +793,7 @@ 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.
@@ -783,8 +801,8 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
match = compl_first_match;
do {
if (!match_at_original_text(match)
- && STRNCMP(match->cp_str, str, len) == 0
- && ((int)STRLEN(match->cp_str) <= len || match->cp_str[len] == NUL)) {
+ && 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;
}
@@ -802,7 +820,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.
@@ -811,10 +829,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;
@@ -829,9 +847,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]);
}
@@ -877,7 +893,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) {
@@ -886,23 +902,23 @@ 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((char *)compl_leader + get_compl_len());
+ ins_bytes(compl_leader + get_compl_len());
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
@@ -919,8 +935,8 @@ static void ins_compl_longest_match(compl_T *match)
p = compl_leader;
s = match->cp_str;
while (*p != NUL) {
- c1 = utf_ptr2char((char *)p);
- c2 = utf_ptr2char((char *)s);
+ c1 = utf_ptr2char(p);
+ c2 = utf_ptr2char(s);
if ((match->cp_flags & CP_ICASE)
? (mb_tolower(c1) != mb_tolower(c2))
@@ -936,7 +952,7 @@ static void ins_compl_longest_match(compl_T *match)
*p = NUL;
had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete();
- ins_bytes((char *)compl_leader + get_compl_len());
+ ins_bytes(compl_leader + get_compl_len());
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
@@ -957,7 +973,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.
@@ -999,7 +1015,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
@@ -1008,12 +1024,16 @@ void completeopt_was_set(void)
{
compl_no_insert = false;
compl_no_select = false;
+ compl_longest = false;
if (strstr(p_cot, "noselect") != NULL) {
compl_no_select = true;
}
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
@@ -1126,7 +1146,7 @@ static int ins_compl_build_pum(void)
XFREE_CLEAR(compl_leader);
}
- const int lead_len = compl_leader != NULL ? (int)STRLEN(compl_leader) : 0;
+ const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
do {
if (!match_at_original_text(compl)
@@ -1224,7 +1244,7 @@ void ins_compl_show_pum(void)
do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif");
// Update the screen before drawing the popup menu over it.
- update_screen(0);
+ update_screen();
int cur = -1;
bool array_changed = false;
@@ -1273,10 +1293,10 @@ void ins_compl_show_pum(void)
///
/// @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;
@@ -1307,16 +1327,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;
}
@@ -1334,7 +1354,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,
@@ -1354,7 +1374,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);
}
@@ -1374,12 +1394,12 @@ theend:
/// skipping the word at 'skip_word'.
///
/// @return OK on success.
-static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, char_u *skip_word)
+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_u *ptr = *buf_arg;
+ char *ptr = *buf_arg;
while (!got_int) {
// Find start of the next word. Skip white
// space and punctuation.
@@ -1387,7 +1407,7 @@ static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, c
if (*ptr == NUL || *ptr == NL) {
break;
}
- char_u *wstart = ptr;
+ char *wstart = ptr;
// Find end of the word.
// Japanese words may have characters in
@@ -1396,7 +1416,7 @@ static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, c
while (*ptr != NUL) {
const int l = utfc_ptr2len((const char *)ptr);
- if (l < 2 && !vim_iswordc(*ptr)) {
+ if (l < 2 && !vim_iswordc((uint8_t)(*ptr))) {
break;
}
ptr += l;
@@ -1405,7 +1425,7 @@ static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, c
// Add the word. Skip the regexp match.
if (wstart != skip_word) {
status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
- (char_u *)fname, dir, false);
+ fname, dir, false);
if (status == FAIL) {
break;
}
@@ -1419,21 +1439,21 @@ static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, c
/// 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) {
@@ -1444,7 +1464,7 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
// Check each line for a match.
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);
@@ -1453,12 +1473,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) {
// For a thesaurus, add all the words in the line
ptr = buf;
- add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir,
- regmatch->startp[0]);
+ 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
@@ -1481,24 +1500,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;
}
@@ -1508,12 +1527,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--;
}
@@ -1621,8 +1641,8 @@ int ins_compl_len(void)
/// 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;
@@ -1644,12 +1664,12 @@ 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) {
@@ -1678,7 +1698,7 @@ static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
ins_compl_delete();
- ins_bytes((char *)compl_leader + get_compl_len());
+ ins_bytes(compl_leader + get_compl_len());
compl_used_match = false;
if (compl_started) {
@@ -1743,8 +1763,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();
}
@@ -1755,7 +1775,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;
@@ -1764,7 +1784,7 @@ 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.
@@ -1772,11 +1792,11 @@ static void ins_compl_set_original_text(char_u *str)
// 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
&& 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);
}
}
@@ -1784,13 +1804,13 @@ 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 (!match_at_original_text(compl_shown_match)) {
@@ -1801,17 +1821,17 @@ void ins_compl_addfrommatch(void)
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))) {
+ || ins_compl_equal(cp, compl_leader, strlen(compl_leader))) {
p = cp->cp_str;
break;
}
}
- if (p == NULL || (int)STRLEN(p) <= len) {
+ if (p == NULL || (int)strlen(p) <= len) {
return;
}
}
p += len;
- c = utf_ptr2char((char *)p);
+ c = utf_ptr2char(p);
ins_compl_addleader(c);
}
@@ -1953,7 +1973,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// 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_u *ptr;
+ char *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
ptr = compl_curr_match->cp_str;
} else {
@@ -2004,7 +2024,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// but only do this, if the Popup is still visible
if (c == Ctrl_E) {
ins_compl_delete();
- char_u *p = NULL;
+ char *p = NULL;
if (compl_leader != NULL) {
p = compl_leader;
} else if (compl_first_match != NULL) {
@@ -2012,9 +2032,9 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
if (p != NULL) {
const int compl_len = get_compl_len();
- const int len = (int)STRLEN(p);
+ const int len = (int)strlen(p);
if (len > compl_len) {
- ins_bytes_len((char *)p + compl_len, (size_t)(len - compl_len));
+ ins_bytes_len(p + compl_len, (size_t)(len - compl_len));
}
}
retval = true;
@@ -2044,7 +2064,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
if (c == Ctrl_C && cmdwin_type != 0) {
// Avoid the popup menu remains displayed when leaving the
// command line window.
- update_screen(0);
+ update_screen();
}
// Indent now if a key was typed that is in 'cinkeys'.
@@ -2105,7 +2125,7 @@ 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(p_cot, "longest") != NULL);
+ compl_get_longest = compl_longest;
compl_used_match = true;
}
@@ -2160,11 +2180,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) {
@@ -2185,7 +2205,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
@@ -2220,30 +2240,128 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
return buf;
}
+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.
+int set_completefunc_option(void)
+{
+ int retval = option_set_callback_func(curbuf->b_p_cfu, &cfu_cb);
+ if (retval == OK) {
+ set_buflocal_cfu_callback(curbuf);
+ }
+
+ return retval;
+}
+
+/// 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.
+int set_omnifunc_option(void)
+{
+ int retval = option_set_callback_func(curbuf->b_p_ofu, &ofu_cb);
+ if (retval == OK) {
+ set_buflocal_ofu_callback(curbuf);
+ }
+
+ return retval;
+}
+
+/// 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.
+int set_thesaurusfunc_option(void)
+{
+ 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);
+ }
+
+ return retval;
+}
+
+/// 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_u *get_complete_funcname(int type)
+static char *get_complete_funcname(int type)
{
switch (type) {
case CTRL_X_FUNCTION:
- return (char_u *)curbuf->b_p_cfu;
+ return curbuf->b_p_cfu;
case CTRL_X_OMNI:
- return (char_u *)curbuf->b_p_ofu;
+ return curbuf->b_p_ofu;
case CTRL_X_THESAURUS:
- return *curbuf->b_p_tsrfu == NUL ? (char_u *)p_tsrfu : (char_u *)curbuf->b_p_tsrfu;
+ 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;
@@ -2260,7 +2378,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
@@ -2268,8 +2386,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;
@@ -2360,7 +2480,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
tv_clear(&user_data);
return FAIL;
}
- int status = ins_compl_add((char_u *)word, -1, NULL, (char_u **)cptext, true,
+ int status = ins_compl_add((char *)word, -1, NULL, cptext, true,
&user_data, dir, flags, dup);
if (status != OK) {
tv_clear(&user_data);
@@ -2422,6 +2542,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) {
@@ -2430,8 +2551,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;
}
@@ -2451,9 +2572,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 {
@@ -2512,12 +2634,12 @@ void f_complete_check(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -2590,15 +2712,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;
}
}
@@ -2606,8 +2728,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)) {
@@ -2631,6 +2752,7 @@ 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);
@@ -2726,7 +2848,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
// 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->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;
@@ -2750,17 +2872,19 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
goto done;
}
compl_type = CTRL_X_DICTIONARY;
- st->dict = (char_u *)st->ins_buf->b_fname;
+ st->dict = st->ins_buf->b_fname;
st->dict_f = DICT_EXACT;
}
- msg_hist_off = true; // reset in msg_trunc_attr()
- vim_snprintf((char *)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((char *)IObuff, true, HL_ATTR(HLF_R));
+ 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 {
@@ -2773,7 +2897,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
compl_type = CTRL_X_THESAURUS;
}
if (*++st->e_cpt != ',' && *st->e_cpt != NUL) {
- st->dict = (char_u *)st->e_cpt;
+ st->dict = st->e_cpt;
st->dict_f = DICT_FIRST;
}
} else if (*st->e_cpt == 'i') {
@@ -2781,16 +2905,18 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
} else if (*st->e_cpt == 'd') {
compl_type = CTRL_X_PATH_DEFINES;
} else if (*st->e_cpt == ']' || *st->e_cpt == 't') {
- msg_hist_off = true; // reset in msg_trunc_attr()
compl_type = CTRL_X_TAGS;
- vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags."));
- (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
+ 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, (char *)IObuff, IOSIZE, ",");
+ (void)copy_option_part(&st->e_cpt, IObuff, IOSIZE, ",");
st->found_all = true;
if (compl_type == -1) {
@@ -2807,8 +2933,8 @@ done:
/// included files.
static void get_next_include_file_completion(int compl_type)
{
- find_pattern_in_path((char_u *)compl_pattern, compl_direction,
- STRLEN(compl_pattern), false, false,
+ 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),
@@ -2817,17 +2943,17 @@ static void get_next_include_file_completion(int compl_type)
/// 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_u *dict, int dict_f)
+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, (char_u *)compl_pattern);
+ 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 : (char_u *)curbuf->b_p_tsr)
+ ? (*curbuf->b_p_tsr == NUL ? (char *)p_tsr : curbuf->b_p_tsr)
: (*curbuf->b_p_dict ==
- NUL ? (char_u *)p_dict : (char_u *)curbuf->b_p_dict)),
- (char_u *)compl_pattern,
+ NUL ? p_dict : curbuf->b_p_dict)),
+ compl_pattern,
dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS);
}
@@ -2838,7 +2964,7 @@ 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((char_u *)compl_pattern);
+ 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
@@ -2866,7 +2992,7 @@ static void get_next_filename_completion(void)
}
// May change home directory back to "~".
- tilde_replace((char_u *)compl_pattern, num_matches, matches);
+ 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++) {
@@ -2890,8 +3016,8 @@ static void get_next_cmdline_completion(void)
{
char **matches;
int num_matches;
- if (expand_cmdline(&compl_xp, (char_u *)compl_pattern,
- (int)STRLEN(compl_pattern),
+ if (expand_cmdline(&compl_xp, compl_pattern,
+ (int)strlen(compl_pattern),
&num_matches, &matches) == EXPAND_OK) {
ins_compl_add_matches(num_matches, matches, false);
}
@@ -2901,7 +3027,7 @@ static void get_next_cmdline_completion(void)
static void get_next_spell_completion(linenr_T lnum)
{
char **matches;
- int num_matches = expand_spelling(lnum, (char_u *)compl_pattern, &matches);
+ int num_matches = expand_spelling(lnum, compl_pattern, &matches);
if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic);
} else {
@@ -2915,11 +3041,11 @@ static void get_next_spell_completion(linenr_T lnum)
/// @param cur_match_pos current match position
/// @param match_len
/// @param cont_s_ipos next ^X<> will set initial_pos
-static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len,
- bool *cont_s_ipos)
+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_u *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col;
+ 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()) {
@@ -2928,14 +3054,14 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p
}
ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
if (!p_paste) {
- ptr = (char_u *)skipwhite((char *)ptr);
+ ptr = skipwhite(ptr);
}
}
- len = (int)STRLEN(ptr);
+ len = (int)strlen(ptr);
} else {
- char_u *tmp_ptr = ptr;
+ char *tmp_ptr = ptr;
- if (compl_status_adding() && compl_length <= (int)STRLEN(tmp_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)) {
@@ -2952,10 +3078,10 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p
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, len); // NOLINT(runtime/printf)
+ // 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 = (char_u *)skipwhite((char *)ptr);
+ tmp_ptr = ptr = skipwhite(ptr);
// Find start of next word.
tmp_ptr = find_word_start(tmp_ptr);
// Find end of next word.
@@ -2977,7 +3103,7 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p
if (tmp_ptr - ptr >= IOSIZE - len) {
tmp_ptr = ptr + IOSIZE - len - 1;
}
- STRLCPY(IObuff + len, ptr, IOSIZE - len);
+ xstrlcpy(IObuff + len, ptr, (size_t)(IOSIZE - len));
len += (int)(tmp_ptr - ptr);
*cont_s_ipos = true;
}
@@ -3031,7 +3157,7 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
// 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, (char_u *)compl_pattern);
+ compl_direction, compl_pattern);
} else {
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
NULL, compl_direction, (char_u *)compl_pattern, 1L,
@@ -3078,13 +3204,13 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
continue;
}
int len;
- char_u *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
- &len, &cont_s_ipos);
+ 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 : (char_u *)st->ins_buf->b_sfname,
+ st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) {
found_new_match = OK;
break;
@@ -3131,7 +3257,7 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_
case CTRL_X_FUNCTION:
case CTRL_X_OMNI:
- expand_by_function(type, (char_u *)compl_pattern);
+ expand_by_function(type, compl_pattern);
break;
case CTRL_X_SPELL:
@@ -3184,7 +3310,7 @@ static int ins_compl_get_exp(pos_T *ini)
assert(st.ins_buf != NULL);
compl_old_match = compl_curr_match; // remember the last current match
- st.cur_match_pos = (compl_dir_forward() ? &st.last_match_pos : &st.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 (;;) {
@@ -3275,7 +3401,7 @@ static int ins_compl_get_exp(pos_T *ini)
static void ins_compl_update_shown_match(void)
{
while (!ins_compl_equal(compl_shown_match,
- compl_leader, STRLEN(compl_leader))
+ 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;
@@ -3284,10 +3410,10 @@ static void ins_compl_update_shown_match(void)
// 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))
+ && !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))
+ 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;
@@ -3321,7 +3447,7 @@ void ins_compl_delete(void)
/// "in_compl_func" is true when called from complete_check().
void ins_compl_insert(bool in_compl_func)
{
- ins_bytes((char *)compl_shown_match->cp_str + get_compl_len());
+ ins_bytes(compl_shown_match->cp_str + get_compl_len());
compl_used_match = !match_at_original_text(compl_shown_match);
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
@@ -3346,7 +3472,7 @@ static void ins_compl_show_filename(void)
// the text that fits in "space" between "s" and "e".
char *s;
char *e;
- for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
+ for (s = e = compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
space -= ptr2cells(e);
while (space < 0) {
space += ptr2cells(s);
@@ -3354,9 +3480,9 @@ static void ins_compl_show_filename(void)
}
}
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);
+ 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!
}
@@ -3432,7 +3558,7 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
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.
@@ -3513,13 +3639,13 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
// Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) {
- ins_bytes((char *)compl_orig_text + get_compl_len());
+ ins_bytes(compl_orig_text + get_compl_len());
compl_used_match = false;
} else if (insert_match) {
if (!compl_get_longest || compl_used_match) {
ins_compl_insert(in_compl_func);
} else {
- ins_bytes((char *)compl_leader + get_compl_len());
+ ins_bytes(compl_leader + get_compl_len());
}
} else {
compl_used_match = false;
@@ -3527,7 +3653,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();
@@ -3553,17 +3679,6 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
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
@@ -3697,36 +3812,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_status_adding()) {
- while (--startcol >= 0 && vim_isIDc(line[startcol])) {}
+ 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_status_adding()) {
- char_u *prefix = (char_u *)"\\<";
+ 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 {
@@ -3749,13 +3864,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);
}
}
@@ -3765,17 +3879,17 @@ 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((char *)line);
+ compl_col = (colnr_T)getwhitecols(line);
compl_length = (int)curs_col - (int)compl_col;
if (compl_length < 0) { // cursor in indent: empty pattern
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;
@@ -3783,17 +3897,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;
@@ -3802,17 +3916,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
@@ -3837,7 +3951,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;
@@ -3852,7 +3966,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;
@@ -3893,9 +4008,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;
}
@@ -3919,8 +4034,8 @@ 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;
}
@@ -3934,7 +4049,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
/// become invalid and needs to be fetched again.
///
/// @return OK on success.
-static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *line_invalid)
+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)
@@ -3973,7 +4088,7 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
/// 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_u *line)
+static void ins_compl_continue_search(char *line)
{
// it is a continued search
compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
@@ -3985,7 +4100,7 @@ static void ins_compl_continue_search(char_u *line)
// 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((char *)line);
+ 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
@@ -3995,9 +4110,7 @@ static void ins_compl_continue_search(char_u *line)
// 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_startpos.col = (colnr_T)(skipwhite(line + compl_length + compl_startpos.col) - line);
}
compl_col = compl_startpos.col;
}
@@ -4036,7 +4149,7 @@ static int ins_compl_start(void)
return FAIL;
}
- char_u *line = ml_get(curwin->w_cursor.lnum);
+ char *line = ml_get(curwin->w_cursor.lnum);
colnr_T curs_col = curwin->w_cursor.col;
compl_pending = 0;
@@ -4108,7 +4221,7 @@ static int ins_compl_start(void)
// Always add completion for the original text.
xfree(compl_orig_text);
- compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
+ compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length);
int flags = CP_ORIGINAL_TEXT;
if (p_ic) {
flags |= CP_ICASE;
@@ -4163,18 +4276,18 @@ static void ins_compl_show_statusmsg(void)
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);
}
- edit_submode_extra = (char *)match_ref;
+ edit_submode_extra = match_ref;
edit_submode_highl = HLF_R;
if (dollar_vcol >= 0) {
curs_columns(curwin, false);
@@ -4298,7 +4411,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
@@ -4312,7 +4425,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;
@@ -4333,7 +4446,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--;
@@ -4355,6 +4468,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 2b00d371f0..a4228fcc7e 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
@@ -347,8 +355,7 @@ 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[] =
-{
+} mouse_table[] = {
{ KE_LEFTMOUSE, MOUSE_LEFT, true, false },
{ KE_LEFTDRAG, MOUSE_LEFT, false, true },
{ KE_LEFTRELEASE, MOUSE_LEFT, false, false },
@@ -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;
@@ -539,7 +548,7 @@ char_u *get_special_key_name(int c, int modifiers)
}
}
} 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_u *const dst,
const int flags, const bool escape_ks, bool *const did_simplify)
FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -616,7 +625,7 @@ 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)
{
@@ -624,7 +633,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
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_u *const end = (char_u *)(*srcp) + src_len - 1;
const bool in_string = flags & FSK_IN_STRING;
int modifiers;
int bit;
@@ -636,7 +645,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
return 0;
}
- src = *srcp;
+ src = (char_u *)(*srcp);
if (src[0] != '<') {
return 0;
}
@@ -650,7 +659,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
if (*bp == '-') {
last_dash = bp;
if (bp + 1 <= end) {
- l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1);
+ l = utfc_ptr2len_len((char *)bp + 1, (int)(end - bp) + 1);
// Anything accepted, like <C-?>.
// <C-"> or <M-"> are not special in strings as " is
// the string delimiter. With a backslash it works: <M-\">
@@ -665,7 +674,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') {
bp += 3; // skip t_xx, xx may be '-' or '>'
} else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) {
- vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true);
+ vim_str2nr((char *)bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true);
if (l == 0) {
emsg(_(e_invarg));
return 0;
@@ -695,7 +704,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
if (STRNICMP(last_dash + 1, "char-", 5) == 0
&& ascii_isdigit(last_dash[6])) {
// <Char-123> or <Char-033> or <Char-0x33>
- vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true);
+ vim_str2nr((char *)last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true);
if (l == 0) {
emsg(_(e_invarg));
return 0;
@@ -744,7 +753,7 @@ int find_special_key(const char_u **const srcp, const size_t src_len, int *const
}
*modp = modifiers;
- *srcp = end_of_name;
+ *srcp = (char *)end_of_name;
return key;
} // else { ELOG("unknown key: '%s'", src); }
}
@@ -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,18 +933,18 @@ 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++] = KS_EXTRA;
+ result[dlen++] = (char)K_SPECIAL;
+ result[dlen++] = (char)KS_EXTRA;
result[dlen++] = KE_SNR;
- snprintf((char *)result + dlen, buf_len - dlen, "%" PRId64,
+ snprintf(result + dlen, buf_len - dlen, "%" PRId64,
(int64_t)current_sctx.sc_sid);
- dlen += STRLEN(result + dlen);
+ dlen += strlen(result + dlen);
result[dlen++] = '_';
continue;
}
}
- slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen,
+ slen = trans_special(&src, (size_t)(end - src) + 1, (char_u *)result + dlen,
FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY),
true, did_simplify);
if (slen) {
@@ -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,7 +1059,7 @@ 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 *res = xmalloc(strlen(p) * 4 + 1);
char_u *d = res;
for (char_u *s = (char_u *)p; *s != NUL;) {
if (s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) {
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..629a31c913
--- /dev/null
+++ b/src/nvim/linematch.c
@@ -0,0 +1,385 @@
+// 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"
+#include "nvim/vim.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
index 3e0774c096..6322271073 100644
--- a/src/nvim/locale.c
+++ b/src/nvim/locale.c
@@ -3,8 +3,10 @@
// locale.c: functions for language/locale configuration
-#include "auto/config.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include "auto/config.h"
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
@@ -13,8 +15,11 @@
#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"
@@ -23,6 +28,7 @@
#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"
@@ -42,7 +48,7 @@ static char *get_locale_val(int what)
/// @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)
+static bool is_valid_mess_lang(const char *lang)
{
return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
}
@@ -270,7 +276,7 @@ void ex_language(exarg_T *eap)
static char **locales = NULL; // Array of all available locales
-# ifndef WIN32
+# ifndef MSWIN
static bool did_init_locales = false;
/// @return an array of strings for all available locales + NULL for the
@@ -284,8 +290,7 @@ static char **find_locales(void)
// 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);
+ char *locale_a = get_cmd_output("locale -a", NULL, kShellOptSilent, NULL);
if (locale_a == NULL) {
return NULL;
}
@@ -311,24 +316,27 @@ static char **find_locales(void)
/// Lazy initialization of all available locales.
static void init_locales(void)
{
-# ifndef WIN32
- if (!did_init_locales) {
- did_init_locales = true;
- locales = find_locales();
+# ifndef MSWIN
+ if (did_init_locales) {
+ return;
}
+
+ 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);
+ if (locales == NULL) {
+ return;
+ }
+
+ for (int i = 0; locales[i] != NULL; i++) {
+ xfree(locales[i]);
}
+ XFREE_CLEAR(locales);
}
# endif
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 9bdf327430..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 };
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..3c129fe7ce 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 = (char *)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 42aa13cfc1..12ddbd094f 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/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/ui_compositor.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"));
@@ -1033,9 +1150,9 @@ int nlua_call(lua_State *lstate)
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);
@@ -1100,6 +1217,7 @@ static int nlua_rpc(lua_State *lstate, bool request)
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);
}
}
@@ -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(UPD_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"));
@@ -1678,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;
@@ -1691,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,
@@ -1752,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;
@@ -1783,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_u *nlua_register_table_as_callable(const typval_T *const arg)
{
LuaRef table_ref = LUA_NOREF;
if (arg->v_type == VAR_DICT) {
@@ -1817,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_u *name = register_luafunc(func);
lua_pop(lstate, 1); // []
assert(top == lua_gettop(lstate));
@@ -1927,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");
@@ -1943,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 {
@@ -1953,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;
@@ -1978,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);
@@ -2001,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);
@@ -2022,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);
@@ -2116,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..0a566b2f86 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)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 1b874e673a..dac96790d7 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -1,50 +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
+#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
@@ -60,8 +49,8 @@ static int regex_match(lua_State *lstate, regprog_T **prog, char_u *str)
*prog = rm.regprog;
if (match) {
- lua_pushinteger(lstate, (lua_Integer)(rm.startp[0] - str));
- lua_pushinteger(lstate, (lua_Integer)(rm.endp[0] - str));
+ lua_pushinteger(lstate, (lua_Integer)(rm.startp[0] - (char *)str));
+ lua_pushinteger(lstate, (lua_Integer)(rm.endp[0] - (char *)str));
return 2;
}
return 0;
@@ -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");
@@ -127,7 +116,7 @@ static int regex_match_line(lua_State *lstate)
line[end] = NUL;
}
- int nret = regex_match(lstate, prog, line + start);
+ int nret = regex_match(lstate, prog, (char_u *)line + start);
if (end >= 0) {
line[end] = save;
@@ -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);
@@ -304,8 +293,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 +360,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 +385,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);
}
@@ -499,7 +508,7 @@ static int nlua_iconv(lua_State *lstate)
vimconv_T vimconv;
vimconv.vc_type = CONV_NONE;
- convert_setup_ext(&vimconv, from, false, to, false);
+ convert_setup_ext(&vimconv, (char *)from, false, (char *)to, false);
char_u *ret = (char_u *)string_convert(&vimconv, (char *)str, &str_len);
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index 7ff4fbbff4..fecf746036 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -6,23 +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/log.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"
@@ -177,19 +182,19 @@ int tslua_add_language(lua_State *L)
uv_lib_t lib;
if (uv_dlopen(path, &lib)) {
- snprintf((char *)IObuff, IOSIZE, "Failed to load parser: uv_dlopen: %s",
+ snprintf(IObuff, IOSIZE, "Failed to load parser: uv_dlopen: %s",
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);
}
@@ -332,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 "";
@@ -834,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);
}
@@ -1276,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]
@@ -1369,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 5601db274b..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
@@ -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 883f946cad..6f14a00949 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -3,43 +3,52 @@
#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 <time.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/mark.h"
-#include "nvim/mbyte.h"
+#include "nvim/memfile_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -48,34 +57,38 @@
#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/pty_process.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"
@@ -84,22 +97,23 @@
#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"
+#include "nvim/tui/tui.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"
@@ -168,7 +182,7 @@ void early_init(mparm_T *paramp)
runtime_init();
highlight_init();
-#ifdef WIN32
+#ifdef MSWIN
OSVERSIONINFO ovi;
ovi.dwOSVersionInfoSize = sizeof(ovi);
GetVersionEx(&ovi);
@@ -185,11 +199,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.
@@ -210,13 +224,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;
@@ -228,10 +242,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.
@@ -265,8 +279,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) {
@@ -276,9 +289,29 @@ int main(int argc, char **argv)
}
}
- server_init(params.listen_addr);
+ bool use_builtin_ui = (!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 = !params.input_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) {
@@ -314,7 +347,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");
@@ -329,57 +362,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) {
+ if (!params.input_isatty && !params.input_istext && silent_mode && exmode_active) {
input_start(STDIN_FILENO);
}
+ 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);
@@ -432,10 +465,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");
@@ -468,20 +499,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, UPD_VALID);
- } else {
- screenclear(); // clear screen
- TIME_MSG("clearing screen");
- }
+ redraw_later(curwin, UPD_VALID);
no_wait_return = true;
@@ -527,7 +551,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) {
@@ -551,6 +575,12 @@ int main(int argc, char **argv)
TIME_MSG("UIEnter autocommands");
}
+#ifdef MSWIN
+ if (use_builtin_ui) {
+ os_icon_init();
+ }
+#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
@@ -576,13 +606,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;
@@ -593,9 +629,13 @@ 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.
@@ -619,9 +659,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;
}
@@ -712,6 +751,7 @@ void getout(int exitval)
if (did_emsg) {
// give the user a chance to read the (error) message
no_wait_return = false;
+ // TODO(justinmk): this may call getout(0), clobbering exitval...
wait_return(false);
}
@@ -723,18 +763,21 @@ void getout(int exitval)
ui_call_set_title(cstr_as_string((char *)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);
+#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
{
@@ -753,15 +796,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;
@@ -770,7 +813,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);
}
@@ -819,15 +862,24 @@ 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);
+ os_errmsg("Remote ui failed to start: ");
+ os_errmsg(connect_error);
+ os_errmsg("\n");
exit(1);
}
@@ -852,15 +904,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);
}
@@ -870,28 +922,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);
@@ -907,14 +959,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)
+ && !(embedded_mode && stdin_fd <= 0)
+ && (!exmode_active || parmp->input_istext)
&& !parmp->input_isatty
- && scriptin[0] == NULL; // `-s -` was not given.
- return explicit || implicit;
+ && parmp->scriptin == NULL; // `-s -` was not given.
+ return parmp->had_stdin_file || implicit;
}
/// Scan the command line arguments.
@@ -923,7 +975,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;
@@ -943,9 +994,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) {
@@ -955,17 +1006,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
@@ -1061,7 +1110,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;
@@ -1073,10 +1122,6 @@ static void command_line_scan(mparm_T *parmp)
p_hkmap = true;
set_option_value_give_err("rl", 1L, NULL, 0);
break;
- case 'l': // "-l" lisp mode, 'lisp' and 'showmatch' on.
- set_option_value_give_err("lisp", 1L, NULL, 0);
- p_sm = true;
- break;
case 'M': // "-M" no changes or writing of files
reset_modifiable();
FALLTHROUGH;
@@ -1156,12 +1201,12 @@ static void command_line_scan(mparm_T *parmp)
p_verbose = get_number_arg(argv[0], &argv_idx, 10);
if (argv[0][argv_idx] != NUL) {
set_option_value_give_err("verbosefile", 0L, argv[0] + argv_idx, 0);
- argv_idx = (int)STRLEN(argv[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_give_err("window", n, NULL, 0);
break;
@@ -1181,6 +1226,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
@@ -1226,7 +1272,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;
@@ -1261,38 +1307,34 @@ static void command_line_scan(mparm_T *parmp)
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];
@@ -1305,7 +1347,7 @@ 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_give_err("window", n, NULL, 0);
@@ -1314,26 +1356,18 @@ scripterror:
}
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;
@@ -1354,7 +1388,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);
@@ -1369,31 +1403,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);
@@ -1404,6 +1432,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.
@@ -1416,8 +1446,6 @@ static void init_startuptime(mparm_T *paramp)
break;
}
}
-
- starttime = time(NULL);
}
static void check_and_set_isatty(mparm_T *paramp)
@@ -1427,14 +1455,6 @@ static void check_and_set_isatty(mparm_T *paramp)
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
TIME_MSG("window checked");
}
@@ -1452,7 +1472,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);
@@ -1460,14 +1480,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) {
@@ -1479,18 +1497,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, p_menc) < 0) {
+ vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef);
+ if (qf_init(NULL, (char *)p_ef, p_efm, true, IObuff, p_menc) < 0) {
msg_putchar('\n');
os_exit(3);
}
@@ -1498,17 +1514,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.
@@ -1540,18 +1554,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;
}
@@ -1662,7 +1704,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;
@@ -1670,9 +1712,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++;
@@ -1685,7 +1725,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
arg_idx = 1;
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) {
@@ -1781,40 +1821,36 @@ 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.
- */
+ // 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;
@@ -1910,12 +1946,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);
@@ -1923,15 +1959,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;
@@ -1975,6 +2012,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
@@ -1993,35 +2065,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);
- }
- }
- if (secure == 2) {
- need_wait_return = true;
+ do_exrc_initialization();
}
- secure = 0;
}
TIME_MSG("sourcing vimrc file(s)");
}
@@ -2036,19 +2081,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:
@@ -2064,17 +2110,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);
}
@@ -2083,8 +2129,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;
@@ -2095,53 +2141,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..46d7217364 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,18 @@ 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 input_istext; // stdin is text, not executable (-E/-Es)
bool output_isatty; // stdout is a terminal
bool err_isatty; // stderr is a terminal
- bool input_neverscript; // never treat stdin as script (-E/-Es)
+
int no_swap_file; // "-n" argument used
int use_debug_break_level;
int window_count; // number of windows to use
@@ -40,6 +45,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 333fb847f6..04a0107fe8 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -5,32 +5,40 @@
#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/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/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/strings.h"
#include "nvim/vim.h"
/// List used for abbreviations.
@@ -150,7 +158,7 @@ static void showmap(mapblock_T *mp, bool local)
{
size_t len = 1;
- if (message_filtered((char *)mp->m_keys) && message_filtered(mp->m_str)
+ if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)
&& (mp->m_desc == NULL || message_filtered(mp->m_desc))) {
return;
}
@@ -176,7 +184,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);
@@ -203,7 +211,7 @@ static void showmap(mapblock_T *mp, bool local)
} else if (mp->m_str[0] == NUL) {
msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
} else {
- msg_outtrans_special((char_u *)mp->m_str, false, 0);
+ msg_outtrans_special(mp->m_str, false, 0);
}
if (mp->m_desc != NULL) {
@@ -214,7 +222,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 +270,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);
+ 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 +344,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 +407,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 +419,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 +447,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,16 +462,16 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
}
}
- mp->m_keys = vim_strsave(keys);
- mp->m_str = (char *)args->rhs;
- mp->m_orig_str = (char *)args->orig_rhs;
+ mp->m_keys = xstrdup(keys);
+ mp->m_str = args->rhs;
+ mp->m_orig_str = args->orig_rhs;
mp->m_luaref = args->rhs_lua;
if (!simplified) {
args->rhs = NULL;
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 +497,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 +516,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 +553,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 +567,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 +591,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 +640,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 +674,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 +693,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 +717,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);
- p = (char_u *)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
@@ -761,8 +775,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
XFREE_CLEAR(mp->m_str);
XFREE_CLEAR(mp->m_orig_str);
}
- mp->m_str = (char *)args->rhs;
- mp->m_orig_str = (char *)args->orig_rhs;
+ mp->m_str = args->rhs;
+ mp->m_orig_str = args->orig_rhs;
mp->m_luaref = args->rhs_lua;
if (!keyround1_simplified) {
args->rhs = NULL;
@@ -791,7 +805,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 +854,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 +898,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 +912,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 +979,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 +1032,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 +1067,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 +1087,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;
@@ -1195,7 +1209,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 +1227,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;
@@ -1259,7 +1273,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
int hash;
int count;
int round;
- char_u *p;
+ char *p;
int i;
*num_file = 0; // return values in case of FAIL
@@ -1272,28 +1286,28 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
for (i = 0; i < 7; i++) {
if (i == 0) {
- p = (char_u *)"<silent>";
+ p = "<silent>";
} else if (i == 1) {
- p = (char_u *)"<unique>";
+ p = "<unique>";
} else if (i == 2) {
- p = (char_u *)"<script>";
+ p = "<script>";
} else if (i == 3) {
- p = (char_u *)"<expr>";
+ p = "<expr>";
} else if (i == 4 && !expand_buffer) {
- p = (char_u *)"<buffer>";
+ p = "<buffer>";
} else if (i == 5) {
- p = (char_u *)"<nowait>";
+ p = "<nowait>";
} else if (i == 6) {
- p = (char_u *)"<special>";
+ p = "<special>";
} else {
continue;
}
- if (vim_regexec(regmatch, (char *)p, (colnr_T)0)) {
+ if (vim_regexec(regmatch, p, (colnr_T)0)) {
if (round == 1) {
count++;
} else {
- (*file)[count++] = (char *)vim_strsave(p);
+ (*file)[count++] = xstrdup(p);
}
}
}
@@ -1311,12 +1325,12 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
}
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)) {
+ p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS);
+ if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) {
if (round == 1) {
count++;
} else {
- (*file)[count++] = (char *)p;
+ (*file)[count++] = p;
p = NULL;
}
}
@@ -1330,7 +1344,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
}
if (round == 1) {
- *file = xmalloc((size_t)count * sizeof(char_u *));
+ *file = xmalloc((size_t)count * sizeof(char *));
}
} // for (round)
@@ -1344,7 +1358,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
char **ptr3 = ptr1 + count;
while (ptr2 < ptr3) {
- if (STRCMP(*ptr1, *ptr2)) {
+ if (strcmp(*ptr1, *ptr2) != 0) {
*++ptr1 = *ptr2++;
} else {
xfree(*ptr2++);
@@ -1373,12 +1387,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 +1418,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 +1431,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 +1455,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 +1505,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 +1517,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 = (char_u *)mp->m_str;
+ 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 +1542,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 +1552,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((char_u *)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 +1582,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 +1590,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 +1603,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 +1623,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;
@@ -1639,8 +1662,8 @@ int makemap(FILE *fd, buf_T *buf)
if (mp->m_luaref != LUA_NOREF) {
continue;
}
- for (p = (char_u *)mp->m_str; *p != NUL; p++) {
- if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
+ for (p = mp->m_str; *p != NUL; p++) {
+ if ((uint8_t)p[0] == K_SPECIAL && (uint8_t)p[1] == KS_EXTRA
&& p[2] == KE_SNR) {
break;
}
@@ -1783,7 +1806,7 @@ int makemap(FILE *fd, buf_T *buf)
}
if (putc(' ', fd) < 0
- || put_escstr(fd, mp->m_keys, 0) == FAIL
+ || put_escstr(fd, (char_u *)mp->m_keys, 0) == FAIL
|| putc(' ', fd) < 0
|| put_escstr(fd, (char_u *)mp->m_str, 1) == FAIL
|| put_eol(fd) < 0) {
@@ -1912,14 +1935,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 +1963,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;
}
@@ -1956,7 +1979,7 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb
*local_ptr = local;
}
*rhs_lua = mp->m_luaref;
- return mp->m_luaref == LUA_NOREF ? (char_u *)mp->m_str : NULL;
+ return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL;
}
}
}
@@ -1989,18 +2012,19 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData 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 +2039,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 +2107,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 +2130,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);
}
}
@@ -2147,29 +2174,36 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData 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,17 +2214,17 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
@@ -2315,14 +2349,14 @@ 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
langmap_init(); // back to one-to-one map
- for (p = (char_u *)p_langmap; p[0] != NUL;) {
+ for (p = p_langmap; p[0] != NUL;) {
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
MB_PTR_ADV(p2)) {
if (p2[0] == '\\' && p2[1] != NUL) {
@@ -2342,7 +2376,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 +2384,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 +2415,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 +2435,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 +2454,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 +2472,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 +2555,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 +2631,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 +2653,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 b430c167ce..904eaf32c0 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -1,17 +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
-/*
- * 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 <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"
@@ -19,8 +18,15 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/eval/typval.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 +35,27 @@
#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/sign.h"
#include "nvim/strings.h"
#include "nvim/textobject.h"
-#include "nvim/ui.h"
+#include "nvim/types.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 +89,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 +119,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 +196,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 +238,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 +499,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 +710,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 +728,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,51 +807,44 @@ 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("~/", 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((char_u *)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) {
@@ -872,11 +862,11 @@ void fmarks_check_names(buf_T *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 +939,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 +976,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) {
@@ -1012,7 +996,7 @@ void ex_marks(exarg_T *eap)
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 +1028,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,14 +1045,14 @@ 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) {
name = mark_line(p, 15);
mustfree = true;
}
- if (!message_filtered((char *)name)) {
+ if (!message_filtered(name)) {
if (!did_title) {
// Highlight title
msg_puts_title(_("\nmark line col file/text"));
@@ -1076,13 +1060,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((char *)name, current ? HL_ATTR(HLF_D) : 0);
+ msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
}
}
- ui_flush(); // show one line at a time
}
if (mustfree) {
xfree(name);
@@ -1090,12 +1073,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 +1092,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,7 +1110,7 @@ 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++) {
@@ -1173,13 +1154,11 @@ 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
@@ -1191,10 +1170,10 @@ void ex_jumps(exarg_T *eap)
// 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((char *)name)) {
+ if (name == NULL || message_filtered(name)) {
xfree(name);
continue;
}
@@ -1204,18 +1183,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_attr((char *)name,
+ 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,13 +1207,11 @@ 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"));
@@ -1246,19 +1222,17 @@ void ex_changes(exarg_T *eap)
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((char *)name, HL_ATTR(HLF_D));
+ 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 +1242,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 +1371,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 +1447,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,9 +1512,7 @@ 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++) {
@@ -1643,9 +1610,7 @@ 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;
@@ -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,9 +1824,7 @@ 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;
@@ -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 48f0d0fc05..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,
@@ -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,8 +207,8 @@ 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;
+ 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;
}
@@ -231,15 +249,14 @@ static int match_delete(win_T *wp, int id, bool perr)
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 = UPD_VALID;
}
+ xfree(cur->mit_pos_array);
xfree(cur);
redraw_later(wp, rtype);
return 0;
@@ -281,9 +299,10 @@ 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;
}
@@ -292,12 +311,12 @@ void clear_matches(win_T *wp)
/// 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,12 +880,14 @@ 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;
@@ -883,13 +918,13 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, EvalFuncData 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,22 +939,22 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
}
@@ -1133,8 +1168,8 @@ void f_matcharg(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -1159,9 +1194,9 @@ void f_matchdelete(typval_T *argvars, typval_T *rettv, EvalFuncData 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 = (char_u *)skiptowhite(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 = (char_u *)skip_regexp((char *)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 = find_nextcmd((char *)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 b874f0dc94..93ac0fccfa 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");
@@ -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,21 +393,19 @@ 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.
- */
+// Remove all BOM from "s" by moving remaining text.
void remove_bom(char_u *s)
{
char *p = (char *)s;
@@ -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
{
@@ -536,13 +534,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((char_u *)p, size) < utf8len_tab[(uint8_t)(*p)]) {
return 1; // truncated
}
c = utf_ptr2char((char *)p);
@@ -587,7 +585,7 @@ 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))) {
+ p += utfc_ptr2len_len((char *)p, (int)size + (int)(p - (char_u *)str))) {
clen += (size_t)utf_ptr2cells((char *)p);
}
@@ -601,7 +599,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 +644,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,10 +697,8 @@ 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!
- */
+// 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)
{
int c;
@@ -714,10 +708,8 @@ int mb_ptr2char_adv(const char_u **const pp)
return c;
}
-/*
- * Get character at **pp and advance *pp to the next character.
- * Note: composing characters are returned as separate characters.
- */
+// 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)
{
int c;
@@ -727,12 +719,10 @@ int mb_cptr2char_adv(const char_u **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,26 +744,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 *p_in, int *pcc)
+int utfc_ptr2char(const char *p, int *pcc)
{
- uint8_t *p = (uint8_t *)p_in;
int i = 0;
- int c = utf_ptr2char((char *)p);
- int 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)) {
- int 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;
}
}
@@ -786,29 +775,27 @@ int utfc_ptr2char(const char *p_in, 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);
int i = 0;
- int len = utf_ptr2len_len(p, maxlen);
+ int len = utf_ptr2len_len((char_u *)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);
+ int len_cc = utf_ptr2len_len((char_u *)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;
}
@@ -847,24 +834,20 @@ 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.
- */
+// 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)
{
int len;
@@ -891,21 +874,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) {
@@ -916,23 +898,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;
@@ -940,35 +920,31 @@ 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;
}
// Skip over first UTF-8 char, stopping at a NUL byte.
- len = utf_ptr2len_len(p, size);
+ len = utf_ptr2len_len((char_u *)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.
- */
- len_next_char = utf_ptr2len_len(p + len, size - len);
+ // Next character length should not go beyond size to ensure that
+ // utf_composinglike(...) does not read beyond size.
+ len_next_char = utf_ptr2len_len((char_u *)p + len, size - len);
if (len_next_char > size - len) {
break;
}
@@ -1046,26 +1022,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 }
@@ -1074,12 +1045,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);
@@ -1208,11 +1177,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
@@ -1233,15 +1200,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) {
@@ -1265,12 +1229,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) {
@@ -1296,12 +1257,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) {
@@ -1396,7 +1354,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
@@ -1498,16 +1456,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) {
- clen = (size_t)utf_ptr2len_len(s + i, (int)(len - i));
+ for (size_t i = 0; i < len; i += clen) {
+ clen = (size_t)utf_ptr2len_len((char_u *)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++;
@@ -1525,7 +1483,7 @@ 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
@@ -1541,17 +1499,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
@@ -1568,13 +1525,11 @@ 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;
@@ -1585,7 +1540,7 @@ void show_utf8(void)
// Get the byte length of the char under the cursor, including composing
// characters.
- line = get_cursor_pos_ptr();
+ line = (char_u *)get_cursor_pos_ptr();
len = utfc_ptr2len((char *)line);
if (len == 0) {
msg("NUL");
@@ -1602,37 +1557,40 @@ void show_utf8(void)
}
clen = utf_ptr2len((char *)line + i);
}
- sprintf((char *)IObuff + rlen, "%02x ",
+ sprintf(IObuff + rlen, "%02x ", // NOLINT(runtime/printf)
(line[i] == NL) ? NUL : 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;
+ const uint8_t *q;
for (q = p;; q--) {
// Move s to the last byte of this char.
- const char_u *s;
+ const uint8_t *s;
for (s = q; (s[1] & 0xc0) == 0x80; s++) {}
// Move q to the first byte of this char.
@@ -1657,7 +1615,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) {
@@ -1826,11 +1784,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_u *base, const char *p_in)
{
+ const uint8_t *p = (uint8_t *)p_in;
int i;
int j;
@@ -1918,19 +1877,17 @@ 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;
vimconv.vc_type = CONV_NONE;
- if (enc_canon_props((char_u *)curbuf->b_p_fenc) & ENC_8BIT) {
+ if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) {
// 'encoding' is "utf-8" but we are editing a 8-bit encoded file,
// possibly a utf-8 file with illegal bytes. Setup for conversion
// from utf-8 to 'fileencoding'.
@@ -1942,27 +1899,26 @@ void utf_find_illegal(void)
p = get_cursor_pos_ptr();
if (vimconv.vc_type != CONV_NONE) {
xfree(tofree);
- tofree = (char_u *)string_convert(&vimconv, (char *)p, NULL);
+ tofree = (char_u *)string_convert(&vimconv, p, NULL);
if (tofree == NULL) {
break;
}
- p = tofree;
+ p = (char *)tofree;
}
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 {
int l;
- len = (int)(p - tofree);
+ len = (int)(p - (char *)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;
}
}
@@ -2010,10 +1966,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);
@@ -2030,8 +1984,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) {
@@ -2042,7 +1996,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
@@ -2058,7 +2012,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);
@@ -2068,9 +2022,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_u *p = (char_u *)str;
int count;
if (p == NULL) {
@@ -2147,10 +2101,10 @@ const char *mb_unescape(const char **const pp)
/// 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;
@@ -2164,47 +2118,47 @@ char *enc_skip(char *p)
char *enc_canonize(char *enc)
FUNC_ATTR_NONNULL_RET
{
- char_u *p, *s;
- if (STRCMP(enc, "default") == 0) {
+ char *p, *s;
+ if (strcmp(enc, "default") == 0) {
// Use the default encoding as found by set_init_1().
- return (char *)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 = (char_u *)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;
// Skip "2byte-" and "8bit-".
- p = (char_u *)enc_skip((char *)r);
+ 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);
}
@@ -2217,19 +2171,19 @@ char *enc_canonize(char *enc)
} 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 (char *)r;
+ 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) {
+ if (strcmp(name, enc_alias_table[i].name) == 0) {
return enc_alias_table[i].canon;
}
}
@@ -2240,10 +2194,8 @@ 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.
- */
+// Get the canonicalized encoding of the current locale.
+// Returns an allocated string when successful, NULL when not.
char_u *enc_locale(void)
{
int i;
@@ -2309,12 +2261,10 @@ enc_locale_copy_enc:
#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).
- */
+// 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)
{
iconv_t fd;
@@ -2330,13 +2280,11 @@ void *my_iconv_open(char_u *to, char_u *from)
fd = iconv_open(enc_skip((char *)to), enc_skip((char *)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.
- */
+ // 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;
tolen = ICONV_TESTLEN;
(void)iconv(fd, NULL, NULL, &p, &tolen);
@@ -2352,14 +2300,12 @@ 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,
+// 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, const char_u *str, size_t slen,
size_t *unconvlenp, size_t *resultlenp)
{
const char *from;
@@ -2418,7 +2364,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) {
@@ -2448,12 +2394,12 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
/// @return FAIL when conversion is not supported, OK otherwise.
int convert_setup(vimconv_T *vcp, char *from, char *to)
{
- return convert_setup_ext(vcp, (char_u *)from, true, (char_u *)to, true);
+ 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;
@@ -2471,7 +2417,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c
// 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;
}
@@ -2506,8 +2452,8 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c
#ifdef HAVE_ICONV
else { // NOLINT(readability/braces)
// 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 ? (char_u *)"utf-8" : (char_u *)to,
+ from_is_utf8 ? (char_u *)"utf-8" : (char_u *)from);
if (vcp->vc_fd != (iconv_t)-1) {
vcp->vc_type = CONV_ICONV;
vcp->vc_factor = 4; // could be longer too...
@@ -2531,11 +2477,9 @@ char *string_convert(const vimconv_T *const vcp, char *ptr, size_t *lenp)
return (char *)string_convert_ext(vcp, (char_u *)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.
- */
+// 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)
{
@@ -2546,12 +2490,12 @@ 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((char *)ptr);
} else {
len = *lenp;
}
if (len == 0) {
- return vim_strsave((char_u *)"");
+ return (char_u *)xstrdup("");
}
switch (vcp->vc_type) {
@@ -2853,9 +2797,9 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, EvalFuncData 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/memfile.c b/src/nvim/memfile.c
index 9446aaee4f..61d7893948 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -43,19 +43,26 @@
#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/types.h"
#include "nvim/vim.h"
#define MEMFILE_PAGE_SIZE 4096 /// default page size
@@ -170,7 +177,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 +217,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);
}
}
@@ -751,8 +758,8 @@ void mf_free_fnames(memfile_T *mfp)
/// name so we must work out the full path name.
void mf_set_fnames(memfile_T *mfp, char *fname)
{
- mfp->mf_fname = (char_u *)fname;
- mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false);
+ mfp->mf_fname = fname;
+ mfp->mf_ffname = FullName_save(mfp->mf_fname, false);
}
/// Make name of memfile's swapfile a full path.
@@ -760,11 +767,13 @@ void mf_set_fnames(memfile_T *mfp, char *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.
@@ -789,7 +798,7 @@ static bool mf_do_open(memfile_T *mfp, char *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 +824,10 @@ static bool mf_do_open(memfile_T *mfp, char *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 b0b6b675cb..eee3b9d517 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 = 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;
@@ -430,7 +410,7 @@ void ml_setname(buf_T *buf)
if (vim_rename(mfp->mf_fname, fname) == 0) {
success = true;
mf_free_fnames(mfp);
- mf_set_fnames(mfp, (char *)fname);
+ mf_set_fnames(mfp, fname);
ml_upd_block0(buf, UB_SAME_DIR);
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,16 +457,16 @@ 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, (char *)fname); // consumes fname!
+ (void)mf_open_file(mfp, fname); // consumes fname!
}
buf->b_may_swap = false;
return;
}
// Try all directories in 'directory' option.
- dirp = p_dir;
+ char *dirp = p_dir;
bool found_existing_dir = false;
for (;;) {
if (*dirp == NUL) {
@@ -499,14 +475,14 @@ 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
}
if (fname == NULL) {
continue;
}
- if (mf_open_file(mfp, (char *)fname) == OK) { // consumes fname!
+ if (mf_open_file(mfp, fname) == OK) { // consumes fname!
ml_upd_block0(buf, UB_SAME_DIR);
// Flush block zero, so others can read it
@@ -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,22 +675,21 @@ 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,
buf->b_p_fenc, (size_t)n);
@@ -727,6 +698,23 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf)
}
}
+/// 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,66 +723,51 @@ 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);
+ 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
&& 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((char_u *)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((char_u *)fname, true, 0, NULL);
msg_putchar('\n');
msg_puts(_("Enter number of swap file to use (0 to quit): "));
i = get_number(false, NULL);
@@ -803,7 +776,7 @@ void ml_recover(bool checkext)
}
}
// get the swap file name that will be used
- (void)recover_names(fname, false, i, &fname_used);
+ (void)recover_names((char_u *)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,12 +799,10 @@ 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"!
- mfp = mf_open((char *)fname_used, O_RDONLY);
+ // 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) {
semsg(_("E306: Cannot open %s"), fname_used);
@@ -843,30 +810,26 @@ 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);
- msg_outtrans_attr((char *)mfp->mf_fname, attr | MSG_HIST);
+ msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
attr | MSG_HIST);
msg_end();
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((char *)mfp->mf_fname, MSG_HIST);
+ msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
MSG_HIST);
msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
@@ -879,7 +842,7 @@ void ml_recover(bool checkext)
}
if (b0_magic_wrong(b0p)) {
msg_start();
- msg_outtrans_attr((char *)mfp->mf_fname, attr | MSG_HIST);
+ msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_(" cannot be used on this computer.\n"),
attr | MSG_HIST);
msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
@@ -891,22 +854,21 @@ 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;
mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
if (mfp->mf_page_size < previous_page_size) {
msg_start();
- msg_outtrans_attr((char *)mfp->mf_fname, attr | MSG_HIST);
+ msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
attr | MSG_HIST);
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 {
@@ -925,31 +887,29 @@ void ml_recover(bool checkext)
// If .swp file name given directly, use name from swap file for buffer.
if (directly) {
expand_env((char *)b0p->b0_fname, NameBuff, MAXPATHL);
- if (setfname(curbuf, (char *)NameBuff, NULL, true) == FAIL) {
+ 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)) {
@@ -962,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);
@@ -991,35 +947,29 @@ void ml_recover(bool checkext)
set_fileformat(b0_ff - 1, OPT_LOCAL);
}
if (b0_fenc != NULL) {
- set_option_value_give_err("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);
@@ -1033,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) {
@@ -1049,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,
@@ -1073,10 +1021,8 @@ void ml_recover(bool checkext)
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;
@@ -1101,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) {
@@ -1116,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"
@@ -1129,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);
@@ -1151,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,8 +1117,8 @@ void ml_recover(bool checkext)
} else {
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();
@@ -1188,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);
@@ -1217,7 +1155,14 @@ 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(UPD_NOT_VALID);
@@ -1231,7 +1176,7 @@ theend:
}
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);
}
@@ -1256,17 +1201,14 @@ theend:
/// @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_u *fname, int list, int nr, char **fname_out)
{
int num_names;
char *(names[6]);
char_u *tail;
- char_u *p;
- int num_files;
+ char *p;
int file_count = 0;
char **files;
- char *dirp;
- char_u *dir_name;
char_u *fname_res = NULL;
#ifdef HAVE_READLINK
char_u fname_buf[MAXPATHL];
@@ -1276,7 +1218,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
#ifdef HAVE_READLINK
// Expand symlink in the file name, because the swap file is created
// with the actual file instead of with the symlink.
- if (resolve_symlink(fname, fname_buf) == OK) {
+ if (resolve_symlink((char *)fname, (char *)fname_buf) == OK) {
fname_res = fname_buf;
} else
#endif
@@ -1291,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 = 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) {
@@ -1308,34 +1250,35 @@ 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, (char *)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,
+ tail = (char_u *)make_percent_swname(dir_name,
(char *)fname_res);
} else {
tail = (char_u *)path_tail((char *)fname_res);
- tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, true);
+ tail = (char_u *)concat_fnames(dir_name, (char *)tail, true);
}
- num_names = recov_file_names(names, tail, false);
+ num_names = recov_file_names(names, (char *)tail, false);
xfree(tail);
}
}
+ int num_files;
if (num_names == 0) {
num_files = 0;
} else if (expand_wildcards(num_names, names, &num_files, &files,
@@ -1343,15 +1286,13 @@ 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);
if (swapname != NULL) {
- if (os_path_exists(swapname)) {
+ if (os_path_exists((char *)swapname)) {
files = xmalloc(sizeof(char_u *));
files[0] = (char *)swapname;
swapname = NULL;
@@ -1361,15 +1302,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.
@@ -1387,7 +1326,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) {
@@ -1410,7 +1349,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
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"));
@@ -1438,17 +1377,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;
}
@@ -1470,7 +1411,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,
@@ -1495,7 +1436,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;
@@ -1507,28 +1448,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]"));
@@ -1562,7 +1504,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;
}
@@ -1589,10 +1531,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;
}
@@ -1606,6 +1547,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;
@@ -1616,20 +1559,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;
@@ -1637,7 +1593,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;
}
@@ -1645,14 +1601,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]);
@@ -1681,10 +1637,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
@@ -1715,10 +1669,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
/// @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) {
@@ -1734,27 +1685,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;
@@ -1781,25 +1730,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;
@@ -1813,20 +1760,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) {
@@ -1848,23 +1792,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
@@ -1879,10 +1820,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;
@@ -1927,7 +1867,7 @@ int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile)
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
@@ -1947,7 +1887,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, (char *)line, len, newfile, false);
}
/// @param lnum append after this line (can be 0)
@@ -1955,20 +1895,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;
@@ -1979,18 +1907,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;
@@ -1998,32 +1925,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;
}
@@ -2039,22 +1963,17 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
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 {
@@ -2063,7 +1982,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);
@@ -2071,31 +1990,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;
@@ -2113,14 +2026,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;
@@ -2144,7 +2055,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;
@@ -2164,9 +2075,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;
@@ -2179,25 +2088,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;
}
@@ -2205,9 +2108,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;
@@ -2234,12 +2135,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;
}
@@ -2248,35 +2147,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) {
@@ -2303,18 +2195,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
@@ -2384,11 +2274,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;
}
@@ -2400,9 +2290,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
@@ -2414,18 +2302,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;
@@ -2439,7 +2328,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.
@@ -2454,7 +2343,7 @@ int ml_replace(linenr_T lnum, char *line, bool copy)
/// 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;
@@ -2468,7 +2357,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
@@ -2506,18 +2395,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;
}
@@ -2526,42 +2403,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--;
- 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 {
@@ -2571,29 +2446,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);
@@ -2616,7 +2488,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;
}
@@ -2630,7 +2502,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;
}
@@ -2638,9 +2510,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);
}
@@ -2651,8 +2521,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) {
@@ -2662,15 +2530,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;
}
@@ -2678,31 +2545,23 @@ 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;
+ 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;
@@ -2719,30 +2578,22 @@ 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;
+ 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;
@@ -2768,18 +2619,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) {
@@ -2794,31 +2633,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,
@@ -2826,7 +2664,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;
}
}
@@ -2903,39 +2741,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;
}
@@ -2944,10 +2771,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);
}
@@ -2957,14 +2782,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]);
+ 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;
@@ -2979,24 +2805,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;
@@ -3013,15 +2835,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;
+ bool dirty = false;
+ int idx;
for (idx = 0; idx < (int)pp->pb_count; idx++) {
- t = pp->pb_pointer[idx].pe_line_count;
+ 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;
@@ -3030,9 +2853,7 @@ 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) {
@@ -3067,11 +2888,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) {
@@ -3111,18 +2930,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"));
@@ -3142,10 +2958,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) {
@@ -3153,7 +2968,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.
@@ -3162,7 +2977,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.
@@ -3185,60 +3000,57 @@ int resolve_symlink(const char_u *fname, char_u *buf)
// If it's relative, build a new path based on the directory
// portion of the filename (if any) and the path the symlink
// points to.
- if (path_is_absolute(buf)) {
+ if (path_is_absolute((char_u *)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;
}
@@ -3260,30 +3072,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;
@@ -3293,7 +3102,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);
@@ -3313,7 +3122,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"));
}
@@ -3329,7 +3138,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--;
@@ -3345,9 +3154,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
@@ -3402,24 +3211,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
@@ -3439,7 +3241,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;
}
@@ -3464,14 +3266,14 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// buffer don't compare the directory names, they can
// have a different mountpoint.
if (b0.b0_flags & B0_SAME_DIR) {
- if (FNAMECMP(path_tail((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((char *)b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino((char_u *)buf->b_ffname, (char_u *)NameBuff,
+ if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
differ = true;
}
@@ -3480,7 +3282,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// The name in the swap file may be
// "~user/path/file". Expand it first.
expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino((char_u *)buf->b_ffname, (char_u *)NameBuff,
+ if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
differ = true;
}
@@ -3499,7 +3301,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// 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"));
@@ -3511,12 +3313,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.
@@ -3541,13 +3343,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) {
@@ -3582,7 +3384,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
// If the file was deleted this fname can be used.
- if (!os_path_exists((char_u *)fname)) {
+ if (!os_path_exists(fname)) {
break;
}
} else {
@@ -3596,12 +3398,10 @@ 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"));
@@ -3634,8 +3434,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;
}
@@ -3687,26 +3487,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;
@@ -3716,23 +3514,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;
}
@@ -3750,7 +3544,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;
@@ -3781,7 +3575,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);
@@ -3790,8 +3584,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
///
@@ -3827,20 +3623,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;
@@ -3928,10 +3720,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;
@@ -4044,10 +3834,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;
@@ -4179,7 +3967,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);
@@ -4212,8 +4000,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;
}
@@ -4221,15 +4009,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 a9785fcb7c..aa8314b23d 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -6,23 +6,33 @@
#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/ui_compositor.h"
+#include "nvim/usercmd.h"
#include "nvim/vim.h"
#ifdef UNIT_TESTING
@@ -112,8 +122,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;
@@ -143,8 +153,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();
}
}
@@ -165,8 +175,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();
}
}
@@ -185,7 +195,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();
}
@@ -437,9 +447,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`.
@@ -501,22 +510,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]".
@@ -545,7 +554,7 @@ static void arena_free_reuse_blks(void)
}
}
-/// 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
@@ -575,41 +584,48 @@ void alloc_block(Arena *arena)
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 unique one
+/// @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 (!arena) {
return xmalloc(size);
}
- if (align) {
- arena->pos = (arena->pos + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1);
+ if (!arena->cur_blk) {
+ alloc_block(arena);
}
- if (arena->pos + size > arena->size || !arena->cur_blk) {
+ 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.
- if (!arena->cur_blk) {
- // to simplify free-list management, arena->cur_blk must
- // always be a normal, ARENA_BLOCK_SIZE sized, block
- alloc_block(arena);
- }
arena_alloc_count++;
- char *alloc = xmalloc(size + sizeof(struct consumed_blk));
+ 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 {
- alloc_block(arena);
+ 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;
}
@@ -645,7 +661,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"
@@ -654,33 +669,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;
@@ -807,6 +813,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;
@@ -822,8 +833,8 @@ 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();
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index f407192331..5b9798dc0d 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -1,10 +1,10 @@
#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"
@@ -39,13 +39,13 @@ extern MemRealloc mem_realloc;
extern bool entered_free_all_mem;
#endif
-EXTERN size_t arena_alloc_count INIT(=0);
+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;
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index cb6918cd27..0fa45ac24a 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -1,35 +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
-/*
- * 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 <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 +56,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 +88,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 +108,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 +150,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 +173,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 +188,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 +203,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 +222,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 +231,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 +239,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 +297,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 +379,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 +398,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) {
@@ -444,7 +435,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 +444,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 +482,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 +511,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 +564,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 +613,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 +635,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 +696,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);
@@ -888,7 +868,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 +889,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
-/*
- * 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
{
@@ -940,10 +916,10 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
}
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 {
@@ -984,7 +960,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 +970,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 +998,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;
@@ -1073,10 +1045,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 +1064,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 +1075,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;
}
@@ -1312,13 +1282,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];
@@ -1370,7 +1338,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;
@@ -1393,21 +1361,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 '-'
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
@@ -1475,15 +1443,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.
@@ -1563,7 +1533,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);
@@ -1730,9 +1700,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
@@ -1748,11 +1716,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;
@@ -1762,10 +1728,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.
@@ -1796,9 +1760,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)) {
@@ -1810,10 +1772,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;
@@ -1841,9 +1801,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;
@@ -1855,10 +1813,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;
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 80b5f53f4e..de4acd601f 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -1,57 +1,62 @@
// 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;
@@ -59,19 +64,21 @@ struct msgchunk_S {
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
#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;
@@ -82,41 +89,39 @@ 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,7 +214,7 @@ 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
int msg(char *s)
@@ -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();
@@ -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,
@@ -313,11 +314,9 @@ 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;
}
@@ -329,15 +328,15 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
|| (*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;
@@ -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;
@@ -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);
}
@@ -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,7 +738,7 @@ 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
@@ -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,7 +764,7 @@ 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
bool emsg(const char *s)
@@ -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) {
@@ -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;
@@ -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;
}
@@ -1184,10 +1175,6 @@ void wait_return(int redraw)
c = CAR; // no need for a return in ex mode
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) {
@@ -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);
@@ -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,19 +1483,19 @@ 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);
+ char *name = home_replace_save(NULL, fname);
msg_outtrans_attr(name, attr);
xfree(name);
}
@@ -1526,12 +1512,12 @@ int msg_outtrans(char *str)
int msg_outtrans_attr(const char *str, int attr)
{
- return msg_outtrans_len_attr((char_u *)str, (int)STRLEN(str), attr);
+ return msg_outtrans_len_attr(str, (int)strlen(str), attr);
}
int msg_outtrans_len(const char *str, int len)
{
- return msg_outtrans_len_attr((char_u *)str, len, 0);
+ return msg_outtrans_len_attr(str, len, 0);
}
/// Output one character at "p".
@@ -1543,19 +1529,19 @@ char *msg_outtrans_one(char *p, int attr)
int l;
if ((l = utfc_ptr2len(p)) > 1) {
- msg_outtrans_len_attr((char_u *)p, l, attr);
+ 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++;
}
@@ -1663,12 +1647,12 @@ void msg_make(char *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);
@@ -1679,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
@@ -1703,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.
@@ -1726,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
@@ -1818,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) {
@@ -1841,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--;
}
@@ -1873,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 {
@@ -1894,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;
@@ -1920,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;
@@ -1929,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);
@@ -2023,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;
@@ -2084,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) {
@@ -2105,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;
@@ -2148,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;
@@ -2168,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;
@@ -2186,19 +2170,19 @@ 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
// ourselves).
if (t_col > 0) {
// output postponed text
- t_puts(&t_col, (char *)t_s, (char *)s, attr);
+ t_puts(&t_col, t_s, s, attr);
}
// When no more prompt and no more room, truncate here
@@ -2207,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
@@ -2221,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 = (char_u *)screen_puts_mbyte((char *)s, l, attr);
+ s = screen_puts_mbyte((char *)s, l, attr);
did_last_char = true;
} else {
did_last_char = false;
@@ -2250,10 +2234,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
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--;
}
@@ -2276,13 +2258,13 @@ 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'
|| *s == '\t' || *s == BELL)) {
// Output any postponed text.
- t_puts(&t_col, (char *)t_s, (char *)s, attr);
+ t_puts(&t_col, t_s, s, attr);
}
if (wrap && p_more && !recurse) {
@@ -2312,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 = (char_u *)screen_puts_mbyte((char *)s, l, attr) - 1;
+ s = screen_puts_mbyte((char *)s, l, attr) - 1;
} else {
msg_screen_putchar(*s, attr);
}
@@ -2343,9 +2325,9 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
// Output any postponed text.
if (t_col > 0) {
- t_puts(&t_col, (char *)t_s, (char *)s, attr);
+ 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);
}
@@ -2367,14 +2349,7 @@ bool message_filtered(char *msg)
/// 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)
@@ -2383,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
@@ -2465,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(UPD_NOT_VALID);
}
msg_scrolled = 0;
msg_scrolled_at_flush = 0;
+ msg_grid_scroll_discount = 0;
}
/// Increment "msg_scrolled".
@@ -2679,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;
@@ -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);
}
}
@@ -2828,9 +2801,7 @@ static int do_more_prompt(int typed_char)
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;
@@ -2976,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
@@ -2984,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));
@@ -3028,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(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(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(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!
@@ -3080,13 +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, (char *)s, Rows - 1, 0, attr);
+ grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr);
if (full) {
grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
- Rows - 1, vim_strsize((char *)s), attr);
+ Rows - 1, vim_strsize(s), attr);
}
}
@@ -3120,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();
}
}
@@ -3142,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)) {
@@ -3170,12 +3149,10 @@ void msg_clr_cmdline(void)
/// @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;
@@ -3192,6 +3169,7 @@ int msg_end(void)
void msg_ext_ui_flush(void)
{
if (!ui_has(kUIMessages)) {
+ msg_ext_kind = NULL;
return;
}
@@ -3281,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) {
@@ -3306,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) {
@@ -3319,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);
}
@@ -3332,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);
@@ -3469,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,11 +3502,11 @@ void msg_advance(int col)
/// @param textfiel IObuff for inputdialog(), NULL otherwise
/// @param ex_cmd when true pressing : accepts default and starts Ex command
/// @returns 0 if cancelled, otherwise the nth button (1-indexed).
-int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton,
- char_u *textfield, int ex_cmd)
+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;
@@ -3545,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);
@@ -3578,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]) {
@@ -3608,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;
}
@@ -3635,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;
@@ -3643,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)'
@@ -3663,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
@@ -3689,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);
@@ -3709,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.
@@ -3732,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) {
@@ -3788,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:
@@ -3818,12 +3794,12 @@ 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"),
+ _("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
dflt, NULL, false)) {
case 1:
return VIM_YES;
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 73147204a1..b7d15fe9af 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1,24 +1,46 @@
// 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 <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 +53,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_u *line;
+ int cclass;
+ int col;
+
+ line = (char_u *)ml_get(pos->lnum);
+ cclass = get_mouse_class((char *)line + pos->col);
+
+ while (pos->col > 0) {
+ col = pos->col - 1;
+ col -= utf_head_off((char *)line, (char *)line + col);
+ if (get_mouse_class((char *)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 = (char_u *)ml_get(pos->lnum);
+ if (*p_sel == 'e' && pos->col > 0) {
+ pos->col--;
+ pos->col -= utf_head_off((char *)line, (char *)line + pos->col);
+ }
+ cclass = get_mouse_class((char *)line + pos->col);
+ while (line[pos->col] != NUL) {
+ col = pos->col + utfc_ptr2len((char *)line + pos->col);
+ if (get_mouse_class((char *)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 +237,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 +981,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 +1030,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,6 +1073,9 @@ 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(UPD_INVERTED); // delete the inversion
@@ -211,6 +1110,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 +1141,10 @@ retnomove:
return IN_OTHER_WIN | MOUSE_WINBAR;
}
+ if (on_statuscol) {
+ return IN_OTHER_WIN | MOUSE_STATUSCOL;
+ }
+
fdc = win_fdccol_count(wp);
dragwin = NULL;
@@ -303,17 +1210,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,6 +1245,9 @@ 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
@@ -631,9 +1539,9 @@ 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 *line = ml_get_buf(wp->w_buffer, lnum, false);
+ char_u *line = (char_u *)ml_get_buf(wp->w_buffer, lnum, false);
chartabsize_T cts;
- init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ init_chartabsize_arg(&cts, wp, lnum, 0, (char *)line, (char *)line);
while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) {
cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
MB_PTR_ADV(cts.cts_ptr);
@@ -654,7 +1562,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;
@@ -666,7 +1574,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_u *line = (char_u *)ml_get(lnum);
if (*line != NUL) {
for (;;) {
int numchar = win_chartabsize(curwin, (char *)line, col);
@@ -771,7 +1679,7 @@ 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 *line = (char_u *)ml_get(lnum);
char_u *ptr = line;
char_u *ptr_end;
char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end`
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index 21ff56bbbc..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 b3ec3a8e7a..dc2f4b4844 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,28 @@
#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/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/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 +56,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;
@@ -133,21 +138,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 +148,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;
@@ -250,14 +245,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,9 +321,7 @@ 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;
@@ -349,22 +340,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 +350,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 +379,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 +416,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);
@@ -453,9 +432,7 @@ void changed_window_setting_win(win_T *wp)
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;
@@ -475,11 +452,9 @@ void set_topline(win_T *wp, linenr_T lnum)
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 +467,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 +482,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 +490,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 +506,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 +514,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 +524,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
@@ -633,77 +596,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 +677,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 +707,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 +715,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;
@@ -871,8 +828,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
@@ -980,11 +936,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 +954,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
@@ -1079,10 +1102,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();
@@ -1200,10 +1221,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 +1236,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));
@@ -1261,10 +1278,8 @@ void scrolldown_clamp(void)
}
}
-/*
- * 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 +1311,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)) {
@@ -1322,12 +1335,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)) {
@@ -1349,11 +1360,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 +1371,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 +1382,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 +1399,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 +1424,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 +1446,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 +1458,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 +1490,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 +1570,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 +1664,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;
@@ -1760,18 +1747,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 +1778,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 +1787,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 +1839,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;
@@ -2044,18 +2021,16 @@ int onepage(Direction dir, long count)
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 +2100,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;
@@ -2185,9 +2158,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;
@@ -2247,9 +2218,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;
@@ -2294,9 +2263,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 d22bcb29d5..0c23a7798c 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -1,36 +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 <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/rstream.h"
-#include "nvim/event/socket.h"
+#include "nvim/event/stream.h"
#include "nvim/event/wstream.h"
-#include "nvim/lib/kvec.h"
+#include "nvim/globals.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 +100,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 +135,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 };
@@ -244,8 +250,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));
}
+ 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,7 +299,7 @@ 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));
return;
@@ -352,6 +358,7 @@ static void request_event(void **argv)
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,
@@ -434,11 +441,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,
@@ -482,7 +491,6 @@ static void broadcast_event(const char *name, Array args)
});
if (!kv_size(subscribed)) {
- api_free_array(args);
goto end;
}
@@ -537,26 +545,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);
}
}
@@ -602,22 +592,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);
}
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/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 532e684f93..b1e033d9f1 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
@@ -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);
}
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
index 24480835a1..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,7 +186,7 @@ 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;
}
@@ -290,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
@@ -310,17 +318,19 @@ bool unpacker_advance(Unpacker *p)
}
}
- 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()
p->arena = (Arena)ARENA_EMPTY;
+ p->state = 13;
}
}
@@ -347,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 {
@@ -372,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) { \
@@ -419,14 +430,14 @@ redo:
}
return true;
} else {
- p->state = 13;
+ 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) {
@@ -447,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);
@@ -504,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 35048fb877..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;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 47ad000385..8f4240c062 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,16 +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"
@@ -145,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 },
@@ -613,6 +620,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);
}
@@ -629,18 +637,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 = (char_u *)keep_msg;
+ 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 = (char *)kmsg;
+ keep_msg = kmsg;
- kmsg = vim_strsave((char_u *)keep_msg);
+ kmsg = xstrdup(keep_msg);
msg_attr((const char *)kmsg, keep_msg_attr);
xfree(kmsg);
}
@@ -1132,6 +1140,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;
}
@@ -1222,8 +1231,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();
}
}
@@ -1284,9 +1292,9 @@ static void normal_redraw(NormalState *s)
if (VIsual_active) {
redraw_curbuf_later(UPD_INVERTED); // update inverted part
- update_screen(0);
+ update_screen();
} else if (must_redraw) {
- update_screen(0);
+ update_screen();
} else if (redraw_cmdline || clear_cmdline || redraw_mode) {
showmode();
}
@@ -1389,6 +1397,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
@@ -1433,856 +1444,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 ? UPD_INVERTED : UPD_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(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;
-}
-
-/// 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().
@@ -2416,7 +1577,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;
@@ -2429,7 +1590,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 '['.
@@ -2440,7 +1601,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);
}
@@ -2453,7 +1614,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
|| (find_type & FIND_IDENT))
&& (!(find_type & FIND_EVAL)
|| prevcol == 0
- || !find_is_eval_item(ptr + prevcol, &prevcol, &bn, BACKWARD))) {
+ || !find_is_eval_item((char_u *)ptr + prevcol, &prevcol, &bn, BACKWARD))) {
break;
}
col = prevcol;
@@ -2479,7 +1640,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;
}
@@ -2496,8 +1657,8 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text
: mb_get_class(ptr + col) != 0)
|| ((find_type & FIND_EVAL)
&& col <= (int)startcol
- && find_is_eval_item(ptr + col, &col, &bn, FORWARD)))) {
- col += utfc_ptr2len((char *)ptr + col);
+ && find_is_eval_item((char_u *)ptr + col, &col, &bn, FORWARD)))) {
+ col += utfc_ptr2len(ptr + col);
}
assert(col >= 0);
@@ -2622,9 +1783,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;
@@ -2663,12 +1822,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;
@@ -2681,7 +1840,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++;
@@ -2692,9 +1851,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;
@@ -2751,8 +1910,8 @@ bool add_to_showcmd(int 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 +1943,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 +1976,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 +2000,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);
@@ -2843,7 +2012,7 @@ static void display_showcmd(void)
grid_puts_line_start(&msg_grid_adj, showcmd_row);
if (!showcmd_is_clear) {
- grid_puts(&msg_grid_adj, (char *)showcmd_buf, showcmd_row, sc_col,
+ grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col,
HL_ATTR(HLF_MSG));
}
@@ -3059,7 +2228,7 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock)
/// @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_u *line, int offset)
{
bool incomment = false;
int instring = 0;
@@ -3100,7 +2269,7 @@ static bool is_ident(char_u *line, int offset)
/// @return fail when not found.
bool find_decl(char_u *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 +2285,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((char *)ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", // NOLINT(runtime/printf)
(int)len, ptr);
old_pos = curwin->w_cursor;
save_p_ws = p_ws;
@@ -3134,7 +2303,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--;
}
}
@@ -3144,7 +2313,7 @@ bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_
clearpos(&found_pos);
for (;;) {
t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
- pat, 1L, searchflags, RE_LAST, NULL);
+ (char_u *)pat, 1L, searchflags, RE_LAST, NULL);
if (curwin->w_cursor.lnum >= old_pos.lnum) {
t = false; // match after start is failure too
}
@@ -3171,13 +2340,13 @@ 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;
continue;
}
- bool valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col);
+ bool valid = is_ident((char_u *)get_cursor_line_ptr(), curwin->w_cursor.col);
// If the current position is not a valid identifier and a previous match is
// present, favor that one instead.
@@ -3361,7 +2530,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
virtcol -= vim_strsize((char *)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 +2579,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);
@@ -3514,6 +2683,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 +2724,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 +2750,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 +2759,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 +2776,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;
@@ -3735,8 +2906,8 @@ 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;
}
@@ -3756,10 +2927,10 @@ 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;
@@ -4057,7 +3228,7 @@ static void nv_colon(cmdarg_T *cap)
} 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))
+ (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);
@@ -4164,7 +3335,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 +3372,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 +3387,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 +3405,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,14 +3446,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 : (char_u *)curbuf->b_p_kp; // 'keywordprg'
- bool kp_help = (*kp == NUL || STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0);
+ char *kp = *curbuf->b_p_kp == NUL ? (char *)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;
}
bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command
- size_t buf_size = n * 2 + 30 + STRLEN(kp);
+ size_t buf_size = n * 2 + 30 + strlen(kp);
char *buf = xmalloc(buf_size);
buf[0] = NUL;
@@ -4294,9 +3465,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
@@ -4311,11 +3482,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:
@@ -4336,45 +3503,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, *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;
}
@@ -4382,13 +3549,13 @@ 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 == '*' ? '/' : '?', buf, 0, NULL);
} else {
@@ -4425,14 +3592,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) {
@@ -4451,7 +3618,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);
}
}
@@ -4478,7 +3645,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;
@@ -4600,7 +3769,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());
}
}
}
@@ -4648,10 +3817,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;
}
@@ -4723,7 +3892,7 @@ 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)) {
@@ -4734,7 +3903,7 @@ static void nv_gotofile(cmdarg_T *cap)
return;
}
- ptr = grab_file_name(cap->count1, &lnum);
+ ptr = (char *)grab_file_name(cap->count1, &lnum);
if (ptr != NULL) {
// do autowrite if necessary
@@ -4742,7 +3911,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;
@@ -4849,7 +4018,7 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrappe
curwin->w_set_curswant = true;
CLEAR_FIELD(sia);
- int i = do_search(cap->oap, dir, dir, (char_u *)pat, cap->count1,
+ 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;
@@ -5058,7 +4227,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
@@ -5151,9 +4320,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();
@@ -5297,7 +4465,7 @@ static void nv_kundo(cmdarg_T *cap)
/// Handle the "r" command.
static void nv_replace(cmdarg_T *cap)
{
- char_u *ptr;
+ char *ptr;
int had_ctrl_v;
if (checkclearop(cap->oap)) {
@@ -5361,7 +4529,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;
@@ -5692,6 +4860,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
@@ -5743,7 +4915,7 @@ static void nv_pcmark(cmdarg_T *cap)
fm = get_changelist(curbuf, curwin, (int)cap->count1);
} else {
fm = get_jumplist(curwin, (int)cap->count1);
- flags |= KMarkNoContext | kMarkJumpList;
+ 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
@@ -5841,10 +5013,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;
}
@@ -5854,10 +5028,9 @@ 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;
@@ -6068,7 +5241,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) {
@@ -6106,9 +5279,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
@@ -6139,9 +5310,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();
}
}
@@ -6151,7 +5320,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;
@@ -6513,8 +5682,7 @@ static void n_opencmd(cmdarg_T *cap)
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))
- )
+ (cap->cmdchar == 'o' ? 1 : 0)))
&& open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD,
has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0,
0, NULL)) {
@@ -6834,7 +6002,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;
}
}
@@ -6917,10 +6085,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"));
@@ -6971,7 +6136,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());
}
}
@@ -7064,7 +6229,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
@@ -7072,7 +6237,7 @@ static void nv_object(cmdarg_T *cap)
include = true; // "ax" = an object: include white space
}
// Make sure (), [], {} and <> are in 'matchpairs'
- mps_save = (char_u *)curbuf->b_p_mps;
+ mps_save = curbuf->b_p_mps;
curbuf->b_p_mps = "(:),{:},[:],<:>";
switch (cap->nchar) {
@@ -7127,7 +6292,7 @@ static void nv_object(cmdarg_T *cap)
break;
}
- curbuf->b_p_mps = (char *)mps_save;
+ curbuf->b_p_mps = mps_save;
if (!flag) {
clearopbeep(cap->oap);
}
@@ -7396,12 +6561,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 13ea233658..90a5c4103e 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -3,7 +3,7 @@
#include <stdbool.h>
-#include "nvim/buffer_defs.h" // for win_T
+#include "nvim/buffer_defs.h"
#include "nvim/pos.h"
// Values for find_ident_under_cursor()
@@ -22,9 +22,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,9 +51,7 @@ 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
int prechar; // prefix character (optional, always 'g')
@@ -76,6 +72,12 @@ typedef struct cmdarg_S {
#define CA_COMMAND_BUSY 1 // skip restarting edit() once
#define CA_NO_ADJ_OP_END 2 // don't adjust operator end
+// 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 4f2b84d20f..34432c58db 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,13 +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"
@@ -67,15 +74,13 @@ 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
@@ -85,7 +90,7 @@ struct block_def {
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
@@ -96,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
@@ -225,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) {
@@ -262,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--;
}
@@ -333,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;
@@ -356,22 +358,20 @@ 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;
@@ -388,7 +388,7 @@ static void shift_block(oparg_T *oap, int amount)
total += incr;
cts.cts_vcol += incr;
}
- bd.textstart = (char_u *)cts.cts_ptr;
+ bd.textstart = cts.cts_ptr;
bd.start_vcol = cts.cts_vcol;
clear_chartabsize_arg(&cts);
@@ -403,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;
@@ -419,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
@@ -454,7 +452,7 @@ static void shift_block(oparg_T *oap, int amount)
cts.cts_vcol += incr;
}
non_white_col = cts.cts_vcol;
- non_white = (char_u *)cts.cts_ptr;
+ non_white = cts.cts_ptr;
clear_chartabsize_arg(&cts);
const colnr_T block_space_width = non_white_col - oap->start_vcol;
@@ -487,7 +485,7 @@ static void shift_block(oparg_T *oap, int amount)
MB_PTR_ADV(cts.cts_ptr);
}
verbatim_copy_width = cts.cts_vcol;
- verbatim_copy_end = (char_u *)cts.cts_ptr;
+ verbatim_copy_end = cts.cts_ptr;
clear_chartabsize_arg(&cts);
// If "destination_col" is different from the width of the initial
@@ -502,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;
@@ -513,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,
@@ -525,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
@@ -580,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);
@@ -617,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);
@@ -638,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;
@@ -668,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 {
@@ -691,9 +689,9 @@ 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 :
@@ -715,19 +713,17 @@ 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);
+ new_line = (char *)getcmdline('=', 0L, 0, true);
if (new_line == NULL) {
return NUL;
}
@@ -741,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;
@@ -750,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.
@@ -906,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;
@@ -919,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(UPD_VALID);
-
- changed_cmdheight = true;
- }
-
apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf);
}
} else { // stop recording
@@ -939,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);
}
@@ -960,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("");
@@ -976,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(UPD_CLEAR);
- }
}
return retval;
}
@@ -1000,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)) {
@@ -1014,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[0] = p;
reg->y_size = 1;
reg->y_type = kMTCharWise;
}
@@ -1055,13 +1037,13 @@ static char_u *execreg_line_continuation(char **lines, size_t *idx)
garray_T ga;
ga_init(&ga, (int)sizeof(char_u), 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;
}
@@ -1071,22 +1053,22 @@ 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;
- return str;
+ return (char_u *)str;
}
/// Execute a yank register: copy it into the stuff buffer
@@ -1098,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
@@ -1127,18 +1109,18 @@ int do_execreg(int regname, int colon, int addcr, int silent)
// don't keep the cmdline containing @:
XFREE_CLEAR(new_last_cmdline);
// Escape all control characters with a CTRL-V
- p = vim_strsave_escaped_ext((char_u *)last_cmdline,
- (char_u *)"\001\002\003\004\005\006\007"
+ p = vim_strsave_escaped_ext(last_cmdline,
+ "\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) {
- retval = put_in_typebuf(p + 5, true, true, silent);
+ if (VIsual_active && strncmp(p, "'<,'>", 5) == 0) {
+ retval = put_in_typebuf((char_u *)p + 5, true, true, silent);
} else {
- retval = put_in_typebuf(p, true, true, silent);
+ retval = put_in_typebuf((char_u *)p, true, true, silent);
}
xfree(p);
} else if (regname == '=') {
@@ -1146,15 +1128,15 @@ int do_execreg(int regname, int colon, int addcr, int silent)
if (p == NULL) {
return FAIL;
}
- retval = put_in_typebuf(p, true, colon, silent);
+ retval = put_in_typebuf((char_u *)p, true, colon, silent);
xfree(p);
} else if (regname == '.') { // use last inserted text
- p = get_last_insert_save();
+ p = (char *)get_last_insert_save();
if (p == NULL) {
emsg(_(e_noinstext));
return FAIL;
}
- retval = put_in_typebuf(p, false, colon, silent);
+ retval = put_in_typebuf((char_u *)p, false, colon, silent);
xfree(p);
} else {
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
@@ -1165,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
@@ -1179,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);
+ str = (char *)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);
}
@@ -1277,11 +1257,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;
@@ -1354,7 +1332,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;
@@ -1408,7 +1386,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
@@ -1447,8 +1425,8 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
cmdline_paste_str((char_u *)"\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;
@@ -1478,7 +1456,7 @@ int op_delete(oparg_T *oap)
int n;
linenr_T lnum;
char_u *ptr;
- char_u *newp, *oldp;
+ char *newp, *oldp;
struct block_def bd = { 0 };
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
@@ -1503,17 +1481,15 @@ 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
&& oap->motion_force == NUL
&& oap->op_type == OP_DELETE) {
- ptr = ml_get(oap->end.lnum) + oap->end.col;
+ ptr = (char_u *)ml_get(oap->end.lnum) + oap->end.col;
if (*ptr != NUL) {
ptr += oap->inclusive;
}
@@ -1523,10 +1499,8 @@ int op_delete(oparg_T *oap)
}
}
- /*
- * 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
@@ -1544,11 +1518,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;
@@ -1592,9 +1564,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) {
@@ -1618,7 +1588,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
@@ -1628,7 +1598,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,
@@ -1662,7 +1632,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,
@@ -1735,8 +1705,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
@@ -1816,7 +1786,7 @@ setmarks:
static void mb_adjust_opend(oparg_T *oap)
{
if (oap->inclusive) {
- char *p = (char *)ml_get(oap->end.lnum);
+ char *p = ml_get(oap->end.lnum);
oap->end.col += utf_cp_tail_off(p, p + oap->end.col);
}
}
@@ -1825,7 +1795,7 @@ static void mb_adjust_opend(oparg_T *oap)
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);
}
@@ -1849,10 +1819,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) {
@@ -1873,9 +1843,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++) {
@@ -1927,7 +1895,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')) {
@@ -1953,7 +1921,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
@@ -1966,16 +1934,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);
@@ -1990,7 +1958,7 @@ 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--;
}
@@ -2001,10 +1969,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
@@ -2013,6 +1983,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;
@@ -2028,9 +1999,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
@@ -2104,7 +2080,7 @@ 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--;
}
@@ -2119,7 +2095,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;
}
@@ -2162,7 +2138,7 @@ static int swapchars(int op_type, pos_T *pos, int length)
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) {
@@ -2225,7 +2201,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 {
@@ -2240,7 +2216,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;
@@ -2252,7 +2228,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(UPD_INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
+ update_screen();
if (oap->motion_type == kMTBlockWise) {
// When 'virtualedit' is used, need to insert the extra spaces before
@@ -2270,7 +2247,7 @@ 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--;
}
@@ -2286,7 +2263,7 @@ 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) {
@@ -2409,12 +2386,10 @@ void op_insert(oparg_T *oap, long count1)
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) {
@@ -2436,9 +2411,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,10 +2438,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;
@@ -2499,8 +2474,8 @@ int op_change(oparg_T *oap)
coladvance_force(getviscol());
}
firstline = ml_get(oap->start.lnum);
- pre_textlen = (long)STRLEN(firstline);
- pre_indent = (long)getwhitecols((char *)firstline);
+ pre_textlen = (long)strlen(firstline);
+ pre_indent = (long)getwhitecols(firstline);
bd.textcol = curwin->w_cursor.col;
}
@@ -2508,31 +2483,35 @@ int op_change(oparg_T *oap)
fix_indent();
}
+ // 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
// the indent, exclude that indent change from the inserted text.
firstline = ml_get(oap->start.lnum);
if (bd.textcol > (colnr_T)pre_indent) {
- long new_indent = (long)getwhitecols((char *)firstline);
+ long new_indent = (long)getwhitecols(firstline);
pre_textlen += new_indent - pre_indent;
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);
@@ -2548,7 +2527,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);
@@ -2559,7 +2538,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);
}
@@ -2639,8 +2618,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
@@ -2691,7 +2670,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: {
@@ -2707,8 +2686,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++;
@@ -2741,7 +2719,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) {
@@ -2776,13 +2754,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;
@@ -2809,7 +2787,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),
@@ -2845,8 +2826,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);
@@ -2934,9 +2915,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;
@@ -2973,10 +2954,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');
@@ -3025,14 +3004,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);
@@ -3056,10 +3035,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;
@@ -3077,18 +3054,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;
@@ -3136,12 +3113,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();
@@ -3149,8 +3126,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;
}
@@ -3209,7 +3186,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) {
@@ -3232,9 +3209,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;
@@ -3247,7 +3222,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);
@@ -3296,10 +3271,9 @@ 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);
+ oldlen = strlen(oldp);
chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0,
- oldp, oldp);
+ 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)
@@ -3307,13 +3281,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
cts.cts_vcol += incr;
}
vcol = cts.cts_vcol;
- ptr = (char_u *)cts.cts_ptr;
+ 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;
@@ -3322,22 +3296,21 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
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,
- (char_u *)y_array[i], (char_u *)y_array[i]);
+ init_chartabsize_arg(&cts, curwin, 0, 0, y_array[i], y_array[i]);
for (int j = 0; j < yanklen; j++) {
spaces -= lbr_chartabsize(&cts);
cts.cts_ptr++;
@@ -3357,9 +3330,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;
@@ -3392,7 +3364,7 @@ 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);
@@ -3412,6 +3384,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;
@@ -3420,7 +3395,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;
}
@@ -3433,7 +3408,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;
@@ -3484,7 +3459,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,
@@ -3499,7 +3474,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++) {
@@ -3507,7 +3482,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);
@@ -3555,22 +3530,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;
@@ -3590,7 +3565,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
@@ -3607,7 +3582,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));
}
}
}
@@ -3617,9 +3592,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) {
@@ -3663,13 +3638,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;
@@ -3788,10 +3763,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;
@@ -3813,7 +3788,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
}
@@ -3857,67 +3832,66 @@ 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);
- msg_outtrans_len((char *)p, clen);
+ 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;
}
}
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
- && !message_filtered((char *)p)) {
+ 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((char_u *)last_cmdline, false);
+ dis_msg(last_cmdline, false);
}
// display current file name
if (curbuf->b_fname != NULL
- && (arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int
+ && (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(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((char *)last_search_pat())) {
msg_puts("\n c \"/ ");
- dis_msg(last_search_pat(), false);
+ dis_msg((char *)last_search_pat(), false);
}
// display last used expression
- if (expr_line != NULL && (arg == NULL || vim_strchr((char *)arg, '=') != NULL)
- && !got_int && !message_filtered((char *)expr_line)) {
+ 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);
}
@@ -3927,7 +3901,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;
@@ -3936,12 +3910,12 @@ 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) {
- msg_outtrans_len((char *)p, l);
+ && (n -= ptr2cells(p)) >= 0) {
+ if ((l = utfc_ptr2len(p)) > 1) {
+ msg_outtrans_len(p, l);
p += l;
} else {
- msg_outtrans_len((char *)p++, 1);
+ msg_outtrans_len(p++, 1);
}
}
os_breakcheck();
@@ -3958,11 +3932,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) {
@@ -3984,7 +3958,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;
@@ -4022,11 +3996,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
@@ -4055,18 +4029,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 {
@@ -4075,17 +4049,17 @@ 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;
@@ -4106,16 +4080,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();
@@ -4129,17 +4103,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++;
@@ -4165,17 +4137,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.
@@ -4188,11 +4161,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);
@@ -4200,11 +4171,9 @@ 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();
@@ -4220,6 +4189,29 @@ theend:
return ret;
}
+/// Reset 'linebreak' and take care of side effects.
+/// @return the previous value, to be passed to restore_lbr().
+static bool reset_lbr(void)
+{
+ if (!curwin->w_p_lbr) {
+ return false;
+ }
+ // 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;
+}
+
+/// Restore 'linebreak' and take care of side effects.
+static void restore_lbr(bool lbr_saved)
+{
+ if (!curwin->w_p_lbr && lbr_saved) {
+ // 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
///
/// for delete:
@@ -4234,15 +4226,14 @@ theend:
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;
@@ -4271,11 +4262,11 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
bdp->pre_whitesp = 0;
bdp->pre_whitesp_c = 0;
}
- prev_pstart = (char_u *)cts.cts_ptr;
+ prev_pstart = cts.cts_ptr;
MB_PTR_ADV(cts.cts_ptr);
}
bdp->start_vcol = cts.cts_vcol;
- pstart = (char_u *)cts.cts_ptr;
+ pstart = cts.cts_ptr;
clear_chartabsize_arg(&cts);
bdp->start_char_vcols = incr;
@@ -4313,17 +4304,16 @@ 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);
+ init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol, line, pend);
prev_pend = pend;
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 = (char_u *)cts.cts_ptr;
+ prev_pend = cts.cts_ptr;
incr = lbr_chartabsize_adv(&cts);
cts.cts_vcol += incr;
}
bdp->end_vcol = cts.cts_vcol;
- pend = (char_u *)cts.cts_ptr;
+ pend = cts.cts_ptr;
clear_chartabsize_arg(&cts);
if (bdp->end_vcol <= oap->end_vcol
@@ -4357,7 +4347,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.
@@ -4409,20 +4399,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;
}
@@ -4479,12 +4469,12 @@ 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 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;
@@ -4514,7 +4504,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;
}
@@ -4586,7 +4576,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;
@@ -4605,7 +4595,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;
@@ -4654,7 +4644,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);
}
@@ -4750,7 +4740,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
// When there are many leading zeros it could be very long.
// Allocate a bit too much.
buf1 = xmalloc((size_t)length + NUMBUFLEN);
- ptr = buf1;
+ ptr = (char *)buf1;
if (negative && (!visual || was_positive)) {
*ptr++ = '-';
}
@@ -4759,7 +4749,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--;
}
@@ -4781,15 +4771,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.
@@ -4900,13 +4890,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;
}
@@ -4948,9 +4938,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);
@@ -4967,16 +4957,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++;
}
@@ -4984,18 +4970,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';
}
@@ -5036,7 +5018,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);
}
@@ -5045,9 +5027,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"));
@@ -5067,7 +5049,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);
}
@@ -5090,16 +5072,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((char_u *)str, RE_SEARCH, true, true);
return;
}
@@ -5107,14 +5089,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) {
@@ -5131,7 +5113,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;
@@ -5155,7 +5137,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);
}
@@ -5221,8 +5203,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;
@@ -5241,7 +5223,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str,
}
// 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);
@@ -5329,7 +5311,7 @@ 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 *p;
char_u buf1[50];
char_u buf2[40];
linenr_T lnum;
@@ -5413,7 +5395,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) {
@@ -5440,12 +5422,12 @@ void cursor_pos_info(dict_T *dict)
break;
}
if (s != NULL) {
- byte_count_cursor += line_count_info(s, &word_count_cursor,
+ byte_count_cursor += line_count_info((char_u *)s, &word_count_cursor,
&char_count_cursor, len, eol_size);
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;
}
}
@@ -5455,14 +5437,14 @@ void cursor_pos_info(dict_T *dict)
word_count_cursor += word_count;
char_count_cursor += char_count;
byte_count_cursor = byte_count
- + line_count_info(ml_get(lnum), &word_count_cursor,
+ + line_count_info((char_u *)ml_get(lnum), &word_count_cursor,
&char_count_cursor,
(varnumber_T)curwin->w_cursor.col + 1,
eol_size);
}
}
// Add to the running totals
- byte_count += line_count_info(ml_get(lnum), &word_count, &char_count,
+ byte_count += line_count_info((char_u *)ml_get(lnum), &word_count, &char_count,
(varnumber_T)MAXCOL, eol_size);
}
@@ -5485,7 +5467,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"),
@@ -5494,7 +5476,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;"
@@ -5510,11 +5492,11 @@ void cursor_pos_info(dict_T *dict)
validate_virtcol();
col_print((char *)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 ""),
@@ -5524,7 +5506,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,
_("Col %s of %s; Line %" PRId64 " of %" PRId64 ";"
" Word %" PRId64 " of %" PRId64 ";"
" Char %" PRId64 " of %" PRId64 ";"
@@ -5541,20 +5523,20 @@ 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 = (char_u *)p_shm;
+ p = p_shm;
p_shm = "";
if (p_ch < 1) {
msg_start();
msg_scroll = true;
}
- msg((char *)IObuff);
- p_shm = (char *)p;
+ msg(IObuff);
+ p_shm = p;
}
}
@@ -5588,13 +5570,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 {
@@ -5629,7 +5620,7 @@ static Callback opfunc_cb;
/// @return OK or FAIL
int set_operatorfunc_option(void)
{
- return option_set_callback_func((char_u *)p_opfunc, &opfunc_cb);
+ return option_set_callback_func(p_opfunc, &opfunc_cb);
}
#if defined(EXITFREE)
@@ -5639,12 +5630,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;
@@ -5671,9 +5667,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;
@@ -5790,10 +5788,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;
@@ -5900,10 +5895,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') {
@@ -5933,7 +5928,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;
@@ -5951,7 +5946,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;
@@ -6082,7 +6077,7 @@ 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;
+ restore_lbr(lbr_saved);
redraw_curbuf_later(UPD_INVERTED);
}
}
@@ -6090,7 +6085,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
// Include the trailing byte of a multi-byte char.
if (oap->inclusive) {
- const int l = utfc_ptr2len((char *)ml_get_pos(&oap->end));
+ const int l = utfc_ptr2len(ml_get_pos(&oap->end));
if (l > 1) {
oap->end.col += l - 1;
}
@@ -6114,7 +6109,7 @@ 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;
+ restore_lbr(lbr_saved);
redraw_curbuf_later(UPD_INVERTED);
}
@@ -6135,7 +6130,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;
@@ -6190,7 +6185,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);
}
@@ -6214,10 +6209,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;
}
@@ -6241,7 +6234,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,
@@ -6285,9 +6282,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);
@@ -6311,12 +6307,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.
@@ -6337,7 +6333,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);
}
@@ -6375,7 +6371,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;
}
@@ -6390,7 +6386,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 {
@@ -6399,7 +6395,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
@@ -6437,7 +6433,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
@@ -6529,7 +6525,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) {
@@ -6550,7 +6546,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;
}
@@ -6593,8 +6589,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]) {
@@ -6637,7 +6633,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) {
@@ -6656,7 +6652,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;
}
@@ -6906,13 +6902,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 9bc5409ff9..c44ed036f8 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -21,14 +21,18 @@
#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"
@@ -36,23 +40,26 @@
#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/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"
@@ -65,30 +72,32 @@
#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"
@@ -106,34 +115,6 @@ static char e_number_required_after_equal[]
static char e_preview_window_already_exists[]
= N_("E590: A preview window already exists");
-/*
- * 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 *p_term = NULL;
static char *p_ttytype = NULL;
@@ -151,27 +132,12 @@ static long p_tw_nopaste;
static long p_wm_nopaste;
static char *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.
- */
+// 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"
@@ -179,10 +145,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.
@@ -197,10 +177,8 @@ void set_init_1(bool clean_arg)
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) {
@@ -215,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" };
@@ -240,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]);
@@ -252,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)) {
@@ -275,15 +251,15 @@ void set_init_1(bool clean_arg)
}
{
- char_u *cdpath;
- char_u *buf;
+ char *cdpath;
+ char *buf;
int i;
int j;
// Initialize the 'cdpath' option's default value.
- cdpath = (char_u *)vim_getenv("CDPATH");
+ cdpath = vim_getenv("CDPATH");
if (cdpath != NULL) {
- buf = xmalloc(2 * STRLEN(cdpath) + 2);
+ buf = xmalloc(2 * strlen(cdpath) + 2);
{
buf[0] = ','; // start with ",", current dir first
j = 1;
@@ -310,28 +286,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);
@@ -354,10 +308,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;
@@ -380,15 +332,13 @@ 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.
- */
+ // 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) {
continue;
@@ -398,7 +348,7 @@ void set_init_1(bool clean_arg)
&& options[opt_idx].var != NULL) {
p = _(*(char **)options[opt_idx].var);
} else {
- p = (char *)option_expand(opt_idx, NULL);
+ p = option_expand(opt_idx, NULL);
}
if (p != NULL) {
p = xstrdup(p);
@@ -406,7 +356,7 @@ void set_init_1(bool clean_arg)
if (options[opt_idx].flags & P_DEF_ALLOCED) {
xfree(options[opt_idx].def_val);
}
- options[opt_idx].def_val = (char_u *)p;
+ options[opt_idx].def_val = p;
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
@@ -434,7 +384,7 @@ void set_init_1(bool clean_arg)
// use utf-8 as 'default' if locale encoding can't be detected.
p = (char_u *)xmemdupz(S_LEN("utf-8"));
}
- fenc_default = p;
+ fenc_default = (char *)p;
#ifdef HAVE_WORKING_LIBINTL
// GNU gettext 0.10.37 supports this feature: set the codeset used for
@@ -463,12 +413,12 @@ static void set_option_default(int opt_idx, int opt_flags)
// freeing and allocating the value.
if (options[opt_idx].indir != PV_NONE) {
set_string_option_direct(NULL, opt_idx,
- (char *)options[opt_idx].def_val, opt_flags, 0);
+ options[opt_idx].def_val, opt_flags, 0);
} else {
if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
free_string_option(*(char **)(varp));
}
- *(char_u **)varp = options[opt_idx].def_val;
+ *(char **)varp = options[opt_idx].def_val;
options[opt_idx].flags &= ~P_ALLOCED;
}
} else if (flags & P_NUM) {
@@ -547,16 +497,14 @@ static void set_string_default(const char *name, char *val, bool allocated)
xfree(options[opt_idx].def_val);
}
- options[opt_idx].def_val = allocated
- ? (char_u *)val
- : (char_u *)xstrdup(val);
+ options[opt_idx].def_val = allocated ? val : xstrdup(val);
options[opt_idx].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;
@@ -565,10 +513,10 @@ static char_u *find_dup_item(char_u *origval, const char_u *newval, uint32_t fla
return NULL;
}
- const size_t newlen = STRLEN(newval);
- for (char_u *s = origval; *s != NUL; s++) {
+ 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;
}
@@ -589,11 +537,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;
}
}
@@ -608,7 +554,7 @@ void free_all_options(void)
free_string_option(*(char **)options[i].var);
}
if (options[i].flags & P_DEF_ALLOCED) {
- free_string_option((char *)options[i].def_val);
+ 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
@@ -616,6 +562,7 @@ void free_all_options(void)
}
}
free_operatorfunc_option();
+ free_tagfunc_option();
}
#endif
@@ -635,15 +582,12 @@ void set_init_2(bool headless)
}
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
@@ -664,42 +608,42 @@ 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 = "|& tee";
- options[idx_sp].def_val = (char_u *)p_sp;
+ options[idx_sp].def_val = p_sp;
}
if (do_srr) {
p_srr = ">&";
- options[idx_srr].def_val = (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 = "2>&1| tee";
- options[idx_sp].def_val = (char_u *)p_sp;
+ options[idx_sp].def_val = p_sp;
}
if (do_srr) {
p_srr = ">%s 2>&1";
- options[idx_srr].def_val = (char_u *)p_srr;
+ options[idx_srr].def_val = p_srr;
}
}
xfree(p);
@@ -732,14 +676,14 @@ void set_helplang_default(const char *lang)
int idx = findoption("hlg");
if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
if (options[idx].flags & P_ALLOCED) {
- free_string_option((char *)p_hlg);
+ free_string_option(p_hlg);
}
- p_hlg = (char_u *)xmemdupz(lang, lang_len);
+ 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_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') {
+ 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';
@@ -756,21 +700,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;
}
}
@@ -790,6 +730,347 @@ 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 *oldval = NULL; // previous value if *varp
+ char *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;
+ 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.
+ 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;
+ }
+ }
+
+ // 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, *s) != NULL) {
+ // Remove the duplicated value and the next comma.
+ STRMOVE(s, s + 2);
+ continue;
+ }
+ } else {
+ if ((!(flags & P_COMMA) || *s != ',')
+ && vim_strchr(s + 1, *s) != NULL) {
+ 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.
+ saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
+ saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : NULL;
+ saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : NULL;
+
+ // newval (and varp) may become invalid if the buffer is closed by
+ // autocommands.
+ 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
@@ -807,23 +1088,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 *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);
@@ -831,16 +1096,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 = 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 && !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++;
@@ -855,17 +1120,19 @@ int do_set(char *arg, int 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_>;>
@@ -886,7 +1153,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;
@@ -900,40 +1167,41 @@ 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 = 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
@@ -1016,9 +1284,7 @@ int do_set(char *arg, int opt_flags)
|| (prefix == 1
&& vim_strchr("=:&<", nextchar) == NULL
&& !(flags & P_BOOL))) {
- /*
- * print value
- */
+ // print value
if (did_show) {
msg_putchar('\n'); // cursor below last one
} else {
@@ -1048,8 +1314,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 == ':') {
@@ -1057,11 +1323,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 == '&') {
@@ -1076,10 +1340,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;
@@ -1130,8 +1392,9 @@ int do_set(char *arg, int opt_flags)
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 = e_number_required_after_equal;
goto skip;
@@ -1141,373 +1404,26 @@ int do_set(char *arg, int opt_flags)
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 == (char_u *)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 = (char_u *)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 **)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 **)varp, (char *)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
@@ -1516,18 +1432,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++;
@@ -1541,8 +1455,8 @@ skip:
}
if (errmsg != NULL) {
- STRLCPY(IObuff, _(errmsg), IOSIZE);
- i = (int)STRLEN(IObuff) + 2;
+ xstrlcpy(IObuff, _(errmsg), IOSIZE);
+ int i = (int)strlen(IObuff) + 2;
if (i + (arg - startarg) < IOSIZE) {
// append the argument with the error
STRCAT(IObuff, ": ");
@@ -1551,10 +1465,10 @@ skip:
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
+ emsg(IObuff); // show error highlighted
no_wait_return--;
return FAIL;
@@ -1567,11 +1481,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;
@@ -1605,7 +1519,7 @@ void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked
int string_to_key(char_u *arg)
{
if (*arg == '<') {
- return find_key_option(arg + 1, true);
+ return find_key_option((char *)arg + 1, true);
}
if (*arg == '^') {
return CTRL_CHR(arg[1]);
@@ -1629,10 +1543,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)) {
@@ -1685,9 +1597,7 @@ 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_u *p = find_shada_parameter(type);
if (p != NULL && ascii_isdigit(*p)) {
return atoi((char *)p);
}
@@ -1718,7 +1628,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) {
@@ -1726,30 +1636,28 @@ 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:".
- */
- expand_env_esc(val, (char_u *)NameBuff, MAXPATHL,
+ // 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 == (char_u **)&p_sps ? (char_u *)"file:" :
+ (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;
}
- return (char_u *)NameBuff;
+ return NameBuff;
}
/// After setting various option values: recompute variables that depend on
@@ -1930,15 +1838,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) {
@@ -1953,6 +1862,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.
@@ -2040,14 +1989,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
@@ -2107,10 +2054,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 = '\\';
@@ -2125,9 +2071,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;
@@ -2149,13 +2094,14 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
if (curwin->w_p_spell) {
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) {
@@ -2172,7 +2118,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// 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));
@@ -2186,9 +2132,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// Force-set the necessary keymap for arabic.
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) {
@@ -2209,48 +2153,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),
@@ -2347,6 +2259,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) {
@@ -2405,7 +2319,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) {
@@ -2539,7 +2453,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) {
@@ -2615,9 +2531,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")) {
@@ -2657,41 +2574,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),
@@ -2711,14 +2595,17 @@ 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)
{
- // 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();
}
@@ -2728,9 +2615,7 @@ void check_redraw(uint32_t flags)
if (flags & P_RWINONLY) {
redraw_later(curwin, UPD_NOT_VALID);
}
- if (doclear) {
- redraw_all_later(UPD_CLEAR);
- } else if (all) {
+ if (all) {
redraw_all_later(UPD_NOT_VALID);
}
}
@@ -2744,15 +2629,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;
@@ -2797,11 +2681,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);
}
}
@@ -2910,6 +2794,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.
@@ -2919,7 +2804,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;
@@ -2930,7 +2816,12 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, i
return gov_unknown;
}
- char_u *varp = (char_u *)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
@@ -3021,15 +2912,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;
}
}
@@ -3040,9 +2929,8 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
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
@@ -3076,7 +2964,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 {
@@ -3087,47 +2975,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 information for option at 'opt_idx'
+vimoption_T *get_option(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)
-{
- 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
@@ -3149,10 +3000,7 @@ 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 {
@@ -3170,7 +3018,7 @@ char *set_option_value(const char *const name, const long number, const char *co
return set_string_option(opt_idx, s, opt_flags);
}
- varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
+ char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
if (varp != NULL) { // hidden option is not changed
if (number == 0 && string != NULL) {
int idx;
@@ -3197,14 +3045,13 @@ char *set_option_value(const char *const name, const long number, const char *co
numval = -1;
} else {
char *s = NULL;
- (void)get_option_value(name, &numval, &s, OPT_GLOBAL);
+ (void)get_option_value(name, &numval, &s, NULL, 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_bool_option(opt_idx, varp, (int)numval, opt_flags);
}
}
return NULL;
@@ -3222,6 +3069,12 @@ void set_option_value_give_err(const char *name, long number, const char *string
}
}
+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.
/// Returns false if option "name" does not exist.
bool is_string_option(const char *name)
@@ -3236,16 +3089,15 @@ bool is_string_option(const char *name)
int find_key_option_len(const char_u *arg_arg, size_t len, bool has_lt)
{
int key = 0;
- int modifiers;
- const char_u *arg = arg_arg;
+ const char *arg = (char *)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
@@ -3255,9 +3107,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((char_u *)arg, strlen(arg), has_lt);
}
/// if 'all' == 0: show changed options
@@ -3266,16 +3118,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
@@ -3294,16 +3136,16 @@ 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(p->fullname)) {
continue;
}
- varp = NULL;
+ char_u *varp = NULL;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
if (p->indir != PV_NONE) {
varp = (char_u *)get_varp_scope(p, opt_flags);
@@ -3313,13 +3155,14 @@ static void showoptions(int all, int opt_flags)
}
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)) {
@@ -3328,15 +3171,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;
}
@@ -3344,18 +3187,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();
}
}
@@ -3363,7 +3205,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
@@ -3375,7 +3217,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
@@ -3413,7 +3255,7 @@ static void showoneopt(vimoption_T *p, int opt_flags)
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()
char_u *varp = (char_u *)get_varp_scope(p, opt_flags);
@@ -3431,7 +3273,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;
@@ -3460,25 +3302,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 *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
@@ -3492,7 +3324,7 @@ 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;
@@ -3507,7 +3339,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
@@ -3517,7 +3350,7 @@ 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 = (char_u *)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 = (char_u *)varp;
@@ -3530,6 +3363,7 @@ 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 = (char *)varp_local, round++) {
+ char *cmd;
if (round == 1 || (opt_flags & OPT_GLOBAL)) {
cmd = "set";
} else {
@@ -3593,10 +3427,8 @@ int makefoldset(FILE *fd)
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;
@@ -3606,16 +3438,14 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_
// options some characters have to be escaped with
// CTRL-V or backslash
if (valuep == &p_pt) {
- s = (char_u *)(*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_u *)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);
@@ -3632,7 +3462,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_
if (put_eol(fd) == FAIL) {
goto fail;
}
- p = (char *)buf;
+ char *p = (char *)buf;
while (*p != NUL) {
// for each comma separated option part, append value to
// the option, :set rtp+=value
@@ -3713,8 +3543,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);
@@ -3804,19 +3633,24 @@ void unset_global_local_option(char *name, void *from)
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 *get_varp_scope(vimoption_T *p, int opt_flags)
+///
+/// @param scope can be OPT_LOCAL, OPT_GLOBAL or a combination.
+char *get_varp_scope(vimoption_T *p, int scope)
{
- if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
+ if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
return GLOBAL_WO(get_varp(p));
}
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 *)&(curbuf->b_p_fp);
@@ -3880,13 +3714,6 @@ static char *get_varp_scope(vimoption_T *p, int opt_flags)
return (char *)get_varp(p);
}
-/// Get pointer to option variable at 'opt_idx', depending on local or global
-/// scope.
-char *get_option_varp_scope(int opt_idx, int opt_flags)
-{
- return get_varp_scope(&(options[opt_idx]), opt_flags);
-}
-
/// Get pointer to option variable.
static char_u *get_varp(vimoption_T *p)
{
@@ -4093,6 +3920,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_cfu);
case PV_OFU:
return (char_u *)&(curbuf->b_p_ofu);
+ case PV_EOF:
+ return (char_u *)&(curbuf->b_p_eof);
case PV_EOL:
return (char_u *)&(curbuf->b_p_eol);
case PV_FIXEOL:
@@ -4127,6 +3956,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_fex);
case PV_LISP:
return (char_u *)&(curbuf->b_p_lisp);
+ case PV_LOP:
+ return (char_u *)&(curbuf->b_p_lop);
case PV_ML:
return (char_u *)&(curbuf->b_p_ml);
case PV_MPS:
@@ -4191,6 +4022,8 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curwin->w_p_winhl);
case PV_WINBL:
return (char_u *)&(curwin->w_p_winbl);
+ case PV_STC:
+ return (char_u *)&(curwin->w_p_stc);
default:
iemsg(_("E356: get_varp ERROR"));
}
@@ -4198,18 +4031,6 @@ static char_u *get_varp(vimoption_T *p)
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 the full name of the option at 'opt_idx'
-char *get_option_fullname(int opt_idx)
-{
- return options[opt_idx].fullname;
-}
-
/// Get the value of 'equalprg', either the buffer-local one or the global one.
char_u *get_equalprg(void)
{
@@ -4291,6 +4112,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
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));
@@ -4328,6 +4150,7 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_ve);
check_string_option(&wop->wo_wbr);
+ check_string_option(&wop->wo_stc);
}
/// Free the allocated memory inside a winopt_T.
@@ -4354,6 +4177,7 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_ve);
clear_string_option(&wop->wo_wbr);
+ clear_string_option(&wop->wo_stc);
}
void didset_window_options(win_T *wp, bool valid_cursor)
@@ -4400,13 +4224,10 @@ 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;
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'.
@@ -4433,7 +4254,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 = (char_u *)buf->b_p_isk;
buf->b_p_isk = NULL;
@@ -4503,17 +4324,20 @@ void buf_copy_options(buf_T *buf, int flags)
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 = xstrdup(p_cfu);
COPY_OPT_SCTX(buf, BV_CFU);
+ set_buflocal_cfu_callback(buf);
buf->b_p_ofu = xstrdup(p_ofu);
COPY_OPT_SCTX(buf, BV_OFU);
+ set_buflocal_ofu_callback(buf);
buf->b_p_tfu = xstrdup(p_tfu);
COPY_OPT_SCTX(buf, BV_TFU);
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;
@@ -4551,6 +4375,8 @@ void buf_copy_options(buf_T *buf, int flags)
COPY_OPT_SCTX(buf, BV_CINO);
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;
@@ -4622,12 +4448,10 @@ void buf_copy_options(buf_T *buf, int flags)
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 = (char *)save_p_isk;
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
@@ -4657,10 +4481,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;
}
@@ -4702,30 +4524,27 @@ 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;
+ char *p;
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;
+ 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) == '\\') {
@@ -4739,23 +4558,26 @@ 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;
+
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;
@@ -4775,8 +4597,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 == '*') {
@@ -4815,7 +4637,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;
@@ -4823,30 +4645,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;
@@ -4856,24 +4677,24 @@ 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;
}
}
@@ -4882,17 +4703,15 @@ void set_context_in_set_cmd(expand_T *xp, char_u *arg, int opt_flags)
int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
{
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
// 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);
@@ -4906,7 +4725,8 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
}
}
}
- 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;
@@ -4916,7 +4736,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
continue;
}
match = false;
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)
+ if (vim_regexec(regmatch, str, (colnr_T)0)
|| (options[opt_idx].shortname != NULL
&& vim_regexec(regmatch,
options[opt_idx].shortname,
@@ -4928,7 +4748,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
if (loop == 0) {
num_normal++;
} else {
- (*file)[count++] = (char *)vim_strsave(str);
+ (*file)[count++] = xstrdup(str);
}
}
}
@@ -4939,7 +4759,7 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
} else {
return OK;
}
- *file = xmalloc((size_t)(*num_file) * sizeof(char_u *));
+ *file = xmalloc((size_t)(*num_file) * sizeof(char *));
}
}
return OK;
@@ -4947,14 +4767,12 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***fi
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);
}
@@ -4962,14 +4780,14 @@ void ExpandOldSetting(int *num_file, char ***file)
if (expand_option_idx >= 0) {
// Put string of option value in NameBuff.
option_value2string(&options[expand_option_idx], expand_option_flags);
- var = (char_u *)NameBuff;
+ 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_u *buf = (char_u *)vim_strsave_escaped(var, (char *)escape_chars);
#ifdef BACKSLASH_IN_FILENAME
// For MS-Windows et al. we don't double backslashes at the start and
@@ -4978,7 +4796,7 @@ 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);
}
@@ -4993,34 +4811,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 = (char_u *)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, (char *)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 **)opp->var == &p_pt) {
- str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL);
+ str2specialbuf((const char *)p_pt, NameBuff, MAXPATHL);
} else {
- STRLCPY(NameBuff, varp, MAXPATHL);
+ xstrlcpy(NameBuff, varp, MAXPATHL);
}
}
}
@@ -5028,7 +4846,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;
@@ -5060,10 +4878,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) {
@@ -5232,15 +5048,12 @@ void reset_option_was_set(const char *name)
/// 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 = (char_u *)p_breakat; *p; p++) {
+ for (char_u *p = (char_u *)p_breakat; *p; p++) {
breakat_flags[*p] = true;
}
}
@@ -5258,16 +5071,16 @@ int fill_culopt_flags(char *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;
}
@@ -5289,10 +5102,24 @@ int fill_culopt_flags(char *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);
@@ -5301,10 +5128,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;
}
@@ -5312,11 +5139,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;
}
@@ -5355,6 +5182,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)
{
@@ -5371,7 +5209,7 @@ char_u *get_showbreak_value(win_T *const win)
if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) {
return (char_u *)p_sbr;
}
- if (STRCMP(win->w_p_sbr, "NONE") == 0) {
+ if (strcmp(win->w_p_sbr, "NONE") == 0) {
return (char_u *)empty_option;
}
return (char_u *)win->w_p_sbr;
@@ -5517,13 +5355,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
@@ -5681,7 +5519,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 78cb324fe4..8ad1b8b2d7 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[]) { \
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,9 +384,7 @@ 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 *p_ambw; ///< 'ambiwidth'
@@ -472,15 +475,6 @@ EXTERN long p_ph; // 'pumheight'
EXTERN long p_pw; // 'pumwidth'
EXTERN char *p_com; ///< 'comments'
EXTERN char *p_cpo; // 'cpoptions'
-EXTERN char *p_csprg; // 'cscopeprg'
-EXTERN int p_csre; // 'cscoperelative'
-EXTERN char *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 *p_debug; // 'debug'
EXTERN char *p_def; // 'define'
EXTERN char *p_inc;
@@ -494,7 +488,7 @@ 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 *p_ead; // 'eadirection'
@@ -505,7 +499,8 @@ 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_gp; // 'grepprg'
+EXTERN int p_eof; ///< 'endoffile'
EXTERN int p_eol; ///< 'endofline'
EXTERN char *p_ei; // 'eventignore'
EXTERN int p_et; ///< 'expandtab'
@@ -539,20 +534,12 @@ EXTERN char *p_fo; ///< 'formatoptions'
EXTERN char_u *p_fp; // 'formatprg'
EXTERN int p_fs; // 'fsync'
EXTERN int p_gd; // 'gdefault'
-EXTERN char_u *p_pdev; // 'printdevice'
-EXTERN char *p_penc; // 'printencoding'
-EXTERN char *p_pexpr; // 'printexpr'
-EXTERN char *p_pmfn; // 'printmbfont'
-EXTERN char *p_pmcs; // 'printmbcharset'
-EXTERN char *p_pfn; // 'printfont'
-EXTERN char *p_popt; // 'printoptions'
-EXTERN char_u *p_header; // 'printheader'
EXTERN char *p_guicursor; // 'guicursor'
EXTERN char_u *p_guifont; // 'guifont'
EXTERN char_u *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 *p_hl; // 'highlight'
EXTERN int p_hls; // 'hlsearch'
@@ -590,6 +577,7 @@ EXTERN char_u *p_lm; // 'langmenu'
EXTERN long p_lines; // 'lines'
EXTERN long p_linespace; // 'linespace'
EXTERN int p_lisp; ///< 'lisp'
+EXTERN char *p_lop; ///< 'lispoptions'
EXTERN char_u *p_lispwords; // 'lispwords'
EXTERN long p_ls; // 'laststatus'
EXTERN long p_stal; // 'showtabline'
@@ -602,8 +590,6 @@ EXTERN char *p_menc; // 'makeencoding'
EXTERN char *p_mef; // 'makeef'
EXTERN char_u *p_mp; // 'makeprg'
EXTERN char *p_mps; ///< 'matchpairs'
-EXTERN char_u *p_cc; // 'colorcolumn'
-EXTERN int p_cc_cols[256]; // array for 'colorcolumn' columns
EXTERN long p_mat; // 'matchtime'
EXTERN long p_mco; // 'maxcombine'
EXTERN long p_mfd; // 'maxfuncdepth'
@@ -618,6 +604,7 @@ EXTERN int p_ma; ///< 'modifiable'
EXTERN int p_mod; ///< 'modified'
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);
@@ -629,7 +616,7 @@ EXTERN char *p_opfunc; // 'operatorfunc'
EXTERN char_u *p_para; // 'paragraphs'
EXTERN int p_paste; // 'paste'
EXTERN char *p_pt; // 'pastetoggle'
-EXTERN char_u *p_pex; // 'patchexpr'
+EXTERN char *p_pex; // 'patchexpr'
EXTERN char *p_pm; // 'patchmode'
EXTERN char_u *p_path; // 'path'
EXTERN char_u *p_cdpath; // 'cdpath'
@@ -685,11 +672,11 @@ EXTERN unsigned ssop_flags;
#define SSOP_TERMINAL 0x10000
#define SSOP_SKIP_RTP 0x20000
-EXTERN char_u *p_sh; // 'shell'
+EXTERN char *p_sh; // 'shell'
EXTERN char_u *p_shcf; // 'shellcmdflag'
EXTERN char *p_sp; // 'shellpipe'
-EXTERN char_u *p_shq; // 'shellquote'
-EXTERN char_u *p_sxq; // 'shellxquote'
+EXTERN char *p_shq; // 'shellquote'
+EXTERN char *p_sxq; // 'shellxquote'
EXTERN char_u *p_sxe; // 'shellxescape'
EXTERN char *p_srr; // 'shellredir'
EXTERN int p_stmp; // 'shelltemp'
@@ -703,6 +690,7 @@ EXTERN long p_sw; ///< 'shiftwidth'
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'
@@ -731,8 +719,10 @@ EXTERN char *p_tfu; ///< 'tagfunc'
EXTERN char *p_umf; ///< 'usermarkfunc'
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'
@@ -783,7 +773,7 @@ EXTERN char *p_shada; ///< 'shada'
EXTERN char *p_shadafile; ///< 'shadafile'
EXTERN char *p_vsts; ///< 'varsofttabstop'
EXTERN char *p_vts; ///< 'vartabstop'
-EXTERN char_u *p_vdir; ///< 'viewdir'
+EXTERN char *p_vdir; ///< 'viewdir'
EXTERN char *p_vop; ///< 'viewoptions'
EXTERN unsigned vop_flags; ///< uses SSOP_ flags
EXTERN int p_vb; ///< 'visualbell'
@@ -864,6 +854,7 @@ enum {
BV_CFU,
BV_DEF,
BV_INC,
+ BV_EOF,
BV_EOL,
BV_FIXEOL,
BV_EP,
@@ -886,6 +877,7 @@ enum {
BV_KMAP,
BV_KP,
BV_LISP,
+ BV_LOP,
BV_LW,
BV_MENC,
BV_MA,
@@ -926,11 +918,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,
@@ -968,6 +958,7 @@ enum {
WV_CULOPT,
WV_CC,
WV_SBR,
+ WV_STC,
WV_STL,
WV_WFH,
WV_WFW,
@@ -994,4 +985,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 1bdfcbecb9..a97e9b7c7e 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -2,14 +2,13 @@
// 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 <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,27 +16,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/ex_getln.h"
-#include "nvim/hardcopy.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"
@@ -75,6 +87,7 @@ 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 };
@@ -107,16 +120,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 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, };
+ SHM_COMPLETIONMENU, SHM_COMPLETIONSCAN, SHM_RECORDING, SHM_FILEINFO,
+ SHM_SEARCHCOUNT, 0, };
/// After setting various option values: recompute variables that depend on
/// option values.
@@ -146,8 +162,8 @@ 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
@@ -177,7 +193,7 @@ void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *o
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);
+ apply_autocmds(EVENT_OPTIONSET, get_option(opt_idx)->fullname, NULL, false, NULL);
reset_v_option_vars();
}
}
@@ -225,6 +241,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);
@@ -252,7 +269,7 @@ 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 *p)
@@ -279,19 +296,19 @@ void check_string_option(char **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 **varp)
+static void set_string_option_global(vimoption_T *opt, char **varp)
{
char **p;
// the global value is always allocated
- if (is_window_local_option(opt_idx)) {
+ if (opt->var == VAR_WIN) {
p = (char **)GLOBAL_WO(varp);
} else {
- p = (char **)get_option_var(opt_idx);
+ p = (char **)opt->var;
}
- if (!is_global_option(opt_idx) && p != varp) {
+ if (opt->indir != PV_NONE && p != varp) {
char *s = xstrdup(*varp);
free_string_option(*p);
*p = s;
@@ -323,30 +340,32 @@ 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)) {
+ 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, 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) {
+ if ((opt->indir & PV_BOTH) && both) {
free_string_option(*varp);
*varp = empty_option;
}
@@ -392,24 +411,24 @@ void set_string_option_direct_in_win(win_T *wp, const char *name, int opt_idx, c
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;
@@ -430,11 +449,11 @@ char *set_string_option(const int opt_idx, const char *const value, const int op
// call autocommand after handling side effects
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)));
}
}
@@ -463,7 +482,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.
@@ -531,8 +550,8 @@ static int check_signcolumn(char *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])) {
@@ -637,20 +656,21 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
char *errmsg = NULL;
char *s, *p;
int did_chartab = false;
- bool free_oldval = (get_option_flags(opt_idx) & P_ALLOCED);
+ 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_option_varp_scope(opt_idx, OPT_GLOBAL);
+ char **gvarp = (char **)get_varp_scope(opt, OPT_GLOBAL);
// Disallow changing some options from secure mode
if ((secure || sandbox != 0)
- && (get_option_flags(opt_idx) & P_SECURE)) {
+ && (opt->flags & P_SECURE)) {
errmsg = e_secure;
- } else if (((get_option_flags(opt_idx) & P_NFNAME)
+ } else if (((opt->flags & P_NFNAME)
&& strpbrk(*varp, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
- || ((get_option_flags(opt_idx) & P_NDNAME)
+ || ((opt->flags & P_NDNAME)
&& strpbrk(*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
@@ -682,7 +702,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
}
}
} else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
- if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
+ if (strcmp(*p_bex == '.' ? p_bex + 1 : p_bex,
*p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
errmsg = e_backupext_and_patchmode_are_equal;
}
@@ -690,6 +710,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (briopt_check(curwin) == FAIL) {
errmsg = e_invarg;
}
+ // list setting requires a redraw
+ if (curwin->w_briopt_list) {
+ redraw_all_later(UPD_NOT_VALID);
+ }
} else if (varp == &p_isi
|| varp == &(curbuf->b_p_isk)
|| varp == &p_isp
@@ -718,9 +742,9 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
}
} else if (varp == &curwin->w_p_cc) { // 'colorcolumn'
errmsg = check_colorcolumn(curwin);
- } else if (varp == (char **)&p_hlg) { // 'helplang'
+ } else if (varp == &p_hlg) { // 'helplang'
// Check for "", "ab", "ab,cd", etc.
- for (s = (char *)p_hlg; *s != NUL; s += 3) {
+ for (s = p_hlg; *s != NUL; s += 3) {
if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
errmsg = e_invarg;
break;
@@ -730,7 +754,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
}
}
} else if (varp == &p_hl) { // 'highlight'
- if (STRCMP(*varp, HIGHLIGHT_INIT) != 0) {
+ if (strcmp(*varp, HIGHLIGHT_INIT) != 0) {
errmsg = e_unsupportedoption;
}
} else if (varp == &p_jop) { // 'jumpoptions'
@@ -822,24 +846,19 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
}
if (errmsg == NULL) {
- // canonize the value, so that STRCMP() can be used on it
+ // canonize the value, so that strcmp() can be used on it
p = enc_canonize(*varp);
xfree(*varp);
*varp = p;
if (varp == &p_enc) {
// only encoding=utf-8 allowed
- if (STRCMP(p_enc, "utf-8") != 0) {
+ if (strcmp(p_enc, "utf-8") != 0) {
errmsg = e_unsupportedoption;
} else {
spell_reload();
}
}
}
- } else if (varp == &p_penc) {
- // Canonize printencoding if VIM standard one
- p = enc_canonize(p_penc);
- xfree(p_penc);
- p_penc = p;
} else if (varp == &curbuf->b_p_keymap) {
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
@@ -985,13 +1004,14 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
} 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')
+ 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 = (get_option_flags(opt_idx) & P_ALLOCED);
+ free_oldval = (opt->flags & P_ALLOCED);
for (s = p_shada; *s;) {
// Check it's a valid character
if (vim_strchr("!\"%'/:<@cfhnrs", *s) == NULL) {
@@ -1014,7 +1034,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (errbuf != NULL) {
vim_snprintf(errbuf, errbuflen,
_("E526: Missing number after <%s>"),
- transchar_byte(*(s - 1)));
+ transchar_byte((uint8_t)(*(s - 1))));
errmsg = errbuf;
} else {
errmsg = "";
@@ -1045,10 +1065,6 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
}
} 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'
@@ -1090,6 +1106,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
errmsg = e_invarg;
}
+ } else if (varp == &p_spk) { // 'splitkeep'
+ if (check_opt_strings(p_spk, p_spk_values, false) != OK) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_debug) { // 'debug'
if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
errmsg = e_invarg;
@@ -1125,7 +1145,8 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
// 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) {
+ if (opt_strings_flags(curwin->w_s->b_p_spo, p_spo_values, &(curwin->w_s->b_p_spo_flags),
+ true) != OK) {
errmsg = e_invarg;
}
} else if (varp == &p_sps) { // 'spellsuggest'
@@ -1156,12 +1177,14 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
redraw_titles();
}
} else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal
- || varp == &p_ruf) {
- // 'statusline', 'winbar', 'tabline' or 'rulerformat'
+ || varp == &p_ruf || varp == &curwin->w_p_stc) {
+ // 'statusline', 'winbar', 'tabline', 'rulerformat' or 'statuscolumn'
int wid;
if (varp == &p_ruf) { // reset ru_wid first
ru_wid = 0;
+ } else if (varp == &curwin->w_p_stc) {
+ curwin->w_nrwidth_line_count = 0;
}
s = *varp;
if (varp == &p_ruf && *s == '%') {
@@ -1176,7 +1199,8 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
errmsg = check_stl_option(p_ruf);
}
} else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
- // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!"
+ // check 'statusline', 'winbar', 'tabline' or 'statuscolumn'
+ // only if it doesn't start with "%!"
errmsg = check_stl_option(s);
}
if (varp == &p_ruf && errmsg == NULL) {
@@ -1245,6 +1269,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
&& (curwin->w_p_nu || curwin->w_p_rnu)) {
curwin->w_nrwidth_line_count = 0;
}
+ } else if (varp == &p_sloc) { // 'showcmdloc'
+ if (check_opt_strings(p_sloc, p_sloc_values, false) != OK) {
+ errmsg = e_invarg;
+ }
} else if (varp == &curwin->w_p_fdc
|| varp == &curwin->w_allbuf_opt.wo_fdc) {
// 'foldcolumn'
@@ -1256,7 +1284,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (*p_pt) {
p = NULL;
(void)replace_termcodes(p_pt,
- STRLEN(p_pt),
+ strlen(p_pt),
&p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
CPO_TO_CPO_FLAGS);
if (p != NULL) {
@@ -1312,10 +1340,6 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
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(*varp, ',');
if (p == NULL) {
@@ -1356,33 +1380,20 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
} else {
if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
errmsg = e_invarg;
- } else if (STRCMP(p_ve, oldval) != 0) {
+ } else if (strcmp(p_ve, oldval) != 0) {
// Recompute cursor position in case the new 've' setting
// changes something.
validate_virtcol();
coladvance(curwin->w_virtcol);
}
}
- } else if (varp == &p_csqf) {
- if (p_csqf != NULL) {
- p = p_csqf;
- while (*p != NUL) {
- if (vim_strchr(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;
- }
- }
- }
} else if (gvarp == &p_cino) { // 'cinoptions'
// TODO(vim): recognize errors
parse_cino(curbuf);
+ } else if (gvarp == &p_lop) { // 'lispoptions'
+ if (**varp != NUL && strcmp(*varp, "expr:0") != 0 && strcmp(*varp, "expr:1") != 0) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_icm) { // 'inccommand'
if (check_opt_strings(p_icm, p_icm_values, false) != OK) {
errmsg = e_invarg;
@@ -1391,7 +1402,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
- value_changed = STRCMP(oldval, *varp) != 0;
+ 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.
@@ -1401,7 +1412,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (!valid_filetype(*varp)) {
errmsg = e_invarg;
} else {
- value_changed = STRCMP(oldval, *varp) != 0;
+ 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.
@@ -1468,6 +1479,63 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
}
}
}
+ } else if (varp == &p_dex
+ || varp == &curwin->w_p_fde
+ || varp == &curwin->w_p_fdt
+ || gvarp == &p_fex
+ || gvarp == &p_inex
+ || gvarp == &p_inde
+ || varp == &p_pex) { // '*expr' options
+ char **p_opt = NULL;
+
+ // If the option value starts with <SID> or s:, then replace that with
+ // the script identifier.
+
+ if (varp == &p_dex) { // 'diffexpr'
+ p_opt = &p_dex;
+ }
+ if (varp == &curwin->w_p_fde) { // 'foldexpr'
+ p_opt = &curwin->w_p_fde;
+ }
+ if (varp == &curwin->w_p_fdt) { // 'foldtext'
+ p_opt = &curwin->w_p_fdt;
+ }
+ if (gvarp == &p_fex) { // 'formatexpr'
+ p_opt = &curbuf->b_p_fex;
+ }
+ if (gvarp == &p_inex) { // 'includeexpr'
+ p_opt = &curbuf->b_p_inex;
+ }
+ if (gvarp == &p_inde) { // 'indentexpr'
+ p_opt = &curbuf->b_p_inde;
+ }
+ if (varp == &p_pex) { // 'patchexpr'
+ p_opt = &p_pex;
+ }
+
+ if (p_opt != NULL) {
+ char *name = get_scriptlocal_funcname(*p_opt);
+ if (name != NULL) {
+ free_string_option(*p_opt);
+ *p_opt = name;
+ }
+ }
+
+ if (varp == &curwin->w_p_fde && foldmethodIsExpr(curwin)) {
+ foldUpdateAll(curwin);
+ }
+ } else if (gvarp == &p_cfu) { // 'completefunc'
+ if (set_completefunc_option() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_ofu) { // 'omnifunc'
+ if (set_omnifunc_option() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_tsrfu) { // 'thesaurusfunc'
+ if (set_thesaurusfunc_option() == FAIL) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_opfunc) { // 'operatorfunc'
if (set_operatorfunc_option() == FAIL) {
errmsg = e_invarg;
@@ -1476,6 +1544,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
if (qf_process_qftf_option() == FAIL) {
errmsg = e_invarg;
}
+ } else if (gvarp == &p_tfu) { // 'tagfunc'
+ if (set_tagfunc_option() == FAIL) {
+ errmsg = e_invarg;
+ }
} else {
// Options that are a list of flags.
p = NULL;
@@ -1520,18 +1592,18 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
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 = get_option_varp_scope(opt_idx, OPT_LOCAL);
+ 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.
@@ -1577,7 +1649,7 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
char *q = curwin->w_s->b_p_spl;
// Skip the first name if it is "cjk".
- if (STRNCMP(q, "cjk,", 4) == 0) {
+ if (strncmp(q, "cjk,", 4) == 0) {
q += 4;
}
@@ -1601,12 +1673,18 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
setmouse(); // in case 'mouse' changed
}
+ // Changing Formatlistpattern when briopt includes the list setting:
+ // redraw
+ if ((varp == &p_flp || varp == &(curbuf->b_p_flp)) && curwin->w_briopt_list) {
+ redraw_all_later(UPD_NOT_VALID);
+ }
+
if (curwin->w_curswant != MAXCOL
- && (get_option_flags(opt_idx) & (P_CURSWANT | P_RALL)) != 0) {
+ && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {
curwin->w_set_curswant = true;
}
- check_redraw(get_option_flags(opt_idx));
+ check_redraw(opt->flags);
return errmsg;
}
@@ -1639,8 +1717,8 @@ static int opt_strings_flags(char *val, char **values, unsigned *flagp, bool lis
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);
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 c940c86675..e1db7a8ef7 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 (char *)expand_env_save_opt(src, false);
}
/// Similar to expand_env_save() but when "one" is `true` handle the string as
@@ -538,10 +551,10 @@ 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_u *expand_env_save_opt(char *src, bool one)
{
char_u *p = xmalloc(MAXPATHL);
- expand_env_esc(src, p, MAXPATHL, false, one, NULL);
+ expand_env_esc(src, (char *)p, MAXPATHL, false, one, NULL);
return p;
}
@@ -555,7 +568,7 @@ char_u *expand_env_save_opt(char_u *src, bool one)
/// @param dstlen Maximum length of the result
void expand_env(char *src, char *dst, int dstlen)
{
- expand_env_esc((char_u *)src, (char_u *)dst, dstlen, false, false, NULL);
+ expand_env_esc(src, dst, dstlen, false, false, NULL);
}
/// Expand environment variable with path name and escaping.
@@ -567,34 +580,34 @@ void expand_env(char *src, char *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,7 +649,7 @@ 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)
}
@@ -644,17 +657,17 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
} 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;
+ 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;
@@ -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_u *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;
}
}
@@ -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);
}
@@ -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);
@@ -1159,7 +1176,7 @@ char *get_env_name(expand_T *xp, int idx)
assert(idx >= 0);
char *envname = os_getenvname_at_index((size_t)idx);
if (envname) {
- STRLCPY(xp->xp_buf, envname, EXPAND_BUF_LEN);
+ xstrlcpy(xp->xp_buf, envname, EXPAND_BUF_LEN);
xfree(envname);
return xp->xp_buf;
}
@@ -1174,7 +1191,7 @@ 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
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index b1710737d0..e93e1febcb 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -11,22 +11,19 @@
#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 "nvim/globals.h"
+#include "auto/config.h"
+#include "nvim/gettext.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 +68,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 +76,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 +167,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 c0d5616666..e0449d468a 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,9 +131,8 @@ 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.
@@ -151,7 +160,7 @@ bool os_isdir(const char *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,7 +295,7 @@ 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
@@ -351,7 +359,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 +379,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 +454,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 +764,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 +779,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_u *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_u *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 +860,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.
@@ -946,6 +985,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 +1278,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
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..5d2ac1e102 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
@@ -38,6 +46,8 @@ 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"
@@ -97,13 +107,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 +140,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 +167,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 +266,7 @@ 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), buf, FSK_KEYCODE, true, NULL);
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
@@ -287,36 +314,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 +373,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 +477,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/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/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..ec0f03a1dc 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,6 +10,10 @@
# include "os/os_win_console.c.generated.h"
#endif
+static HWND hWnd = NULL;
+static HICON hOrigIconSmall = NULL;
+static HICON hOrigIcon = NULL;
+
int os_get_conin_fd(void)
{
const HANDLE conin_handle = CreateFile("CONIN$",
@@ -45,3 +50,45 @@ 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);
+ }
+ }
+}
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 461a79c37b..8177f06c64 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((char *)s);
- (*file)[i] = (char *)s;
+ backslash_halve(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;
}
}
@@ -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++) {
@@ -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] == '`') {
@@ -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,7 +518,7 @@ 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;
}
@@ -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;
@@ -555,7 +570,7 @@ notfound:
char **shell_build_argv(const char *cmd, const char *extra_args)
FUNC_ATTR_NONNULL_RET
{
- size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize(p_shcf, NULL) : 0);
+ size_t argc = tokenize(p_sh, NULL) + (cmd ? tokenize((char *)p_shcf, NULL) : 0);
char **rv = xmalloc((argc + 4) * sizeof(*rv));
// Split 'shell'
@@ -566,7 +581,7 @@ char **shell_build_argv(const char *cmd, const char *extra_args)
}
if (cmd) {
- i += tokenize(p_shcf, rv + i); // Split 'shellcmdflag'
+ i += tokenize((char *)p_shcf, rv + i); // Split 'shellcmdflag'
rv[i++] = shell_xescape_xquote(cmd); // Copy (and escape) `cmd`.
}
@@ -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.
@@ -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, (char *)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..a99a8d25ce 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"),
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 a5cec6772f..9350335e54 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -2,11 +2,16 @@
// 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 <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 +19,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
@@ -64,7 +71,7 @@ FileComparison path_full_compare(char *const s1, char *const s2, const bool chec
if (expandenv) {
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 +80,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;
}
}
@@ -147,11 +154,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_u *invocation_path_tail(const char *invocation, size_t *len)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1)
{
- const char_u *tail = (char_u *)get_past_head((char *)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);
@@ -164,7 +171,7 @@ const char_u *invocation_path_tail(const char_u *invocation, size_t *len)
*len = (size_t)(p - tail);
}
- return tail;
+ return (char_u *)tail;
}
/// Get the next path component of a path name.
@@ -190,7 +197,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;
@@ -205,7 +212,7 @@ int path_head_length(void)
/// - False otherwise
bool is_path_head(const char_u *path)
{
-#ifdef WIN32
+#ifdef MSWIN
return isalpha(path[0]) && path[1] == ':';
#else
return vim_ispathsep(*path);
@@ -219,7 +226,7 @@ char *get_past_head(const char *path)
{
const char *retval = path;
-#ifdef WIN32
+#ifdef MSWIN
// May skip "c:"
if (is_path_head((char_u *)path)) {
retval = path + 2;
@@ -248,9 +255,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)
@@ -274,13 +279,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) {
@@ -300,7 +305,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;
}
@@ -312,27 +317,33 @@ void shorten_dir_len(char_u *str, int trim_len)
/// It's done in-place.
void shorten_dir(char *str)
{
- shorten_dir_len((char_u *)str, 1);
+ 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)
+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;
*p = NUL;
- bool retval = os_isdir((char *)fname);
+ bool retval = os_isdir(fname);
*p = c;
return retval;
}
/// 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.
@@ -384,7 +395,7 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, size_t le
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
@@ -509,7 +520,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)) {
@@ -532,9 +543,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);
@@ -544,7 +553,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)) {
@@ -558,7 +567,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;
}
}
@@ -573,10 +582,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);
@@ -600,7 +609,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
{
@@ -619,18 +628,18 @@ 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. */
+ // 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)) {
@@ -640,10 +649,10 @@ 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
+#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));
@@ -655,10 +664,10 @@ 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. */
+ // Remove backslashes between "wildoff" and the start of the wildcard
+ // component.
for (p = buf + wildoff; p < s; p++) {
- if (rem_backslash((char *)p)) {
+ if (rem_backslash(p)) {
STRMOVE(p, p + 1);
e--;
s--;
@@ -674,7 +683,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;
@@ -702,8 +711,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);
@@ -714,27 +723,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);
@@ -752,11 +761,12 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// no more wildcards, check if there is a match
// remove backslashes for the remaining components only
if (*path_end != NUL) {
- backslash_halve((char *)buf + len + 1);
+ 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,15 +788,13 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
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
@@ -802,21 +810,21 @@ static int find_previous_pathsep(char_u *path, char_u **psep)
/// 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)
+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
}
@@ -824,33 +832,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 : (char_u *)curbuf->b_p_path;
- char_u *buf = xmalloc(MAXPATHL);
+ char *buf = xmalloc(MAXPATHL);
while (*path_option != NUL) {
- copy_option_part((char **)&path_option, (char *)buf, MAXPATHL, " ,");
+ copy_option_part((char **)&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) {
@@ -862,12 +868,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)) {
+ } else if (!path_is_absolute((char_u *)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);
@@ -876,20 +882,18 @@ 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
- */
+// 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)
{
int maxlen = 0;
@@ -900,7 +904,7 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap)
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)
@@ -923,32 +927,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;
@@ -961,28 +963,28 @@ 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
- path_cutoff = get_path_cutoff(path, &path_ga);
+ path_cutoff = (char *)get_path_cutoff((char_u *)path, &path_ga);
// Don't assume all files can be reached without path when search
// pattern starts with **/, so only remove path_cutoff
@@ -990,26 +992,26 @@ 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;
}
}
}
- if (path_is_absolute(path)) {
+ if (path_is_absolute((char_u *)path)) {
// Last resort: shorten relative to curdir if possible.
// 'possible' means:
// 1. It is under the current directory.
@@ -1023,8 +1025,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();
@@ -1032,15 +1034,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;
@@ -1050,9 +1052,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]);
@@ -1111,21 +1113,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 = (char *)ga_concat_strings(&path_ga);
ga_clear_strings(&path_ga);
int glob_flags = 0;
@@ -1135,7 +1137,7 @@ 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, (char *)pattern, gap, glob_flags);
+ globpath(paths, pattern, gap, glob_flags);
xfree(paths);
return gap->ga_len;
@@ -1206,34 +1208,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] == '=')) {
+ && !(vim_backtick(pat[i]) && pat[i][1] == '=')) {
return os_expand_wildcards(num_pat, pat, num_file, file, flags);
}
}
@@ -1241,64 +1240,59 @@ 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;
}
} else {
// First expand environment variables, "~/" and "~user/".
- if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') {
- p = expand_env_save_opt(p, true);
+ if ((has_env_var((char_u *)p) && !(flags & EW_NOTENV)) || *p == '~') {
+ p = (char *)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((char_u *)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)
+ && !path_is_absolute((char_u *)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;
@@ -1312,10 +1306,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 = (char_u *)backslash_halve_save((char *)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 {
@@ -1330,7 +1324,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);
}
}
@@ -1355,10 +1349,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.
@@ -1366,20 +1360,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) {
@@ -1397,7 +1390,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++;
}
@@ -1428,7 +1421,7 @@ void slash_adjust(char_u *p)
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;
}
@@ -1452,7 +1445,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;
@@ -1460,7 +1453,7 @@ 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;
}
@@ -1472,7 +1465,7 @@ void addfile(garray_T *gap, char_u *f, int flags)
}
#endif
- isdir = os_isdir((char *)f);
+ isdir = os_isdir(f);
if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE))) {
return;
}
@@ -1480,35 +1473,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_u *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);
}
GA_APPEND(char_u *, 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;
@@ -1528,8 +1517,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] == '.'
@@ -1537,10 +1526,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)) {
@@ -1561,86 +1550,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;
@@ -1664,7 +1653,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);
}
@@ -1682,56 +1671,55 @@ 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);
}
}
if (options & FNAME_EXP) {
- file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true,
- rel_fname);
+ file_name = (char *)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);
- file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
- true, rel_fname);
+ len = strlen(ptr);
+ file_name = (char *)find_file_in_path(ptr, len, options & ~FNAME_MESS,
+ 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 =
+ (char *)find_file_in_path(ptr, len, options, false, rel_fname);
}
} else {
- file_name = vim_strnsave(ptr, len);
+ file_name = xstrnsave(ptr, len);
}
xfree(tofree);
@@ -1809,9 +1797,9 @@ bool path_with_extension(const char *path, const char *extension)
}
/// Return true if "name" is a full (absolute) path name or URL.
-bool vim_isAbsName(char_u *name)
+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((char_u *)name);
}
/// Save absolute file name to "buf[len]".
@@ -1834,7 +1822,7 @@ 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
+#ifdef MSWIN
slash_adjust((char_u *)buf);
#endif
return FAIL;
@@ -1849,7 +1837,7 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename
}
-#ifdef WIN32
+#ifdef MSWIN
slash_adjust((char_u *)buf);
#endif
return rv;
@@ -1883,7 +1871,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;
@@ -1926,13 +1914,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)) {
@@ -1951,12 +1939,12 @@ void path_fix_case(char *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)
+bool same_directory(char *f1, char *f2)
{
char ffname[MAXPATHL];
char *t1;
@@ -1967,18 +1955,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 +2043,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,27 +2065,27 @@ 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)) {
+ if (len == (size_t)path_head_length() && is_path_head((char_u *)dir_name)) {
return full_path + len;
}
// 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,21 +2111,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,
- true);
+ eval_pat = (char *)eval_vars((char_u *)exp_pat, (char_u *)exp_pat, &usedlen, NULL, &ignored_msg,
+ NULL,
+ true);
emsg_off--;
if (eval_pat != NULL) {
- exp_pat = concat_str((char *)eval_pat, exp_pat + usedlen);
+ star_follows = strcmp(exp_pat + usedlen, "*") == 0;
+ exp_pat = concat_str(eval_pat, exp_pat + usedlen);
}
}
@@ -2148,6 +2138,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);
}
@@ -2173,7 +2173,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);
@@ -2183,9 +2183,7 @@ 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;
@@ -2214,13 +2212,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;
}
}
}
@@ -2234,27 +2232,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 = (char *)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;
@@ -2272,14 +2270,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;
}
@@ -2299,7 +2297,7 @@ int path_full_dir_name(char *directory, char *buffer, size_t len)
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;
}
@@ -2364,22 +2362,15 @@ static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
// expand it if forced or not an absolute path
if (force || !path_is_absolute((char_u *)fname)) {
p = strrchr(fname, '/');
-#ifdef WIN32
+#ifdef MSWIN
if (p == NULL) {
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;
- }
+ 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;
@@ -2400,7 +2391,7 @@ static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
/// @return `true` if "fname" is absolute.
int path_is_absolute(const char_u *fname)
{
-#ifdef WIN32
+#ifdef MSWIN
if (*fname == NUL) {
return false;
}
@@ -2429,7 +2420,7 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
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);
@@ -2447,11 +2438,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 cc730ba307..e4d1ddf8d8 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -10,25 +10,21 @@
#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/types.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "plines.c.generated.h"
@@ -37,6 +33,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)
{
@@ -71,6 +70,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)
{
@@ -102,11 +104,11 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
unsigned int col;
int width;
- s = (char *)ml_get_buf(wp->w_buffer, lnum, false);
+ s = ml_get_buf(wp->w_buffer, lnum, false);
if (*s == NUL) { // empty line
return 1;
}
- col = win_linetabsize(wp, lnum, (char_u *)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,7 +146,7 @@ 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 *line = ml_get_buf(wp->w_buffer, lnum, false);
colnr_T col = 0;
chartabsize_T cts;
@@ -232,9 +234,8 @@ 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(p);
}
+ return ptr2cells(p);
}
/// Return the number of characters the string 's' will take on the screen,
@@ -243,9 +244,9 @@ int win_chartabsize(win_T *wp, char *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, (char *)s);
+ return linetabsize_col(0, s);
}
/// Like linetabsize(), but "s" starts at column "startcol".
@@ -257,7 +258,7 @@ int linetabsize(char_u *s)
int linetabsize_col(int startcol, char *s)
{
chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, 0, startcol, (char_u *)s, (char_u *)s);
+ init_chartabsize_arg(&cts, curwin, 0, startcol, s, s);
while (*cts.cts_ptr != NUL) {
cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
@@ -272,11 +273,11 @@ int linetabsize_col(int startcol, char *s)
/// @param len
///
/// @return Number of characters the string will take on the screen.
-unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len)
+unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
{
chartabsize_T cts;
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
- for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < (char *)line + len);
+ 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);
}
@@ -288,14 +289,13 @@ unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len
///
/// "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, colnr_T col, char_u *line,
- char_u *ptr)
+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_lnum = lnum;
cts->cts_vcol = col;
- cts->cts_line = (char *)line;
- cts->cts_ptr = (char *)ptr;
+ 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;
@@ -354,7 +354,7 @@ 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_u *s = (char_u *)cts->cts_ptr;
+ char *s = cts->cts_ptr;
colnr_T vcol = cts->cts_vcol;
colnr_T col2;
@@ -363,7 +363,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
int added;
int mb_added = 0;
int numberextra;
- char_u *ps;
+ char *ps;
int n;
cts->cts_cur_text_width = 0;
@@ -374,16 +374,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
if (wp->w_p_wrap) {
return win_nolbr_chartabsize(cts, headp);
}
- return win_chartabsize(wp, (char *)s, vcol);
+ return win_chartabsize(wp, s, vcol);
}
// First get normal size, without 'linebreak' or virtual text
- int size = win_chartabsize(wp, (char *)s, vcol);
+ int size = win_chartabsize(wp, s, vcol);
if (cts->cts_has_virt_text) {
// TODO(bfredl): inline virtual text
}
- int c = *s;
+ int c = (uint8_t)(*s);
if (*s == TAB) {
col_adj = size - 1;
}
@@ -392,7 +392,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
// 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
@@ -413,14 +413,14 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
for (;;) {
ps = s;
MB_PTR_ADV(s);
- c = *s;
+ c = (uint8_t)(*s);
if (!(c != NUL
- && (vim_isbreak(c) || col2 == vcol || !vim_isbreak((int)(*ps))))) {
+ && (vim_isbreak(c) || col2 == vcol || !vim_isbreak((uint8_t)(*ps))))) {
break;
}
- col2 += win_chartabsize(wp, (char *)s, col2);
+ col2 += win_chartabsize(wp, s, col2);
if (col2 >= colmax) { // doesn't fit
size = colmax - vcol + col_adj;
@@ -428,7 +428,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
}
}
} else if ((size == 2)
- && (MB_BYTE2LEN(*s) > 1)
+ && (MB_BYTE2LEN((uint8_t)(*s)) > 1)
&& wp->w_p_wrap
&& in_win_border(wp, vcol)) {
// Count the ">" in the last column.
@@ -439,9 +439,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
// 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);
+ char *const sbr = c == NUL ? empty_option : (char *)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);
@@ -456,7 +456,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
vcol %= numberextra;
}
if (*sbr != NUL) {
- sbrlen = (colnr_T)mb_charlen((char_u *)sbr);
+ sbrlen = (colnr_T)mb_charlen(sbr);
if (vcol >= sbrlen) {
vcol -= sbrlen;
}
@@ -492,7 +492,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
}
if (wp->w_p_bri) {
- added += get_breakindent_win(wp, (char_u *)line);
+ added += get_breakindent_win(wp, line);
}
size += added;
diff --git a/src/nvim/plines.h b/src/nvim/plines.h
index 7b228f3e91..808f6d284e 100644
--- a/src/nvim/plines.h
+++ b/src/nvim/plines.h
@@ -1,12 +1,14 @@
#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;
- linenr_T cts_lnum; // zero when not using text properties
char *cts_line; // start of the line
char *cts_ptr; // current position in line
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 d64789660e..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"
diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po
index 5869e6567c..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"
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 bce5c0fa76..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"
diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po
index f4eab3f0d4..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"
diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po
index 1c3284f867..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"
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index ce3fd77ede..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"
diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po
index 81ee9ed6a0..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 ""
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 263fb61b18..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"
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 8a44f6a534..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"
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index 1c0da244ba..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"
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 be2141cd6d..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"
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index 346f0faa84..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"
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index 152ed2cbe3..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"
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 09be710374..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 "새 데이터베이스 더하기"
diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po
index 9bc730ae71..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"
diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po
index 4d2e55adc6..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"
diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po
index 9bc730ae71..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"
diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po
index 7e978113f6..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"
diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po
index bfd64b4d28..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
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index da356770d7..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 "Добавить новую базу данных"
diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po
index cb8b8c6abb..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"
diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po
index 4f9e1fe185..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"
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index cbdf736d0f..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 "Додај нову базу"
diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po
index 406900f7b2..e46579c067 100644
--- a/src/nvim/po/sv.po
+++ b/src/nvim/po/sv.po
@@ -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 20b667d198..7d6af4d2cf 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 "E168: :finish used outside of a sourced file"
-msgstr "E168: :finish 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ı"
-#, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "Şu anki %sdil: \"%s\""
+msgid "E492: Not an editor command"
+msgstr "E492: Bir düzenleyici komutu değil"
-#, c-format
-msgid "E197: Cannot set language to \"%s\""
-msgstr "E197: \"%s\" diline ayarlanamıyor"
+msgid "E498: no :source file name to substitute for \"<sfile>\""
+msgstr "E498: \"<kdosyası>\" yerine koymak için :source dosya adı yok"
-#, c-format
-msgid "E184: No such user-defined command: %s"
-msgstr "E184: Böyle bir kullanıcı tanımlı komut yok: %s"
+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 "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 "E5248: Invalid character in group name"
-msgstr "E5248: 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"
@@ -5864,12 +5827,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 +5855,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 427abd9b77..b521e37177 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: Використано забагато різних атрибутів кольору"
diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po
index ad59718a30..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"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index afa2f29029..0606adcc37 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,1181 +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: 2022-11-17 22:29+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:139
+#: ../eval.c:4855
+msgid "filter() argument"
+msgstr "filter() 参数"
+
+#: ../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:147
+#: ../eval.c:8831
+msgid "E736: Invalid operation for Dictionary"
+msgstr "E736: 操作对字典无效"
+
+#: ../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.c:4904
+#: ../eval/decode.c:898
+#, fuzzy, c-format
+msgid "E474: Trailing characters: %.*s"
+msgstr "E488: 多余的尾部字符"
+
+#: ../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/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.c:6499
+#: ../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/encode.c:724
+msgid "E474: Unable to convert EXT string to JSON"
+msgstr "E474: 无法将 EXT 字符串转换为 JSON"
-#: ../eval.c:6524
+#: ../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
+#: ../eval/executor.c:23
#, c-format
-msgid "E725: Calling dict function without Dictionary: %s"
-msgstr "E725: 调用字典函数但是没有字典:%s"
+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/funcs.c:1483
+msgid "dictwatcheradd() argument"
+msgstr "dictwatcheradd() 参数"
-#: ../eval.c:8692
+#: ../eval/funcs.c:2178
+msgid "E900: maxdepth must be non-negative number"
+msgstr "E900: maxdepth 必须是非负整数"
+
+#: ../eval/funcs.c:2186
+msgid "flatten() argument"
+msgstr "flatten() 参数"
+
+#: ../eval/funcs.c:2197 ../eval/typval.c:2458
msgid "extend() argument"
msgstr "extend() 参数"
-#: ../eval.c:8915
-msgid "map() argument"
-msgstr "map() 参数"
-
-#: ../eval.c:8916
-msgid "filter() argument"
-msgstr "filter() 参数"
+#: ../eval/funcs.c:2877 ../eval/funcs.c:3891
+msgid "E5000: Cannot find tab number."
+msgstr "E5000: 找不到标签页号。"
-#: ../eval.c:9229
-#, c-format
-msgid "+-%s%3ld line: "
-msgid_plural "+-%s%3ld lines: "
-msgstr[0] "+-%s%3ld 行: "
+#: ../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:9291
-#, c-format
-msgid "E700: Unknown function: %s"
-msgstr "E700: 未知的函数: %s"
+#: ../eval/funcs.c:2892 ../eval/funcs.c:3906
+msgid "E5002: Cannot find window number."
+msgstr "E5002: 找不到窗口号。"
-#: ../eval.c:10729
+#: ../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
+#: ../eval/funcs.c:7040
+#, c-format
+msgid "E5010: List item %d of the second argument is not a string"
+msgstr "E5010: 第二个参数的列表项 %d 不是字符串"
+
+#: ../eval/funcs.c:7916
+#, c-format
+msgid "E962: Invalid action: '%s'"
+msgstr "E962: 动作无效:'%s'"
+
+#: ../eval/funcs.c:8056
+#, c-format
+msgid "connection failed: %s"
+msgstr "连接失败:%s"
+
+#: ../eval/funcs.c:8328
+#, fuzzy, c-format
+msgid "E6100: \"%s\" is not a valid stdpath"
+msgstr "E236: 字体 \"%s\" 不是等宽字体"
+
+#: ../eval/funcs.c:8420 ../os/time.c:189
+msgid "(Invalid)"
+msgstr "(无效)"
+
+#: ../eval/funcs.c:8774
+#, c-format
+msgid "E935: invalid submatch number: %d"
+msgstr "E935: 子匹配号无效:%d"
+
+#: ../eval/funcs.c:9213
+msgid "Can only call this function in an unmodified buffer"
+msgstr "只能在未修改的缓冲区中调用这个函数"
+
+#: ../eval/funcs.c:10026
+msgid "writefile() first argument must be a List or a Blob"
+msgstr "writefile() 的第一个参数必须是列表或者 blob"
+
+#. 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/funcs.c:10067
+msgid "E482: Can't open file with an empty name"
+msgstr "E482: 无法打开名称为空的文件"
+
+#: ../eval/funcs.c:10072
+#, c-format
+msgid "E482: Can't open file %s for writing: %s"
+msgstr ""
+
+#: ../eval/funcs.c:10085
+#, c-format
+msgid "E80: Error when closing file %s: %s"
+msgstr "E80: 关闭文件 %s 时出错: %s"
+
+#: ../eval/typval.c:44
+#, c-format
+msgid "E1174: String required for argument %d"
+msgstr "E1174: 参数 %d 需要字符串"
+
+#: ../eval/typval.c:46
+#, c-format
+msgid "E1175: Non-empty string required for argument %d"
+msgstr "E1175: 参数 %d 需要非空字符串"
+
+#: ../eval/typval.c:48
+#, c-format
+msgid "E1210: Number required for argument %d"
+msgstr "E1210: 参数 %d 需要整数"
+
+#: ../eval/typval.c:50
+#, c-format
+msgid "E1222: String or List required for argument %d"
+msgstr "E1222: 参数 %d 需要字符串或列表"
+
+#: ../eval/typval.c:76
+#, c-format
+msgid "E5142: Failed to open file %s: %s"
+msgstr "E5142: 无法打开文件 %s:%s"
+
+#: ../eval/typval.c:98
+#, c-format
+msgid "E5143: Failed to write to file %s: %s"
+msgstr "E5143: 无法写入文件 %s:%s"
+
+#: ../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 "-c 参数"
+msgstr "sort() 参数"
-#: ../eval.c:13721
-#, fuzzy
+#: ../eval/typval.c:1151
msgid "uniq() argument"
-msgstr "-c 参数"
+msgstr "uniq() 参数"
-#: ../eval.c:13776
+#: ../eval/typval.c:1242
msgid "E702: Sort compare function failed"
msgstr "E702: Sort 比较函数失败"
-#: ../eval.c:13806
+#: ../eval/typval.c:1265
msgid "E882: Uniq compare function failed"
msgstr "E882: Uniq 比较函数失败"
-#: ../eval.c:14085
-msgid "(Invalid)"
-msgstr "(无效)"
+#: ../eval/typval.c:2198
+msgid "E6000: Argument is not a function or function name"
+msgstr ""
-#: ../eval.c:14590
-msgid "E677: Error writing temp file"
-msgstr "E677: 写临时文件出错"
+#: ../eval/typval.c:2476
+#, c-format
+msgid "E737: Key already exists: %s"
+msgstr "E737: 键已存在:%s"
-#: ../eval.c:16159
-#, fuzzy
-msgid "E805: Using a Float as a Number"
-msgstr "E805: 将浮点数当做数字使用"
+#: ../eval/typval.c:3315
+msgid "E743: variable nested too deep for (un)lock"
+msgstr "E743: 变量嵌套过深,无法锁定/解锁"
-#: ../eval.c:16162
-msgid "E703: Using a Funcref as a Number"
-msgstr "E703: 将函数当做数字使用"
+#: ../eval/typval.c:3455
+#, c-format
+msgid "E741: Value is locked: %.*s"
+msgstr "E741: 值已锁定:%.*s"
+
+#: ../eval/typval.c:3458
+#, c-format
+msgid "E742: Cannot change value of %.*s"
+msgstr "E742: 无法改变 %.*s 的值"
+
+#: ../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.c:16170
+#: ../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: 将列表当做数字使用"
+msgstr "E745: 将列表当作整数使用"
-#: ../eval.c:16173
+#: ../eval/typval.c:3626
msgid "E728: Using a Dictionary as a Number"
-msgstr "E728: 将字典当做数字使用"
+msgstr "E728: 将字典当作整数使用"
+
+#: ../eval/typval.c:3627
+msgid "E805: Using a Float as a Number"
+msgstr "E805: 将浮点数当作整数使用"
-#: ../eval.c:16259
-msgid "E729: using Funcref as a String"
-msgstr "E729: 将函数当做字符串使用"
+#: ../eval/typval.c:3628
+msgid "E974: Using a Blob as a Number"
+msgstr "E974: 将 Blob 当作整数使用"
-#: ../eval.c:16262
+#: ../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: 将列表当做字符串使用"
+msgstr "E730: 将列表当作字符串使用"
-#: ../eval.c:16265
+#: ../eval/typval.c:3671
msgid "E731: using Dictionary as a String"
-msgstr "E731: 将字典当做字符串使用"
+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.c:16619
+#: ../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/userfunc.c:67
#, c-format
-msgid "E706: Variable type mismatch for: %s"
-msgstr "E706: 变量类型不匹配: %s"
+msgid "E122: Function %s already exists, add ! to replace it"
+msgstr "E122: 函数 %s 已存在,请加 ! 强制替换"
-#: ../eval.c:16705
-#, fuzzy, c-format
-msgid "E795: Cannot delete variable %s"
-msgstr "E738: 无法列出 %s 的变量"
+#: ../eval/userfunc.c:68
+msgid "E717: Dictionary entry already exists"
+msgstr "E717: 字典项已存在"
+
+#: ../eval/userfunc.c:69
+msgid "E718: Funcref required"
+msgstr "E718: 需要 Funcref"
-#: ../eval.c:16724
+#: ../eval/userfunc.c:70
#, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E704: Funcref 变量名必须以大写字母开头: %s"
+msgid "E130: Unknown function: %s"
+msgstr "E130: 函数未知: %s"
-#: ../eval.c:16732
+#: ../eval/userfunc.c:72
#, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr "E705: 变量名与已有函数名冲突: %s"
+msgid "E1068: No white space allowed before '%s': %s"
+msgstr "E1068: '%s' 前不允许有空白:%s"
-#: ../eval.c:16763
+#: ../eval/userfunc.c:124
#, c-format
-msgid "E741: Value is locked: %s"
-msgstr "E741: 值已锁定: %s"
+msgid "E125: Illegal argument: %s"
+msgstr "E125: 参数无效: %s"
-#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
-msgid "Unknown"
-msgstr "未知"
+#: ../eval/userfunc.c:137
+#, c-format
+msgid "E853: Duplicate argument name: %s"
+msgstr "E853: 参数名重复:%s"
+
+#: ../eval/userfunc.c:171
+msgid "E989: Non-default argument follows default argument"
+msgstr "E989: 默认参数后有非默认参数"
-#: ../eval.c:16768
+#: ../eval/userfunc.c:498
#, c-format
-msgid "E742: Cannot change value of %s"
-msgstr "E742: 无法改变 %s 的值"
+msgid "E740: Too many arguments for function %s"
+msgstr "E740: 函数 %s 的参数过多"
-#: ../eval.c:16838
-msgid "E698: variable nested too deep for making a copy"
-msgstr "E698: 变量嵌套过深无法复制"
+#: ../eval/userfunc.c:500
+#, c-format
+msgid "E116: Invalid arguments for function %s"
+msgstr "E116: 函数 %s 的参数无效"
+
+#: ../eval/userfunc.c:859
+msgid "E132: Function call depth is higher than 'maxfuncdepth'"
+msgstr "E132: 函数调用深度超出 'maxfuncdepth'"
+
+#: ../eval/userfunc.c:1039
+#, c-format
+msgid "calling %s"
+msgstr "调用 %s"
+
+#: ../eval/userfunc.c:1154
+#, c-format
+msgid "%s aborted"
+msgstr "%s 已中止"
+
+#: ../eval/userfunc.c:1156
+#, c-format
+msgid "%s returning #%<PRId64>"
+msgstr "%s 返回 #%<PRId64> "
+
+#: ../eval/userfunc.c:1173
+#, c-format
+msgid "%s returning %s"
+msgstr "%s 返回 %s"
+
+#: ../eval/userfunc.c:1196 ../runtime.c:2083
+#, c-format
+msgid "continuing in %s"
+msgstr "在 %s 中继续"
+
+#: ../eval/userfunc.c:1391
+msgid "E699: Too many arguments"
+msgstr "E699: 参数过多"
+
+#: ../eval/userfunc.c:1441
+#, c-format
+msgid "E117: Unknown function: %s"
+msgstr "E117: 未知的函数:%s"
+
+#: ../eval/userfunc.c:1444
+#, c-format
+msgid "E276: Cannot use function as a method: %s"
+msgstr "E276: 不能将函数用作方法:%s"
-#: ../eval.c:17249
+#: ../eval/userfunc.c:1447
+#, c-format
+msgid "E933: Function was deleted: %s"
+msgstr "E933: 函数已被删除:%s"
+
+#: ../eval/userfunc.c:1453
+#, c-format
+msgid "E119: Not enough arguments for function: %s"
+msgstr "E119: 函数 %s 的参数不足"
+
+#: ../eval/userfunc.c:1457
+#, c-format
+msgid "E120: Using <SID> not in a script context: %s"
+msgstr "E120: <SID> 不能在 script 上下文外使用:%s"
+
+#: ../eval/userfunc.c:1461
+#, c-format
+msgid "E725: Calling dict function without Dictionary: %s"
+msgstr "E725: 调用字典函数但是没有字典:%s"
+
+#: ../eval/userfunc.c:1759
+msgid "E129: Function name required"
+msgstr "E129: 需要函数名"
+
+#: ../eval/userfunc.c:1895
+#, c-format
+msgid "E128: Function name must start with a capital or \"s:\": %s"
+msgstr "E128: 函数名必须以大写字母或 \"s:\" 开头:%s"
+
+#: ../eval/userfunc.c:1904
+#, c-format
+msgid "E884: Function name cannot contain a colon: %s"
+msgstr "E884: 函数名不能包含冒号:%s"
+
+#: ../eval/userfunc.c:1972
+msgid "E454: function list was modified"
+msgstr "E454: 函数列表已被修改"
+
+#: ../eval/userfunc.c:2125
#, c-format
msgid "E123: Undefined function: %s"
-msgstr "E123: 函数 %s 尚未定义"
+msgstr "E123: 函数未定义:%s"
-#: ../eval.c:17260
+#: ../eval/userfunc.c:2135
#, c-format
msgid "E124: Missing '(': %s"
-msgstr "E124: 缺少 '(': %s"
+msgstr "E124: 缺少 '(':%s"
-#: ../eval.c:17293
-#, fuzzy
+#: ../eval/userfunc.c:2167
msgid "E862: Cannot use g: here"
-msgstr "E284: 不能设定 IC 值"
+msgstr "E862: 此处不能使用 g:"
-#: ../eval.c:17312
+#: ../eval/userfunc.c:2197
#, c-format
-msgid "E125: Illegal argument: %s"
-msgstr "E125: 无效的参数: %s"
-
-#: ../eval.c:17323
-#, fuzzy, c-format
-msgid "E853: Duplicate argument name: %s"
-msgstr "E125: 无效的参数: %s"
+msgid "E932: Closure function should not be at top level: %s"
+msgstr "E932: 闭包函数不能位于顶层:%s"
-#: ../eval.c:17416
+#: ../eval/userfunc.c:2272
msgid "E126: Missing :endfunction"
msgstr "E126: 缺少 :endfunction"
-#: ../eval.c:17537
-#, fuzzy, c-format
+#: ../eval/userfunc.c:2327
+#, c-format
+msgid "W22: Text found after :endfunction: %s"
+msgstr "W22: :endfunction 后有文本:%s"
+
+#: ../eval/userfunc.c:2363
+msgid "E1058: function nesting too deep"
+msgstr "E1058: 函数嵌套层数过深"
+
+#: ../eval/userfunc.c:2472
+#, c-format
msgid "E707: Function name conflicts with variable: %s"
-msgstr "E746: 函数名与脚本文件名不匹配: %s"
+msgstr "E707: 函数名与变量名冲突:%s"
-#: ../eval.c:17549
+#: ../eval/userfunc.c:2488
#, c-format
msgid "E127: Cannot redefine function %s: It is in use"
msgstr "E127: 函数 %s 正在使用中,不能重新定义"
-#: ../eval.c:17604
+#: ../eval/userfunc.c:2555
#, c-format
msgid "E746: Function name does not match script file name: %s"
msgstr "E746: 函数名与脚本文件名不匹配: %s"
-#: ../eval.c:17716
-msgid "E129: Function name required"
-msgstr "E129: 需要函数名"
+#: ../eval/userfunc.c:2778
+#, c-format
+msgid "E131: Cannot delete function %s: It is in use"
+msgstr "E131: 函数 %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:2784
+#, c-format
+msgid "Cannot delete function %s: It is being used internally"
+msgstr "无法删除函数 %s,内部使用这个函数"
-#: ../eval.c:17833
-#, fuzzy, c-format
-msgid "E884: Function name cannot contain a colon: %s"
-msgstr "E128: 函数名必须以大写字母开头或者包含冒号: %s"
+#: ../eval/userfunc.c:2916
+msgid "E133: :return not inside a function"
+msgstr "E133: :return 在函数外"
+
+#. 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.c:18336
+#: ../eval/vars.c:53
#, c-format
-msgid "E131: Cannot delete function %s: It is in use"
-msgstr "E131: 无法删除函数 %s: 正在使用中"
+msgid "E940: Cannot lock or unlock variable %s"
+msgstr "E940: 不能锁定或解锁变量 %s"
-#: ../eval.c:18441
-msgid "E132: Function call depth is higher than 'maxfuncdepth'"
-msgstr "E132: 函数调用深度超出 'maxfuncdepth'"
+#: ../eval/vars.c:77
+msgid "E991: cannot use =<< here"
+msgstr "E991: 此处不能使用 =<<"
+
+#: ../eval/vars.c:109
+msgid "E221: Marker cannot start with lower case letter"
+msgstr "E221: 标记不能以小写字母开头"
-#: ../eval.c:18568
+#: ../eval/vars.c:113
+msgid "E172: Missing marker"
+msgstr "E172: 缺少标记"
+
+#: ../eval/vars.c:124
#, c-format
-msgid "calling %s"
-msgstr "调用 %s"
+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.c:18651
+#: ../eval/vars.c:532
#, c-format
-msgid "%s aborted"
-msgstr "%s 已中止"
+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.c:18653
+#: ../eval/vars.c:1015
#, c-format
-msgid "%s returning #%<PRId64>"
-msgstr "%s 返回 #%<PRId64> "
+msgid "E108: No such variable: \"%s\""
+msgstr "E108: 无此变量:\"%s\""
-#: ../eval.c:18670
+#: ../eval/vars.c:1331
#, c-format
-msgid "%s returning %s"
-msgstr "%s 返回 %s"
+msgid "E963: setting %s to value with wrong type"
+msgstr "E963: 将 %s 设置为类型错误的值"
-#: ../eval.c:18691 ../ex_cmds2.c:2695
+#: ../eval/vars.c:1414
#, c-format
-msgid "continuing in %s"
-msgstr "在 %s 中继续"
+msgid "E794: Cannot set variable in the sandbox: \"%.*s\""
+msgstr "E794: 不能在沙盒中设置变量:\"%.*s\""
-#: ../eval.c:18795
-msgid "E133: :return not inside a function"
-msgstr "E133: :return 不在函数中"
+#: ../eval/vars.c:1460
+#, c-format
+msgid "E795: Cannot delete variable %.*s"
+msgstr "E795: 无法删除变量 %.*s"
-#: ../eval.c:19159
-msgid ""
-"\n"
-"# global variables:\n"
-msgstr ""
-"\n"
-"# 全局变量:\n"
+#: ../eval/vars.c:1483
+#, c-format
+msgid "E704: Funcref variable name must start with a capital: %s"
+msgstr "E704: Funcref 变量名必须以大写字母开头: %s"
-#: ../eval.c:19254
-msgid ""
-"\n"
-"\tLast set from "
-msgstr ""
-"\n"
-"\t最近修改于 "
+#: ../eval/vars.c:1490
+#, c-format
+msgid "E705: Variable name conflicts with existing function: %s"
+msgstr "E705: 变量名与已有函数名冲突: %s"
-#: ../eval.c:19272
-#, fuzzy
-msgid "No old files"
-msgstr "没有包含文件"
+#: ../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:122
+#: ../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:145
+#: ../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:146
+#: ../ex_cmds.c:221
#, c-format
msgid "> %d, Hex %08x, Octal %o"
msgstr "> %d, 十六进制 %08x, 八进制 %o"
-#: ../ex_cmds.c:684
-msgid "E134: Move lines into themselves"
-msgstr "E134: 把行移动到自已中"
+#: ../ex_cmds.c:914
+msgid "E134: Cannot move a range of lines into itself"
+msgstr "E134: 不能把一系列行移动到自身当中"
-#: ../ex_cmds.c:747
+#: ../ex_cmds.c:1021
+#, c-format
msgid "1 line moved"
-msgstr "移动了 1 行"
+msgid_plural "%<PRId64> lines moved"
+msgstr[0] "移动了 %<PRId64> 行"
-#: ../ex_cmds.c:749
+#. will call wait_return()
+#: ../ex_cmds.c:1335
#, c-format
-msgid "%<PRId64> lines moved"
-msgstr "移动了 %<PRId64> 行"
+msgid "E482: Can't create file %s"
+msgstr "E482: 无法创建文件 %s"
-#: ../ex_cmds.c:1175
+#: ../ex_cmds.c:1436
#, c-format
msgid "%<PRId64> lines filtered"
msgstr "过滤了 %<PRId64> 行"
-#: ../ex_cmds.c:1194
+#: ../ex_cmds.c:1458
msgid "E135: *Filter* Autocommands must not change current buffer"
msgstr "E135: *Filter* 自动命令不可以改变当前缓冲区"
-#: ../ex_cmds.c:1244
+#: ../ex_cmds.c:1498
msgid "[No write since last change]\n"
msgstr "[已修改但尚未保存]\n"
-# bad to translate
-#: ../ex_cmds.c:1424
-#, c-format
-msgid "%sviminfo: %s in line: "
-msgstr "%sviminfo: %s 位于行: "
-
-#: ../ex_cmds.c:1431
-msgid "E136: viminfo: Too many errors, skipping rest of file"
-msgstr "E136: viminfo: 错误过多,忽略文件的剩余部分"
-
-#: ../ex_cmds.c:1458
-#, c-format
-msgid "Reading viminfo file \"%s\"%s%s%s"
-msgstr "读取 viminfo 文件 \"%s\"%s%s%s"
-
-#: ../ex_cmds.c:1460
-msgid " info"
-msgstr " 信息"
-
-#: ../ex_cmds.c:1461
-msgid " marks"
-msgstr " 标记"
-
-#: ../ex_cmds.c:1462
-#, fuzzy
-msgid " oldfiles"
-msgstr "没有包含文件"
-
-#: ../ex_cmds.c:1463
-msgid " FAILED"
-msgstr " 失败"
-
-#. avoid a wait_return for this message, it's annoying
-#: ../ex_cmds.c:1541
+#: ../ex_cmds.c:1793
#, c-format
-msgid "E137: Viminfo file is not writable: %s"
-msgstr "E137: Viminfo 文件不可写入: %s"
+msgid "E503: \"%s\" is not a file or writable device"
+msgstr "E503: \"%s\" 不是文件或可写的设备"
-#: ../ex_cmds.c:1626
-#, c-format
-msgid "E138: Can't write viminfo file %s!"
-msgstr "E138: 无法写入 viminfo 文件 %s!"
-
-#: ../ex_cmds.c:1635
-#, c-format
-msgid "Writing viminfo file \"%s\""
-msgstr "写入 viminfo 文件 \"%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"
-
-# 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"
-
-# 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"
-
-#: ../ex_cmds.c:1800
-msgid "Illegal starting char"
-msgstr "无效的启动字符"
-
-#: ../ex_cmds.c:2162
+#: ../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"
@@ -1198,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"
@@ -1209,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
+#: ../ex_cmds.c:4612
#, c-format
-msgid "E661: Sorry, no '%s' help for %s"
-msgstr "E661: 抱歉,没有 '%s' 的 %s 的说明"
-
-#: ../ex_cmds.c:4719
-#, 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> 行"
+msgid "Close \"%s\"?"
+msgstr "关闭 \"%s\"?"
-#: ../ex_cmds2.c:942
-#, fuzzy
-msgid "E750: First use \":profile start {fname}\""
-msgstr "E750: 请先使用 :profile start <fname>"
-
-#: ../ex_cmds2.c:1269
+#: ../ex_cmds2.c:380
#, c-format
-msgid "Save changes to \"%s\"?"
-msgstr "将改变保存到 \"%s\" 吗?"
+msgid "E947: Job still running in buffer \"%s\""
+msgstr "E947: 任务仍在缓冲区 \"%s\" 中运行"
-#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851
-msgid "Untitled"
-msgstr "未命名"
-
-#: ../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_cmds2.c:3020
-msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: 警告: 错误的行分隔符,可能是少了 ^M"
+#: ../ex_docmd.c:90
+msgid "E464: Ambiguous use of user-defined command"
+msgstr "E464: 不确定的用户自定义命令"
-#: ../ex_cmds2.c:3139
-msgid "E167: :scriptencoding used outside of a sourced file"
-msgstr "E167: 在脚本文件外使用了 :scriptencoding"
+#: ../ex_docmd.c:92
+msgid "E492: Not an editor command"
+msgstr "E492: 不是编辑器的命令"
-#: ../ex_cmds2.c:3166
-msgid "E168: :finish used outside of a sourced file"
-msgstr "E168: 在脚本文件外使用了 :finish"
+#: ../ex_docmd.c:94
+msgid "E498: no :source file name to substitute for \"<sfile>\""
+msgstr "E498: 没有用于替换 \"<sfile>\" 的 :source 文件名"
-#: ../ex_cmds2.c:3389
-#, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "当前的 %s语言: \"%s\""
+#: ../ex_docmd.c:96
+msgid "E489: no call stack to substitute for \"<stack>\""
+msgstr "E489: 没有用于替换 \"<stack>\" 的调用栈"
-#: ../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:1006
+#: ../ex_docmd.c:269
+msgid "line %"
+msgstr "在行号 %"
+
+#: ../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:2466
+#: ../fileio.c:2116
+msgid "[New File]"
+msgstr "[新文件]"
+
+#: ../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"
@@ -2279,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> 行,"
+msgid "%<PRId64> line, "
+msgid_plural "%<PRId64> lines, "
+msgstr[0] "%<PRId64> 行,"
-#: ../fileio.c:3836
-msgid "1 character"
-msgstr "1 个字符"
-
-#: ../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:1024
+#: ../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: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 "E46: Cannot change read-only variable \"%s\""
-msgstr "E46: 不能改变只读变量 \"%s\""
+msgid "E734: Wrong variable type for %s="
+msgstr "E734: %s= 的变量类型不正确"
-#: ../globals.h:1075
-#, fuzzy, c-format
-msgid "E794: Cannot set variable in the sandbox: \"%s\""
-msgstr "E46: 不能在 sandbox 中设定变量: \"%s\""
+#: ../globals.h:940
+#, c-format
+msgid "E461: Illegal variable name: %s"
+msgstr "E461: 变量名无效: %s"
+
+#: ../globals.h:941
+msgid "E995: Cannot modify existing variable"
+msgstr "E995: 不能修改已有变量"
+
+#: ../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:1076
+#: ../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:1114
+#: ../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: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"
-#: ../hardcopy.c:1772
+#: ../highlight.c:95
+msgid "E424: Too many different highlighting attributes in use"
+msgstr "E424: 使用了太多不同的高亮度属性"
+
+#: ../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"
-#: ../hardcopy.c:2254
-msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: 没有指定多字节打印的默认字体"
+#: ../highlight_group.c:1051 ../highlight_group.c:1099
+msgid "E423: Illegal argument"
+msgstr "E423: 无效的参数"
-#: ../hardcopy.c:2426
-msgid "E324: Can't open PostScript output file"
-msgstr "E324: 无法打开 PostScript 输出文件"
+#: ../highlight_group.c:1072
+#, c-format
+msgid "E416: missing equal sign: %s"
+msgstr "E416: 缺少等号: %s"
+
+#: ../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: 组名中存在不可显示字符"
+
+#: ../highlight_group.c:1872
+msgid "E849: Too many highlight and syntax groups"
+msgstr "E849: 高亮和语法组过多"
-#: ../hardcopy.c:2877
-msgid "Sending to printer..."
-msgstr "发送到打印机……"
+#: ../input.c:232
+msgid "Type number and <Enter> or click with the mouse (q or empty cancels): "
+msgstr "输入数字并按 <Enter> 或点击鼠标(q 或空取消):"
-#: ../hardcopy.c:2881
-msgid "E365: Failed to print PostScript file"
-msgstr "E365: 无法打印 PostScript 文件"
+#: ../input.c:235
+msgid "Type number and <Enter> (q or empty cancels): "
+msgstr "输入数字并按 <Enter>(q 或空取消):"
-#: ../hardcopy.c:2883
-msgid "Print job sent."
-msgstr "打印任务已被发送。"
+#: ../insexpand.c:99
+msgid " Keyword completion (^N^P)"
+msgstr " 关键字补全 (^N^P)"
-#: ../if_cscope.c:85
-msgid "Add a new database"
-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:87
-msgid "Query for a pattern"
-msgstr "查询一个模式"
+#. CTRL_X_SCROLL: depends on state
+#: ../insexpand.c:102
+msgid " Whole line completion (^L^N^P)"
+msgstr " 整行补全 (^L^N^P)"
-#: ../if_cscope.c:89
-msgid "Show this message"
-msgstr "显示此信息"
+#: ../insexpand.c:103
+msgid " File name completion (^F^N^P)"
+msgstr " 文件名补全 (^F^N^P)"
-#: ../if_cscope.c:91
-msgid "Kill a connection"
-msgstr "结束一个连接"
+#: ../insexpand.c:104
+msgid " Tag completion (^]^N^P)"
+msgstr " Tag 补全 (^]^N^P)"
-#: ../if_cscope.c:93
-msgid "Reinit all connections"
-msgstr "重置所有连接"
+#: ../insexpand.c:105
+msgid " Path pattern completion (^N^P)"
+msgstr " 头文件模式补全 (^N^P)"
-#: ../if_cscope.c:95
-msgid "Show connections"
-msgstr "显示连接"
+#: ../insexpand.c:106
+msgid " Definition completion (^D^N^P)"
+msgstr " 定义补全 (^D^N^P)"
-#: ../if_cscope.c:101
-#, c-format
-msgid "E560: Usage: cs[cope] %s"
-msgstr "E560: 用法: cs[cope] %s"
+#. CTRL_X_FINISHED
+#: ../insexpand.c:108
+msgid " Dictionary completion (^K^N^P)"
+msgstr " Dictionary 补全 (^K^N^P)"
-#: ../if_cscope.c:225
-msgid "This cscope command does not support splitting the window.\n"
-msgstr "这个 cscope 命令不支持分割窗口。\n"
+#: ../insexpand.c:109
+msgid " Thesaurus completion (^T^N^P)"
+msgstr " Thesaurus 补全 (^T^N^P)"
-#: ../if_cscope.c:266
-msgid "E562: Usage: cstag <ident>"
-msgstr "E562: 用法: cstag <ident>"
+#. 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:313
-msgid "E257: cstag: tag not found"
-msgstr "E257: cstag: 找不到 tag"
+#: ../insexpand.c:111
+msgid " User defined completion (^U^N^P)"
+msgstr " 用户自定义补全 (^U^N^P)"
-#: ../if_cscope.c:461
-#, c-format
-msgid "E563: stat(%s) error: %d"
-msgstr "E563: stat(%s) 错误: %d"
+#: ../insexpand.c:112
+msgid " Omni completion (^O^N^P)"
+msgstr " 全能补全 (^O^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:113
+msgid " Spelling suggestion (s^N^P)"
+msgstr " 拼写建议 (s^N^P)"
-#: ../if_cscope.c:566
-#, c-format
-msgid "Added cscope database %s"
-msgstr "添加了 cscope 数据库 %s"
+#: ../insexpand.c:114
+msgid " Keyword Local completion (^N^P)"
+msgstr " 关键字局部补全 (^N^P)"
+
+#: ../insexpand.c:190
+msgid "Hit end of paragraph"
+msgstr "已到段落结尾"
-#: ../if_cscope.c:616
+#: ../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 " 增加"
-#: ../if_cscope.c:890
-msgid "E623: Could not spawn cscope process"
-msgstr "E623: 无法生成 cscope 进程"
+#. 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 "-- 查找中..."
+
+#: ../insexpand.c:4263
+msgid "Back at original"
+msgstr "回到起点"
-#: ../if_cscope.c:932
-msgid "E567: no cscope connections"
-msgstr "E567: 没有 cscope 连接"
+#: ../insexpand.c:4266
+msgid "Word from other line"
+msgstr "另一行的词"
+
+#: ../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 ""
+
+#: ../lua/executor.c:1928
+#, c-format
+msgid "Error executing vim.on_key Lua callback: %.*s"
+msgstr ""
-#: ../main.c:148
+#: ../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:2099
+msgid " -- Only file names after this\n"
+msgstr " -- 在这以后只有文件名\n"
-#: ../main.c:2193
-msgid ""
-"\n"
-" or:"
-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"
@@ -3590,7 +4228,7 @@ msgstr ""
"标记 行 列 文件/文本"
#. Highlight title
-#: ../mark.c:789
+#: ../mark.c:1002
msgid ""
"\n"
" jump line col file/text"
@@ -3599,7 +4237,7 @@ msgstr ""
" 跳转 行 列 文件/文本"
#. Highlight title
-#: ../mark.c:831
+#: ../mark.c:1054
msgid ""
"\n"
"change line col text"
@@ -3607,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."
@@ -3717,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."
@@ -3746,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"
@@ -3847,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: "
@@ -3919,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: "
@@ -3935,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: "
@@ -3947,7 +4630,7 @@ msgstr ""
"\n"
" 主机名: "
-#: ../memline.c:1575
+#: ../memline.c:1479
msgid ""
"\n"
" process ID: "
@@ -3955,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]"
@@ -3967,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 \""
@@ -4065,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"
@@ -4110,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"
@@ -4122,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"
@@ -4152,7 +4833,7 @@ msgstr ""
"退出(&Q)\n"
"中止(&A)"
-#: ../memline.c:3467
+#: ../memline.c:3314
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
@@ -4168,56 +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
+#: ../menu.c:310
msgid "E792: Empty menu name"
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 ---"
@@ -4225,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"
@@ -4295,7 +4998,7 @@ msgstr ""
"是(&Y)\n"
"否(&N)"
-#: ../message.c:3033
+#: ../message.c:3781
msgid ""
"&Yes\n"
"&No\n"
@@ -4305,7 +5008,7 @@ msgstr ""
"否(&N)\n"
"取消(&C)"
-#: ../message.c:3045
+#: ../message.c:3795
msgid ""
"&Yes\n"
"&No\n"
@@ -4319,206 +5022,123 @@ msgstr ""
"全部丢弃(&D)\n"
"取消(&C)"
-#: ../message.c:3058
-msgid "E766: Insufficient arguments for printf()"
-msgstr "E766: printf() 的参数不足"
-
-#: ../message.c:3119
-msgid "E807: Expected Float argument for printf()"
-msgstr "E807: 期盼浮点数作为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> 行"
-
-#: ../ops.c:2521
-msgid "block of 1 line yanked"
-msgstr "复制了 1 行的块"
+msgid "%<PRId64> line changed"
+msgid_plural "%<PRId64> lines changed"
+msgstr[0] "改变了 %<PRId64> 行"
-#: ../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"
+"E883: search pattern and expression register may not contain two or more "
+"lines"
+msgstr "E883: 搜索模式和表达式寄存器只能包含一行文本"
-#: ../ops.c:4575
-#, c-format
-msgid "E574: Unknown register type %d"
-msgstr "E574: 未知的寄存器类型 %d"
-
-#: ../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; "
@@ -4527,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; "
@@ -4536,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 "
@@ -4545,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 "
@@ -4554,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 ---"
@@ -4711,7 +5242,7 @@ msgstr ""
"\n"
"--- 全局选项值 ---"
-#: ../option.c:6153
+#: ../option.c:3200
msgid ""
"\n"
"--- Local option values ---"
@@ -4719,7 +5250,7 @@ msgstr ""
"\n"
"--- 局部选项值 ---"
-#: ../option.c:6155
+#: ../option.c:3202
msgid ""
"\n"
"--- Options ---"
@@ -4727,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"
-#: ../path.c:1449
+#: ../os/time.c:191
+msgid "%a %b %d %H:%M:%S %Y"
+msgstr "%Y-%m-%d %H:%M:%S"
+
+#: ../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: 此处需要字符串或列表"
+
+#: ../quickfix.c:7246
+#, c-format
+msgid "E927: Invalid action: '%s'"
+msgstr "E927: 动作无效:'%s'"
-#: ../regexp.c:359
+#: ../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:117
+msgid "E956: Cannot use pattern recursively"
+msgstr "E956: 不能递归地使用模式"
-#: ../regexp.c:1371
-msgid "E50: Too many \\z("
-msgstr "E50: 太多 \\z("
-
-#: ../regexp.c:1378
+#: ../regexp.c:119
#, c-format
-msgid "E51: Too many %s("
-msgstr "E51: 太多 %s("
-
-#: ../regexp.c:1427
-msgid "E52: Unmatched \\z("
-msgstr "E52: 不匹配的 \\z("
+msgid "E1204: No Number allowed after .: '\\%%%c'"
+msgstr "E1204: . 后不能加整数: '\\%%%c'"
-#: ../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"
+
+#: ../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:7435
+#: ../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"
+msgstr " 阿拉伯文"
-#: ../screen.c:7456
-msgid " (lang)"
-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 ""
+
+#: ../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 ""
-#: ../search.c:4681
+#: ../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 ---"
+
+#: ../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 定义过多"
-#: ../spell.c:951
+#: ../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 "
@@ -5382,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 "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 %d: %s"
+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
-#, c-format
-msgid "E782: error while reading .sug file: %s"
-msgstr "E782: 当读取.sug 文件时错误"
-
-#. 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:2941
+msgid "syntax iskeyword not set"
+msgstr "syntax iskeyword 未设置"
-#: ../syntax.c:3299
+#: ../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 ---"
@@ -5717,7 +6668,7 @@ msgstr ""
"\n"
"--- 语法同步项目 (Syntax sync items) ---"
-#: ../syntax.c:3452
+#: ../syntax.c:3257
msgid ""
"\n"
"syncing on items"
@@ -5725,7 +6676,7 @@ msgstr ""
"\n"
"同步中:"
-#: ../syntax.c:3457
+#: ../syntax.c:3262
msgid ""
"\n"
"--- Syntax items ---"
@@ -5733,277 +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:4531
+#: ../syntax.c:4112
+#, c-format
+msgid "E890: trailing char after ']': %s]%s"
+msgstr "E890: ']' 后有尾随的字符:%s]%s"
+
+#: ../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: 组名中存在不可显示字符"
-
-#: ../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 "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"
@@ -6011,1619 +6896,2498 @@ 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 文件"
+msgstr "字段名重复:%s"
-#: ../term.c:1735
-msgid "E558: Terminal entry not found in terminfo"
-msgstr "E558: 在 terminfo 中找不到终端项"
+#: ../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:1737
-msgid "E559: Terminal entry not found in termcap"
-msgstr "E559: 在 termcap 中找不到终端项"
+#: ../testing.c:40
+msgid "E1115: assert_fails() fourth argument must be a number"
+msgstr "E1115: assert_fails() 的第四个参数必须是整数"
-#: ../term.c:1878
-#, c-format
-msgid "E436: No \"%s\" entry in termcap"
-msgstr "E436: termcap 中没有 \"%s\" 项"
+#: ../testing.c:42
+msgid "E1116: assert_fails() fifth argument must be a string"
+msgstr "E1116: assert_fails() 的第五个参数必须是字符串"
-#: ../term.c:2249
-msgid "E437: terminal capability \"cm\" required"
-msgstr "E437: 终端需要能力 \"cm\""
-
-#. 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
+#. file in a way it becomes shorter.
+#: ../undo.c:357
msgid "E881: Line count changed unexpectedly"
msgstr "E881: 行数意外地改变了"
-#: ../undo.c:627
+#: ../undo.c:628
#, c-format
msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E828: 无法打开撤销文件去写入"
+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: 没有指定属性"
+
+#: ../usercmd.c:710
+msgid "E176: Invalid number of arguments"
+msgstr "E176: 无效的参数个数"
+
+#: ../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 需要参数"
-#: ../version.c:639 ../version.c:864
-msgid "Modified by "
-msgstr "修改者 "
+#: ../usercmd.c:774
+msgid "E179: argument required for -addr"
+msgstr "E179: -addr 需要参数"
-#: ../version.c:646
+#: ../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:2805
+msgid "Nvim is open source and freely distributable"
+msgstr "Nvim 是可自由分发的开放源代码软件"
-#: ../version.c:712
-msgid "Linking: "
-msgstr "链接方式: "
+#: ../version.c:2806
+msgid "https://neovim.io/#chat"
+msgstr ""
-#: ../version.c:717
-msgid " DEBUG BUILD"
-msgstr " 调试版本"
+#: ../version.c:2808
+msgid "type :help nvim<Enter> if you are new! "
+msgstr ""
-#: ../version.c:767
-msgid "VIM - Vi IMproved"
-msgstr "VIM - Vi IMproved"
+#: ../version.c:2809
+msgid "type :checkhealth<Enter> to optimize Nvim"
+msgstr ""
-#: ../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 ""
-#: ../version.c:774
-msgid "Vim is open source and freely distributable"
-msgstr "Vim 是可自由分发的开放源代码软件"
+#: ../version.c:2813
+msgid "type :help news<Enter> to see changes in"
+msgstr ""
-#: ../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"
@@ -7634,17 +9398,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"
@@ -7669,6 +9440,13 @@ msgstr "E446: 光标处没有文件名"
#~ msgid ""
#~ "\n"
+#~ "Normal version "
+#~ msgstr ""
+#~ "\n"
+#~ "正常版本 "
+
+#~ msgid ""
+#~ "\n"
#~ "RISC OS version"
#~ msgstr ""
#~ "\n"
@@ -7676,73 +9454,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 文件: \""
@@ -7750,182 +9918,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 e95b1e2cad..cba95e2af2 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -3024,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 "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
-#, 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 "新增資料庫"
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 0d9080ceb7..1d55b2ef31 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;
}
@@ -159,10 +167,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
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,
@@ -264,12 +272,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;
@@ -397,8 +408,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;
@@ -507,24 +518,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 = reverse_text((char *)st);
+ char *rt = reverse_text(st);
char *rt_start = rt;
int size = vim_strsize(rt);
@@ -542,13 +553,13 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&pum_grid, 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;
} else {
// use grid_puts_len() to truncate the text
- grid_puts(&pum_grid, (char *)st, row, grid_col, attr);
+ grid_puts(&pum_grid, st, row, grid_col, attr);
xfree(st);
grid_col += width;
}
@@ -751,20 +762,19 @@ static bool pum_set_selected(int n, int repeat)
}
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
@@ -818,7 +828,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)) {
@@ -830,7 +840,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;
}
}
@@ -901,6 +911,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.
@@ -1030,10 +1051,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;
}
}
@@ -1045,6 +1072,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;
@@ -1100,8 +1131,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 cded231c85..acbaf09a6e 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
@@ -283,31 +297,31 @@ void ex_profile(exarg_T *eap)
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 = (char *)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 = (char_u *)skiptowhite(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);
@@ -684,7 +701,7 @@ 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;
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index f9c4892b91..c895ac16f1 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"
@@ -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
@@ -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;
@@ -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)) {
+ 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);
@@ -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
@@ -1098,7 +1114,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu
// 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);
@@ -1175,11 +1191,11 @@ 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;
+ size_t len = strlen(title) + 1;
char *p = xmallocz(len);
qfl->qf_title = p;
- STRLCPY(p, title, len + 1);
+ xstrlcpy(p, title, len + 1);
}
}
@@ -1242,15 +1258,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((char *)rmp->startp[midx], 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 +1280,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 +1291,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 +1302,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 +1313,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 +1324,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 +1335,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 +1349,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 +1368,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 +1379,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 +1391,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 +1411,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 +1428,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 +1443,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;
}
@@ -1480,7 +1499,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);
}
@@ -1564,7 +1583,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 +1607,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 +1621,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);
@@ -1790,7 +1802,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)
@@ -2063,13 +2075,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 +2097,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 +2128,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);
@@ -2153,12 +2165,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 +2234,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;
}
@@ -2686,6 +2697,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 +2717,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 +2733,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 +2750,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 +2785,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 +2798,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 +2821,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 +2853,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 +2878,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 +2887,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)
{
@@ -2965,14 +2983,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;
}
@@ -3016,7 +3039,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 +3050,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);
}
}
@@ -3054,7 +3077,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
}
msg_putchar('\n');
- msg_outtrans_attr((char *)IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr);
+ msg_outtrans_attr(IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr);
if (qfp->qf_lnum != 0) {
msg_puts_attr(":", qfSepAttr);
@@ -3062,21 +3085,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 +3111,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
@@ -3200,18 +3221,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 +3253,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);
@@ -3460,12 +3481,10 @@ 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_no_errors));
return;
@@ -3834,7 +3853,7 @@ static buf_T *qf_find_buf(qf_info_T *qi)
/// @return OK or FAIL
int qf_process_qftf_option(void)
{
- return option_set_callback_func((char_u *)p_qftf, &qftf_cb);
+ return option_set_callback_func(p_qftf, &qftf_cb);
}
/// Update the w:quickfix_title variable in the quickfix/location list window in
@@ -3868,14 +3887,22 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
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;
}
+ // autocommands may cause trouble
+ incr_quickfix_busy();
+
aco_save_T aco;
if (old_last == NULL) {
@@ -3900,6 +3927,9 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
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();
}
}
@@ -3911,18 +3941,18 @@ 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
@@ -3931,13 +3961,13 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli
&& (errbuf->b_sfname == NULL
|| path_is_absolute((char_u *)errbuf->b_sfname))) {
if (*dirname == NUL) {
- os_dirname((char_u *)dirname, MAXPATHL);
+ os_dirname(dirname, MAXPATHL);
}
shorten_buf_fname(errbuf, (char_u *)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 +3975,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 +3994,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) {
+ if (ml_append_buf(buf, lnum, (char_u *)IObuff,
+ (colnr_T)strlen(IObuff) + 1, false) == FAIL) {
return FAIL;
}
return OK;
@@ -3980,6 +4010,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 +4049,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 +4076,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;
@@ -4182,8 +4219,7 @@ int grep_internal(cmdidx_T cmdidx)
|| cmdidx == CMD_lgrep
|| cmdidx == CMD_grepadd
|| cmdidx == CMD_lgrepadd)
- && STRCMP("internal",
- *curbuf->b_p_gp == NUL ? p_gp : (char_u *)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,12 +4249,12 @@ 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) {
@@ -4275,10 +4311,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 +4362,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));
}
@@ -4345,9 +4388,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;
@@ -5075,7 +5118,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 +5163,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,7 +5182,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);
+ 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;
@@ -5155,7 +5197,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,
@@ -5177,12 +5219,12 @@ 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 {
- 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]);
@@ -5220,7 +5262,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 +5278,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 +5304,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 +5347,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(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 +5370,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 +5459,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);
}
@@ -5541,8 +5581,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 = {
@@ -5622,6 +5662,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 +5675,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)) {
@@ -6500,7 +6541,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 +6709,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 +6721,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 +6738,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 +6856,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();
@@ -6952,10 +7002,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 +7020,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 +7030,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 +7062,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;
}
@@ -7035,9 +7085,9 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char
// Go through all directories in 'runtimepath'
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 +7095,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:
@@ -7065,6 +7115,7 @@ 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;
+ const bool save_cpo_allocated = is_option_allocated("cpo");
p_cpo = empty_option;
bool new_qi = false;
@@ -7104,7 +7155,9 @@ void ex_helpgrep(exarg_T *eap)
if (*p_cpo == NUL) {
set_option_value_give_err("cpo", 0L, save_cpo, 0);
}
- free_string_option(save_cpo);
+ if (save_cpo_allocated) {
+ free_string_option(save_cpo);
+ }
}
if (updated) {
@@ -7139,7 +7192,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;
}
}
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 a52343e28b..e396e54ced 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)
@@ -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,19 +316,13 @@ 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
@@ -388,11 +386,9 @@ 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;
@@ -410,12 +406,10 @@ 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;
@@ -482,15 +476,37 @@ static char_u *skip_anyof(char *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 *skip_regexp(char *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;
+ magic_T mymagic;
char *p = startp;
if (magic) {
@@ -517,6 +533,9 @@ char *skip_regexp(char *startp, int dirc, int magic, char **newp)
*newp = xstrdup(startp);
p = *newp + (p - startp);
}
+ if (dropped != NULL) {
+ (*dropped)++;
+ }
STRMOVE(p, p + 1);
} else {
p++; // skip next character
@@ -528,6 +547,9 @@ char *skip_regexp(char *startp, int dirc, int magic, char **newp)
}
}
}
+ if (magic_val != NULL) {
+ *magic_val = mymagic;
+ }
return p;
}
@@ -536,9 +558,7 @@ 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".
- */
+// Start parsing at "str".
static void initchr(char_u *str)
{
regparse = (char *)str;
@@ -548,10 +568,8 @@ static void initchr(char_u *str)
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;
@@ -565,9 +583,7 @@ 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;
@@ -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;
@@ -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,12 +885,10 @@ 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;
@@ -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,13 +931,9 @@ 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
@@ -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;
lpos_T *reg_startpos;
lpos_T *reg_endpos;
+
win_T *reg_win;
buf_T *reg_buf;
linenr_T reg_firstlnum;
@@ -1026,9 +1018,7 @@ 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".
- */
+// Get pointer to the line "lnum", which is relative to "reg_firstlnum".
static char_u *reg_getline(linenr_T lnum)
{
// when looking behind for a match/no-match lnum is negative. But we
@@ -1040,7 +1030,7 @@ static char_u *reg_getline(linenr_T lnum)
// Must have matched the "\n" in the last line.
return (char_u *)"";
}
- return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false);
+ return (char_u *)ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false);
}
static char_u *reg_startzp[NSUBEXP]; // Workspace to mark beginning
@@ -1051,9 +1041,7 @@ 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;
}
@@ -1165,7 +1150,7 @@ static bool reg_match_visual(void)
rex.line = reg_getline(rex.lnum);
rex.input = rex.line + col;
- unsigned int cols_u = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, 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,11 +1179,9 @@ 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) {
@@ -1239,19 +1220,17 @@ static void reg_nextline(void)
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 +1239,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);
@@ -1273,16 +1252,16 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
}
// Get the line to compare with.
- p = reg_getline(clnum);
+ p = (char *)reg_getline(clnum);
assert(p);
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 +1307,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 +1373,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;
@@ -1424,9 +1402,9 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
c1 = mb_ptr2char_adv((const char_u **)&str1);
c2 = mb_ptr2char_adv((const char_u **)&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);
@@ -1523,32 +1501,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 +1551,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 +1560,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 +1583,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 +1599,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;
@@ -1677,7 +1653,7 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, in
rex.reg_maxline = 0;
rex.reg_buf = curbuf;
rex.reg_line_lbr = true;
- int result = vim_regsub_both(source, expr, dest, destlen, flags);
+ int result = vim_regsub_both((char *)source, expr, (char *)dest, destlen, flags);
rex_in_use = rex_in_use_save;
if (rex_in_use) {
@@ -1705,7 +1681,7 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
rex.reg_firstlnum = lnum;
rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
rex.reg_line_lbr = false;
- int result = vim_regsub_both(source, NULL, dest, destlen, flags);
+ int result = vim_regsub_both((char *)source, NULL, (char *)dest, destlen, flags);
rex_in_use = rex_in_use_save;
if (rex_in_use) {
@@ -1717,7 +1693,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 +1704,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 +1745,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 +1781,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 +1802,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 +1824,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 +1837,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 +1851,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) {
@@ -1914,7 +1889,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 +1926,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 +1941,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 +1949,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.
@@ -1999,12 +1974,12 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
if (clnum < 0 || rex.reg_mmatch->endpos[no].lnum < 0) {
s = NULL;
} else {
- s = reg_getline(clnum) + rex.reg_mmatch->startpos[no].col;
+ s = (char *)reg_getline(clnum) + rex.reg_mmatch->startpos[no].col;
if (rex.reg_mmatch->endpos[no].lnum == clnum) {
len = rex.reg_mmatch->endpos[no].col
- rex.reg_mmatch->startpos[no].col;
} else {
- len = (int)STRLEN(s);
+ len = (int)strlen(s);
}
}
} else {
@@ -2030,11 +2005,11 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
*dst = CAR;
}
dst++;
- s = reg_getline(++clnum);
+ s = (char *)reg_getline(++clnum);
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 +2035,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 +2053,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 +2063,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,36 +2087,33 @@ 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;
rex.reg_firstlnum = rsm.sm_firstlnum;
rex.reg_maxline = rsm.sm_maxline;
- s = reg_getline(lnum);
+ s = (char *)reg_getline(lnum);
rex.reg_firstlnum = save_first;
rex.reg_maxline = save_max;
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 +2124,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 +2141,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 +2159,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 +2186,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 +2221,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 +2246,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,
@@ -2309,21 +2297,19 @@ static char_u regname[][30] = {
};
#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 +2339,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((char_u *)expr,
re_flags + (regexp_engine == AUTOMATIC_ENGINE ? RE_AUTO : 0));
} else {
- prog = bt_regengine.regcomp(expr, re_flags);
+ prog = bt_regengine.regcomp((char_u *)expr, re_flags);
}
// Check for error compiling regexp with initial engine.
@@ -2380,8 +2366,8 @@ regprog_T *vim_regcomp(char *expr_arg, int re_flags)
// But don't try if an error message was given.
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);
+ report_re_switch((char_u *)expr);
+ prog = bt_regengine.regcomp((char_u *)expr, re_flags);
}
}
@@ -2395,9 +2381,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) {
@@ -2469,12 +2453,12 @@ 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);
+ report_re_switch((char_u *)pat);
+ 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);
@@ -2560,16 +2544,16 @@ 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;
- report_re_switch(pat);
+ report_re_switch((char_u *)pat);
// 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 88f0d781af..f930d872b6 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 +.
@@ -273,10 +263,8 @@ static int classcodes[] = {
UPPER, NUPPER
};
-/*
- * When regcode is set to this value, code is not emitted and size is computed
- * instead.
- */
+// When regcode is set to this value, code is not emitted and size is computed
+// instead.
#define JUST_CALC_SIZE ((char_u *)-1)
// Values for rs_state in regitem_T.
@@ -297,11 +285,9 @@ 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
@@ -327,12 +313,10 @@ 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
@@ -359,69 +343,63 @@ typedef struct backpos_S {
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)
@@ -449,9 +427,7 @@ 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.
- */
+// Setup to parse the regexp. Used once to get the length and once to do it.
static void regcomp_start(char_u *expr, int re_flags) // see vim_regcomp()
{
initchr(expr);
@@ -484,9 +460,7 @@ 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) {
@@ -496,9 +470,7 @@ static void regc(int 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,10 +1451,8 @@ static void reg_equi_class(int c)
regmbc(c);
}
-/*
- * Emit a node.
- * Return pointer to generated code.
- */
+// Emit a node.
+// Return pointer to generated code.
static char_u *regnode(int op)
{
char_u *ret;
@@ -1500,9 +1468,7 @@ static char_u *regnode(int op)
return ret;
}
-/*
- * Write a four bytes number at "p" and return pointer to the next char.
- */
+// Write a four bytes number at "p" and return pointer to the next char.
static char_u *re_put_uint32(char_u *p, uint32_t val)
{
*p++ = (char_u)((val >> 24) & 0377);
@@ -1512,11 +1478,9 @@ static char_u *re_put_uint32(char_u *p, uint32_t val)
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.
- */
+// regnext - dig the "next" pointer out of a node
+// Returns NULL when calculating size, when there is no next item and when
+// there is an error.
static char_u *regnext(char_u *p)
FUNC_ATTR_NONNULL_ALL
{
@@ -1573,9 +1537,7 @@ static void regtail(char_u *p, char_u *val)
}
}
-/*
- * Like regtail, on item after a BRANCH; nop if none.
- */
+// Like regtail, on item after a BRANCH; nop if none.
static void regoptail(char_u *p, char_u *val)
{
// When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless"
@@ -1587,11 +1549,9 @@ 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.
- */
+// Insert an operator in front of already-emitted operand
+//
+// Means relocating the operand.
static void reginsert(int op, char_u *opnd)
{
char_u *src;
@@ -1615,10 +1575,8 @@ static void reginsert(int op, char_u *opnd)
*place = NUL;
}
-/*
- * Insert an operator in front of already-emitted operand.
- * Add a number to the operator.
- */
+// Insert an operator in front of already-emitted operand.
+// Add a number to the operator.
static void reginsert_nr(int op, long val, char_u *opnd)
{
char_u *src;
@@ -1644,12 +1602,10 @@ static void reginsert_nr(int op, long val, char_u *opnd)
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.
- */
+// Insert an operator in front of already-emitted operand.
+// The operator has the given limit values as operands. Also set next pointer.
+//
+// Means relocating the operand.
static void reginsert_limits(int op, long minval, long maxval, char_u *opnd)
{
char_u *src;
@@ -1704,13 +1660,11 @@ 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.
- */
+// Parse the lowest level.
+//
+// Optimization: gobbles an entire sequence of ordinary characters so that
+// it can turn them into a single node, which is smaller to store and
+// faster to run. Don't do this when one_exactly is set.
static char_u *regatom(int *flagp)
{
char_u *ret;
@@ -1857,14 +1811,14 @@ static char_u *regatom(int *flagp)
char_u *lp;
ret = regnode(EXACTLY);
- lp = reg_prev_sub;
+ lp = (char_u *)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 - (char_u *)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;
@@ -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();
}
@@ -2115,9 +2076,9 @@ static char_u *regatom(int *flagp)
*regcode++ = (char_u)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;
}
@@ -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,15 +2453,13 @@ 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.
- */
+// Parse something followed by possible [*+=].
+//
+// Note that the branching code sequences used for = and the general cases
+// of * and + are somewhat optimized: they use the same NOTHING node as
+// both the endmarker for their branch list and the body of the last branch.
+// It might seem that this node could be dispensed with entirely, but the
+// endmarker role is not redundant.
static char_u *regpiece(int *flagp)
{
char_u *ret;
@@ -2637,10 +2595,8 @@ static char_u *regpiece(int *flagp)
return ret;
}
-/*
- * Parse one alternative of an | or & operator.
- * Implements the concatenation operator.
- */
+// Parse one alternative of an | or & operator.
+// Implements the concatenation operator.
static char_u *regconcat(int *flagp)
{
char_u *first = NULL;
@@ -2715,10 +2671,8 @@ static char_u *regconcat(int *flagp)
return first;
}
-/*
- * Parse one alternative of an | operator.
- * Implements the & operator.
- */
+// Parse one alternative of an | operator.
+// Implements the & operator.
static char_u *regbranch(int *flagp)
{
char_u *ret;
@@ -2867,27 +2821,25 @@ 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.
- */
+// bt_regcomp() - compile a regular expression into internal code for the
+// traditional back track matcher.
+// Returns the program in allocated space. Returns NULL for an error.
+//
+// We can't allocate space until we know how big the compiled form will be,
+// but we can't compile it (and thus know how big it is) until we've got a
+// place to put the code. So we cheat: we compile it twice, once with code
+// generation turned off and size counting turned on, and once "for real".
+// This also means that we don't allocate space until we are sure that the
+// thing really will compile successfully, and we never have to move the
+// code and thus invalidate pointers into it. (Note that it has to be in
+// one piece because free() must be able to free it all.)
+//
+// Whether upper/lower case is to be ignored is decided when executing the
+// program, it does not matter here.
+//
+// Beware that the optimization-preparation code in here knows about some
+// of the structure of the compiled regexp.
+// "re_flags": RE_MAGIC and/or RE_STRING.
static regprog_T *bt_regcomp(char_u *expr, int re_flags)
{
char_u *scan;
@@ -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;
@@ -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;
@@ -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) {
@@ -3487,10 +3429,8 @@ do_class:
return (int)count;
}
-/*
- * Push an item onto the regstack.
- * Returns pointer to new item. Returns NULL when out of memory.
- */
+// Push an item onto the regstack.
+// Returns pointer to new item. Returns NULL when out of memory.
static regitem_T *regstack_push(regstate_T state, char_u *scan)
{
regitem_T *rp;
@@ -3509,9 +3449,7 @@ static regitem_T *regstack_push(regstate_T state, char_u *scan)
return rp;
}
-/*
- * Pop an item from the regstack.
- */
+// Pop an item from the regstack.
static void regstack_pop(char_u **scan)
{
regitem_T *rp;
@@ -3601,8 +3539,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 +3566,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");
}
}
}
@@ -3720,7 +3658,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
@@ -3765,7 +3703,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
? curwin : rex.reg_win,
rex.reg_firstlnum + rex.lnum,
- rex.line,
+ (char *)rex.line,
(colnr_T)(rex.input - rex.line)) + 1,
scan)) {
status = RA_NOMATCH;
@@ -3778,7 +3716,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) {
@@ -3794,7 +3732,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) {
@@ -3829,7 +3767,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();
@@ -3838,7 +3776,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();
@@ -4038,15 +3976,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
@@ -4270,7 +4208,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;
}
}
@@ -4283,8 +4221,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 {
@@ -4319,8 +4257,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;
@@ -4636,7 +4574,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;
@@ -4646,7 +4584,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;
}
@@ -4745,7 +4683,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;
@@ -4758,15 +4696,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);
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 {
@@ -4849,7 +4787,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
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);
@@ -4975,13 +4913,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));
+ (char_u *)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]));
+ (char_u *)xstrnsave((char *)reg_startzp[i], (size_t)(reg_endzp[i] - reg_startzp[i]));
}
}
}
@@ -4992,15 +4930,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(char_u *line, colnr_T startcol, proftime_T *tm, int *timed_out)
{
bt_regprog_T *prog;
char_u *s;
+ colnr_T col = startcol;
long retval = 0L;
// Create "regstack" and "backpos" if they are not allocated yet.
@@ -5028,8 +4967,8 @@ static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *time
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 = (char_u **)rex.reg_match->startp;
+ rex.reg_endp = (char_u **)rex.reg_match->endp;
}
// Be paranoid...
@@ -5069,14 +5008,14 @@ static long bt_regexec_both(char_u *line, colnr_T col, proftime_T *tm, int *time
// 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) {
+ 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) {
+ if (cstrncmp((char *)s, (char *)prog->regmust, &prog->regmlen) == 0) {
break; // Found it.
}
MB_PTR_ADV(s);
@@ -5175,10 +5114,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;
}
}
@@ -5226,23 +5173,11 @@ 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.
- */
+// Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL.
static int re_num_cmp(uint32_t val, char_u *scan)
{
uint32_t n = (uint32_t)OPERAND_MIN(scan);
@@ -5258,9 +5193,7 @@ 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
- */
+// regdump - dump a regexp onto stdout in vaguely comprehensible form
static void regdump(char_u *pattern, bt_regprog_T *r)
{
char_u *s;
@@ -5346,9 +5279,7 @@ static void regdump(char_u *pattern, bt_regprog_T *r)
#ifdef REGEXP_DEBUG
-/*
- * regprop - printable representation of opcode
- */
+// regprop - printable representation of opcode
static char_u *regprop(char_u *op)
{
char *p;
@@ -5594,7 +5525,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:
@@ -5609,7 +5540,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:
@@ -5621,7 +5552,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:
@@ -5639,7 +5570,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:
@@ -5651,7 +5582,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:
@@ -5663,7 +5594,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:
@@ -5703,7 +5634,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:
@@ -5713,7 +5645,7 @@ 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;
}
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 d4db710d93..6e7ff31c03 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>
@@ -282,6 +280,7 @@ typedef struct {
char_u *end;
} line[NSUBEXP];
} list;
+ colnr_T orig_start_col; // list.multi[0].start_col without \zs
} regsub_T;
typedef struct {
@@ -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,11 +516,9 @@ 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.
- */
+// 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)
{
nfa_state_T *p = start;
@@ -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,16 +563,14 @@ 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].
- */
+// 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)
{
#define CLASS_not 0x80
@@ -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;
@@ -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'):
@@ -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 = (char_u *)reg_prev_sub; *lp != NUL; MB_CPTR_ADV(lp)) {
EMIT(utf_ptr2char((char *)lp));
- if (lp != reg_prev_sub) {
+ if (lp != (char_u *)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,24 +2209,20 @@ 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.
- */
+ // [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);
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.
- */
+ // 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);
if (result != FAIL) {
if (result >= NFA_FIRST_NL && result <= NFA_LAST_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
@@ -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;
@@ -3299,9 +3278,7 @@ static FILE *log_fd;
static char_u e_log_open_failed[] =
N_("Could not open temporary log file for writing, displaying on stderr... ");
-/*
- * Print the postfix notation of the current regexp.
- */
+// Print the postfix notation of the current regexp.
static void nfa_postfix_dump(char_u *expr, int retval)
{
int *p;
@@ -3329,9 +3306,7 @@ static void nfa_postfix_dump(char_u *expr, int retval)
}
}
-/*
- * 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;
@@ -3358,10 +3333,10 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)
int last = indent->ga_len - 3;
char_u 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);
}
@@ -3401,9 +3376,7 @@ 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");
@@ -3425,12 +3398,10 @@ static void nfa_dump(nfa_regprog_T *prog)
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 +3413,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 +3443,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 +3458,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 +3468,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 +3479,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 +3492,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 +3535,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 +3547,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 +3560,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 +3764,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 +3801,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 +4285,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,9 +4423,7 @@ 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;
@@ -4489,6 +4432,7 @@ static void copy_sub(regsub_T *to, regsub_T *from)
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);
@@ -4496,9 +4440,7 @@ static void copy_sub(regsub_T *to, regsub_T *from)
}
}
-/*
- * 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) {
@@ -4516,9 +4458,7 @@ static void copy_sub_off(regsub_T *to, regsub_T *from)
}
}
-/*
- * 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) {
@@ -4623,6 +4563,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 +4589,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));
@@ -4925,7 +4882,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 +5022,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 +5043,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 +5236,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 +5376,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 +5396,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 +5420,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 +5445,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 +5469,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)
@@ -5590,12 +5539,12 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T
rex.line = 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;
}
@@ -5656,27 +5605,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,9 +5769,7 @@ 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);
@@ -5844,17 +5780,15 @@ 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, char_u *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;
@@ -5887,6 +5821,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 +5831,8 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
break;
}
}
+
+ *startcol = col;
return 0L;
#undef PTR2LEN
@@ -5971,16 +5908,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 +5936,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 +5956,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 +6002,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 +6045,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 +6341,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 +6362,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 +6574,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 +6841,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.reg_firstlnum + rex.lnum, 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);
}
@@ -6939,7 +6870,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 +7116,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 +7251,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 +7298,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));
+ (char_u *)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));
+ (char_u *)xstrnsave((char *)lpos->start, (size_t)(lpos->end - lpos->start));
}
}
}
@@ -7402,8 +7338,8 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm, int
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 = (char_u **)rex.reg_match->startp;
+ rex.reg_endp = (char_u **)rex.reg_match->endp;
}
// Be paranoid...
@@ -7460,7 +7396,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,16 +7442,18 @@ 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.
- */
+// 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)
{
nfa_regprog_T *prog = NULL;
@@ -7535,11 +7479,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 +7496,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 +7506,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 +7531,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,9 +7551,7 @@ 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) {
@@ -7686,16 +7622,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 e85167f8fd..e009b883ca 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -5,25 +5,47 @@
///
/// Management of runtime files (including packages)
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stddef.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 +105,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 ? (char *)ufunc->uf_name_exp : ufunc->uf_name,
lnum);
if (entry != NULL) {
entry->es_info.ufunc = ufunc;
@@ -193,19 +214,19 @@ void ex_runtime(exarg_T *eap)
{
char *arg = eap->arg;
char *p = skiptowhite(arg);
- ptrdiff_t len = p - 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);
}
@@ -250,7 +271,7 @@ int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, vo
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)) {
@@ -265,9 +286,9 @@ int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, vo
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 = buf + STRLEN(buf);
+ tail = buf + strlen(buf);
// Loop over all patterns in "name"
char *np = name;
@@ -343,7 +364,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) {
@@ -397,10 +418,10 @@ int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *c
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(buf);
- tail = buf + STRLEN(buf);
+ tail = buf + strlen(buf);
// Loop over all patterns in "name"
char *np = name;
@@ -545,7 +566,7 @@ int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callb
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/" : "";
@@ -556,7 +577,7 @@ int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callb
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);
@@ -568,7 +589,7 @@ int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callb
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);
@@ -578,7 +599,7 @@ int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callb
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);
@@ -630,13 +651,13 @@ static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle
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);
@@ -651,7 +672,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)
@@ -669,7 +690,7 @@ RuntimeSearchPath runtime_search_path_build(void)
char *cur_entry = entry;
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);
@@ -679,7 +700,7 @@ RuntimeSearchPath runtime_search_path_build(void)
for (rtp_entry = p_rtp; *rtp_entry != NUL;) {
char *cur_entry = rtp_entry;
copy_option_part(&rtp_entry, buf, MAXPATHL, ",");
- size_t buflen = STRLEN(buf);
+ size_t buflen = strlen(buf);
if (path_is_after(buf, buflen)) {
rtp_entry = cur_entry;
@@ -713,7 +734,7 @@ RuntimeSearchPath runtime_search_path_build(void)
// "after" dirs in rtp
for (; *rtp_entry != NUL;) {
copy_option_part(&rtp_entry, buf, MAXPATHL, ",");
- expand_rtp_entry(&search_path, &rtp_used, buf, path_is_after(buf, STRLEN(buf)));
+ expand_rtp_entry(&search_path, &rtp_used, buf, path_is_after(buf, strlen(buf)));
}
// strings are not owned
@@ -891,7 +912,7 @@ static int add_pack_dir_to_rtp(char *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
@@ -901,8 +922,8 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
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);
@@ -969,7 +990,7 @@ static int load_pack_plugin(bool opt, char *fname)
static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT
char *const ffname = fix_fname(fname);
- size_t len = strlen(ffname) + STRLEN(ftpat);
+ size_t len = strlen(ffname) + strlen(ftpat);
char *pat = xmallocz(len);
vim_snprintf(pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT
@@ -1061,11 +1082,11 @@ static void add_pack_start_dir(char *fname, void *cookie)
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);
}
@@ -1140,7 +1161,7 @@ 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
@@ -1166,14 +1187,14 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname
{
*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.
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 7;
+ size_t size = strlen(dirnames[i]) + pat_len + 7;
char *s = xmalloc(size);
snprintf(s, size, "%s/%s*.vim", dirnames[i], pat);
globpath(p_rtp, s, &ga, 0);
@@ -1186,7 +1207,7 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname
if (flags & DIP_START) {
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 22;
+ size_t size = strlen(dirnames[i]) + pat_len + 22;
char *s = xmalloc(size);
snprintf(s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
globpath(p_pp, s, &ga, 0);
@@ -1198,7 +1219,7 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname
}
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 22;
+ size_t size = strlen(dirnames[i]) + pat_len + 22;
char *s = xmalloc(size);
snprintf(s, size, "start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
globpath(p_pp, s, &ga, 0);
@@ -1212,7 +1233,7 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname
if (flags & DIP_OPT) {
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 20;
+ size_t size = strlen(dirnames[i]) + pat_len + 20;
char *s = xmalloc(size);
snprintf(s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
globpath(p_pp, s, &ga, 0);
@@ -1224,7 +1245,7 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname
}
for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 20;
+ size_t size = strlen(dirnames[i]) + pat_len + 20;
char *s = xmalloc(size);
snprintf(s, size, "opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
globpath(p_pp, s, &ga, 0);
@@ -1239,7 +1260,7 @@ int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirname
for (int i = 0; i < ga.ga_len; i++) {
char *match = ((char **)ga.ga_data)[i];
char *s = match;
- char *e = s + STRLEN(s);
+ char *e = s + strlen(s);
if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0
|| ((flags & DIP_LUA)
&& STRNICMP(e - 4, ".lua", 4) == 0))) {
@@ -1277,7 +1298,7 @@ int ExpandPackAddDir(char *pat, int *num_file, char ***file)
*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;
@@ -1291,7 +1312,7 @@ int ExpandPackAddDir(char *pat, int *num_file, char ***file)
for (int i = 0; i < ga.ga_len; i++) {
char *match = ((char **)ga.ga_data)[i];
s = path_tail(match);
- memmove(match, s, STRLEN(s) + 1);
+ memmove(match, s, strlen(s) + 1);
}
if (GA_EMPTY(&ga)) {
@@ -1451,7 +1472,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;
@@ -1521,7 +1542,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;
@@ -1633,7 +1654,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);
@@ -1664,7 +1685,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);
@@ -1698,7 +1719,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, c
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;
@@ -1728,7 +1749,7 @@ 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;
@@ -1822,7 +1843,7 @@ 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 *)ga.ga_data)[ga.ga_len - 1] = NUL;
@@ -1908,7 +1929,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 == '_')
@@ -2021,7 +2042,7 @@ 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, "utf-8", p_enc);
@@ -2064,8 +2085,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);
}
@@ -2121,7 +2142,7 @@ 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;
}
@@ -2153,11 +2174,11 @@ void ex_scriptnames(exarg_T *eap)
for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
if (SCRIPT_ITEM(i).sn_name != NULL) {
- home_replace(NULL, SCRIPT_ITEM(i).sn_name, (char *)NameBuff, MAXPATHL, true);
- vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
- if (!message_filtered((char *)IObuff)) {
+ 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();
}
}
@@ -2199,16 +2220,16 @@ char *get_scriptname(LastSet last_set, bool *should_free)
case SID_LUA:
return _("Lua");
case SID_API_CLIENT:
- snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
- return (char *)IObuff;
+ snprintf(IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
+ return IObuff;
case SID_STR:
return _("anonymous :source");
default: {
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 (char *)IObuff;
+ return IObuff;
}
*should_free = true;
@@ -2290,7 +2311,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
ga_init(&ga, (int)sizeof(char), 400);
ga_concat(&ga, line);
while (sp->nextline != NULL
- && concat_continued_line(&ga, 400, sp->nextline, STRLEN(sp->nextline))) {
+ && concat_continued_line(&ga, 400, sp->nextline, strlen(sp->nextline))) {
xfree(sp->nextline);
sp->nextline = get_one_sourceline(sp);
}
@@ -2353,7 +2374,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.
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index 053c71212e..de363020f8 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
@@ -74,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 *);
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index d268dde845..b18bf7ed6a 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
@@ -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((char *)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, (char *)buf, row, 0, attr);
- if (selstart != NULL && highlight) {
- *selend = NUL;
- grid_puts(grid, (char *)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
@@ -574,7 +349,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 +360,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 +380,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);
}
@@ -787,8 +547,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 +622,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;
@@ -917,234 +678,14 @@ static void recording_mode(int 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();
- 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, "+", 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 = (char_u *)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, (char *)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;
-
- 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));
-}
-
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.
@@ -1173,7 +714,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
@@ -1237,6 +780,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, true, false, 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;
@@ -1264,7 +817,7 @@ int number_width(win_T *wp)
/// Returns 0 for invalid hex or invalid UTF-8 byte.
static int get_encoded_char_adv(const char_u **p)
{
- const char_u *s = *p;
+ const char *s = (const char *)(*p);
if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
int64_t num = 0;
@@ -1281,7 +834,7 @@ static int get_encoded_char_adv(const char_u **p)
}
// TODO(bfredl): use schar_T representation and utfc_ptr2len
- int clen = utf_ptr2len((const char *)s);
+ int clen = utf_ptr2len(s);
int c = mb_cptr2char_adv(p);
if (clen == 1 && c > 127) { // Invalid UTF-8 byte
return 0;
@@ -1297,8 +850,8 @@ static int get_encoded_char_adv(const char_u **p)
/// @return error message, NULL if it's OK.
char *set_chars_option(win_T *wp, char **varp, bool apply)
{
- const char_u *last_multispace = NULL; // Last occurrence of "multispace:"
- const 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);
@@ -1328,6 +881,7 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
{ &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[] = {
@@ -1344,18 +898,18 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
struct chars_tab *tab;
int entries;
- const char_u *value = (char_u *)(*varp);
+ 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) {
- value = (char_u *)p_lcs; // local value is empty, use the global value
+ 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) {
- value = (char_u *)p_fcs; // local value is empty, use the global value
+ value = p_fcs; // local value is empty, use the global value
}
}
@@ -1390,15 +944,15 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
}
}
}
- const char_u *p = value;
+ const char *p = value;
while (*p) {
int i;
for (i = 0; i < entries; i++) {
- const size_t len = 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) {
- const char_u *s = p + len + 1;
+ const char_u *s = (char_u *)p + len + 1;
int c1 = get_encoded_char_adv(&s);
if (c1 == 0 || char2cells(c1) > 1) {
return e_invarg;
@@ -1429,20 +983,20 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
*(tab[i].cp) = c1;
}
}
- p = s;
+ p = (char *)s;
break;
}
}
}
if (i == entries) {
- const size_t len = STRLEN("multispace");
- const size_t len2 = STRLEN("leadmultispace");
+ const size_t len = strlen("multispace");
+ const size_t len2 = strlen("leadmultispace");
if (is_listchars
- && STRNCMP(p, "multispace", len) == 0
+ && strncmp(p, "multispace", len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
- const char_u *s = p + len + 1;
+ const char_u *s = (char_u *)p + len + 1;
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
@@ -1458,7 +1012,7 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
// lcs-multispace cannot be an empty string
return e_invarg;
}
- p = s;
+ p = (char *)s;
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
@@ -1467,13 +1021,13 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
}
}
- p = s;
+ p = (char *)s;
}
} else if (is_listchars
- && STRNCMP(p, "leadmultispace", len2) == 0
+ && strncmp(p, "leadmultispace", len2) == 0
&& p[len2] == ':'
&& p[len2 + 1] != NUL) {
- const char_u *s = p + len2 + 1;
+ const char_u *s = (char_u *)p + len2 + 1;
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
@@ -1489,7 +1043,7 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
// lcs-leadmultispace cannot be an empty string
return e_invarg;
}
- p = s;
+ p = (char *)s;
} else {
int multispace_pos = 0;
while (*s != NUL && *s != ',') {
@@ -1498,7 +1052,7 @@ char *set_chars_option(win_T *wp, char **varp, bool apply)
wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
}
}
- p = s;
+ p = (char *)s;
}
} else {
return e_invarg;
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 e995081df8..871d2f9a0a 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_u *pat, char_u **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;
@@ -157,41 +158,38 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc
rc_did_emsg = true;
return FAIL;
}
- pat = spats[i].pat;
+ pat = (char_u *)spats[i].pat;
magic = spats[i].magic;
no_smartcase = spats[i].no_scs;
} else if (options & SEARCH_HIS) { // put new pattern in history
- add_to_history(HIST_SEARCH, pat, true, NUL);
+ add_to_history(HIST_SEARCH, (char *)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 = (char_u *)reverse_text((char *)pat);
- mr_pattern_alloced = true;
+ mr_pattern = reverse_text((char *)pat);
} else {
- mr_pattern = pat;
+ mr_pattern = xstrdup((char *)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) {
- save_re_pat(RE_SEARCH, pat, magic);
+ save_re_pat(RE_SEARCH, (char *)pat, magic);
}
// substitute or global command
if (pat_save == RE_SUBST || pat_save == RE_BOTH) {
- save_re_pat(RE_SUBST, pat, magic);
+ save_re_pat(RE_SUBST, (char *)pat, magic);
}
}
- regmatch->rmm_ic = ignorecase(pat);
+ regmatch->rmm_ic = ignorecase((char *)pat);
regmatch->rmm_maxcol = 0;
regmatch->regprog = vim_regcomp((char *)pat, magic ? RE_MAGIC : 0);
if (regmatch->regprog == NULL) {
@@ -200,19 +198,17 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc
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].pat = xstrdup(pat);
spats[idx].magic = magic;
spats[idx].no_scs = no_smartcase;
spats[idx].timestamp = os_time();
@@ -226,10 +222,8 @@ void save_re_pat(int idx, char_u *pat, int magic)
}
}
-/*
- * 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)
@@ -237,11 +231,16 @@ 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[0].pat = xstrdup(spats[0].pat);
}
saved_spats[1] = spats[1];
if (spats[1].pat != NULL) {
- saved_spats[1].pat = vim_strsave(spats[1].pat);
+ 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;
@@ -256,6 +255,8 @@ void restore_search_patterns(void)
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);
}
@@ -275,11 +276,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 +305,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;
@@ -351,24 +348,24 @@ static void restore_incsearch_state(void)
char_u *last_search_pattern(void)
{
- return spats[RE_SEARCH].pat;
+ return (char_u *)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)
+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
&& !(ctrl_x_mode_not_default()
&& curbuf->b_p_inf)) {
- ic = !pat_has_uppercase(pat);
+ ic = !pat_has_uppercase((char_u *)pat);
}
no_smartcase = false;
@@ -379,17 +376,21 @@ int ignorecase_opt(char_u *pat, int ic_in, int scs)
bool pat_has_uppercase(char_u *pat)
FUNC_ATTR_NONNULL_ALL
{
- char_u *p = pat;
+ char *p = (char *)pat;
+ magic_T magic_val = MAGIC_ON;
+
+ // get the magicness of the pattern
+ (void)skip_regexp_ex((char *)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"
@@ -399,7 +400,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++;
@@ -411,7 +418,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)
@@ -447,22 +454,18 @@ void set_csearch_until(int t_cmd)
char_u *last_search_pat(void)
{
- return spats[last_idx].pat;
+ return (char_u *)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.
- */
+// 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)
{
free_spat(&spats[idx]);
@@ -470,7 +473,7 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
if (*s == NUL) {
spats[idx].pat = NULL;
} else {
- spats[idx].pat = (char_u *)xstrdup((char *)s);
+ spats[idx].pat = xstrdup((char *)s);
}
spats[idx].timestamp = os_time();
spats[idx].additional_data = NULL;
@@ -490,7 +493,7 @@ 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;
}
@@ -500,11 +503,9 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
}
}
-/*
- * 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) {
@@ -512,7 +513,7 @@ void last_pat_prog(regmmatch_T *regmatch)
return;
}
emsg_off++; // So it doesn't beep if bad expr
- (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
+ (void)search_regcomp((char_u *)"", NULL, 0, last_idx, SEARCH_KEEP, regmatch);
emsg_off--;
}
@@ -545,7 +546,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
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;
@@ -570,7 +571,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);
@@ -578,9 +579,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
@@ -592,10 +591,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;
@@ -615,13 +614,11 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
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;
@@ -662,18 +659,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
@@ -687,11 +683,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
@@ -700,20 +694,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,
@@ -744,13 +740,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
@@ -792,7 +786,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.
@@ -801,13 +795,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.
@@ -825,10 +818,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;
}
@@ -847,7 +838,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--;
@@ -964,7 +955,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--;
}
@@ -1026,41 +1017,35 @@ static int first_submatch(regmmatch_T *rp)
/// @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 {
@@ -1087,17 +1072,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(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;
@@ -1114,22 +1095,20 @@ 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 = (char_u *)skip_regexp((char *)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
@@ -1154,7 +1133,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 '+'
@@ -1174,8 +1153,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.
@@ -1185,7 +1164,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) {
@@ -1196,10 +1175,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) {
@@ -1222,12 +1201,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);
@@ -1238,16 +1217,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);
@@ -1261,14 +1240,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 = (char_u *)reverse_text(trunc != NULL ? (char *)trunc : (char *)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) {
@@ -1277,7 +1256,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();
@@ -1287,13 +1266,11 @@ 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--) {
@@ -1319,7 +1296,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
}
c = searchit(curwin, curbuf, &pos, NULL, dirc == '/' ? FORWARD : BACKWARD,
- searchstr, count,
+ (char_u *)searchstr, count,
(spats[0].off.end * SEARCH_END
+ (options
& (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG
@@ -1328,7 +1305,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)
@@ -1350,9 +1327,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;
@@ -1377,9 +1352,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;
@@ -1419,7 +1392,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;
@@ -1439,25 +1412,24 @@ 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;
@@ -1492,7 +1464,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
start = pos->lnum;
}
ptr = ml_get_buf(buf, pos->lnum, false);
- p = (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
@@ -1505,7 +1477,7 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
// Expanding lines or words.
assert(ins_compl_len() >= 0);
if ((p_ic ? mb_strnicmp(p, pat, (size_t)ins_compl_len())
- : STRNCMP(p, pat, ins_compl_len())) == 0) {
+ : strncmp(p, pat, (size_t)ins_compl_len())) == 0) {
return OK;
}
}
@@ -1513,9 +1485,7 @@ 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.
@@ -1528,7 +1498,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;
@@ -1537,13 +1507,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);
}
}
}
@@ -1576,12 +1546,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;
}
@@ -1595,7 +1565,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;
@@ -1618,15 +1588,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);
@@ -1637,7 +1603,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) {
@@ -1646,32 +1612,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;
@@ -1726,27 +1691,24 @@ static void find_mps_values(int *initc, int *findc, bool *backwards, bool switch
}
}
-/*
- * 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
@@ -1755,7 +1717,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
@@ -1768,7 +1730,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);
@@ -1784,12 +1746,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 == '/') {
@@ -1807,31 +1767,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;
@@ -1852,21 +1806,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--;
}
for (;;) {
- initc = utf_ptr2char((char *)linep + pos.col);
+ initc = utf_ptr2char(linep + pos.col);
if (initc == NUL) {
break;
}
@@ -1875,11 +1825,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;
@@ -1897,17 +1847,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;
@@ -1925,36 +1873,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++;
}
}
@@ -1977,17 +1925,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
// backward search: Check if this line contains a single-line comment
if ((backwards && comment_dir) || lisp) {
- comment_col = check_linecomment((char *)linep);
+ comment_col = check_linecomment(linep);
}
if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) {
lispcomm = true; // find match inside this comment
}
while (!got_int) {
- /*
- * Go to the next position, forward or backward. We could use
- * 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) {
@@ -2004,13 +1950,13 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
}
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();
// Check if this line contains a single-line comment
if (comment_dir || lisp) {
- comment_col = check_linecomment((char *)linep);
+ comment_col = check_linecomment(linep);
}
// skip comment
if (lisp && comment_col != MAXCOL) {
@@ -2043,10 +1989,10 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
do_quotes = -1;
line_breakcheck();
if (lisp) { // find comment pos in new line
- comment_col = check_linecomment((char *)linep);
+ comment_col = check_linecomment(linep);
}
} else {
- pos.col += utfc_ptr2len((char *)linep + pos.col);
+ pos.col += utfc_ptr2len(linep + pos.col);
}
}
@@ -2061,23 +2007,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
@@ -2114,18 +2058,14 @@ 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++) {
if (ptr == linep + pos.col + backwards) {
@@ -2141,10 +2081,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] == '\\') {
@@ -2159,7 +2097,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;
@@ -2180,18 +2118,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
@@ -2219,13 +2155,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) {
@@ -2253,10 +2187,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
@@ -2322,7 +2254,7 @@ int check_linecomment(const char *line)
}
} else if (!in_str && ((p - line) < 2
|| (*(p - 1) != '\\' && *(p - 2) != '#'))
- && !is_pos_in_string((char_u *)line, (colnr_T)(p - line))) {
+ && !is_pos_in_string(line, (colnr_T)(p - line))) {
break; // found!
}
p++;
@@ -2336,7 +2268,7 @@ int check_linecomment(const char *line)
// because * / / * is an end and start of a C comment. Only
// accept the position if it is not inside a string.
if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')
- && !is_pos_in_string((char_u *)line, (colnr_T)(p - line))) {
+ && !is_pos_in_string(line, (colnr_T)(p - line))) {
break;
}
p++;
@@ -2366,21 +2298,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 = (char_u *)curbuf->b_p_mps; *p != NUL; p++) {
- if (utf_ptr2char((char *)p) == c && (curwin->w_p_rl ^ p_ri)) {
+ for (p = curbuf->b_p_mps; *p != NUL; p++) {
+ 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;
}
@@ -2409,7 +2339,7 @@ void showmatch(int c)
dollar_vcol = -1;
}
curwin->w_virtcol++; // do display ')' just before "$"
- update_screen(UPD_VALID); // show the new char first
+ update_screen(); // show the new char first
save_dollar_vcol = dollar_vcol;
save_state = State;
@@ -2426,10 +2356,8 @@ void showmatch(int c)
// and has a higher column number.
dollar_vcol = save_dollar_vcol;
- /*
- * brief pause, unless 'm' is present in 'cpo' and a character is
- * available.
- */
+ // 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()) {
@@ -2479,8 +2407,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
}
@@ -2513,7 +2440,7 @@ int current_search(long count, bool forward)
result = searchit(curwin, curbuf, &pos, &end_pos,
(dir ? FORWARD : BACKWARD),
- spats[last_idx].pat, i ? count : 1,
+ (char_u *)spats[last_idx].pat, i ? count : 1,
SEARCH_KEEP | flags, RE_SEARCH, NULL);
p_ws = old_p_ws;
@@ -2534,7 +2461,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));
}
}
}
@@ -2587,7 +2514,7 @@ int current_search(long count, bool forward)
/// 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)
+static int is_zero_width(char *pattern, int move, pos_T *cur, Direction direction)
{
regmmatch_T regmatch;
int nmatched = 0;
@@ -2600,7 +2527,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((char_u *)pattern, NULL, RE_SEARCH, RE_SEARCH,
SEARCH_KEEP, &regmatch) == FAIL) {
return -1;
}
@@ -2615,7 +2542,7 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
// accept a match at the cursor position
flag = SEARCH_START;
}
- if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1,
+ if (searchit(curwin, curbuf, &pos, NULL, direction, (char_u *)pattern, 1,
SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) {
// Zero-width pattern should match somewhere, then we can check if
// start and end are in the same position.
@@ -2646,16 +2573,16 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
/// 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;
@@ -2700,7 +2627,7 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh
len += 2;
}
- memmove(msgbuf + STRLEN(msgbuf) - len, t, len);
+ memmove(msgbuf + strlen(msgbuf) - len, t, len);
if (dirc == '?' && stat.cur == maxcount + 1) {
stat.cur = -1;
}
@@ -2708,7 +2635,7 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh
// keep the message even after redraw, but don't put in history
msg_hist_off = true;
msg_ext_set_kind("search_count");
- give_warning((char *)msgbuf, false);
+ give_warning(msgbuf, false);
msg_hist_off = false;
}
}
@@ -2732,7 +2659,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;
@@ -2756,8 +2683,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)
@@ -2807,7 +2734,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;
@@ -2825,7 +2752,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
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;
@@ -2869,9 +2796,9 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -2917,7 +2844,7 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -3016,7 +2943,7 @@ typedef struct {
/// 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,
+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
{
@@ -3053,14 +2980,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;
+ 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;
@@ -3082,11 +3009,10 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz,
/// 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
@@ -3127,7 +3053,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,
@@ -3187,15 +3113,15 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc
int *const outScore, uint32_t *const matches, const int maxMatches)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- const int len = mb_charlen(str);
+ const int len = mb_charlen((char *)str);
bool complete = false;
int numMatches = 0;
*outScore = 0;
- char_u *const save_pat = vim_strsave(pat_arg);
- char_u *pat = save_pat;
- char_u *p = pat;
+ char *const save_pat = xstrdup((char *)pat_arg);
+ char *pat = save_pat;
+ char *p = pat;
// Try matching each word in 'pat_arg' in 'str'
while (true) {
@@ -3203,12 +3129,12 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc
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
@@ -3220,7 +3146,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, (char *)str, 0, &score, (char *)str, len, NULL,
+ matches + numMatches,
maxMatches - numMatches, 0, &recursionCount);
if (matchCount == 0) {
numMatches = 0;
@@ -3265,8 +3192,8 @@ static int fuzzy_match_item_compare(const void *const s1, const void *const s2)
/// 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)
@@ -3289,17 +3216,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];
@@ -3310,7 +3237,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);
@@ -3318,7 +3245,7 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m
}
int score;
- if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches,
+ if (itemstr != NULL && fuzzy_match((char_u *)itemstr, (char_u *)str, matchseq, &score, matches,
MAX_FUZZY_MATCHES)) {
items[match_count].idx = (int)match_count;
items[match_count].item = li;
@@ -3329,9 +3256,9 @@ static void fuzzy_match_in_list(list_T *const l, char_u *const str, const bool m
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++;
}
@@ -3416,7 +3343,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) {
@@ -3435,7 +3362,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;
@@ -3466,7 +3393,8 @@ 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);
}
@@ -3486,10 +3414,10 @@ void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// 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;
}
@@ -3505,7 +3433,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
@@ -3513,19 +3441,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;
@@ -3534,8 +3462,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 *already = NULL;
+ char *startp = NULL;
win_T *curwin_save = NULL;
const int l_g_do_tagpreview = g_do_tagpreview;
@@ -3549,12 +3477,13 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
// 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()) {
- pat = xmalloc(len + 5);
+ 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;
@@ -3562,7 +3491,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
char *inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
if (*inc_opt != NUL) {
- incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0);
+ incl_regmatch.regprog = vim_regcomp(inc_opt, magic_isset() ? RE_MAGIC : 0);
if (incl_regmatch.regprog == NULL) {
goto fpip_end;
}
@@ -3571,7 +3500,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) {
def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
? p_def : curbuf->b_p_def,
- p_magic ? RE_MAGIC : 0);
+ magic_isset() ? RE_MAGIC : 0);
if (def_regmatch.regprog == NULL) {
goto fpip_end;
}
@@ -3592,9 +3521,9 @@ 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(inc_opt, "\\zs") != NULL) {
// Use text from '\zs' to '\ze' (or end) of 'include'.
@@ -3605,8 +3534,9 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
1L, p_fname);
} 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);
+ new_fname = (char *)file_name_in_line((char_u *)incl_regmatch.endp[0], 0,
+ FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, (char_u *)p_fname,
+ NULL);
}
already_searched = false;
if (new_fname != NULL) {
@@ -3618,7 +3548,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) {
@@ -3666,12 +3596,10 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
if (new_fname != NULL) {
// using "new_fname" is more reliable, e.g., when
// 'includeexpr' is set.
- msg_outtrans_attr((char *)new_fname, HL_ATTR(HLF_D));
+ 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(inc_opt, "\\zs") != NULL) {
// pattern contains \zs, use the match
@@ -3681,14 +3609,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.
@@ -3702,7 +3630,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
save_char = p[i];
p[i] = NUL;
- msg_outtrans_attr((char *)p, HL_ATTR(HLF_D));
+ msg_outtrans_attr(p, HL_ATTR(HLF_D));
p[i] = save_char;
}
@@ -3714,7 +3642,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) {
@@ -3738,7 +3665,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) {
@@ -3752,56 +3679,51 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
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_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
@@ -3809,18 +3731,16 @@ 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++) {
@@ -3847,13 +3767,12 @@ 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;
+ char *aux = p = startp;
if (compl_status_adding()) {
p += ins_compl_len();
if (vim_iswordp(p)) {
@@ -3865,8 +3784,8 @@ search_line:
i = (int)(p - aux);
if (compl_status_adding() && i == ins_compl_len()) {
- // IOSIZE > compl_length, so the STRNCPY works
- STRNCPY(IObuff, aux, i);
+ // 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
@@ -3884,7 +3803,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) {
@@ -3904,7 +3823,7 @@ 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;
}
@@ -3917,7 +3836,7 @@ search_line:
}
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) {
@@ -4023,7 +3942,7 @@ exit_matched:
&& action == ACTION_EXPAND
&& !compl_status_sol()
&& *startp != NUL
- && *(p = startp + utfc_ptr2len((char *)startp)) != NUL) {
+ && *(p = startp + utfc_ptr2len(startp)) != NUL) {
goto search_line;
}
}
@@ -4035,11 +3954,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);
@@ -4047,7 +3964,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;
@@ -4056,7 +3973,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;
}
@@ -4112,11 +4029,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
@@ -4127,7 +4044,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') {
@@ -4139,15 +4056,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
// Definition continues until line that doesn't end with '\'
if (got_int || type != FIND_DEFINE || p < line || *p != '\\') {
@@ -4158,7 +4074,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..092098d5fd 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.
diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c
index 53c9cb7c81..72ef74b46c 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"
@@ -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..a1d8f670d5 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
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index d2f1b39ca4..754139a147 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2,48 +2,58 @@
// 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"
#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/version.h"
#include "nvim/vim.h"
@@ -166,7 +176,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 +298,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 +462,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 +757,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 +887,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 +1013,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 +1058,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 +1132,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 +1197,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 +1217,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 +1252,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 +1327,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 +1341,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 +1424,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);
}
@@ -1470,7 +1484,7 @@ static char *shada_filename(const char *file)
file = p_shadafile;
} else {
if ((file = (char *)find_shada_parameter('n')) == NULL || *file == NUL) {
- file = shada_get_default_file();
+ 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
@@ -1505,7 +1519,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 +1565,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 +1660,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 +1703,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 +1714,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 +1827,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 +1847,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 +1966,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 +2016,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 +2061,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 +2107,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 +2123,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 +2246,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 +2379,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 +2500,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 +2524,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 +2532,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 +2660,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);
}
@@ -2807,7 +2825,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 +3025,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));
@@ -3068,37 +3085,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 +3137,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 +3267,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 +3355,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 +3585,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;
@@ -3999,9 +4017,9 @@ static bool shada_removable(const char *name)
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;
}
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index ed7f53d3ba..e211b0069e 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,23 +93,22 @@ 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);
+ hash = hash_hash((char_u *)groupname);
+ 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;
group->sg_next_sign_id = 1;
- hash_add_item(&sg_table, hi, group->sg_name, hash);
+ hash_add_item(&sg_table, hi, (char_u *)group->sg_name, hash);
} else {
// existing group
group = HI2SG(hi);
@@ -99,11 +120,11 @@ 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, (char *)groupname);
+ hashitem_T *hi = hash_find(&sg_table, groupname);
if (!HASHITEM_EMPTY(hi)) {
group = HI2SG(hi);
group->sg_refcount--;
@@ -118,16 +139,16 @@ static void sign_group_unref(char_u *groupname)
/// @return true if 'sign' is in 'group'.
/// A sign can either be in the global group (sign->group == NULL)
/// or in a named group. If 'group' is '*', then the sign is part of the group.
-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));
@@ -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
@@ -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);
- backslash_halve((char *)sp->sn_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;
@@ -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,7 +1119,7 @@ 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;
@@ -1085,7 +1127,7 @@ static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T at
if (sign_id == 0) {
// Delete all the signs in the specified buffer
redraw_buf_later(buf, UPD_NOT_VALID);
- buf_delete_signs(buf, (char *)sign_group);
+ 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,43 +1372,43 @@ 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);
- arg = (char_u *)skiptowhite((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 = (char_u *)skiptowhite((char *)arg + 1);
- } else if (STRNCMP(arg, "name=", 5) == 0) {
+ arg = skiptowhite(arg + 1);
+ } else if (strncmp(arg, "name=", 5) == 0) {
arg += 5;
name = arg;
- arg = (char_u *)skiptowhite((char *)arg);
+ arg = skiptowhite(arg);
if (*arg != NUL) {
*arg++ = NUL;
}
@@ -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 = (char_u *)skiptowhite((char *)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);
- arg = (char_u *)skiptowhite((char *)arg);
- } else if (STRNCMP(arg, "file=", 5) == 0) {
+ *prio = atoi(arg);
+ arg = skiptowhite(arg);
+ } 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,19 +1464,19 @@ 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;
// Parse the subcommand.
- p = (char_u *)skiptowhite((char *)arg);
+ p = skiptowhite(arg);
idx = sign_cmd_idx(arg, p);
if (idx == SIGNCMD_LAST) {
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,18 +1488,18 @@ 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".
- p = (char_u *)skiptowhite((char *)arg);
+ p = skiptowhite(arg);
if (*p != NUL) {
*p++ = NUL;
}
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,28 +1811,28 @@ 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 = (char_u *)skiptowhite((char *)arg);
+ end_subcmd = skiptowhite(arg);
if (*end_subcmd == NUL) {
// expand subcmd name
// :sign {subcmd}<CTRL-D>
@@ -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 = (char_u *)skiptowhite((char *)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;
}
@@ -2005,7 +2048,7 @@ void f_sign_getdefined(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -2062,8 +2105,7 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -2103,7 +2145,7 @@ void f_sign_jump(typval_T *argvars, typval_T *rettv, EvalFuncData 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;
}
@@ -2269,13 +2311,13 @@ void f_sign_placelist(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
@@ -2308,7 +2350,7 @@ void f_sign_undefine(typval_T *argvars, typval_T *rettv, EvalFuncData 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);
}
}
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 1259736e0e..48aed9c6de 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -50,72 +50,73 @@
// 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
// specific word.
-#include <assert.h> // for assert
-#include <inttypes.h> // for uint32_t, uint16_t, uint8_t
-#include <limits.h> // for INT_MAX
-#include <stdbool.h> // for false, true, bool
-#include <stddef.h> // for NULL, size_t, ptrdiff_t
-#include <stdio.h> // for snprintf
-#include <string.h> // for memmove, strstr, memcpy, memset
-
-#include "nvim/ascii.h" // for NUL, ascii_isdigit, ascii_iswhite
-#include "nvim/autocmd.h" // for apply_autocmds
-#include "nvim/buffer.h" // for bufref_valid, set_bufref, buf_is_empty
-#include "nvim/buffer_defs.h" // for win_T, synblock_T, buf_T, w_p_...
-#include "nvim/change.h" // for changed_bytes
-#include "nvim/charset.h" // for skipwhite, getwhitecols, skipbin
-#include "nvim/cursor.h" // for get_cursor_line_ptr
-#include "nvim/drawscreen.h" // for NOT_VALID, redraw_later
-#include "nvim/eval/typval.h" // for semsg
-#include "nvim/ex_cmds.h" // for do_sub_msg
-#include "nvim/ex_cmds_defs.h" // for exarg_T
-#include "nvim/ex_docmd.h" // for do_cmdline_cmd
-#include "nvim/garray.h" // for garray_T, GA_EMPTY, GA_APPEND_...
-#include "nvim/gettext.h" // for _, N_
-#include "nvim/hashtab.h" // for hash_clear_all, hash_init, has...
-#include "nvim/highlight_defs.h" // for HLF_COUNT, hlf_T, HLF_SPB, HLF...
-#include "nvim/insexpand.h" // for ins_compl_add_infercase, ins_c...
-#include "nvim/log.h" // for ELOG
-#include "nvim/macros.h" // for MB_PTR_ADV, MB_PTR_BACK, ASCII...
-#include "nvim/mark.h" // for clearpos
-#include "nvim/mbyte.h" // for utf_ptr2char, utf_char2bytes
-#include "nvim/memline.h" // for ml_append, ml_get_buf, ml_close
-#include "nvim/memline_defs.h" // for memline_T
-#include "nvim/memory.h" // for xfree, xmalloc, xcalloc, xstrdup
-#include "nvim/message.h" // for emsg, msg_puts, give_warning
-#include "nvim/option.h" // for copy_option_part, set_option_v...
-#include "nvim/option_defs.h" // for p_ws, OPT_LOCAL, p_enc, SHM_SE...
-#include "nvim/os/fs.h" // for os_remove
-#include "nvim/os/input.h" // for line_breakcheck
-#include "nvim/os/os_defs.h" // for MAXPATHL
-#include "nvim/path.h" // for path_full_compare, path_tail...
-#include "nvim/pos.h" // for pos_T, colnr_T, linenr_T
-#include "nvim/regexp.h" // for vim_regfree, vim_regexec, vim_...
-#include "nvim/regexp_defs.h" // for regmatch_T, regprog_T
-#include "nvim/runtime.h" // for DIP_ALL, do_in_runtimepath
-#include "nvim/search.h" // for SEARCH_KEEP, for do_search
-#include "nvim/spell.h" // for FUNC_ATTR_NONNULL_ALL, FUNC_AT...
-#include "nvim/spell_defs.h" // for slang_T, langp_T, MAXWLEN, sal...
-#include "nvim/spellfile.h" // for spell_load_file
-#include "nvim/spellsuggest.h" // for spell_suggest_list
-#include "nvim/strings.h" // for vim_strchr, vim_snprintf, conc...
-#include "nvim/syntax.h" // for syn_get_id, syntax_present
-#include "nvim/types.h" // for char_u
-#include "nvim/undo.h" // for u_save_cursor
-#include "nvim/vim.h" // for curwin, STRLEN, STRLCPY, STRNCMP
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#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/ex_cmds.h"
+#include "nvim/ex_cmds_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/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/option.h"
+#include "nvim/os/fs.h"
+#include "nvim/os/input.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.
@@ -129,14 +130,14 @@ 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 *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
// 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
@@ -159,7 +160,7 @@ typedef struct matchinf_S {
win_T *mi_win; // buffer being checked
// for NOBREAK
- int mi_result2; // "mi_resul" without following word
+ int mi_result2; // "mi_result" without following word
char_u *mi_end2; // "mi_end" without following word
} matchinf_T;
@@ -172,7 +173,7 @@ typedef struct spelload_S {
#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;
@@ -193,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.
@@ -220,7 +221,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
size_t nrlen = 0; // found a number first
size_t wrongcaplen = 0;
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
@@ -251,7 +252,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
}
// Find the normal end of the word (until the next non-word character).
- mi.mi_word = ptr;
+ mi.mi_word = (char *)ptr;
mi.mi_fend = ptr;
if (spell_iswordp(mi.mi_fend, wp)) {
bool this_upper = false; // init for gcc
@@ -299,9 +300,9 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
MB_PTR_ADV(mi.mi_fend);
}
- (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword,
+ (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), (char_u *)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.
@@ -343,7 +344,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// Count the word in the first language where it's found to be OK.
if (count_word && mi.mi_result == SP_OK) {
- count_common_word(mi.mi_lp->lp_slang, ptr,
+ count_common_word(mi.mi_lp->lp_slang, (char *)ptr,
(int)(mi.mi_end - ptr), 1);
count_word = false;
}
@@ -368,7 +369,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
int r = vim_regexec(&regmatch, (char *)ptr, 0);
wp->w_s->b_cap_prog = regmatch.regprog;
if (r) {
- *capcol = (int)(regmatch.endp[0] - ptr);
+ *capcol = (int)(regmatch.endp[0] - (char *)ptr);
}
}
@@ -386,15 +387,15 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// at which any word would be valid.
mi.mi_lp = LANGP_ENTRY(wp->w_s->b_langp, 0);
if (mi.mi_lp->lp_slang->sl_fidxs != NULL) {
- p = mi.mi_word;
- fp = mi.mi_fword;
+ p = (char_u *)mi.mi_word;
+ fp = (char_u *)mi.mi_fword;
for (;;) {
MB_PTR_ADV(p);
MB_PTR_ADV(fp);
if (p >= mi.mi_end) {
break;
}
- mi.mi_compoff = (int)(fp - mi.mi_fword);
+ mi.mi_compoff = (int)(fp - (char_u *)mi.mi_fword);
find_word(&mi, FIND_COMPOUND);
if (mi.mi_result != SP_BAD) {
mi.mi_end = p;
@@ -434,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;
@@ -517,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 = ' ';
}
@@ -561,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
@@ -574,7 +575,7 @@ static void find_word(matchinf_T *mip, int mode)
if (utf_head_off(ptr, ptr + wlen) > 0) {
continue; // not at first byte of character
}
- if (spell_iswordp(ptr + wlen, mip->mi_win)) {
+ if (spell_iswordp((char_u *)ptr + wlen, mip->mi_win)) {
if (slang->sl_compprog == NULL && !slang->sl_nobreak) {
continue; // next char is a word character
}
@@ -591,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);
@@ -611,11 +612,11 @@ static void find_word(matchinf_T *mip, int mode)
// For keep-case tree the case is always right. For prefixes we
// don't bother to check.
if (mode == FIND_FOLDWORD) {
- if (mip->mi_cend != mip->mi_word + wlen) {
+ if (mip->mi_cend != (char_u *)mip->mi_word + wlen) {
// mi_capflags was set for a different word length, need
// to do it again.
- mip->mi_cend = mip->mi_word + wlen;
- mip->mi_capflags = captype(mip->mi_word, mip->mi_cend);
+ mip->mi_cend = (char_u *)mip->mi_word + wlen;
+ mip->mi_capflags = captype((char_u *)mip->mi_word, mip->mi_cend);
}
if (mip->mi_capflags == WF_KEEPCAP
@@ -628,7 +629,7 @@ static void find_word(matchinf_T *mip, int mode)
// 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,
+ (char_u *)mip->mi_word + mip->mi_cprefixlen, slang,
false);
if (c == 0) {
continue;
@@ -663,7 +664,7 @@ static void find_word(matchinf_T *mip, int mode)
// For multi-byte chars check character length against
// COMPOUNDMIN.
if (slang->sl_compminlen > 0
- && mb_charlen_len(mip->mi_word + mip->mi_compoff,
+ && mb_charlen_len((char_u *)mip->mi_word + mip->mi_compoff,
wlen - mip->mi_compoff) < slang->sl_compminlen) {
continue;
}
@@ -702,16 +703,16 @@ 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 {
p = mip->mi_word + mip->mi_compoff;
}
- capflags = captype(p, mip->mi_word + wlen);
+ capflags = captype((char_u *)p, (char_u *)mip->mi_word + wlen);
if (capflags == WF_KEEPCAP || (capflags == WF_ALLCAP
&& (flags & WF_FIXCAP) != 0)) {
continue;
@@ -723,7 +724,7 @@ static void find_word(matchinf_T *mip, int mode)
// accept a no-caps word, even when the dictionary
// word specifies ONECAP.
MB_PTR_BACK(mip->mi_word, p);
- if (spell_iswordp_nmw(p, mip->mi_win)
+ if (spell_iswordp_nmw((char_u *)p, mip->mi_win)
? capflags == WF_ONECAP
: (flags & WF_ONECAP) != 0
&& capflags != WF_ONECAP) {
@@ -738,17 +739,17 @@ 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);
+ (void)spell_casefold(mip->mi_win, (char_u *)ptr, wlen, (char_u *)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)) {
+ if (!can_compound(slang, (char *)fword, mip->mi_compflags)) {
continue;
}
} else if (slang->sl_comprules != NULL
@@ -786,8 +787,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);
@@ -877,16 +878,16 @@ static void find_word(matchinf_T *mip, int mode)
if (nobreak_result == SP_BAD) {
if (mip->mi_result2 > res) {
mip->mi_result2 = res;
- mip->mi_end2 = mip->mi_word + wlen;
+ mip->mi_end2 = (char_u *)mip->mi_word + wlen;
} else if (mip->mi_result2 == res
- && mip->mi_end2 < mip->mi_word + wlen) {
- mip->mi_end2 = mip->mi_word + wlen;
+ && mip->mi_end2 < (char_u *)mip->mi_word + wlen) {
+ mip->mi_end2 = (char_u *)mip->mi_word + wlen;
}
} else if (mip->mi_result > res) {
mip->mi_result = res;
- mip->mi_end = mip->mi_word + wlen;
- } else if (mip->mi_result == res && mip->mi_end < mip->mi_word + wlen) {
- mip->mi_end = mip->mi_word + wlen;
+ mip->mi_end = (char_u *)mip->mi_word + wlen;
+ } else if (mip->mi_result == res && mip->mi_end < (char_u *)mip->mi_word + wlen) {
+ mip->mi_end = (char_u *)mip->mi_word + wlen;
}
if (mip->mi_result == SP_OK) {
@@ -907,16 +908,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)
{
for (int i = 0; i + 1 < gap->ga_len; i += 2) {
- char_u *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];
- int 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;
}
}
@@ -924,9 +925,9 @@ 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 };
@@ -949,8 +950,8 @@ bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
// are too many syllables AND the number of compound words is above
// 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;
+ && count_syllables(slang, (char_u *)word) > slang->sl_compsylmax) {
+ return (int)strlen((char *)flags) < slang->sl_compmax;
}
return true;
}
@@ -959,7 +960,7 @@ 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)
{
// loop over all the COMPOUNDRULE entries
for (char_u *p = slang->sl_comprules; *p != NUL; p++) {
@@ -1066,7 +1067,7 @@ static void find_prefix(matchinf_T *mip, int mode)
}
// We use the case-folded word here, since prefixes are always
// case-folded.
- char_u *ptr = mip->mi_fword;
+ 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).
@@ -1109,8 +1110,8 @@ static void find_prefix(matchinf_T *mip, int mode)
}
// Case-folded length may differ from original length.
- mip->mi_cprefixlen = nofold_len(mip->mi_fword, mip->mi_prefixlen,
- mip->mi_word);
+ mip->mi_cprefixlen = nofold_len((char_u *)mip->mi_fword, mip->mi_prefixlen,
+ (char_u *)mip->mi_word);
find_word(mip, FIND_PREFIX);
if (len == 0) {
@@ -1167,9 +1168,9 @@ 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,
+ (char_u *)mip->mi_fword + mip->mi_fwordlen,
MAXWLEN - mip->mi_fwordlen);
- int 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;
}
@@ -1198,6 +1199,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.
@@ -1216,11 +1242,11 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
hlf_T attr = HLF_COUNT;
size_t len;
int has_syntax = syntax_present(wp);
- int col;
- 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;
@@ -1228,6 +1254,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.
//
@@ -1240,10 +1268,23 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
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) {
- char_u *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;
@@ -1258,10 +1299,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((char *)line);
+ capcol = (colnr_T)getwhitecols(line);
} else if (curline && wp == curwin) {
// For spellbadword(): check if first word needs a capital.
- col = (int)getwhitecols((char *)line);
+ col = (colnr_T)getwhitecols(line);
if (check_need_cap(lnum, col)) {
capcol = col;
}
@@ -1277,12 +1318,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),
- ml_get_buf(wp->w_buffer, lnum + 1, false),
+ spell_cat_line(buf + strlen(buf),
+ (char_u *)ml_get_buf(wp->w_buffer, lnum + 1, false),
MAXWLEN);
}
- char_u *p = buf + skip;
- char_u *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.
@@ -1295,7 +1336,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
// start of word
attr = HLF_COUNT;
- len = spell_check(wp, p, &attr, &capcol, false);
+ len = spell_check(wp, (char_u *)p, &attr, &capcol, false);
if (attr != HLF_COUNT) {
// We found a bad word. Check the attribute.
@@ -1308,33 +1349,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)) {
- bool can_spell;
- 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 = (pos_T) {
.lnum = lnum,
- .col = (int)(p - buf),
+ .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.
@@ -1358,8 +1397,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) {
@@ -1429,15 +1468,19 @@ 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_u *line, int maxlen)
{
char_u *p = (char_u *)skipwhite((char *)line);
while (vim_strchr("*#/\"\t", *p) != NULL) {
@@ -1450,7 +1493,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen)
int n = (int)(p - line) + 1;
if (n < maxlen - 1) {
memset(buf, ' ', (size_t)n);
- STRLCPY(buf + n, p, maxlen - n);
+ xstrlcpy(buf + n, (char *)p, (size_t)(maxlen - n));
}
}
}
@@ -1469,6 +1512,10 @@ 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 (int round = 1; round <= 2; round++) {
@@ -1508,16 +1555,18 @@ 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");
+ STRCPY(fname_enc + strlen(fname_enc) - 3, "add.spl");
do_in_runtimepath((char *)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)
{
- 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 (char_u *)p_enc;
}
return (char_u *)"latin1";
@@ -1531,15 +1580,15 @@ static void int_wordlist_spl(char_u *fname)
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);
@@ -1651,7 +1700,7 @@ 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 = spell_load_file((char_u *)fname, slp->sl_lang, NULL, false);
+ slang_T *slang = spell_load_file(fname, (char *)slp->sl_lang, NULL, false);
if (slang != NULL) {
// When a previously loaded file has NOBREAK also use it for the
// ".add" files.
@@ -1672,23 +1721,23 @@ 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)
{
- 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;
}
wordcount_T *wc;
- hash_T hash = hash_hash(p);
- const size_t p_len = STRLEN(p);
+ hash_T hash = hash_hash((char_u *)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);
@@ -1721,17 +1770,17 @@ bool byte_in_str(char_u *str, int n)
int init_syl_tab(slang_T *slang)
{
ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4);
- char_u *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;
}
- char_u *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);
}
@@ -1740,7 +1789,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;
@@ -1760,7 +1809,7 @@ static int count_syllables(slang_T *slang, const char_u *word)
return 0;
}
- for (const char_u *p = word; *p != NUL; p += len) {
+ for (const char *p = (char *)word; *p != NUL; p += len) {
// When running into a space reset counter.
if (*p == ' ') {
len = 1;
@@ -1773,7 +1822,7 @@ static int count_syllables(slang_T *slang, const char_u *word)
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;
}
}
@@ -1782,8 +1831,8 @@ static int count_syllables(slang_T *slang, const char_u *word)
skip = false;
} else {
// No recognized syllable item, at least a syllable char then?
- int 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) {
@@ -1801,25 +1850,25 @@ 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);
@@ -1837,22 +1886,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((char_u *)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, ",");
region = NULL;
- len = (int)STRLEN(lang);
+ len = (int)strlen(lang);
if (!valid_spelllang((char *)lang)) {
continue;
}
- if (STRCMP(lang, "cjk") == 0) {
+ if (strcmp(lang, "cjk") == 0) {
wp->w_s->b_cjk = 1;
continue;
}
@@ -1860,14 +1909,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((char *)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 {
@@ -1901,7 +1950,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;
@@ -1910,12 +1959,12 @@ char *did_set_spelllang(win_T *wp)
// If not found try loading the language now.
if (slang == NULL) {
if (filename) {
- (void)spell_load_file(lang, lang, NULL, false);
+ (void)spell_load_file((char *)lang, (char *)lang, NULL, false);
} else {
- spell_load_lang(lang);
+ spell_load_lang((char_u *)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;
}
@@ -1930,7 +1979,7 @@ char *did_set_spelllang(win_T *wp)
region_mask = REGION_ALL;
if (!filename && region != NULL) {
// find region in sl_regions
- c = find_region(slang->sl_regions, region);
+ c = find_region(slang->sl_regions, (char_u *)region);
if (c == REGION_ALL) {
if (slang->sl_add) {
if (*slang->sl_regions != NUL) {
@@ -1973,7 +2022,7 @@ char *did_set_spelllang(win_T *wp)
if (int_wordlist == NULL) {
continue;
}
- int_wordlist_spl(spf_name);
+ int_wordlist_spl((char_u *)spf_name);
} else {
// One entry in 'spellfile'.
copy_option_part(&spf, (char *)spf_name, MAXPATHL - 5, ",");
@@ -1981,9 +2030,9 @@ char *did_set_spelllang(win_T *wp)
// If it was already found above then skip it.
for (c = 0; c < ga.ga_len; c++) {
- p = (char_u *)LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
+ 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((char *)spf_name, p, false, true) == kEqualFiles) {
break;
}
}
@@ -2006,13 +2055,13 @@ 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((char *)spf_name), MAXWLEN + 1);
+ p = vim_strchr((char *)lang, '.');
if (p != NULL) {
*p = NUL; // truncate at ".encoding.add"
}
}
- slang = spell_load_file(spf_name, lang, NULL, true);
+ slang = spell_load_file((char *)spf_name, (char *)lang, NULL, true);
// If one of the languages has NOBREAK we assume the addition
// files also have this.
@@ -2024,7 +2073,7 @@ char *did_set_spelllang(win_T *wp)
region_mask = REGION_ALL;
if (use_region != NULL && !dont_use_region) {
// find region in sl_regions
- c = find_region(slang->sl_regions, use_region);
+ c = find_region(slang->sl_regions, (char_u *)use_region);
if (c != REGION_ALL) {
region_mask = 1 << c;
} else if (*slang->sl_regions != NUL) {
@@ -2064,7 +2113,7 @@ char *did_set_spelllang(win_T *wp)
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;
@@ -2081,7 +2130,7 @@ char *did_set_spelllang(win_T *wp)
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;
@@ -2104,8 +2153,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
{
@@ -2113,21 +2162,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 = (char *)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((char_u *)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 = (char *)bp;
- STRLCPY(bp + n, p, l + 1);
+ wp->w_s->b_spell_ismw_mb = bp;
+ xstrlcpy(bp + n, p, (size_t)l + 1);
}
p += l;
}
@@ -2136,7 +2185,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_u *rp, const char_u *region)
{
int i;
@@ -2161,7 +2210,7 @@ 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_u *word, const char_u *end)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *p;
@@ -2366,7 +2415,7 @@ bool spell_iswordp(const char_u *p, const win_T *wp)
int c = utf_ptr2char((char *)s);
if (c > 255) {
- return spell_mb_isword_class(mb_get_class(s), wp);
+ return spell_mb_isword_class(mb_get_class((char *)s), wp);
}
return spelltab.st_isw[c];
}
@@ -2377,7 +2426,7 @@ bool spell_iswordp_nmw(const char_u *p, win_T *wp)
{
int c = utf_ptr2char((char *)p);
if (c > 255) {
- return spell_mb_isword_class(mb_get_class(p), wp);
+ return spell_mb_isword_class(mb_get_class((char *)p), wp);
}
return spelltab.st_isw[c];
}
@@ -2470,23 +2519,23 @@ bool check_need_cap(linenr_T lnum, colnr_T col)
return false;
}
- char_u *line = get_cursor_line_ptr();
- char_u *line_copy = NULL;
+ char *line = get_cursor_line_ptr();
+ char *line_copy = NULL;
colnr_T endcol = 0;
- if (getwhitecols((char *)line) >= (int)col) {
+ if (getwhitecols(line) >= (int)col) {
// At start of line, check if previous line is empty or sentence
// ends there.
if (lnum == 1) {
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 = (char_u *)concat_str((char *)line, " ");
+ line_copy = concat_str(line, " ");
line = line_copy;
- endcol = (colnr_T)STRLEN(line);
+ endcol = (colnr_T)strlen(line);
}
}
} else {
@@ -2499,13 +2548,13 @@ bool check_need_cap(linenr_T lnum, colnr_T col)
.regprog = curwin->w_s->b_cap_prog,
.rm_ic = false
};
- char_u *p = line + endcol;
+ char *p = line + endcol;
for (;;) {
MB_PTR_BACK(line, p);
- if (p == line || spell_iswordp_nmw(p, curwin)) {
+ if (p == line || spell_iswordp_nmw((char_u *)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;
@@ -2530,9 +2579,9 @@ void ex_spellrepall(exarg_T *eap)
emsg(_("E752: No previous spell replacement"));
return;
}
- int addlen = (int)(STRLEN(repl_to) - STRLEN(repl_from));
+ int addlen = (int)(strlen(repl_to) - strlen(repl_from));
- size_t frompatlen = STRLEN(repl_from) + 7;
+ size_t frompatlen = strlen(repl_from) + 7;
char_u *frompat = xmalloc(frompatlen);
snprintf((char *)frompat, frompatlen, "\\V\\<%s\\>", repl_from);
p_ws = false;
@@ -2541,20 +2590,20 @@ void ex_spellrepall(exarg_T *eap)
sub_nlines = 0;
curwin->w_cursor.lnum = 0;
while (!got_int) {
- if (do_search(NULL, '/', '/', frompat, 1L, SEARCH_KEEP, NULL) == 0
+ if (do_search(NULL, '/', '/', (char *)frompat, 1L, SEARCH_KEEP, NULL) == 0
|| u_save_cursor() == FAIL) {
break;
}
// Only replace when the right word isn't there yet. This happens
// when changing "etc" to "etc.".
- char_u *line = get_cursor_line_ptr();
- if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col,
- repl_to, STRLEN(repl_to)) != 0) {
- char_u *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_u *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));
+ STRCAT(p, line + curwin->w_cursor.col + strlen(repl_from));
ml_replace(curwin->w_cursor.lnum, (char *)p, false);
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
@@ -2564,7 +2613,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;
@@ -2585,7 +2634,7 @@ 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_u *word, char *wcopy, bool upper)
{
char_u *p = word;
int c = mb_cptr2char_adv((const char_u **)&p);
@@ -2594,8 +2643,8 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper)
} 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, (char *)p, (size_t)(MAXWLEN - l));
}
// Make a copy of "word" with all the letters upper cased into
@@ -2648,7 +2697,7 @@ void make_case_word(char_u *fword, char_u *cword, int flags)
allcap_copy(fword, cword);
} else if (flags & WF_ONECAP) {
// Make the first letter upper-case
- onecap_copy(fword, cword, true);
+ onecap_copy(fword, (char *)cword, true);
} else {
// Use goodword as-is.
STRCPY(cword, fword);
@@ -2672,7 +2721,7 @@ char *eval_soundfold(const char *const word)
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);
+ spell_soundfold(lp->lp_slang, (char *)word, false, (char *)sound);
return xstrdup((const char *)sound);
}
}
@@ -2697,23 +2746,23 @@ 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)
{
if (slang->sl_sofo) {
// SOFOFROM and SOFOTO used
- spell_soundfold_sofo(slang, inword, res);
+ spell_soundfold_sofo(slang, (char_u *)inword, (char_u *)res);
} else {
- char_u fword[MAXWLEN];
- char_u *word;
+ 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, (char_u *)inword, (int)strlen(inword), (char_u *)fword, MAXWLEN);
word = fword;
}
- spell_soundfold_wsal(slang, word, res);
+ spell_soundfold_wsal(slang, (char_u *)word, (char_u *)res);
}
}
@@ -2766,7 +2815,7 @@ 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_u *inword, char_u *res)
{
salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data;
int word[MAXWLEN] = { 0 };
@@ -3097,7 +3146,7 @@ void ex_spelldump(exarg_T *eap)
}
char *spl;
long dummy;
- (void)get_option_value("spl", &dummy, &spl, OPT_LOCAL);
+ (void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL);
// Create a new empty buffer in a new window.
do_cmdline_cmd("new");
@@ -3129,23 +3178,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 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;
@@ -3155,11 +3204,11 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
if (ic) {
dumpflags |= DUMPFLAG_ICASE;
} else {
- n = captype(pat, NULL);
+ n = captype((char_u *)pat, NULL);
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;
}
}
@@ -3169,11 +3218,11 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// regions or none at all.
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;
+ p = (char *)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;
}
@@ -3182,8 +3231,8 @@ 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;
@@ -3198,14 +3247,14 @@ 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;
}
@@ -3215,11 +3264,11 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
for (int round = 1; round <= 2; round++) {
if (round == 1) {
dumpflags &= ~DUMPFLAG_KEEPCASE;
- byts = slang->sl_fbyts;
+ byts = (char *)slang->sl_fbyts;
idxs = slang->sl_fidxs;
} else {
dumpflags |= DUMPFLAG_KEEPCASE;
- byts = slang->sl_kbyts;
+ byts = (char *)slang->sl_kbyts;
idxs = slang->sl_kidxs;
}
if (byts == NULL) {
@@ -3239,7 +3288,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
curi[depth]++;
- c = byts[n];
+ c = (uint8_t)byts[n];
if (c == 0 || depth >= MAXWLEN - 1) {
// End of word or reached maximum length, deal with the
// word.
@@ -3262,8 +3311,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++;
}
@@ -3271,13 +3319,13 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// Apply the prefix, if there is one.
if (c != 0) {
- lnum = dump_prefixes(slang, word, pat, dir,
+ lnum = dump_prefixes(slang, word, (char_u *)pat, dir,
dumpflags, flags, lnum);
}
}
} else {
// Normal char, go one level deeper.
- word[depth++] = (char_u)c;
+ word[depth++] = (char)c;
arridx[depth] = idxs[n];
curi[depth] = 1;
@@ -3289,7 +3337,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// ignore case...
assert(depth >= 0);
if (depth <= patlen
- && mb_strnicmp(word, pat, (size_t)depth) != 0) {
+ && mb_strnicmp((char *)word, pat, (size_t)depth) != 0) {
depth--;
}
}
@@ -3299,15 +3347,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 cword[MAXWLEN];
- char_u badword[MAXWLEN + 10];
+ char *p;
+ char cword[MAXWLEN];
+ char badword[MAXWLEN + 10];
int flags = wordflags;
if (dumpflags & DUMPFLAG_ONECAP) {
@@ -3319,17 +3367,17 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
if ((dumpflags & DUMPFLAG_KEEPCASE) == 0 && (flags & WF_CAPMASK) != 0) {
// Need to fix case according to "flags".
- make_case_word(word, cword, flags);
+ make_case_word((char_u *)word, (char_u *)cword, flags);
p = cword;
} else {
p = word;
if ((dumpflags & DUMPFLAG_KEEPCASE)
- && ((captype(word, NULL) & WF_KEEPCAP) == 0
+ && ((captype((char_u *)word, NULL) & WF_KEEPCAP) == 0
|| (flags & WF_FIXCAP) != 0)) {
keepcap = true;
}
}
- char_u *tw = p;
+ char *tw = p;
if (pat == NULL) {
// Add flags and regions after a slash.
@@ -3347,8 +3395,8 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
if (flags & WF_REGION) {
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);
}
@@ -3361,19 +3409,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;
@@ -3388,21 +3436,21 @@ 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,
+static linenr_T dump_prefixes(slang_T *slang, char *word, char_u *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;
linenr_T lnum = startlnum;
// If the word starts with a lower-case letter make the word with an
// upper-case letter in word_up[].
- int c = utf_ptr2char((char *)word);
+ int c = utf_ptr2char(word);
if (SPELL_TOUPPER(c) != c) {
- onecap_copy(word, word_up, true);
+ onecap_copy((char_u *)word, (char *)word_up, true);
has_word_up = true;
}
@@ -3436,10 +3484,10 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
}
curi[depth] += i - 1;
- c = valid_word_prefix(i, n, flags, word, slang, false);
+ c = valid_word_prefix(i, n, flags, (char_u *)word, slang, false);
if (c != 0) {
- STRLCPY(prefix + depth, word, MAXWLEN - depth);
- dump_word(slang, prefix, pat, dir, dumpflags,
+ xstrlcpy(prefix + depth, word, (size_t)(MAXWLEN - depth));
+ dump_word(slang, (char *)prefix, (char *)pat, dir, dumpflags,
(c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum);
if (lnum != 0) {
lnum++;
@@ -3450,11 +3498,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,
+ c = valid_word_prefix(i, n, flags, (char_u *)word_up, slang,
true);
if (c != 0) {
- STRLCPY(prefix + depth, word_up, MAXWLEN - depth);
- dump_word(slang, prefix, pat, dir, dumpflags,
+ xstrlcpy(prefix + depth, word_up, (size_t)(MAXWLEN - depth));
+ dump_word(slang, (char *)prefix, (char *)pat, dir, dumpflags,
(c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum);
if (lnum != 0) {
lnum++;
@@ -3463,7 +3511,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;
}
@@ -3497,7 +3545,7 @@ int spell_word_start(int startcol)
return startcol;
}
- char_u *line = get_cursor_line_ptr();
+ char_u *line = (char_u *)get_cursor_line_ptr();
char_u *p;
// Find a word character before "startcol".
@@ -3536,11 +3584,11 @@ 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;
- spell_suggest_list(&ga, pat, 100, spell_expand_need_cap, true);
+ spell_suggest_list(&ga, (char_u *)pat, 100, spell_expand_need_cap, true);
*matchp = ga.ga_data;
return ga.ga_len;
}
@@ -3556,8 +3604,8 @@ bool valid_spelllang(const char *val)
bool valid_spellfile(const char *val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (const char_u *s = (char_u *)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;
}
}
@@ -3569,9 +3617,9 @@ 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;
}
}
diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h
index 27b9777dc2..4d146a1706 100644
--- a/src/nvim/spell_defs.h
+++ b/src/nvim/spell_defs.h
@@ -72,8 +72,8 @@ 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.
@@ -115,7 +115,7 @@ 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 *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.
@@ -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])
@@ -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 0f8034312e..e6257aeca1 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -226,82 +226,105 @@
// 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
+# include <time.h>
#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");
@@ -342,8 +365,8 @@ 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 *ae_chop; // text to chop off basic word (can be NULL)
+ char *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
@@ -355,12 +378,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 +438,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
@@ -460,7 +483,7 @@ typedef struct spellinfo_S {
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 +493,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 +504,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 +575,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 +595,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 +606,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 +627,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 = (char *)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,11 +720,11 @@ 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;
}
- set_map_str(lp, p);
+ set_map_str(lp, (char_u *)p);
xfree(p);
break;
@@ -819,7 +842,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];
@@ -876,7 +899,7 @@ void suggest_load_files(void)
slang_T *slang;
char *dotp;
FILE *fd;
- char_u buf[MAXWLEN];
+ char buf[MAXWLEN];
int i;
time_t timestamp;
int wcount;
@@ -895,7 +918,7 @@ void suggest_load_files(void)
slang->sl_sugloaded = true;
dotp = strrchr(slang->sl_fname, '.');
- if (dotp == NULL || FNAMECMP(dotp, ".spl") != 0) {
+ if (dotp == NULL || path_fnamecmp(dotp, ".spl") != 0) {
continue;
}
STRCPY(dotp, ".sug");
@@ -906,9 +929,9 @@ void suggest_load_files(void)
// <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;
@@ -1031,7 +1054,7 @@ static int read_region_section(FILE *fd, slang_T *lp, int len)
if (len > MAXREGIONS * 2) {
return SP_FORMERROR;
}
- SPELL_READ_NONNUL_BYTES((char *)lp->sl_regions, (size_t)len, fd,; );
+ SPELL_READ_NONNUL_BYTES((char *)lp->sl_regions, (size_t)len, fd,; ); // NOLINT(whitespace/parens)
lp->sl_regions[len] = NUL;
return 0;
}
@@ -1098,7 +1121,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 +1144,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) {
@@ -1147,8 +1170,8 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first)
}
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;
@@ -1252,7 +1275,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
// convert the multi-byte strings to wide char strings
smp->sm_lead_w = mb_str2wide(smp->sm_lead);
- smp->sm_leadlen = mb_charlen(smp->sm_lead);
+ smp->sm_leadlen = mb_charlen((char *)smp->sm_lead);
if (smp->sm_oneof == NULL) {
smp->sm_oneof_w = NULL;
} else {
@@ -1314,7 +1337,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;
@@ -1641,7 +1664,7 @@ static int *mb_str2wide(char_u *s)
{
int i = 0;
- int *res = xmalloc(((size_t)mb_charlen(s) + 1) * sizeof(int));
+ int *res = xmalloc(((size_t)mb_charlen((char *)s) + 1) * sizeof(int));
for (char_u *p = s; *p != NUL;) {
res[i++] = mb_ptr2char_adv((const char_u **)&p);
}
@@ -1824,7 +1847,7 @@ static void spell_reload_one(char_u *fname, bool added_word)
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
if (path_full_compare((char *)fname, slang->sl_fname, false, true) == kEqualFiles) {
slang_clear(slang);
- if (spell_load_file(fname, NULL, slang, false) == NULL) {
+ if (spell_load_file((char *)fname, NULL, slang, false) == NULL) {
// reloading failed, clear the language
slang_clear(slang);
}
@@ -1994,7 +2017,7 @@ static void spell_print_tree(wordnode_T *root)
}
}
-#endif // SPELL_PRINTTREE
+#endif // SPELL_PRINTTREE
// Reads the affix file "fname".
// Returns an afffile_T, NULL for complete failure.
@@ -2005,17 +2028,17 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
char_u *line;
char_u *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,11 +2046,11 @@ 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
@@ -2041,8 +2064,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
return NULL;
}
- vim_snprintf((char *)IObuff, IOSIZE, _("Reading affix file %s..."), fname);
- spell_message(spin, (char *)IObuff);
+ 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.
do_rep = GA_EMPTY(&spin->si_rep);
@@ -2063,7 +2086,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
hash_init(&aff->af_comp);
// Read all the lines in the file one by one.
- while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) {
+ while (!vim_fgets((char *)rline, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
lnum++;
@@ -2090,8 +2113,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Split the line up in white separated items. Put a NUL after each
// item.
itemcnt = 0;
- for (p = line;;) {
- while (*p != NUL && *p <= ' ') { // skip white space and CR/NL
+ for (p = (char *)line;;) {
+ while (*p != NUL && (uint8_t)(*p) <= ' ') { // skip white space and CR/NL
p++;
}
if (*p == NUL) {
@@ -2103,11 +2126,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++;
}
}
@@ -2130,11 +2153,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
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"),
@@ -2156,9 +2179,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");
@@ -2171,49 +2194,45 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
midword = (char_u *)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],
+ } 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, (char_u *)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],
+ } 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, (char_u *)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],
+ aff->af_bad = affitem2flag(aff->af_flagtype, (char_u *)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],
+ aff->af_needaffix = affitem2flag(aff->af_flagtype, (char_u *)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],
+ aff->af_circumfix = affitem2flag(aff->af_flagtype, (char_u *)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],
+ aff->af_nosuggest = affitem2flag(aff->af_flagtype, (char_u *)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],
+ aff->af_needcomp = affitem2flag(aff->af_flagtype, (char_u *)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],
+ aff->af_comproot = affitem2flag(aff->af_flagtype, (char_u *)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],
+ aff->af_compforbid = affitem2flag(aff->af_flagtype, (char_u *)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"),
@@ -2221,7 +2240,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
} else if (is_aff_rule(items, itemcnt, "COMPOUNDPERMITFLAG", 2)
&& aff->af_comppermit == 0) {
- aff->af_comppermit = affitem2flag(aff->af_flagtype, items[1],
+ aff->af_comppermit = affitem2flag(aff->af_flagtype, (char_u *)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"),
@@ -2231,7 +2250,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;
@@ -2247,9 +2266,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) {
@@ -2299,8 +2318,8 @@ 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;
}
}
@@ -2324,12 +2343,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;
@@ -2341,7 +2360,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);
@@ -2356,9 +2375,9 @@ 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],
+ cur_aff->ah_flag = affitem2flag(aff->af_flagtype, (char_u *)items[1],
fname, lnum);
- if (cur_aff->ah_flag == 0 || STRLEN(items[1]) >= AH_KEY_LEN) {
+ if (cur_aff->ah_flag == 0 || strlen(items[1]) >= AH_KEY_LEN) {
break;
}
if (cur_aff->ah_flag == aff->af_bad
@@ -2382,7 +2401,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 {
@@ -2398,7 +2417,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]);
}
@@ -2421,10 +2440,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;
@@ -2434,7 +2453,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]);
}
@@ -2443,14 +2462,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) {
- aff_entry->ae_chop = (char_u *)getroom_save(spin, items[2]);
+ if (strcmp(items[2], "0") != 0) {
+ aff_entry->ae_chop = getroom_save(spin, items[2]);
}
- if (STRCMP(items[3], "0") != 0) {
- aff_entry->ae_add = (char_u *)getroom_save(spin, items[3]);
+ 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 = (char_u *)vim_strchr(aff_entry->ae_add, '/');
if (aff_entry->ae_flags != NULL) {
*aff_entry->ae_flags++ = NUL;
aff_process_flags(aff, aff_entry);
@@ -2464,14 +2483,14 @@ 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) {
+ if (strcmp(items[4], ".") != 0) {
char_u buf[MAXLINELEN];
aff_entry->ae_cond = (char_u *)getroom_save(spin, items[4]);
if (*items[0] == 'P') {
- sprintf((char *)buf, "^%s", items[4]);
+ sprintf((char *)buf, "^%s", items[4]); // NOLINT(runtime/printf)
} else {
- sprintf((char *)buf, "%s$", items[4]);
+ sprintf((char *)buf, "%s$", items[4]); // NOLINT(runtime/printf)
}
aff_entry->ae_prog = vim_regcomp((char *)buf, RE_MAGIC + RE_STRING + RE_STRICT);
if (aff_entry->ae_prog == NULL) {
@@ -2493,19 +2512,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);
+ 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;
@@ -2515,11 +2533,10 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// upper-case letter.
if (aff_entry->ae_cond != NULL) {
char_u buf[MAXLINELEN];
- onecap_copy(items[4], buf, true);
- aff_entry->ae_cond = (char_u *)getroom_save(spin, buf);
+ onecap_copy((char_u *)items[4], (char *)buf, true);
+ aff_entry->ae_cond = (char_u *)getroom_save(spin, (char *)buf);
if (aff_entry->ae_cond != NULL) {
- sprintf((char *)buf, "^%s",
- aff_entry->ae_cond);
+ sprintf((char *)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);
}
@@ -2534,10 +2551,9 @@ 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];
- if (str_equal(p, aff_entry->ae_cond)) {
+ for (idx = spin->si_prefcond.ga_len - 1; idx >= 0; idx--) {
+ p = ((char **)spin->si_prefcond.ga_data)[idx];
+ if (str_equal(p, (char *)aff_entry->ae_cond)) {
break;
}
}
@@ -2546,12 +2562,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 : (char_u *)getroom_save(spin, aff_entry->ae_cond);
+ NULL : (char_u *)getroom_save(spin, (char *)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;
}
@@ -2571,7 +2587,7 @@ 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;
}
@@ -2584,11 +2600,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
}
} 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
@@ -2596,8 +2612,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
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
@@ -2619,8 +2635,8 @@ 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
@@ -2640,7 +2656,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if ((!GA_EMPTY(&spin->si_map)
&& vim_strchr(spin->si_map.ga_data, c)
!= NULL)
- || vim_strchr((char *)p, c) != NULL) {
+ || vim_strchr(p, c) != NULL) {
smsg(_("Duplicate character in MAP in %s line %d"),
fname, lnum);
}
@@ -2657,17 +2673,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)
@@ -2676,12 +2692,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 = (char_u *)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);
}
}
@@ -2745,8 +2761,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
if (syllable != NULL) {
- aff_check_string(spin->si_syllable, syllable, "SYLLABLE");
- spin->si_syllable = syllable;
+ aff_check_string(spin->si_syllable, (char *)syllable, "SYLLABLE");
+ spin->si_syllable = (char *)syllable;
}
if (sofofrom != NULL || sofoto != NULL) {
@@ -2756,16 +2772,16 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
} else if (!GA_EMPTY(&spin->si_sal)) {
smsg(_("Both SAL and SOFO lines in %s"), fname);
} else {
- aff_check_string(spin->si_sofofr, sofofrom, "SOFOFROM");
- aff_check_string(spin->si_sofoto, sofoto, "SOFOTO");
- spin->si_sofofr = sofofrom;
- spin->si_sofoto = sofoto;
+ aff_check_string(spin->si_sofofr, (char *)sofofrom, "SOFOFROM");
+ aff_check_string(spin->si_sofoto, (char *)sofoto, "SOFOTO");
+ spin->si_sofofr = (char *)sofofrom;
+ spin->si_sofoto = (char *)sofoto;
}
}
if (midword != NULL) {
- aff_check_string(spin->si_midword, midword, "MIDWORD");
- spin->si_midword = midword;
+ aff_check_string(spin->si_midword, (char *)midword, "MIDWORD");
+ spin->si_midword = (char *)midword;
}
xfree(pc);
@@ -2773,11 +2789,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] == '#'));
}
@@ -2796,7 +2812,7 @@ static void aff_process_flags(afffile_T *affile, affentry_T *entry)
prevp = p;
flag = get_affitem(affile->af_flagtype, &p);
if (flag == affile->af_comppermit || flag == affile->af_compforbid) {
- STRMOVE(prevp, p);
+ STRMOVE(prevp, (char *)p);
p = prevp;
if (flag == affile->af_comppermit) {
entry->ae_comppermit = true;
@@ -2814,15 +2830,15 @@ 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.
@@ -2859,7 +2875,7 @@ static unsigned get_affitem(int flagtype, char_u **pp)
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);
@@ -2879,28 +2895,28 @@ static unsigned get_affitem(int flagtype, char_u **pp)
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) {
@@ -2908,20 +2924,20 @@ 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) {
// Copy non-flag characters directly.
- *tp++ = *p++;
+ *tp++ = (char_u)(*p++);
} else {
// First get the flag number, also checks validity.
prevp = p;
- flag = get_affitem(aff->af_flagtype, &p);
+ flag = get_affitem(aff->af_flagtype, (char_u **)&p);
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;
@@ -2936,7 +2952,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;
}
@@ -3014,42 +3030,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];
fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap);
- (void)spell_casefold(curwin, from, (int)STRLEN(from), word, MAXWLEN);
- ftp->ft_from = (char_u *)getroom_save(spin, word);
- (void)spell_casefold(curwin, to, (int)STRLEN(to), word, MAXWLEN);
- ftp->ft_to = (char_u *)getroom_save(spin, word);
+ (void)spell_casefold(curwin, (char_u *)from, (int)strlen(from), word, MAXWLEN);
+ ftp->ft_from = getroom_save(spin, (char *)word);
+ (void)spell_casefold(curwin, (char_u *)to, (int)strlen(to), word, MAXWLEN);
+ ftp->ft_to = getroom_save(spin, (char *)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().
@@ -3090,13 +3106,13 @@ static void spell_free_aff(afffile_T *aff)
static int spell_read_dic(spellinfo_T *spin, char_u *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;
@@ -3121,22 +3137,22 @@ 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, (char *)IObuff);
+ spell_message(spin, IObuff);
// start with a message for the first line
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] == '/') {
@@ -3144,7 +3160,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--;
}
@@ -3164,7 +3180,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.
@@ -3172,7 +3188,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;
@@ -3181,7 +3197,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;
@@ -3197,7 +3213,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;
@@ -3206,15 +3222,15 @@ 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 = (char_u *)getroom_save(spin, w);
+ dw = getroom_save(spin, (char *)w);
if (dw == NULL) {
retval = FAIL;
xfree(pc);
break;
}
- hash = hash_hash(dw);
- hi = hash_lookup(&ht, (const char *)dw, STRLEN(dw), hash);
+ hash = hash_hash((char_u *)dw);
+ 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"),
@@ -3225,7 +3241,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
}
duplicate++;
} else {
- hash_add_item(&ht, hi, dw, hash);
+ hash_add_item(&ht, hi, (char_u *)dw, hash);
}
flags = 0;
@@ -3265,14 +3281,14 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// Additionally do matching prefixes that combine.
if (store_aff_word(spin, dw, 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,
&affile->af_pref, NULL,
- CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) {
+ CONDIT_SUF, flags, (char *)store_afflist, pfxlen) == FAIL) {
retval = FAIL;
}
}
@@ -3337,18 +3353,18 @@ static int get_affix_flags(afffile_T *affile, char_u *afflist)
static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist)
{
char_u *p;
- char_u *prevp;
+ 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;) {
- prevp = p;
+ prevp = (char *)p;
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 - (char_u *)prevp) + 1);
hi = hash_find(&affile->af_pref, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
id = HI2AH(hi)->ah_newID;
@@ -3372,16 +3388,16 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist)
{
char_u *p;
- char_u *prevp;
+ 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;
+ prevp = (char *)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 - (char_u *)prevp) + 1);
hi = hash_find(&affile->af_comp, (char *)key);
if (!HASHITEM_EMPTY(hi)) {
store_afflist[cnt++] = (char_u)HI2CI(hi)->ci_newID;
@@ -3410,25 +3426,25 @@ 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_u *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;
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;
@@ -3458,9 +3474,9 @@ 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))
+ || vim_regexec_prog(&ae->ae_prog, false, (char_u *)word, (colnr_T)0))
&& (((condit & CONDIT_CFIX) == 0)
== ((condit & CONDIT_AFF) == 0
|| ae->ae_flags == NULL
@@ -3472,9 +3488,9 @@ 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;
+ p = (char_u *)word;
if (ae->ae_chop != NULL) {
// Skip chop string.
i = mb_charlen(ae->ae_chop);
@@ -3485,10 +3501,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 = (char_u *)newword + strlen(newword);
i = mb_charlen(ae->ae_chop);
for (; i > 0; i--) {
MB_PTR_BACK(newword, p);
@@ -3534,7 +3550,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
} else {
use_pfxlen = 0;
}
- use_pfxlist = store_afflist;
+ use_pfxlist = (char *)store_afflist;
// Combine the prefix IDs. Avoid adding the
// same ID twice.
@@ -3552,7 +3568,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;
}
@@ -3577,7 +3593,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;
}
@@ -3608,7 +3624,7 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
// Store the modified word.
if (store_word(spin, newword, use_flags,
- spin->si_region, use_pfxlist,
+ spin->si_region, (char_u *)use_pfxlist,
need_affix) == FAIL) {
retval = FAIL;
}
@@ -3619,7 +3635,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;
}
@@ -3657,8 +3673,8 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *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;
@@ -3675,11 +3691,11 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
return FAIL;
}
- vim_snprintf((char *)IObuff, IOSIZE, _("Reading word file %s..."), fname);
- spell_message(spin, (char *)IObuff);
+ vim_snprintf(IObuff, IOSIZE, _("Reading word file %s..."), fname);
+ spell_message(spin, IObuff);
// Read all the lines in the file one by one.
- while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) {
+ while (!vim_fgets((char *)rline, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
lnum++;
@@ -3689,8 +3705,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) {
@@ -3701,13 +3717,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 = (char_u *)string_convert(&spin->si_conv, (char *)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;
@@ -3715,7 +3731,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);
@@ -3727,7 +3743,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
// Setup for conversion to 'encoding'.
line += 9;
- enc = enc_canonize((char *)line);
+ enc = enc_canonize(line);
if (!spin->si_ascii
&& convert_setup(&spin->si_conv, enc, p_enc) == FAIL) {
smsg(_("Conversion in %s not supported: from %s to %s"),
@@ -3739,17 +3755,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.
@@ -3768,7 +3784,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) {
@@ -3818,9 +3834,9 @@ 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, (char *)IObuff);
+ spell_message(spin, IObuff);
}
return retval;
@@ -3866,9 +3882,9 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align)
/// 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_u *s)
+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);
}
@@ -3919,20 +3935,20 @@ static bool valid_spell_word(const char *word, const char *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_u *pfxlist,
bool need_affix)
{
- int len = (int)STRLEN(word);
- int ct = captype(word, word + len);
+ int len = (int)strlen(word);
+ int ct = captype((char_u *)word, (char_u *)word + len);
char_u foldword[MAXWLEN];
int res = OK;
// Avoid adding illegal bytes to the word tree.
- if (!valid_spell_word((char *)word, (char *)word + len)) {
+ if (!valid_spell_word(word, word + len)) {
return FAIL;
}
- (void)spell_casefold(curwin, word, len, foldword, MAXWLEN);
+ (void)spell_casefold(curwin, (char_u *)word, len, foldword, MAXWLEN);
for (const char_u *p = pfxlist; res == OK; p++) {
if (!need_affix || (p != NULL && *p != NUL)) {
res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags,
@@ -3947,7 +3963,7 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) {
for (const char_u *p = 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) {
@@ -3963,8 +3979,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;
@@ -4094,7 +4110,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)
@@ -4210,10 +4226,10 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
} else {
perc = (tot - n) * 100 / tot;
}
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Compressed %s of %ld nodes; %ld (%ld%%) remaining"),
name, tot, tot - n, perc);
- spell_message(spin, (char *)IObuff);
+ spell_message(spin, IObuff);
}
#ifdef SPELL_PRINTTREE
spell_print_tree(root->wn_sibling);
@@ -4251,7 +4267,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
// Try to find an identical child.
hash = hash_hash(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
@@ -4338,13 +4354,13 @@ 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".
@@ -4377,7 +4393,7 @@ static int write_vim_spell(spellinfo_T *spin, char *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>
}
@@ -4440,7 +4456,7 @@ static int write_vim_spell(spellinfo_T *spin, char *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>
@@ -4500,8 +4516,8 @@ static int write_vim_spell(spellinfo_T *spin, char *fname)
assert(gap->ga_len >= 0);
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>
@@ -4528,8 +4544,8 @@ static int write_vim_spell(spellinfo_T *spin, char *fname)
// <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);
+ char *p = rr == 1 ? ftp->ft_from : ftp->ft_to;
+ l = strlen(p);
assert(l < INT_MAX);
putc((int)l, fd);
if (l > 0) {
@@ -4545,13 +4561,13 @@ static int write_vim_spell(spellinfo_T *spin, char *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>
}
@@ -4572,7 +4588,7 @@ static int write_vim_spell(spellinfo_T *spin, char *fname)
todo = spin->si_commonwords.ht_used;
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);
@@ -4638,10 +4654,10 @@ static int write_vim_spell(spellinfo_T *spin, char *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>
@@ -4653,12 +4669,12 @@ static int write_vim_spell(spellinfo_T *spin, char *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
@@ -4677,7 +4693,7 @@ static int write_vim_spell(spellinfo_T *spin, char *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>
}
@@ -4879,7 +4895,7 @@ void ex_mkspell(exarg_T *eap)
char *arg = eap->arg;
bool ascii = false;
- if (STRNCMP(arg, "-ascii", 6) == 0) {
+ if (strncmp(arg, "-ascii", 6) == 0) {
ascii = true;
arg = skipwhite(arg + 6);
}
@@ -4896,7 +4912,7 @@ void ex_mkspell(exarg_T *eap)
// Writes the file with the name "wfname", with ".spl" changed to ".sug".
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;
@@ -4914,7 +4930,7 @@ static void spell_make_sugfile(spellinfo_T *spin, char *wfname)
}
if (slang == NULL) {
spell_message(spin, _("Reading back spell file..."));
- slang = spell_load_file((char_u *)wfname, NULL, NULL, false);
+ slang = spell_load_file(wfname, NULL, NULL, false);
if (slang == NULL) {
return;
}
@@ -4954,11 +4970,11 @@ static void spell_make_sugfile(spellinfo_T *spin, char *wfname)
// 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);
+ sug_write(spin, (char_u *)fname);
theend:
xfree(fname);
@@ -5019,7 +5035,7 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
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.
@@ -5192,9 +5208,9 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
return;
}
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Writing suggestion file %s..."), fname);
- spell_message(spin, (char *)IObuff);
+ spell_message(spin, IObuff);
// <SUGHEADER>: <fileID> <versionnr> <timestamp>
if (fwrite(VIMSUGMAGIC, VIMSUGMAGICL, (size_t)1, fd) != 1) { // <fileID>
@@ -5233,8 +5249,8 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
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;
@@ -5248,9 +5264,9 @@ 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, (char *)IObuff);
+ spell_message(spin, IObuff);
theend:
// close the file
@@ -5299,8 +5315,8 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
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;
@@ -5310,9 +5326,9 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
incount = 1;
vim_snprintf(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) {
+ } 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(wfname, MAXPATHL, SPL_FNAME_TMPL,
@@ -5339,7 +5355,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
} else {
// Check for overwriting before doing things that may take a lot of
// time.
- if (!over_write && os_path_exists((char_u *)wfname)) {
+ if (!over_write && os_path_exists(wfname)) {
emsg(_(e_exists));
goto theend;
}
@@ -5356,8 +5372,8 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
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;
@@ -5389,7 +5405,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
spin.si_region = 1 << i;
vim_snprintf((char *)fname, MAXPATHL, "%s.aff", innames[i]);
- if (os_path_exists(fname)) {
+ if (os_path_exists((char *)fname)) {
// Read the .aff file. Will init "spin->si_conv" based on the
// "SET" line.
afile[i] = spell_read_aff(&spin, fname);
@@ -5429,16 +5445,16 @@ 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, (char *)IObuff);
+ spell_message(&spin, IObuff);
error = write_vim_spell(&spin, wfname) == FAIL;
spell_message(&spin, _("Done!"));
- vim_snprintf((char *)IObuff, IOSIZE,
+ vim_snprintf(IObuff, IOSIZE,
_("Estimated runtime memory use: %d bytes"), spin.si_memtot);
- spell_message(&spin, (char *)IObuff);
+ spell_message(&spin, IObuff);
// If the file is loaded need to reload it.
if (!error) {
@@ -5500,7 +5516,7 @@ static void spell_message(const spellinfo_T *spin, char *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,
@@ -5512,26 +5528,26 @@ 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 line[MAXWLEN * 2];
long fpos, fpos_next = 0;
int i;
char_u *spf;
- if (!valid_spell_word((char *)word, (char *)word + len)) {
+ if (!valid_spell_word(word, word + len)) {
emsg(_(e_illegal_character_in_word));
return;
}
if (idx == 0) { // use internal wordlist
if (int_wordlist == NULL) {
- int_wordlist = vim_tempname();
+ int_wordlist = (char_u *)vim_tempname();
if (int_wordlist == NULL) {
return;
}
@@ -5581,14 +5597,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.
@@ -5600,7 +5616,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);
}
}
@@ -5625,7 +5641,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;
@@ -5650,7 +5666,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);
}
}
@@ -5672,7 +5688,7 @@ 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;
@@ -5700,32 +5716,32 @@ static void init_spellfile(void)
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 - (char_u *)curbuf->b_s.b_p_spl);
+ xstrlcpy(buf, curbuf->b_s.b_p_spl, (size_t)(lstart - (char_u *)curbuf->b_s.b_p_spl));
} else {
// Copy the path from 'runtimepath' to buf[].
- copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ",");
+ copy_option_part((char **)&rtp, buf, MAXPATHL, ",");
}
- if (os_file_is_writable((char *)buf) == 2) {
+ 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) {
- STRLCPY(buf, curbuf->b_s.b_p_spl, lend - (char_u *)curbuf->b_s.b_p_spl + 1);
+ xstrlcpy(buf, curbuf->b_s.b_p_spl, (size_t)(lend - (char_u *)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(buf + l, MAXPATHL - (size_t)l, "/spell");
+ if (os_file_is_writable(buf) != 2) {
+ os_mkdir(buf, 0755);
}
- l = (int)STRLEN(buf);
- vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l,
+ l = (int)strlen(buf);
+ vim_snprintf(buf + l, MAXPATHL - (size_t)l,
"/%.*s", (int)(lend - lstart), lstart);
}
- l = (int)STRLEN(buf);
+ l = (int)strlen(buf);
fname = (char_u *)LANGP_ENTRY(curwin->w_s->b_langp, 0)
->lp_slang->sl_fname;
- vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, ".%s.add",
+ vim_snprintf(buf + l, MAXPATHL - (size_t)l, ".%s.add",
((fname != NULL
&& strstr(path_tail((char *)fname), ".ascii.") != NULL)
? "ascii"
@@ -5743,7 +5759,7 @@ static void init_spellfile(void)
/// 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_u *fol)
{
// We build the new tables here first, so that we can compare with the
// previous one.
@@ -5810,7 +5826,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);
@@ -5873,7 +5889,7 @@ static void set_map_str(slang_T *lp, char_u *map)
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);
+ 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);
} else {
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index 23c8c621b5..9db7efb03b 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;
@@ -341,9 +375,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,7 +390,7 @@ int spell_check_sps(void)
{
char *p;
char *s;
- char_u buf[MAXPATHL];
+ char buf[MAXPATHL];
int f;
sps_flags = 0;
@@ -370,15 +406,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 +443,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 +483,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,9 +495,9 @@ 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(p, curwin)) {
MB_PTR_BACK(line, p);
}
// Forward to start of word.
@@ -468,7 +509,7 @@ void spell_suggest(int count)
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 +518,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 +528,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 +549,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 +567,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 +591,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 +634,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);
@@ -653,7 +694,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 +716,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,11 +734,11 @@ 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 {
- size_t tmplen = spell_check(curwin, su->su_badptr, &attr, NULL, false);
+ size_t tmplen = spell_check(curwin, (char_u *)su->su_badptr, &attr, NULL, false);
assert(tmplen <= INT_MAX);
su->su_badlen = (int)tmplen;
}
@@ -707,8 +748,8 @@ 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);
- (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword,
+ xstrlcpy((char *)su->su_badword, su->su_badptr, (size_t)su->su_badlen + 1);
+ (void)spell_casefold(curwin, (char_u *)su->su_badptr, su->su_badlen, (char_u *)su->su_fbadword,
MAXWLEN);
// TODO(vim): make this work if the case-folded text is longer than the
@@ -717,8 +758,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 +779,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_u *)su->su_badword, (char_u *)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((char_u *)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 +855,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 +885,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, '/');
@@ -864,7 +905,7 @@ static void spell_suggest_file(suginfo_T *su, char_u *fname)
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 +1009,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 = (char_u *)skiptowhite((char *)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((char_u *)su->su_fbadword, 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 +1079,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, (char_u *)p, (int)strlen(p), (char_u *)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 +1151,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 +1173,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
@@ -1222,10 +1263,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (depth < MAXWLEN - 1 && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
// Set su->su_badflags to the caps type at this position.
// Use the caps type until here for the prefix itself.
- n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
- 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);
+ n = nofold_len((char_u *)fword, sp->ts_fidx, (char_u *)su->su_badptr);
+ 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
@@ -1240,9 +1281,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Move the prefix to preword[] with the right case
// and make find_keepcap_word() works.
tword[sp->ts_twordlen] = NUL;
- make_case_word(tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen, flags);
- sp->ts_prewordlen = (char_u)STRLEN(preword);
+ make_case_word((char_u *)tword + sp->ts_splitoff,
+ (char_u *)preword + sp->ts_prewordlen, flags);
+ sp->ts_prewordlen = (char_u)strlen(preword);
sp->ts_splitoff = sp->ts_twordlen;
}
break;
@@ -1269,7 +1310,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
fword_ends = (fword[sp->ts_fidx] == NUL
|| (soundfold
? ascii_iswhite(fword[sp->ts_fidx])
- : !spell_iswordp(fword + sp->ts_fidx, curwin)));
+ : !spell_iswordp((char_u *)fword + sp->ts_fidx, curwin)));
tword[sp->ts_twordlen] = NUL;
if (sp->ts_prefixdepth <= PFD_NOTSPECIAL
@@ -1284,7 +1325,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
for (c = 0; c < len && pbyts[n + c] == 0; c++) {}
if (c > 0) {
c = valid_word_prefix(c, n, flags,
- tword + sp->ts_splitoff, slang, false);
+ (char_u *)tword + sp->ts_splitoff, slang, false);
if (c == 0) {
break;
}
@@ -1321,16 +1362,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) {
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 +1398,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,
@@ -1369,11 +1410,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
if (compound_ok) {
p = preword;
- while (*skiptowhite((char *)p) != NUL) {
- p = (char_u *)skipwhite(skiptowhite((char *)p));
+ while (*skiptowhite(p) != NUL) {
+ 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,27 +1433,24 @@ 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;
// When appending a compound word after a word character don't
// use Onecap.
- if (p != NULL && spell_iswordp_nmw(p, curwin)) {
+ if (p != NULL && spell_iswordp_nmw((char_u *)p, curwin)) {
c &= ~WF_ONECAP;
}
- make_case_word(tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen, c);
+ make_case_word((char_u *)tword + sp->ts_splitoff,
+ (char_u *)preword + sp->ts_prewordlen, c);
}
if (!soundfold) {
@@ -1445,7 +1482,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
}
if (!spell_valid_case(su->su_badflags,
- captype(preword + sp->ts_prewordlen, NULL))) {
+ captype((char_u *)preword + sp->ts_prewordlen, NULL))) {
newscore += SCORE_ICASE;
}
}
@@ -1457,7 +1494,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
@@ -1476,10 +1513,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// char, e.g., "thes," -> "these".
p = fword + sp->ts_fidx;
MB_PTR_BACK(fword, p);
- if (!spell_iswordp(p, curwin) && *preword != NUL) {
- p = preword + STRLEN(preword);
+ if (!spell_iswordp((char_u *)p, curwin) && *preword != NUL) {
+ p = preword + strlen(preword);
MB_PTR_BACK(preword, p);
- if (spell_iswordp(p, curwin)) {
+ if (spell_iswordp((char_u *)p, curwin)) {
newscore += SCORE_NONWORD;
}
}
@@ -1487,25 +1524,25 @@ 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);
if (su->su_badflags & WF_MIXCAP) {
// We really don't know if the word should be
// upper or lower case, add both.
- c = captype(preword, NULL);
+ c = captype((char_u *)preword, NULL);
if (c == 0 || c == WF_ALLCAP) {
- make_case_word(tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen,
+ make_case_word((char_u *)tword + sp->ts_splitoff,
+ (char_u *)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);
@@ -1590,12 +1627,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
break;
}
p = preword;
- while (*skiptowhite((char *)p) != NUL) {
- p = (char_u *)skipwhite(skiptowhite((char *)p));
+ while (*skiptowhite(p) != NUL) {
+ 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 +1643,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 +1669,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;
@@ -1642,7 +1678,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// non-word character with a space. Always skip a
// character when the word ends. But only when the
// good word can end.
- if (((!try_compound && !spell_iswordp_nmw(fword
+ if (((!try_compound && !spell_iswordp_nmw((char_u *)fword
+ sp->ts_fidx,
curwin))
|| fword_ends)
@@ -1650,7 +1686,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);
@@ -1674,9 +1710,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
- 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);
+ n = nofold_len((char_u *)fword, sp->ts_fidx, (char_u *)su->su_badptr);
+ 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 +1779,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 +1789,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 +1808,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 +1834,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 +1849,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 +1869,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 +1920,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 +1985,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 +1995,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 +2012,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 +2024,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)
@@ -1998,20 +2034,20 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// Don't swap if the first character is not a word character.
// SWAP3 etc. also don't make sense then.
- if (!soundfold && !spell_iswordp(p, curwin)) {
+ if (!soundfold && !spell_iswordp((char_u *)p, curwin)) {
PROF_STORE(sp->ts_state)
sp->ts_state = STATE_REP_INI;
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)) {
+ } else if (!soundfold && !spell_iswordp((char_u *)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 +2077,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 +2089,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 +2100,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);
- if (!soundfold && !spell_iswordp(p + n + fl, curwin)) {
+ n = utf_ptr2len(p);
+ c = utf_ptr2char(p);
+ fl = utf_ptr2len(p + n);
+ c2 = utf_ptr2char(p + n);
+ if (!soundfold && !spell_iswordp((char_u *)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 +2133,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,17 +2145,17 @@ 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)) {
+ if (!soundfold && !spell_iswordp((char_u *)p, curwin)) {
// Middle char is not a word char, skip the rotate. First and
// third char were already checked at swap and swap3.
PROF_STORE(sp->ts_state)
@@ -2141,12 +2177,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 +2193,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 +2214,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 +2230,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 +2256,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 +2286,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 +2303,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 +2332,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 +2380,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;
@@ -2373,7 +2409,7 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
}
// Make an all-cap version of "fword".
- allcap_copy(fword, uword);
+ allcap_copy((char_u *)fword, (char_u *)uword);
// Each character needs to be tried both case-folded and upper-case.
// All this gets very complicated if we keep in mind that changing case
@@ -2402,13 +2438,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 +2479,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 +2521,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 +2532,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 +2553,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 +2564,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 +2589,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 +2619,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 +2658,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,20 +2666,20 @@ 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, (char_u *)su->su_badptr, stp->st_orglen, 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
// removing the space. Don't do it when the good word also contains a
// space.
if (ascii_iswhite(su->su_badptr[su->su_badlen])
- && *skiptowhite((char *)stp->st_word) == NUL) {
+ && *skiptowhite(stp->st_word) == NUL) {
for (p = fword; *(p = (char_u *)skiptowhite((char *)p)) != NUL;) {
- STRMOVE(p, p + 1);
+ STRMOVE(p, (char *)p + 1);
}
}
- spell_soundfold(slang, fword, true, badsound2);
+ spell_soundfold(slang, (char *)fword, true, (char *)badsound2);
pbad = badsound2;
}
@@ -2650,17 +2687,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 +2744,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 +2752,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 +2794,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;
@@ -2782,8 +2819,8 @@ static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_
// times with different scores. Since the following is quite slow only do
// 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);
+ hash = hash_hash((char_u *)goodword);
+ const size_t goodword_len = strlen(goodword);
hi = hash_lookup(&slang->sl_sounddone, (const char *)goodword, goodword_len,
hash);
if (HASHITEM_EMPTY(hi)) {
@@ -2800,14 +2837,14 @@ 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
@@ -2866,7 +2903,7 @@ 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;
@@ -2883,7 +2920,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 +2954,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 +2968,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);
}
}
@@ -3029,7 +3066,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 +3081,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 +3098,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 +3108,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 +3118,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;
}
}
@@ -3103,7 +3140,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword,
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 +3181,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 +3210,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,12 +3220,12 @@ 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);
+ (void)spell_check(curwin, (char_u *)longword, &attr, NULL, false);
if (attr != HLF_COUNT) {
// Remove this entry.
xfree(stp[i].st_word);
@@ -3201,15 +3238,15 @@ 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);
+ hash = hash_hash((char_u *)word);
+ const size_t word_len = strlen(word);
+ hi = hash_lookup(&su->su_banned, word, word_len, hash);
if (HASHITEM_EMPTY(hi)) {
s = xmemdupz(word, word_len);
hash_add_item(&su->su_banned, hi, s, hash);
@@ -3240,7 +3277,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;
}
@@ -3307,15 +3344,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 +3383,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 +3417,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 +3441,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 +3459,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 +3472,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 +3500,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 +3523,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 +3539,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 +3550,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 +3568,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;
@@ -3636,7 +3673,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;
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 9b441ba0a9..db3e3f91bf 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -5,22 +5,41 @@
#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/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/statusline.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -43,11 +62,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 +92,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 = (char_u *)NameBuff;
- len = (int)STRLEN(p);
-
- if (bt_help(wp->w_buffer)
- || wp->w_p_pvw
- || bufIsChanged(wp->w_buffer)
- || wp->w_buffer->b_p_ro) {
+ p = NameBuff;
+ len = (int)strlen(p);
+
+ 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) {
@@ -132,17 +151,27 @@ void win_redr_status(win_T *wp)
row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
col = is_stl_global ? 0 : wp->w_wincol;
- grid_puts(&default_grid, (char *)p, row, col, attr);
+ grid_puts(&default_grid, p, row, col, attr);
grid_fill(&default_grid, row, row + 1, len + col,
this_ru_col + col, fillchar, fillchar, attr);
- if (get_keymap_str(wp, "<%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 +186,244 @@ 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));
+ while (col < len) {
+ click_defs[col++] = cur_click_def;
+ }
+ 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;
+ }
+ }
+ while (col < width) {
+ click_defs[col++] = cur_click_def;
+ }
+}
+
+/// 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 +438,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 +468,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 +517,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 +537,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 +545,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
@@ -344,7 +595,7 @@ void win_redr_ruler(win_T *wp, bool always)
ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
grid_puts(grid, buffer, row, this_ru_col + off, attr);
grid_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 +639,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,234 +647,257 @@ 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;
- 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 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);
- 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 = (char_u *)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;
+ // Use the 'tabline' option if it's set.
+ if (*p_tal != NUL) {
+ win_redr_custom(NULL, false, false);
+ } else {
+ FOR_ALL_TABS(tp) {
+ tabcount++;
}
- fillchar = wp->w_p_fcs_chars.wbr;
- attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
- maxwidth = wp->w_width_inner;
- use_sandbox = was_set_insecurely(wp, "winbar", 0);
-
- stl_clear_click_defs(wp->w_winbar_click_defs, (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 = (char_u *)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 = (char_u *)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 = (char_u *)wp->w_p_stl;
+ scol = col;
+
+ if (tp == curtab) {
+ cwp = curwin;
+ wp = firstwin;
} else {
- stl = (char_u *)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 = (char_u *)NameBuff;
+ while (len > room) {
+ len -= ptr2cells((char *)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, p, textlen, row, col, curattr);
- col += vim_strnsize(p, textlen);
- p = hltab[n].start;
+ grid_puts_len(&default_grid, (char *)p, (int)strlen((char *)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 ? "" : 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(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". If "setnum" is true,
+/// update the "lnum" and "relnum" vim-variables for a new line.
+///
+/// @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, bool setnum, bool wrap, linenr_T lnum, long relnum, int maxwidth,
+ int fillchar, char *buf, stl_hlrec_t **hlrec, statuscol_T *stcp)
+{
+ if (setnum) {
+ set_vim_var_nr(VV_LNUM, lnum);
+ set_vim_var_nr(VV_RELNUM, relnum);
}
- while (col < maxwidth) {
- click_defs[col++] = cur_click_def;
+ set_vim_var_bool(VV_WRAP, wrap);
+
+ 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, &clickrec, stcp);
+ xfree(stc);
+
+ // Allocate and fill click def array if width has changed
+ if (wp->w_status_click_defs_size != (size_t)width) {
+ 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".
@@ -645,15 +918,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;
@@ -668,6 +944,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);
@@ -681,6 +962,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] == '!') {
@@ -711,13 +997,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.
@@ -828,7 +1114,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,
@@ -914,7 +1200,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;
@@ -928,7 +1214,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;
// }
@@ -1099,17 +1385,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: // '{'
@@ -1190,8 +1476,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;
@@ -1216,8 +1502,15 @@ 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) {
+ 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:
@@ -1254,6 +1547,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;
@@ -1277,7 +1576,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:
@@ -1309,9 +1608,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) {
+ 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;
@@ -1323,12 +1629,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;
@@ -1341,7 +1674,7 @@ 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++) {
@@ -1561,7 +1894,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 {
@@ -1594,6 +1927,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;
@@ -1669,7 +2006,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.
@@ -1705,7 +2042,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;
@@ -1803,5 +2140,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 16691d0ded..f0f9fbf51b 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((char *)p))) {
+ if (vim_strchr(esc_chars, (uint8_t)(*p)) != NULL || (bsl && rem_backslash(p))) {
length++; // count a backslash
}
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((char *)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_u *p = (char_u *)string; *p != NUL; MB_PTR_ADV(p)) {
+#ifdef MSWIN
if (!p_ssl) {
if (*p == '"') {
length++; // " -> ""
@@ -227,7 +185,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
length++; // insert backslash
}
}
- if (do_special && find_cmdline_var(p, &l) >= 0) {
+ if (do_special && find_cmdline_var((char *)p, &l) >= 0) {
length++; // insert backslash
p += l - 1;
}
@@ -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,35 +262,29 @@ 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);
- vim_strup(p1);
+ p1 = xstrdup(string);
+ vim_strup((char_u *)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);
- vim_strup(p1);
+ char *p1 = xstrnsave(string, len);
+ vim_strup((char_u *)p1);
return p1;
}
-/*
- * ASCII lower-to-upper case translation, language independent.
- */
+// ASCII lower-to-upper case translation, language independent.
void vim_strup(char_u *p)
FUNC_ATTR_NONNULL_ALL
{
@@ -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,11 +363,9 @@ 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
{
@@ -439,11 +387,9 @@ 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
{
@@ -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,7 +442,7 @@ 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)
@@ -506,18 +450,16 @@ void sort_strings(char **files, int count)
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) {
+ if ((uint8_t)(*p) >= 128) {
return true;
}
}
@@ -544,8 +486,8 @@ bool has_non_ascii_len(const char *const s, const size_t len)
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 *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;
@@ -644,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
@@ -1005,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;
}
@@ -1075,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;
}
@@ -1089,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)
@@ -1111,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);
@@ -1513,7 +1444,7 @@ char *reverse_text(char *s)
FUNC_ATTR_NONNULL_RET
{
// Reverse the pattern.
- size_t len = STRLEN(s);
+ 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++) {
@@ -1538,7 +1469,7 @@ char *reverse_text(char *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;
@@ -1551,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 f055cc9b0e..721cc7707a 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_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.
- */
+ // 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,16 +609,12 @@ 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++) {
@@ -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,7 +719,7 @@ 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);
@@ -805,7 +727,7 @@ static void save_chartab(char_u *chartab)
}
}
-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);
@@ -818,7 +740,7 @@ 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];
+ char buf_chartab[32];
save_chartab(buf_chartab);
regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
@@ -833,18 +755,14 @@ static int syn_match_linecont(linenr_T lnum)
return false;
}
-/*
- * 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();
@@ -881,15 +799,13 @@ static void syn_update_ends(bool startofline)
}
}
- /*
- * 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--) {
@@ -923,37 +839,35 @@ 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)
{
@@ -968,10 +882,8 @@ static void syn_stack_free_block(synblock_T *block)
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);
@@ -984,12 +896,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;
@@ -1056,12 +966,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);
@@ -1137,11 +1045,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;
@@ -1158,10 +1064,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) {
@@ -1175,10 +1079,8 @@ 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);
@@ -1187,10 +1089,8 @@ static void syn_stack_free_entry(synblock_T *block, synstate_T *p)
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;
@@ -1207,10 +1107,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;
@@ -1219,10 +1117,8 @@ 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.
- */
+ // 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
@@ -1254,9 +1150,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();
@@ -1316,9 +1210,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;
@@ -1422,20 +1314,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;
@@ -1446,9 +1339,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)
{
@@ -1473,33 +1365,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();
}
@@ -1587,9 +1471,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);
@@ -1625,8 +1507,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,11 +1518,9 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
// No character, no attributes! Past end of line?
// Do try matching with an empty line (could be the start of a region).
- line = (char_u *)syn_getcurline();
+ 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();
@@ -1657,11 +1537,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;
@@ -1679,23 +1557,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 {
@@ -1707,11 +1581,12 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
// 2. Check for keywords, if on a keyword char after a non-keyword
// char. Don't do this when syncing.
if (do_keywords) {
- line = (char_u *)syn_getcurline();
- const char_u *cur_pos = line + current_col;
+ line = syn_getcurline();
+ 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);
@@ -1757,21 +1632,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;) {
@@ -1781,13 +1650,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.
@@ -1813,9 +1681,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) {
@@ -1830,19 +1696,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;
@@ -1860,10 +1722,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;
@@ -1874,12 +1734,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;
@@ -1890,23 +1748,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;
}
@@ -1914,9 +1767,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
@@ -1941,9 +1793,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;
@@ -1971,15 +1821,13 @@ 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 (!found_match) {
- line = (char_u *)syn_getcurline();
+ line = syn_getcurline();
if (((current_next_flags & HL_SKIPWHITE)
&& ascii_iswhite(line[current_col]))
|| ((current_next_flags & HL_SKIPEMPTY)
@@ -1988,14 +1836,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) {
@@ -2006,10 +1852,8 @@ 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;
@@ -2075,14 +1919,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)
@@ -2102,7 +1944,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
// nextgroup ends at end of line, unless "skipnl" or "skipempty" present
if (current_next_list != NULL
- && (line = (char_u *)syn_getcurline())[current_col] != NUL
+ && (line = syn_getcurline())[current_col] != NUL
&& line[current_col + 1] == NUL
&& !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) {
current_next_list = NULL;
@@ -2142,9 +1984,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;
@@ -2153,15 +1993,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;
@@ -2194,10 +2030,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);
@@ -2225,9 +2059,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;
@@ -2239,12 +2071,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
@@ -2321,10 +2151,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);
@@ -2349,11 +2177,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;
@@ -2372,10 +2198,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;
@@ -2383,19 +2207,15 @@ 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.
- */
+ // 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;
@@ -2455,10 +2275,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,
@@ -2474,7 +2292,7 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
// a "oneline" never continues in the next line
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;
@@ -2491,10 +2309,8 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
}
}
-/*
- * 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);
@@ -2502,9 +2318,7 @@ 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)) {
@@ -2546,29 +2360,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) {
@@ -2577,9 +2387,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++;
@@ -2599,9 +2407,7 @@ 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++) {
int lc_col = matchcol;
@@ -2630,18 +2436,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];
@@ -2664,7 +2466,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) {
@@ -2687,10 +2489,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
@@ -2707,9 +2507,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))) {
@@ -2754,9 +2552,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) {
@@ -2766,9 +2562,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) {
@@ -2790,8 +2584,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;
@@ -2834,8 +2628,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;
@@ -2849,7 +2643,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);
@@ -2871,13 +2665,11 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s
/// Get current line in syntax buffer.
static char *syn_getcurline(void)
{
- return (char *)ml_get_buf(syn_buf, current_lnum, false);
+ 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;
@@ -2932,16 +2724,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) {
@@ -2950,8 +2742,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;
@@ -2982,17 +2774,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;
}
}
@@ -3000,20 +2792,18 @@ 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 = find_nextcmd((char *)arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
- next = (char_u *)skiptowhite((char *)arg);
+ next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_conceal) {
msg("syntax conceal on");
@@ -3029,20 +2819,18 @@ 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 = find_nextcmd((char *)arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
- next = (char_u *)skiptowhite((char *)arg);
+ next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_ic) {
msg("syntax case ignore");
@@ -3061,10 +2849,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 = find_nextcmd((char *)arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3081,7 +2869,7 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
return;
}
- arg_end = (char_u *)skiptowhite((char *)arg);
+ arg_end = skiptowhite(arg);
if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5) {
curwin->w_s->b_syn_foldlevel = SYNFLD_START;
} else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7) {
@@ -3091,26 +2879,24 @@ 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 = find_nextcmd((char *)arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
- next = (char_u *)skiptowhite((char *)arg);
+ next = skiptowhite(arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_spell == SYNSPL_TOP) {
msg("syntax spell toplevel");
@@ -3137,15 +2923,15 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
/// Handle ":syntax iskeyword" command.
static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
{
- char_u *arg = (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) {
@@ -3160,23 +2946,21 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
clear_string_option(&curwin->w_s->b_syn_isk);
} else {
memmove(save_chartab, curbuf->b_chartab, (size_t)32);
- save_isk = (char_u *)curbuf->b_p_isk;
- curbuf->b_p_isk = (char *)vim_strsave(arg);
+ save_isk = curbuf->b_p_isk;
+ curbuf->b_p_isk = xstrdup(arg);
buf_init_chartab(curbuf, false);
memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32);
memmove(curbuf->b_chartab, save_chartab, (size_t)32);
clear_string_option(&curwin->w_s->b_syn_isk);
curwin->w_s->b_syn_isk = curbuf->b_p_isk;
- curbuf->b_p_isk = (char *)save_isk;
+ curbuf->b_p_isk = save_isk;
}
}
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
@@ -3224,9 +3008,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) {
@@ -3236,9 +3018,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
@@ -3261,9 +3041,7 @@ 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;
@@ -3277,10 +3055,8 @@ static void syn_remove_pattern(synblock_T *block, int idx)
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);
@@ -3293,9 +3069,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);
@@ -3303,34 +3077,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 = find_nextcmd((char *)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 {
@@ -3341,43 +3109,37 @@ 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 = (char_u *)skiptowhite((char *)arg);
+ arg_end = skiptowhite(arg);
if (*arg == '@') {
id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
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(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;
@@ -3398,37 +3160,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");
@@ -3437,7 +3191,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];
@@ -3462,10 +3216,10 @@ void syn_maybe_enable(void)
/// @param syncing when true: list syncing items
static void syn_cmd_list(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *arg_end;
+ char *arg = eap->arg;
+ char *arg_end;
- eap->nextcmd = find_nextcmd((char *)arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -3508,9 +3262,7 @@ 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);
}
@@ -3518,11 +3270,9 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
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 = (char_u *)skiptowhite((char *)arg);
+ arg_end = skiptowhite(arg);
if (*arg == '@') {
int id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
if (id == 0) {
@@ -3531,17 +3281,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)
@@ -3586,8 +3336,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" },
@@ -3600,8 +3349,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" },
@@ -3698,9 +3446,7 @@ 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;
@@ -3778,14 +3524,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, 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
@@ -3853,7 +3599,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)) {
@@ -3926,7 +3672,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;
@@ -3944,9 +3690,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;
@@ -3977,16 +3721,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;
@@ -3998,12 +3743,12 @@ static void add_keyword(char_u *const name, const int id, const int flags,
}
kp->next_list = copy_id_list(next_list);
- const hash_T hash = hash_hash(kp->keyword);
+ const hash_T hash = hash_hash((char_u *)kp->keyword);
hashtab_T *const ht = (curwin->w_s->b_syn_ic)
? &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
@@ -4012,11 +3757,11 @@ static void add_keyword(char_u *const name, const int id, const int flags,
if (HASHITEM_EMPTY(hi)) {
// new keyword, add to hashtable
kp->ke_next = NULL;
- hash_add_item(ht, hi, kp->keyword, hash);
+ hash_add_item(ht, hi, (char_u *)kp->keyword, hash);
} else {
// keyword already exists, prepend to list
kp->ke_next = HI2KE(hi);
- hi->hi_key = KE2HIKEY(kp);
+ hi->hi_key = (char *)KE2HIKEY(kp);
}
}
@@ -4053,7 +3798,7 @@ static char *get_group_name(char *arg, char **name_end)
/// Return NULL for any error;
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;
@@ -4092,11 +3837,9 @@ static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, i
}
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;
}
@@ -4162,16 +3905,16 @@ static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, i
emsg(_("E393: group[t]here not accepted here"));
return NULL;
}
- gname_start = (char_u *)arg;
+ gname_start = arg;
arg = skiptowhite(arg);
- if (gname_start == (char_u *)arg) {
+ if (gname_start == arg) {
return NULL;
}
- gname = vim_strnsave(gname_start, (size_t)((char_u *)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
@@ -4200,11 +3943,9 @@ static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, i
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) {
@@ -4223,9 +3964,7 @@ 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 *arg = eap->arg;
@@ -4249,7 +3988,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
emsg(_("E397: Filename required"));
return;
}
- sgl_id = syn_check_cluster((char_u *)arg, (int)(group_name_end - arg));
+ sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
if (sgl_id == 0) {
return;
}
@@ -4257,10 +3996,8 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
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)) {
@@ -4276,10 +4013,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;
@@ -4297,9 +4032,7 @@ 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 *arg = eap->arg;
@@ -4323,7 +4056,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
}
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;
@@ -4359,12 +4092,12 @@ 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 (kw = keyword_copy; --cnt >= 0; kw += strlen(kw) + 1) {
for (p = vim_strchr(kw, '[');;) {
if (p != NULL) {
*p = NUL;
}
- add_keyword((char_u *)kw, syn_id, syn_opt_arg.flags,
+ add_keyword(kw, syn_id, syn_opt_arg.flags,
syn_opt_arg.cont_in_list,
syn_opt_arg.next_list, conceal_char);
if (p == NULL) {
@@ -4399,7 +4132,7 @@ error:
}
if (rest != NULL) {
- eap->nextcmd = (char *)check_nextcmd((char_u *)rest);
+ eap->nextcmd = check_nextcmd(rest);
} else {
semsg(_(e_invarg2), arg);
}
@@ -4439,7 +4172,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
// get the pattern.
init_syn_patterns();
CLEAR_FIELD(item);
- rest = get_syn_pattern((char_u *)rest, &item);
+ rest = get_syn_pattern(rest, &item);
if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) {
syn_opt_arg.flags |= HL_HAS_EOL;
}
@@ -4448,18 +4181,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((char_u *)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(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;
@@ -4492,9 +4221,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
}
}
- /*
- * 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);
@@ -4571,14 +4298,14 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
key_end++;
}
xfree(key);
- key = (char *)vim_strnsave_up((char_u *)rest, (size_t)(key_end - rest));
- if (STRCMP(key, "MATCHGROUP") == 0) {
+ key = vim_strnsave_up(rest, (size_t)(key_end - rest));
+ 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;
@@ -4601,7 +4328,7 @@ 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(rest, (size_t)(p - rest));
@@ -4612,11 +4339,9 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
}
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;
@@ -4631,7 +4356,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
assert(item == ITEM_SKIP || item == ITEM_END);
reg_do_extmatch = REX_USE;
}
- rest = get_syn_pattern((char_u *)rest, ppp->pp_synp);
+ rest = get_syn_pattern(rest, ppp->pp_synp);
reg_do_extmatch = 0;
if (item == ITEM_END && vim_regcomp_had_eol()
&& !(syn_opt_arg.flags & HL_EXCLUDENL)) {
@@ -4654,20 +4379,16 @@ 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((char_u *)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(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 (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next) {
@@ -4708,9 +4429,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
}
}
- /*
- * Free the allocated memory.
- */
+ // 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) {
@@ -4754,9 +4473,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;
}
@@ -4792,13 +4509,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;
@@ -4807,10 +4520,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;
@@ -4823,11 +4534,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;
@@ -4842,10 +4551,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;
@@ -4855,24 +4562,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;
}
}
@@ -4880,25 +4585,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);
@@ -4908,14 +4612,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);
@@ -4931,7 +4634,7 @@ static int syn_add_cluster(char_u *name)
syn_cluster_T *scp = GA_APPEND_VIA_PTR(syn_cluster_T,
&curwin->w_s->b_syn_clusters);
CLEAR_POINTER(scp);
- scp->scl_name = name;
+ scp->scl_name = (char_u *)name;
scp->scl_name_u = vim_strsave_up(name);
scp->scl_list = NULL;
@@ -4945,10 +4648,8 @@ 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 *arg = eap->arg;
@@ -4966,7 +4667,7 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing)
rest = get_group_name(arg, &group_name_end);
if (rest != NULL) {
- int scl_id = syn_check_cluster((char_u *)arg, (int)(group_name_end - arg));
+ int scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
if (scl_id == 0) {
return;
}
@@ -5017,9 +4718,7 @@ 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);
@@ -5030,7 +4729,7 @@ static void init_syn_patterns(void)
/// 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_u *arg, synpat_T *ci)
+static char *get_syn_pattern(char *arg, synpat_T *ci)
{
char *end;
int *p;
@@ -5042,18 +4741,18 @@ static char *get_syn_pattern(char_u *arg, synpat_T *ci)
return NULL;
}
- end = skip_regexp((char *)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 = empty_option;
- ci->sp_prog = vim_regcomp((char *)ci->sp_pattern, RE_MAGIC);
+ ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
p_cpo = cpo_save;
if (ci->sp_prog == NULL) {
@@ -5062,13 +4761,11 @@ static char *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;
}
}
@@ -5122,15 +4819,13 @@ static char *get_syn_pattern(char_u *arg, synpat_T *ci)
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;
@@ -5141,34 +4836,34 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
}
while (!ends_excmd(*arg_start)) {
- arg_end = skiptowhite((char *)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 = skiptowhite((char *)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;
@@ -5184,12 +4879,12 @@ 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;
@@ -5199,8 +4894,8 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
finished = true;
break;
}
- arg_end = skip_regexp((char *)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;
}
@@ -5208,7 +4903,7 @@ 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 =
- (char *)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
@@ -5225,14 +4920,14 @@ 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) {
+ eap->arg = next_arg;
+ if (strcmp(key, "MATCH") == 0) {
syn_cmd_match(eap, true);
- } else if (STRCMP(key, "REGION") == 0) {
+ } else if (strcmp(key, "REGION") == 0) {
syn_cmd_region(eap, true);
- } else if (STRCMP(key, "CLEAR") == 0) {
+ } else if (strcmp(key, "CLEAR") == 0) {
syn_cmd_clear(eap, true);
} else {
illegal = true;
@@ -5246,7 +4941,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
if (illegal) {
semsg(_("E404: Illegal arguments: %s"), arg_start);
} else if (!finished) {
- eap->nextcmd = (char *)check_nextcmd(arg_start);
+ eap->nextcmd = check_nextcmd(arg_start);
redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
@@ -5294,11 +4989,11 @@ static int get_id_list(char **const arg, const int keylen, int16_t **const list,
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;
@@ -5327,12 +5022,10 @@ static int get_id_list(char **const arg, const int keylen, int16_t **const list,
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 {
@@ -5417,9 +5110,7 @@ static int get_id_list(char **const arg, const int keylen, int16_t **const list,
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) {
@@ -5476,10 +5167,8 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
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;
}
@@ -5511,9 +5200,7 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
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;
@@ -5541,8 +5228,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 },
@@ -5565,21 +5251,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++;
}
@@ -5588,8 +5272,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;
}
@@ -5602,14 +5286,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);
@@ -5622,7 +5306,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.
@@ -5631,14 +5315,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);
}
}
@@ -5659,19 +5343,15 @@ static enum {
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;
@@ -5679,9 +5359,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.
@@ -5722,10 +5400,8 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
}
}
-/*
- * 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) {
@@ -5781,12 +5457,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;
@@ -5810,19 +5484,15 @@ 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) {
@@ -5885,18 +5555,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);
@@ -5911,9 +5579,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;
@@ -5928,10 +5594,8 @@ static void syntime_clear(void)
}
}
-/*
- * 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) {
@@ -5955,9 +5619,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)) {
@@ -6025,10 +5687,10 @@ 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((char *)p->pattern, len);
+ msg_outtrans_len(p->pattern, len);
msg_puts("\n");
}
ga_clear(&ga);
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 5270412382..73aa6eb9ca 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,50 +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
@@ -89,45 +93,165 @@ 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.
+int set_tagfunc_option(void)
+{
+ callback_free(&tfu_cb);
+ callback_free(&curbuf->b_tfu_cb);
+
+ if (*curbuf->b_p_tfu == NUL) {
+ return OK;
+ }
+
+ if (option_set_callback_func(curbuf->b_p_tfu, &tfu_cb) == FAIL) {
+ return FAIL;
+ }
+
+ callback_copy(&curbuf->b_tfu_cb, &tfu_cb);
+
+ return OK;
+}
+
+#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
@@ -141,16 +265,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;
@@ -167,13 +288,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;
@@ -183,16 +304,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
@@ -203,7 +323,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
prev_num_matches = num_matches;
- free_string_option((char *)nofile_fname);
+ free_string_option(nofile_fname);
nofile_fname = NULL;
clearpos(&saved_fmark.mark); // shutup gcc 4.0
@@ -216,7 +336,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 = (char *)vim_strsave(tag);
+ ptag_entry.tagname = xstrdup(tag);
}
} else {
if (g_do_tagpreview != 0) {
@@ -228,25 +348,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 = (char *)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]);
}
@@ -262,7 +378,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 = (char *)vim_strsave(tag);
+ tagstack[tagstackidx].tagname = xstrdup(tag);
curwin->w_tagstacklen = tagstacklen;
@@ -271,8 +387,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));
@@ -300,10 +415,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
@@ -325,7 +438,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;
@@ -340,11 +452,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;
@@ -376,7 +486,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:
@@ -401,9 +510,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;
@@ -428,32 +535,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 = (char_u *)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 = (char_u *)ptag_entry.tagname;
+ 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
@@ -474,9 +582,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;
}
@@ -484,13 +589,22 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
flags |= TAG_NO_TAGFUNC;
}
- if (find_tags((char *)name, &new_num_matches, &new_matches, flags,
- max_num_matches, (char *)buf_ffname) == OK
+ if (find_tags(name, &new_num_matches, &new_matches, flags,
+ max_num_matches, buf_ffname) == OK
&& new_num_matches < max_num_matches) {
max_num_matches = MAXCOL; // If less than max_num_matches
// 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.
@@ -502,15 +616,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;
}
}
@@ -529,10 +643,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;
@@ -556,8 +667,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;
@@ -585,12 +694,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 = (char *)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++;
@@ -599,21 +707,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") : "");
@@ -625,11 +730,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();
@@ -638,13 +743,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);
@@ -674,7 +777,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;
@@ -687,8 +789,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.
@@ -697,15 +798,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;
@@ -723,7 +824,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)
@@ -733,12 +834,12 @@ 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((char *)tagp.tagkind,
+ msg_outtrans_len(tagp.tagkind,
(int)(tagp.tagkind_end - tagp.tagkind));
}
msg_advance(13);
@@ -752,7 +853,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
// it and put "..." in the middle
p = tag_full_fname(&tagp);
if (p != NULL) {
- msg_outtrans_attr((char *)p, HL_ATTR(HLF_D));
+ msg_outtrans_attr(p, HL_ATTR(HLF_D));
XFREE_CLEAR(p);
}
if (msg_col > 0) {
@@ -773,28 +874,28 @@ 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;
}
msg_advance(15);
}
- p = (char_u *)msg_outtrans_one((char *)p, attr);
+ p = msg_outtrans_one(p, attr);
if (*p == TAB) {
msg_puts_attr(" ", attr);
break;
@@ -833,7 +934,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) {
@@ -852,7 +953,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
msg_putchar(' ');
p++;
} else {
- p = (char_u *)msg_outtrans_one((char *)p, 0);
+ p = msg_outtrans_one(p, 0);
}
// don't display the "$/;\"" and "$?;\""
@@ -878,14 +979,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);
@@ -897,14 +998,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
@@ -912,7 +1013,7 @@ 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
@@ -920,9 +1021,9 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
lnum = 0;
if (isdigit(*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
@@ -970,7 +1071,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;
@@ -988,16 +1089,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);
@@ -1006,9 +1107,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);
@@ -1024,13 +1123,11 @@ 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;
@@ -1045,35 +1142,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_attr((char *)name, tagstack[i].fmark.fnum == curbuf->b_fnum
+ 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
}
@@ -1087,9 +1181,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;
@@ -1106,7 +1198,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() ? ".[~*\\$" : "\\$",
+ pats->head[pats->headlen]) != NULL) {
break;
}
}
@@ -1117,7 +1210,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;
}
@@ -1134,8 +1227,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;
@@ -1143,7 +1235,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) {
@@ -1154,14 +1246,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);
@@ -1178,14 +1270,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(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--;
@@ -1204,10 +1296,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;
@@ -1223,34 +1315,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) {
@@ -1262,62 +1354,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);
});
}
}
@@ -1335,6 +1427,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((char_u *)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, (char_u *)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
@@ -1358,94 +2317,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 *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
@@ -1460,840 +2358,119 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
p_ic = false;
break;
case TC_FOLLOWSCS:
- p_ic = ignorecase((char_u *)pat);
+ p_ic = ignorecase(pat);
break;
case TC_SMART:
- p_ic = ignorecase_opt((char_u *)pat, true, true);
+ p_ic = ignorecase_opt(pat, true, true);
break;
default:
abort();
}
help_save = curbuf->b_help;
- orgpat.pat = (char_u *)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((char_u *)pat, (size_t)orgpat.len - 3);
- help_lang_find = (char_u *)&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);
+ 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((char_u *)pat, &ga_match[0], &match_count, flags,
- (char_u *)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));
+ 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++) {
- linear = (orgpat.headlen == 0 || !p_tbs || round == 2);
+ 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, (char *)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((char *)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((char *)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 = (char_u *)string_convert(&vimconv, (char *)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, (char *)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((char *)tagp.fname, (char *)tagp.fname_end,
- (char *)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) {
+ if (st.stop_searching || st.linear || (!p_ic && noic)
+ || st.orgpat->regmatch.rm_ic) {
break;
}
- if (use_cscope) {
- 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);
@@ -2305,19 +2482,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)
@@ -2350,11 +2525,9 @@ int get_tagfname(tagname_T *tnp, int first, char *buf)
}
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);
@@ -2374,15 +2547,15 @@ int get_tagfname(tagname_T *tnp, int first, char *buf)
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(buf);
#endif
- simplify_filename((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;
}
@@ -2390,16 +2563,14 @@ int get_tagfname(tagname_T *tnp, int first, char *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) ? (char_u *)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 : (char *)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 = (char *)vim_findfile(tnp->tn_search_ctx);
@@ -2409,7 +2580,7 @@ int get_tagfname(tagname_T *tnp, int first, char *buf)
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) {
@@ -2418,24 +2589,22 @@ int get_tagfname(tagname_T *tnp, int first, char *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, buf, MAXPATHL - 1, " ,");
- r_ptr = (char *)vim_findfile_stopdir((char_u *)buf);
+ r_ptr = (char *)vim_findfile_stopdir(buf);
// move the filename one char forward and truncate the
// filepath with a NUL
- filename = (char_u *)path_tail(buf);
+ filename = path_tail(buf);
STRMOVE(filename + 1, filename);
*filename++ = NUL;
- tnp->tn_search_ctx = vim_findfile_init((char_u *)buf, filename,
- (char_u *)r_ptr, 100,
+ tnp->tn_search_ctx = vim_findfile_init(buf, filename,
+ r_ptr, 100,
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;
}
@@ -2447,9 +2616,7 @@ int get_tagfname(tagname_T *tnp, int first, char *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);
@@ -2466,13 +2633,13 @@ void tagname_free(tagname_T *tnp)
/// @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;
}
@@ -2483,7 +2650,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;
}
@@ -2501,29 +2668,27 @@ 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) {
+ if (strncmp(p, "file:", 5) == 0) {
return true;
}
}
@@ -2531,14 +2696,14 @@ static bool test_for_static(tagptrs_T *tagp)
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.
@@ -2552,18 +2717,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;
@@ -2582,20 +2746,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;
}
@@ -2623,17 +2787,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;
}
@@ -2642,30 +2804,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);
@@ -2691,9 +2852,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;
@@ -2701,24 +2860,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;
}
@@ -2728,19 +2882,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);
}
}
@@ -2748,7 +2898,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;
@@ -2793,7 +2943,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;
@@ -2801,33 +2951,29 @@ 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 = (char_u *)skip_regexp((char *)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;
@@ -2849,11 +2995,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)) {
@@ -2862,26 +3006,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) {
@@ -2906,7 +3047,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
@@ -2918,7 +3059,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);
@@ -2930,10 +3071,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);
}
@@ -2969,19 +3108,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;
@@ -2992,20 +3130,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);
@@ -3020,38 +3155,36 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
/// file.
static int test_for_current(char *fname, char *fname_end, char *tag_fname, char *buf_ffname)
{
- int c;
+ char c;
int retval = false;
if (buf_ffname != NULL) { // if the buffer has a name
{
- c = (unsigned char)(*fname_end);
+ c = *fname_end;
*fname_end = NUL;
}
- char *fullname = (char *)expand_tag_fname((char_u *)fname, (char_u *)tag_fname, true);
+ char *fullname = expand_tag_fname(fname, tag_fname, true);
retval = (path_full_compare(fullname, buf_ffname, true, true) & kEqualFiles);
xfree(fullname);
- *fname_end = (char)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 = (char_u *)skip_regexp((char *)str + 1, *str, false, NULL);
+ str = skip_regexp(str + 1, *str, false);
if (*str != first_char) {
str = NULL;
} else {
@@ -3059,7 +3192,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 +3206,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 +3223,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;
@@ -3107,11 +3240,11 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
extra_flag = 0;
}
if (pat[0] == '/') {
- ret = find_tags((char *)pat + 1, num_file, file,
+ ret = find_tags(pat + 1, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
TAG_MANY, curbuf->b_ffname);
} else {
- ret = find_tags((char *)pat, num_file, file,
+ ret = find_tags(pat, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
TAG_MANY, curbuf->b_ffname);
}
@@ -3121,10 +3254,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);
@@ -3165,10 +3298,10 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char *start
}
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--;
}
@@ -3177,31 +3310,30 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char *start
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;
- ret = find_tags((char *)pat, &num_matches, &matches,
- TAG_REGEXP | TAG_NOIC, MAXCOL, (char *)buf_fname);
+ ret = find_tags(pat, &num_matches, &matches,
+ 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;
@@ -3210,7 +3342,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;
}
@@ -3219,11 +3351,11 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
tv_list_append_dict(list, dict);
full_fname = tag_full_fname(&tp);
- if (add_tag_field(dict, "name", (char *)tp.tagname, (char *)tp.tagname_end) == FAIL
- || add_tag_field(dict, "filename", (char *)full_fname, NULL) == FAIL
- || add_tag_field(dict, "cmd", (char *)tp.command, (char *)tp.command_end) == FAIL
- || add_tag_field(dict, "kind", (char *)tp.tagkind,
- tp.tagkind ? (char *)tp.tagkind_end : NULL) == FAIL
+ if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL
+ || add_tag_field(dict, "filename", full_fname, NULL) == FAIL
+ || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL
+ || add_tag_field(dict, "kind", tp.tagkind,
+ tp.tagkind ? tp.tagkind_end : NULL) == FAIL
|| tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) {
ret = FAIL;
}
@@ -3231,18 +3363,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
@@ -3254,17 +3386,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, (char *)s, (char *)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++;
}
}
@@ -3350,9 +3482,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
@@ -3364,7 +3496,7 @@ static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur
}
wp->w_tagstacklen++;
- tagstack[idx].tagname = (char *)tagname;
+ tagstack[idx].tagname = tagname;
tagstack[idx].cur_fnum = cur_fnum;
tagstack[idx].cur_match = cur_match;
if (tagstack[idx].cur_match < 0) {
@@ -3372,16 +3504,16 @@ static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur
}
tagstack[idx].fmark.mark = mark;
tagstack[idx].fmark.fnum = fnum;
- tagstack[idx].user_data = (char *)user_data;
+ 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;
@@ -3400,8 +3532,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;
}
@@ -3413,7 +3544,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));
}
}
@@ -3458,10 +3589,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 90966bcfad..106e92d43c 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) {
@@ -424,7 +440,7 @@ bool terminal_enter(void)
long save_w_p_so = curwin->w_p_so;
long save_w_p_siso = curwin->w_p_siso;
if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) {
- if (STRCMP(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 = xstrdup("number");
}
@@ -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,7 +717,7 @@ 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]);
+ size_t buff_len = strlen(y_array[0]);
char_u *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) { // -V756
// feed the lines to the terminal
@@ -697,7 +726,7 @@ 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;
@@ -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,8 +1439,8 @@ 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;
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 ce23141c7a..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
@@ -171,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\+', '', ''))
@@ -188,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.
@@ -250,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 = []
@@ -415,12 +419,12 @@ 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 = ''
@@ -430,7 +434,7 @@ for s:test in sort(s:tests)
" 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
@@ -438,10 +442,10 @@ 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 ' . g:run_nr . ':')
@@ -464,7 +468,7 @@ for s:test in sort(s:tests)
let v:errors = []
let g:run_nr += 1
- call RunTheTest(s:test)
+ call RunTheTest(g:testfunc)
if len(v:errors) == 0
" Test passed on rerun.
@@ -473,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 472ed4ca14..f895287469 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -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 3064b199d9..704ff6ec55 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,64 @@ 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
@@ -353,7 +550,7 @@ func Test_WinScrolled_long_wrapped()
call setline(1, repeat('foo', height * width))
call cursor(1, height * width)
END
- call writefile(lines, 'Xtest_winscrolled_long_wrapped')
+ 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>")
@@ -371,7 +568,67 @@ func Test_WinScrolled_long_wrapped()
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
call WaitForAssert({-> assert_match('^3 ', term_getline(buf, 6))}, 1000)
- call delete('Xtest_winscrolled_long_wrapped')
+ 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)
endfunc
func Test_WinClosed()
@@ -447,6 +704,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'
@@ -457,17 +734,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()
@@ -507,6 +787,8 @@ func Test_augroup_warning()
redir END
call assert_notmatch("W19:", res)
au! VimEnter
+
+ call assert_fails('augroup!', 'E471:')
endfunc
func Test_BufReadCmdHelp()
@@ -533,7 +815,9 @@ func Test_BufReadCmdNofile()
\ 'acwrite',
\ 'quickfix',
\ 'help',
+ "\ 'terminal',
\ 'prompt',
+ "\ 'popup',
\ ]
new somefile
exe 'set buftype=' .. val
@@ -650,7 +934,9 @@ func Test_BufEnter()
\ 'acwrite',
\ 'quickfix',
\ 'help',
+ "\ 'terminal',
\ 'prompt',
+ "\ 'popup',
\ ]
new somefile
exe 'set buftype=' .. val
@@ -1945,9 +2231,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'])
@@ -2024,6 +2308,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()
@@ -2111,9 +2396,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)
@@ -2721,6 +3005,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()
@@ -2824,6 +3119,7 @@ func Test_autocmd_invalid_args()
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
@@ -2890,7 +3186,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
@@ -2980,6 +3276,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
@@ -3148,19 +3453,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
@@ -3209,4 +3524,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 70529c14d5..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'))
@@ -297,6 +301,9 @@ func Test_blob_index()
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..995683c68c 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()
@@ -876,17 +877,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 4def3b5df9..549aa691c8 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
@@ -282,7 +274,7 @@ func Test_goto_buf_with_confirm()
call assert_equal(1, &modified)
call assert_equal('', @%)
call feedkeys('y', 'L')
- call assert_fails('confirm b Xfile', 'E37:')
+ call assert_fails('confirm b Xfile', ['', 'E37:'])
call assert_equal(1, &modified)
call assert_equal('', @%)
call feedkeys('n', 'L')
@@ -401,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`
@@ -433,4 +425,24 @@ func Test_buf_pattern_invalid()
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 3b5bcbce89..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(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()
@@ -207,4 +277,20 @@ func Test_setbufline_startup_nofile()
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 d6d44d1901..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()
@@ -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 66ee776a90..d8b29f6c42 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -13,9 +13,7 @@ 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
@@ -64,8 +62,8 @@ 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>")
@@ -89,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 == ''
@@ -102,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))
@@ -142,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
@@ -182,9 +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("call server2client('abc', 'xyz')", 'E258:')
+ 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 4bfd22cb6c..a074263359 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -5,9 +5,22 @@ 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
@@ -25,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()
@@ -89,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
@@ -127,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>")
@@ -151,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')
@@ -235,26 +371,29 @@ 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 feedkeys(":match A\<Tab>\<Home>\"\<CR>", 'xt')
- call assert_equal('"match Aardig', getreg(':'))
+ 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 A\<Tab>\<Home>\"\<CR>", 'xt')
+ call assert_equal('"hi Aardig', getreg(':'))
+ " 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')
@@ -275,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'))
@@ -332,6 +439,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')
@@ -387,6 +496,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)
@@ -461,11 +575,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')
@@ -513,6 +633,18 @@ 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:')
@@ -590,7 +722,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 +870,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 +965,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 +1002,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 +1080,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 +1092,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 +1151,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 +1175,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 +1269,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 +1302,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 +1312,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 +1418,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()
@@ -1424,6 +1659,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
@@ -1438,7 +1674,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('$'))
@@ -1452,13 +1688,7 @@ 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
@@ -1544,6 +1774,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')
@@ -1579,22 +1856,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', @:)
@@ -1610,7 +1881,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', @:)
@@ -1621,27 +1892,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)
@@ -1663,6 +1974,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&
@@ -1719,6 +2038,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()
@@ -1849,34 +2173,41 @@ endfunc
func Test_wildmenu_dirstack()
CheckUnix
%bw!
- call mkdir('Xdir1/dir2/dir3', 'p')
+ 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')
- cd Xdir1/dir2/dir3
+ 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 file3_1.txt', @:)
+ call assert_equal('"e file4_1.txt', @:)
call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt')
- call assert_equal('"e ../dir3/', @:)
+ call assert_equal('"e ../dir4/', @:)
call feedkeys(":e \<Tab>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
- call assert_equal('"e ../../dir2/', @:)
+ 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 ../../dir2/dir3/', @:)
+ call assert_equal('"e ../../dir3/dir4/', @:)
call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
- call assert_equal('"e ../../dir2/dir3/file3_1.txt', @:)
-
+ 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
@@ -1884,17 +2215,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
@@ -1923,6 +2255,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()
@@ -1934,39 +2319,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
@@ -1974,31 +2383,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
@@ -2008,95 +2411,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.
@@ -2107,15 +2492,321 @@ 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
+
+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
+
+" 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
+
" this was going over the end of IObuff
func Test_report_error_with_composing()
let caught = 'no'
@@ -2210,8 +2901,8 @@ func Test_setcmdline()
call assert_equal(a:pos, getcmdpos())
call assert_fails('call setcmdline("' .. a:text .. '", -1)', 'E487:')
- call assert_fails('call setcmdline({}, 0)', 'E928:')
- call assert_fails('call setcmdline("' .. a:text .. '", {})', 'E728:')
+ call assert_fails('call setcmdline({}, 0)', 'E1174:')
+ call assert_fails('call setcmdline("' .. a:text .. '", {})', 'E1210:')
return ''
endfunc
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..7f19085b16 100644
--- a/src/nvim/testdir/test_const.vim
+++ b/src/nvim/testdir/test_const.vim
@@ -231,6 +231,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 +282,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 f13842edc8..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()
@@ -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..47646125db 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()
diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim
index 2be94409ca..4609b9d2f9 100644
--- a/src/nvim/testdir/test_debugger.vim
+++ b/src/nvim/testdir/test_debugger.vim
@@ -1145,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
@@ -1176,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
@@ -1205,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
@@ -1217,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 1cb71664bd..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
***************
@@ -1198,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
@@ -1291,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_display.vim b/src/nvim/testdir/test_display.vim
index 217bb5d781..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})
@@ -407,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 679b877ef6..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
@@ -1228,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&
@@ -1437,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)
@@ -1484,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
@@ -1728,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
@@ -1869,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
@@ -1895,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
@@ -1931,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 9a9e5c546b..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:')
@@ -663,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 aa131a49ff..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
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index 80bfdb8553..454d76f0aa 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -139,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 15622cd6fe..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()
@@ -550,6 +525,21 @@ func Test_funcref()
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()
hi def link 1 Comment
hi def link 2 PreProc
@@ -561,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()
@@ -587,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_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 68ce9148a4..b065df68f7 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -88,9 +88,10 @@ 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'],
@@ -107,6 +108,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 +122,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 +162,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 +174,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 +205,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'],
@@ -213,8 +218,10 @@ let s:filename_checks = {
\ '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'],
@@ -225,6 +232,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'],
@@ -236,6 +244,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'],
@@ -251,6 +260,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'],
@@ -275,16 +285,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'],
@@ -321,8 +333,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'],
@@ -341,6 +354,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',
@@ -358,7 +372,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'],
@@ -379,12 +393,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'],
@@ -392,6 +408,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'],
@@ -404,10 +421,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'],
@@ -418,6 +435,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'],
@@ -444,7 +462,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'],
@@ -508,9 +526,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'],
@@ -526,13 +546,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'],
@@ -573,6 +595,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'],
@@ -589,6 +612,7 @@ 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'],
@@ -597,6 +621,7 @@ let s:filename_checks = {
\ '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'],
@@ -605,6 +630,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'],
@@ -627,12 +653,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'],
@@ -686,6 +713,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'],
@@ -922,7 +956,9 @@ func Test_d_file()
call assert_equal('d', &filetype)
bwipe!
+ " clean up
filetype off
+ call delete('Xfile.d')
endfunc
func Test_dat_file()
@@ -1350,7 +1386,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
@@ -1366,7 +1402,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
@@ -1494,7 +1530,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
@@ -1510,7 +1546,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
@@ -1525,13 +1561,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
@@ -1621,7 +1650,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
@@ -1637,7 +1666,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
@@ -1651,17 +1680,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
@@ -1752,6 +1809,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')
@@ -1824,6 +1886,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
@@ -1903,4 +2003,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..130ad9c7e1 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,48 @@ 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
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 147eda5b0a..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()
@@ -1537,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)
@@ -1601,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()
@@ -1622,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()
@@ -1704,9 +1876,8 @@ endfunc
func Test_confirm()
" requires a UI to be active
throw 'Skipped: use test/functional/vimscript/input_spec.lua'
- if !has('unix') || has('gui_running')
- return
- endif
+ CheckUnix
+ CheckNotGui
call feedkeys('o', 'L')
let a = confirm('Press O to proceed')
@@ -1721,7 +1892,7 @@ func Test_confirm()
call assert_equal(2, a)
" confirm() should return 0 when pressing CTRL-C.
- call feedkeys("\<C-c>", 'L')
+ call feedkeys("\<C-C>", 'L')
let a = confirm('Are you sure?', "&Yes\n&No")
call assert_equal(0, a)
@@ -1816,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)
@@ -1823,11 +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()
@@ -1887,7 +2063,9 @@ func Test_bufadd_bufload()
\ ['acwrite', 1],
\ ['quickfix', 0],
\ ['help', 1],
+ "\ ['terminal', 0],
\ ['prompt', 0],
+ "\ ['popup', 0],
\ ]
bwipe! XotherName
let buf = bufadd('XotherName')
@@ -1902,11 +2080,305 @@ func Test_bufadd_bufload()
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
@@ -1919,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
@@ -1949,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,
@@ -1961,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,
@@ -1973,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,
@@ -1988,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,
@@ -2000,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,
@@ -2015,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
@@ -2023,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 8e808a00d0..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')
@@ -745,8 +741,18 @@ 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
@@ -813,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_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 3e563f29f9..7a9392545e 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()
@@ -188,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()
@@ -258,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()
@@ -702,6 +705,27 @@ func Test_recursive_complete_func()
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
@@ -1256,7 +1280,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))
@@ -1264,6 +1288,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
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index c178c87d3e..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
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 08c415a069..8d7bede403 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,11 +346,12 @@ 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
@@ -378,6 +431,11 @@ func Test_list_locked_var()
call assert_equal(expected[depth][u][1], ps)
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
@@ -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
@@ -620,7 +695,10 @@ func Test_reverse_sort_uniq()
endif
call assert_fails('call reverse("")', 'E899:')
- call assert_fails('call uniq([1, 2], {x, y -> []})', 'E882:')
+ 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
@@ -668,7 +746,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))
@@ -679,6 +757,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
@@ -690,6 +771,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
@@ -863,6 +950,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
@@ -899,3 +1047,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 17c5f433ac..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
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 4f842189b6..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')
@@ -1587,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
@@ -2062,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
@@ -2153,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!
@@ -2177,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
@@ -2638,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')
@@ -2646,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('.'))
@@ -2813,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"
@@ -2848,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?
@@ -2879,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
@@ -2896,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$
@@ -2930,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')
@@ -3266,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
@@ -3370,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()
@@ -3477,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()
@@ -3540,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 655d537336..11c2977a4e 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)
@@ -248,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', @:)
@@ -267,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/ ', @:)
@@ -362,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:')
@@ -385,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)
@@ -398,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')
@@ -845,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&
@@ -981,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')
@@ -1015,4 +1251,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_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 8c9e39570f..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
@@ -104,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")
@@ -261,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
@@ -271,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
@@ -335,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
@@ -371,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
@@ -402,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
@@ -433,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 &&
@@ -442,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 &&
@@ -451,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 &&
@@ -522,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:')
@@ -577,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('.'))
@@ -705,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()
@@ -714,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!
@@ -1079,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
@@ -1126,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')
@@ -1164,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)
@@ -1210,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
@@ -1402,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',
@@ -1414,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',
@@ -1440,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',
@@ -1465,7 +1560,7 @@ func XquickfixChangedByAutocmd(cchar)
endfunc
endif
- augroup testgroup
+ augroup QF_Test
au!
autocmd BufReadCmd test_changed.txt call ReadFunc()
augroup END
@@ -1479,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()
@@ -1617,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()
@@ -1831,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
@@ -1863,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()
@@ -2530,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 = []
@@ -2562,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')
@@ -2585,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 = []
@@ -2615,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')
@@ -2648,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
@@ -2822,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', {})
@@ -2843,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:')
@@ -2860,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)
@@ -2890,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
@@ -3093,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
@@ -3108,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.
@@ -3154,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
@@ -3297,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
@@ -3343,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)
@@ -3628,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
@@ -3764,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')
@@ -3949,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
@@ -4061,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
@@ -4081,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
@@ -4091,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
@@ -4101,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
@@ -4822,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('.')])
@@ -4888,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:')
@@ -5096,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()
@@ -5295,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
@@ -5353,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&
@@ -5476,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()
@@ -5541,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('$'))
@@ -5584,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).
@@ -5652,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
@@ -5660,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 e3ca141328..6a6719da8b 100644
--- a/src/nvim/testdir/test_quotestar.vim
+++ b/src/nvim/testdir/test_quotestar.vim
@@ -98,7 +98,7 @@ 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.
- 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
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..a9cad3ed44 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,21 +1377,6 @@ 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')
-
- call Incsearch_cleanup()
- bwipe!
-endfunc
-
func Test_search_undefined_behaviour()
CheckFeature terminal
@@ -1629,8 +1650,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 +1672,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 +1753,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 +1816,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 +1825,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 +1969,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_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 880ca62685..54b7636f69 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")
@@ -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
@@ -747,10 +761,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 +771,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 +808,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', {})
@@ -943,6 +962,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 +976,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 +1034,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)
@@ -1271,4 +1290,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..5fd30c7da7 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
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 34d1d585ce..cf46b4c5bd 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -203,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
@@ -421,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 ccff01486e..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
@@ -347,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')
@@ -354,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()
@@ -386,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()
@@ -479,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
@@ -564,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',
@@ -607,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..bfa8a277bd 100644
--- a/src/nvim/testdir/test_system.vim
+++ b/src/nvim/testdir/test_system.vim
@@ -141,3 +141,41 @@ func Test_system_with_shell_quote()
call delete('Xdir with spaces', 'rf')
endtry
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
+
+" 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 4eb6e69adf..7a13de12f4 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -1044,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
@@ -1096,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
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 d71bb5bbb8..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
@@ -2223,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 c14624f5b4..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)
@@ -175,4 +192,313 @@ 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..9146cf7ab3 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)
@@ -185,6 +199,22 @@ func Test_setcellwidths()
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
index 2d6d598011..e792db90ab 100644
--- a/src/nvim/testdir/test_viminfo.vim
+++ b/src/nvim/testdir/test_viminfo.vim
@@ -18,4 +18,9 @@ func Test_viminfo_option_error()
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 97e879c9ef..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')
@@ -1834,13 +7053,14 @@ func Test_missing_end()
" 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
@@ -1929,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 4a252dca3e..edb24c6dc5 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)
{
@@ -65,7 +94,7 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
case '\\':
ga_concat(gap, "\\\\"); break;
default:
- if (*p < ' ') {
+ if (*p < ' ' || *p == 0x7f) {
vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
ga_concat(gap, (char *)buf);
} else {
@@ -121,7 +150,10 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s
bool did_copy = false;
int omitted = 0;
- if (opt_msg_tv->v_type != VAR_UNKNOWN) {
+ 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 = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
ga_concat(gap, (char *)tofree);
xfree(tofree);
@@ -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);
}
@@ -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);
@@ -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);
}
@@ -474,11 +506,13 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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, EvalFuncData 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(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,11 +601,23 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData 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
@@ -614,7 +722,11 @@ void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, EvalFuncData
{
// 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
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 125146f712..4af629d091 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -4,20 +4,29 @@
// 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"
@@ -28,6 +37,7 @@
#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"
@@ -97,7 +107,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on
colnr_T len;
colnr_T virtcol;
int orig_col = 0;
- char_u *saved_text = NULL;
+ char *saved_text = NULL;
colnr_T col;
colnr_T end_col;
bool did_do_comment = false;
@@ -117,7 +127,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on
// Don't break until after the comment leader
if (do_comments) {
- char_u *line = get_cursor_line_ptr();
+ char_u *line = (char_u *)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.
@@ -353,7 +363,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on
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());
+ saved_text = xstrdup(get_cursor_pos_ptr());
curwin->w_cursor.col = orig_col;
saved_text[startcol] = NUL;
@@ -425,13 +435,13 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on
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((char *)saved_text);
+ 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());
+ len = (colnr_T)strlen(get_cursor_line_ptr());
if (curwin->w_cursor.col > len) {
curwin->w_cursor.col = len;
}
@@ -465,21 +475,21 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on
/// ('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, bool do_comments)
+static int fmt_check_par(linenr_T lnum, int *leader_len, char **leader_flags, bool do_comments)
{
char_u *flags = NULL; // init for GCC
char_u *ptr;
- ptr = ml_get(lnum);
+ ptr = (char_u *)ml_get(lnum);
if (do_comments) {
- *leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
+ *leader_len = get_leader_len((char *)ptr, leader_flags, false, true);
} else {
*leader_len = 0;
}
if (*leader_len > 0) {
// Search for 'e' flag in comment leader flags.
- flags = *leader_flags;
+ flags = (char_u *)(*leader_flags);
while (*flags && *flags != ':' && *flags != COM_END) {
flags++;
}
@@ -493,14 +503,14 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags,
/// @return true if line "lnum" ends in a white character.
static bool ends_in_white(linenr_T lnum)
{
- char_u *s = ml_get(lnum);
+ char *s = ml_get(lnum);
size_t l;
if (*s == NUL) {
return false;
}
- l = STRLEN(s) - 1;
- return ascii_iswhite(s[l]);
+ l = strlen(s) - 1;
+ return ascii_iswhite((uint8_t)s[l]);
}
/// @return true if the two comment leaders given are the same.
@@ -508,13 +518,13 @@ static bool ends_in_white(linenr_T lnum)
/// @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_u *leader1_flags, int leader2_len,
- char_u *leader2_flags)
+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_u *p;
- char_u *line1;
- char_u *line2;
+ char *p;
+ char *line1;
+ char *line2;
if (leader1_len == 0) {
return leader2_len == 0;
@@ -552,7 +562,7 @@ static bool same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, i
// 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));
+ 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++) {
@@ -579,14 +589,14 @@ static bool 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
+ char *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
+ 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);
+ p = (char_u *)ml_get(lnum - 1);
if (*p == NUL) {
return true; // after empty line
}
@@ -624,8 +634,8 @@ void auto_format(bool trailblank, bool prev_line)
{
pos_T pos;
colnr_T len;
- char_u *old;
- char_u *new, *pnew;
+ char *old;
+ char *new, *pnew;
int wasatend;
int cc;
@@ -644,7 +654,7 @@ void auto_format(bool trailblank, bool prev_line)
// 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));
+ wasatend = (pos.col == (colnr_T)strlen(old));
if (*old != NUL && !trailblank && wasatend) {
dec_cursor();
cc = gchar_cursor();
@@ -663,7 +673,7 @@ void auto_format(bool trailblank, bool prev_line)
// With the 'c' flag in 'formatoptions' and 't' missing: only format
// comments.
if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
- && get_leader_len((char *)old, NULL, false, true) == 0) {
+ && get_leader_len(old, NULL, false, true) == 0) {
return;
}
@@ -698,12 +708,12 @@ void auto_format(bool trailblank, bool prev_line)
// formatted.
if (!wasatend && has_format_option(FO_WHITE_PAR)) {
new = get_cursor_line_ptr();
- len = (colnr_T)STRLEN(new);
+ len = (colnr_T)strlen(new);
if (curwin->w_cursor.col == len) {
- pnew = vim_strnsave(new, (size_t)len + 2);
+ pnew = xstrnsave(new, (size_t)len + 2);
pnew[len] = ' ';
pnew[len + 1] = NUL;
- ml_replace(curwin->w_cursor.lnum, (char *)pnew, false);
+ ml_replace(curwin->w_cursor.lnum, pnew, false);
// remove the space later
did_add_space = true;
} else {
@@ -914,8 +924,8 @@ void format_lines(linenr_T line_count, bool avoid_fex)
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
+ 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;
@@ -1025,8 +1035,8 @@ void format_lines(linenr_T line_count, bool avoid_fex)
// 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((char *)get_cursor_line_ptr()) == MAXCOL) {
+ || strncmp(next_leader_flags, "://", 3) != 0
+ || check_linecomment(get_cursor_line_ptr()) == MAXCOL) {
is_end_par = true;
}
}
@@ -1115,7 +1125,7 @@ void format_lines(linenr_T line_count, bool avoid_fex)
}
first_par_line = false;
// If the line is getting long, format it next time
- if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) {
+ if (strlen(get_cursor_line_ptr()) > (size_t)max_len) {
force_format = true;
} else {
force_format = false;
diff --git a/src/nvim/textformat.h b/src/nvim/textformat.h
index 3c918a028b..fcc9c2d6f4 100644
--- a/src/nvim/textformat.h
+++ b/src/nvim/textformat.h
@@ -1,8 +1,8 @@
#ifndef NVIM_TEXTFORMAT_H
#define NVIM_TEXTFORMAT_H
-#include "nvim/normal.h" // for oparg_T
-#include "nvim/pos.h" // for linenr_T
+#include "nvim/normal.h"
+#include "nvim/pos.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "textformat.h.generated.h"
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
index 02174edeb1..ee6c39a8ba 100644
--- a/src/nvim/textobject.c
+++ b/src/nvim/textobject.c
@@ -4,8 +4,11 @@
// textobject.c: functions for text objects
#include <stdbool.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"
@@ -13,14 +16,19 @@
#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/textformat.h"
+#include "nvim/strings.h"
#include "nvim/textobject.h"
+#include "nvim/types.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -213,11 +221,11 @@ bool findpar(bool *pincl, int dir, long count, int what, bool both)
}
curwin->w_cursor.lnum = curr;
if (curr == curbuf->b_ml.ml_line_count && what != '}') {
- char_u *line = ml_get(curr);
+ 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) {
+ 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;
@@ -229,9 +237,9 @@ bool findpar(bool *pincl, int dir, long count, int what, bool both)
}
/// check if the string 's' is a nroff macro that is in option 'opt'
-static bool inmacro(char_u *opt, char_u *s)
+static bool inmacro(char *opt, const char *s)
{
- char_u *macro;
+ char *macro;
for (macro = opt; macro[0]; macro++) {
// Accept two characters in the option being equal to two characters
@@ -258,14 +266,14 @@ static bool inmacro(char_u *opt, char_u *s)
/// If 'both' is true also stop at '}'
bool startPS(linenr_T lnum, int para, bool both)
{
- char_u *s;
+ char *s;
s = ml_get(lnum);
- if (*s == para || *s == '\f' || (both && *s == '}')) {
+ if ((uint8_t)(*s) == para || *s == '\f' || (both && *s == '}')) {
return true;
}
- if (*s == '.' && (inmacro((char_u *)p_sections, s + 1)
- || (!para && inmacro(p_para, s + 1)))) {
+ if (*s == '.' && (inmacro(p_sections, s + 1)
+ || (!para && inmacro((char *)p_para, s + 1)))) {
return true;
}
return false;
@@ -1032,8 +1040,8 @@ int current_block(oparg_T *oap, long count, bool include, int what, int other)
/// @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;
+ char *line = get_cursor_line_ptr();
+ char *p;
int c;
int lc = NUL;
pos_T pos;
@@ -1089,8 +1097,8 @@ int current_tagblock(oparg_T *oap, long count_arg, bool include)
pos_T start_pos;
pos_T end_pos;
pos_T old_start, old_end;
- char_u *p;
- char_u *cp;
+ char *p;
+ char *cp;
int len;
bool do_include = include;
bool save_p_ws = p_ws;
@@ -1196,7 +1204,7 @@ again:
}
}
} else {
- char_u *c = get_cursor_pos_ptr();
+ 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
@@ -1433,15 +1441,15 @@ extend:
/// @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)
+static int find_next_quote(char *line, int col, int quotechar, char *escape)
{
int c;
for (;;) {
- c = line[col];
+ c = (uint8_t)line[col];
if (c == NUL) {
return -1;
- } else if (escape != NULL && vim_strchr((char *)escape, c)) {
+ } else if (escape != NULL && vim_strchr(escape, c)) {
col++;
if (line[col] == NUL) {
return -1;
@@ -1449,7 +1457,7 @@ static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape)
} else if (c == quotechar) {
break;
}
- col += utfc_ptr2len((char *)line + col);
+ col += utfc_ptr2len(line + col);
}
return col;
}
@@ -1461,7 +1469,7 @@ static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape)
/// @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)
+static int find_prev_quote(char *line, int col_start, int quotechar, char *escape)
{
int n;
@@ -1470,14 +1478,14 @@ static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *e
col_start -= utf_head_off(line, line + col_start);
n = 0;
if (escape != NULL) {
- while (col_start - n > 0 && vim_strchr((char *)escape,
- line[col_start - n - 1]) != 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 (line[col_start] == quotechar) {
+ } else if ((uint8_t)line[col_start] == quotechar) {
break;
}
}
@@ -1493,7 +1501,7 @@ static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *e
bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
FUNC_ATTR_NONNULL_ALL
{
- char_u *line = get_cursor_line_ptr();
+ char *line = get_cursor_line_ptr();
int col_end;
int col_start = curwin->w_cursor.col;
bool inclusive = false;
@@ -1503,7 +1511,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
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
+ 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.
@@ -1542,16 +1550,16 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
// quotes.
if (vis_bef_curs) {
inside_quotes = VIsual.col > 0
- && line[VIsual.col - 1] == quotechar
+ && (uint8_t)line[VIsual.col - 1] == quotechar
&& line[curwin->w_cursor.col] != NUL
- && line[curwin->w_cursor.col + 1] == quotechar;
+ && (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
- && line[curwin->w_cursor.col - 1] == quotechar
+ && (uint8_t)line[curwin->w_cursor.col - 1] == quotechar
&& line[VIsual.col] != NUL
- && line[VIsual.col + 1] == quotechar;
+ && (uint8_t)line[VIsual.col + 1] == quotechar;
i = curwin->w_cursor.col;
col_end = VIsual.col;
}
@@ -1563,14 +1571,14 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
if (line[i] == NUL) {
break;
}
- if (line[i++] == quotechar) {
+ if ((uint8_t)line[i++] == quotechar) {
selected_quote = true;
break;
}
}
}
- if (!vis_empty && line[col_start] == quotechar) {
+ 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) {
@@ -1580,7 +1588,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
if (col_start < 0) {
goto abort_search;
}
- col_end = find_next_quote(line, col_start + 1, quotechar, (char_u *)curbuf->b_p_qe);
+ 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;
@@ -1588,17 +1596,17 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
}
} else {
col_end = find_prev_quote(line, col_start, quotechar, NULL);
- if (line[col_end] != quotechar) {
+ if ((uint8_t)line[col_end] != quotechar) {
goto abort_search;
}
- col_start = find_prev_quote(line, col_end, quotechar, (char_u *)curbuf->b_p_qe);
- if (line[col_start] != quotechar) {
+ 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 (line[col_start] == quotechar || !vis_empty) {
+ } else if ((uint8_t)line[col_start] == quotechar || !vis_empty) {
int first_col = col_start;
if (!vis_empty) {
@@ -1620,7 +1628,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
goto abort_search;
}
// Find close quote character.
- col_end = find_next_quote(line, col_start + 1, quotechar, (char_u *)curbuf->b_p_qe);
+ col_end = find_next_quote(line, col_start + 1, quotechar, curbuf->b_p_qe);
if (col_end < 0) {
goto abort_search;
}
@@ -1633,8 +1641,8 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
}
} else {
// Search backward for a starting quote.
- col_start = find_prev_quote(line, col_start, quotechar, (char_u *)curbuf->b_p_qe);
- if (line[col_start] != quotechar) {
+ 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) {
@@ -1643,7 +1651,7 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
}
// Find close quote character.
- col_end = find_next_quote(line, col_start + 1, quotechar, (char_u *)curbuf->b_p_qe);
+ col_end = find_next_quote(line, col_start + 1, quotechar, curbuf->b_p_qe);
if (col_end < 0) {
goto abort_search;
}
@@ -1677,9 +1685,9 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
|| (vis_bef_curs
&& !selected_quote
&& (inside_quotes
- || (line[VIsual.col] != quotechar
+ || ((uint8_t)line[VIsual.col] != quotechar
&& (VIsual.col == 0
- || line[VIsual.col - 1] != quotechar))))) {
+ || (uint8_t)line[VIsual.col - 1] != quotechar))))) {
VIsual = curwin->w_cursor;
redraw_curbuf_later(UPD_INVERTED);
}
@@ -1707,9 +1715,9 @@ bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
// quote.
if (inside_quotes
|| (!selected_quote
- && line[VIsual.col] != quotechar
+ && (uint8_t)line[VIsual.col] != quotechar
&& (line[VIsual.col] == NUL
- || line[VIsual.col + 1] != quotechar))) {
+ || (uint8_t)line[VIsual.col + 1] != quotechar))) {
dec_cursor();
VIsual = curwin->w_cursor;
}
diff --git a/src/nvim/textobject.h b/src/nvim/textobject.h
index 26f88613fd..e31557f297 100644
--- a/src/nvim/textobject.h
+++ b/src/nvim/textobject.h
@@ -1,9 +1,9 @@
#ifndef NVIM_TEXTOBJECT_H
#define NVIM_TEXTOBJECT_H
-#include "nvim/normal.h" // for oparg_T
-#include "nvim/pos.h" // for linenr_T
-#include "nvim/vim.h" // for Direction
+#include "nvim/normal.h"
+#include "nvim/pos.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "textobject.h.generated.h"
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index d269878f46..b837a380d5 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -1,25 +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 <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/event/multiqueue.h"
+#include "nvim/globals.h"
+#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
+#include "nvim/map.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/tui/input.h"
+#include "nvim/tui/input_defs.h"
#include "nvim/tui/tui.h"
-#include "nvim/vim.h"
-#ifdef WIN32
+#ifdef MSWIN
# include "nvim/os/os_win_console.h"
#endif
#include "nvim/event/rstream.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/ui.h"
#define KEY_BUFFER_SIZE 0xfff
@@ -143,7 +156,7 @@ void tinput_init(TermInput *input, Loop *loop)
// If stdin is not a pty, switch to stderr. For cases like:
// echo q | nvim -es
// ls *.md | xargs nvim
-#ifdef WIN32
+#ifdef MSWIN
if (!os_isatty(input->in_fd)) {
input->in_fd = os_get_conin_fd();
}
@@ -159,14 +172,10 @@ void tinput_init(TermInput *input, Loop *loop)
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_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 +220,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 +234,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, NULL)));
- // 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);
- } 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 +295,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 +325,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;
}
}
@@ -406,8 +371,8 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
}
}
- if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG
- && ev != TERMKEY_MOUSE_RELEASE)) {
+ if ((button == 0 && ev != TERMKEY_MOUSE_RELEASE)
+ || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG && ev != TERMKEY_MOUSE_RELEASE)) {
return;
}
@@ -453,7 +418,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();
@@ -563,7 +529,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;
@@ -610,10 +579,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`).
@@ -695,10 +668,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_respose = 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..507e9df21e 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/api/private/helpers.h"
+#include "nvim/charset.h"
#include "nvim/globals.h"
-#include "nvim/log.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..44f6718039 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1,48 +1,52 @@
// 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/message.h"
+#include "nvim/msgpack_rpc/channel.h"
#include "nvim/option.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
+#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"
+#include "nvim/vim.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 +67,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 +89,6 @@ typedef struct {
} Rect;
struct TUIData {
- UIBridgeData *bridge;
Loop *loop;
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
@@ -83,13 +99,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 +120,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,8 +134,10 @@ 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;
@@ -135,153 +156,142 @@ 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.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 +308,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 +368,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 +377,134 @@ 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);
+ 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
@@ -569,17 +520,15 @@ static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
}
}
-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;
@@ -592,7 +541,7 @@ static void update_attrs(UI *ui, int attr_id)
bool underdouble;
bool underdotted;
bool underdashed;
- if (data->unibi_ext.set_underline_style != -1) {
+ if (tui->unibi_ext.set_underline_style != -1) {
underline = attr & HL_UNDERLINE;
undercurl = attr & HL_UNDERCURL;
underdouble = attr & HL_UNDERDOUBLE;
@@ -609,126 +558,125 @@ 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 (strikethrough && tui->unibi_ext.enter_strikethrough_mode != -1) {
+ unibi_out_ext(tui, tui->unibi_ext.enter_strikethrough_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 (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 (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 (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 (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 (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 && 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 (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 +685,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 +730,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 +746,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 +761,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 +777,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 +792,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 +804,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 +816,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 +837,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 +865,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 +982,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 +1034,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 +1047,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 +1135,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 +1223,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;
+ 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->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;
-
- 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 +1270,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 +1290,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;
+ TUIData *tui = argv[0];
+ bool enable_mouse = tui->mouse_enabled;
+ tui_terminal_stop(tui);
stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
- signal_stop();
+
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);
}
#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 +1466,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 +1499,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 +1526,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 +1536,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 +1586,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 +1663,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 +1781,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 +1863,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 +1901,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 +1934,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 +1994,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 +2027,24 @@ 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");
// 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 +2059,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,57 +2183,56 @@ 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.)
///
@@ -2239,14 +2241,14 @@ static void flush_buf(UI *ui)
static const char *tui_get_stty_erase(void)
{
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) {
stty_erase[0] = (char)t.c_cc[VERASE];
stty_erase[1] = '\0';
DLOG("stty/termios:erase=%s", stty_erase);
}
-# endif
+#endif
return stty_erase;
}
@@ -2280,4 +2282,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 fb10bf21d9..d90c623955 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -45,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 46f4d137c4..b25fa04c8b 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -2,47 +2,43 @@
// 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 <string.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "auto/config.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/defs.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/lua/executor.h"
#include "nvim/main.h"
-#include "nvim/mbyte.h"
#include "nvim/memory.h"
-#include "nvim/move.h"
+#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/normal.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/tui/tui.h"
#include "nvim/ui.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 +57,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 +101,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 +115,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 +123,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 +152,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 +162,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 +200,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 +212,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 +230,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 +242,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 +262,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 +292,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(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 +351,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 +396,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 +428,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 +482,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();
@@ -565,7 +545,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 = (char_u *)p_mouse; *p; p++) {
+ for (char *p = p_mouse; *p; p++) {
switch (*p) {
case 'a':
if (vim_strchr(MOUSE_A, checkfor) != NULL) {
@@ -611,16 +591,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));
@@ -632,7 +606,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;
@@ -665,3 +639,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 996b3467a6..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,6 +52,41 @@ enum {
typedef int LineFlags;
+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;
bool override; ///< Force highest-requested UI capabilities.
@@ -60,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 809d278029..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, NULL);
- Object *copy_value = xmalloc(sizeof(Object));
- *copy_value = copy_object(value, NULL);
- 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 265c54f72d..ff82fd3239 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -1,44 +1,116 @@
// 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/eval.h"
+#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.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/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
+//
+
+uint64_t ui_client_start_server(int argc, char **argv)
+{
+ 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 (ui_client_forward_stdin) {
+ close(0);
+ dup(2);
+ }
+
+ return channel->id;
+}
-void ui_client_init(uint64_t chan)
+void ui_client_run(bool remote_ui)
+ FUNC_ATTR_NORETURN
{
- Array args = ARRAY_DICT_INIT;
- int width = Columns;
- int height = Rows;
- Dictionary opts = ARRAY_DICT_INIT;
+ 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_respose != kNone) {
+ bool is_dark = (ui_client_bg_respose == 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;
- PUT(opts, "rgb", BOOLEAN_OBJ(true));
- PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true));
- PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true));
+ // os_exit() will be invoked when the client channel detaches
+ while (true) {
+ LOOP_PROCESS_EVENTS(&main_loop, resize_events, -1);
+ }
+}
- ADD(args, INTEGER_OBJ((int)width));
- ADD(args, INTEGER_OBJ((int)height));
- ADD(args, DICTIONARY_OBJ(opts));
+void ui_client_stop(void)
+{
+ tui_stop(tui);
+}
- rpc_send_event(chan, "nvim_ui_attach", args);
- ui_client_channel_id = chan;
+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)
@@ -61,20 +133,6 @@ Object handle_ui_client_redraw(uint64_t channel_id, Array args, Arena *arena, Er
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 +141,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 +157,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 +183,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..24b8fad4cc 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -1,8 +1,11 @@
#ifndef NVIM_UI_CLIENT_H
#define NVIM_UI_CLIENT_H
+#include <stddef.h>
+
#include "nvim/api/private/defs.h"
#include "nvim/grid_defs.h"
+#include "nvim/macros.h"
typedef struct {
const char *name;
@@ -14,6 +17,24 @@ 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_respose 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
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_client.h.generated.h"
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 2216e25db9..ad44e2ca22 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -7,24 +7,29 @@
// 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/map.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 +38,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 +62,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 +101,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 +216,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 +236,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 +448,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 +468,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 +496,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 +553,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 +578,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 +607,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 +642,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 97a925f3ad..85e261fc39 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++) {
@@ -621,8 +636,8 @@ void u_compute_hash(buf_T *buf, char_u *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, (char_u *)p, (uint32_t)(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
@@ -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);
@@ -771,7 +786,7 @@ 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, (char_u *)buf->b_u_line_ptr, len)) {
return false;
@@ -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, (char_u *)uep->ue_array[i], len)) {
return false;
}
}
@@ -1064,7 +1079,7 @@ static u_entry_T *unserialize_uep(bufinfo_T *bi, bool *error, const char *file_n
memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size);
}
}
- uep->ue_array = array;
+ uep->ue_array = (char **)array;
for (size_t i = 0; i < (size_t)uep->ue_size; i++) {
int line_len = undo_read_4c(bi);
@@ -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);
@@ -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((char_u *)buf->b_ffname);
+ os_set_acl((char_u *)file_name, acl);
+ os_free_acl(acl);
}
#endif
@@ -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.
@@ -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;
}
}
@@ -2307,7 +2322,7 @@ static void u_undoredo(int undo, bool do_buf_event)
linenr_T 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);
+ newarray[i] = (char_u *)u_save_line(lnum);
// remember we deleted the last line in the buffer, and a
// dummy empty line will be inserted
if (curbuf->b_ml.ml_line_count == 1) {
@@ -2327,9 +2342,9 @@ 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]);
}
@@ -2363,7 +2378,7 @@ static void u_undoredo(int undo, bool do_buf_event)
u_newcount += newsize;
u_oldcount += oldsize;
uep->ue_size = oldsize;
- uep->ue_array = newarray;
+ uep->ue_array = (char **)newarray;
uep->ue_bot = top + newsize + 1;
// insert this entry in front of the new entry list
@@ -2554,7 +2569,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
if (uhp == NULL) {
*msgbuf = NUL;
} else {
- undo_fmt_time(msgbuf, sizeof(msgbuf), uhp->uh_time);
+ undo_fmt_time((char *)msgbuf, sizeof(msgbuf), uhp->uh_time);
}
{
@@ -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;
@@ -2960,7 +2978,7 @@ void u_saveline(linenr_T lnum)
} else {
curbuf->b_u_line_colnr = 0;
}
- curbuf->b_u_line_ptr = (char *)u_save_line(lnum);
+ curbuf->b_u_line_ptr = u_save_line(lnum);
}
/// clear the line saved for the "U" command
@@ -2991,13 +3009,13 @@ void u_undoline(void)
return;
}
- char_u *oldp = u_save_line(curbuf->b_u_line_lnum);
+ 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 = (char *)oldp;
+ curbuf->b_u_line_ptr = oldp;
colnr_T t = curbuf->b_u_line_colnr;
if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) {
@@ -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 5b2101587f..22e092781c 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,7 @@ static const char *command_complete[] =
[EXPAND_TAGS_LISTFILES] = "tag_listfiles",
[EXPAND_USER] = "user",
[EXPAND_USER_VARS] = "var",
+ [EXPAND_BREAKPOINT] = "breakpoint",
};
/// List of names of address types. Must be alphabetical for completion.
@@ -87,8 +103,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" },
@@ -208,6 +223,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;
@@ -259,6 +275,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);
@@ -280,7 +337,7 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
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 "";
}
@@ -346,9 +403,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 +416,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 +451,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 +490,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 +529,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 +553,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 +566,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 +574,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);
@@ -531,7 +586,7 @@ static void uc_list(char *name, size_t name_len)
}
}
- msg_outtrans_special((char_u *)cmd->uc_rep, false,
+ msg_outtrans_special(cmd->uc_rep, false,
name_len == 0 ? Columns - 47 : 0);
if (p_verbose > 0) {
last_set_msg(cmd->uc_script_ctx);
@@ -559,8 +614,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 +662,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 +698,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 +809,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 +871,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 +892,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;
@@ -931,9 +986,9 @@ void ex_command(exarg_T *eap)
while (*p == '-') {
p++;
end = skiptowhite(p);
- if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &compl, (char_u **)&compl_arg,
+ 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 +998,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 +1009,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 +1057,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 +1066,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;
}
@@ -1078,7 +1138,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 +1258,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 +1278,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 +1297,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 +1317,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 +1378,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;
@@ -1401,13 +1475,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 +1545,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) {
@@ -1579,10 +1653,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 +1707,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 4d2cf0d9de..b6bf6c1e33 100644
--- a/src/nvim/usercmd.h
+++ b/src/nvim/usercmd.h
@@ -1,7 +1,12 @@
#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 *uc_name; // The command name
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 5888d4614e..92122b2523 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -7,34 +7,42 @@
/// 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/memory.h"
+#include "nvim/mbyte.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;
@@ -61,23 +69,593 @@ static char *features[] = {
"-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 +672,7 @@ static const int included_patches[] = {
1830,
1829,
1828,
- 1827,
+ // 1827,
1826,
1825,
1824,
@@ -102,58 +680,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 +744,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 +780,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 +809,7 @@ static const int included_patches[] = {
1693,
1692,
1691,
- 1690,
+ // 1690,
1689,
1688,
1687,
@@ -240,140 +818,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 +961,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 +1026,7 @@ static const int included_patches[] = {
1476,
1475,
1474,
- 1473,
+ // 1473,
1472,
1471,
1470,
@@ -458,83 +1036,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 +1123,7 @@ static const int included_patches[] = {
1379,
1378,
1377,
- 1376,
+ // 1376,
1375,
1374,
1373,
@@ -557,12 +1135,12 @@ static const int included_patches[] = {
1367,
1366,
1365,
- 1364,
+ // 1364,
1363,
1362,
1361,
1360,
- 1359,
+ // 1359,
1358,
1357,
1356,
@@ -570,38 +1148,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 +1199,7 @@ static const int included_patches[] = {
1303,
1302,
1301,
- // 1300,
+ 1300,
1299,
1298,
1297,
@@ -641,11 +1219,11 @@ static const int included_patches[] = {
1283,
1282,
1281,
- 1280,
+ // 1280,
1279,
- 1278,
+ // 1278,
1277,
- 1276,
+ // 1276,
1275,
1274,
1273,
@@ -654,15 +1232,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 +1273,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 +1363,10 @@ static const int included_patches[] = {
1139,
1138,
1137,
- 1136,
+ // 1136,
1135,
1134,
- 1133,
+ // 1133,
1132,
1131,
1130,
@@ -815,10 +1393,10 @@ static const int included_patches[] = {
1109,
1108,
1107,
- 1106,
+ // 1106,
1105,
1104,
- 1103,
+ // 1103,
1102,
1101,
1100,
@@ -831,14 +1409,14 @@ static const int included_patches[] = {
1093,
1092,
1091,
- 1090,
+ // 1090,
1089,
1088,
1087,
1086,
1085,
1084,
- 1083,
+ // 1083,
1082,
1081,
1080,
@@ -877,8 +1455,8 @@ static const int included_patches[] = {
1047,
1046,
1045,
- 1044,
- 1043,
+ // 1044,
+ // 1043,
1042,
1041,
1040,
@@ -886,14 +1464,14 @@ static const int included_patches[] = {
1038,
1037,
1036,
- 1035,
+ // 1035,
1034,
1033,
1032,
1031,
1030,
1029,
- 1028,
+ // 1028,
1027,
1026,
1025,
@@ -902,7 +1480,7 @@ static const int included_patches[] = {
1022,
1021,
1020,
- 1019,
+ // 1019,
1018,
1017,
1016,
@@ -917,7 +1495,7 @@ static const int included_patches[] = {
1007,
1006,
1005,
- 1004,
+ // 1004,
1003,
1002,
1001,
@@ -944,15 +1522,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 +1546,7 @@ static const int included_patches[] = {
956,
955,
954,
- 953,
+ // 953,
952,
951,
950,
@@ -981,7 +1559,7 @@ static const int included_patches[] = {
943,
942,
941,
- 940,
+ // 940,
939,
938,
937,
@@ -993,7 +1571,7 @@ static const int included_patches[] = {
931,
930,
929,
- 928,
+ // 928,
927,
926,
925,
@@ -1003,19 +1581,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 +1604,12 @@ static const int included_patches[] = {
898,
897,
896,
- 895,
- 894,
+ // 895,
+ // 894,
893,
892,
891,
- 890,
+ // 890,
889,
888,
887,
@@ -1041,30 +1619,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 +1654,7 @@ static const int included_patches[] = {
848,
847,
846,
- 845,
+ // 845,
844,
843,
842,
@@ -1097,16 +1675,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 +1729,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 +1789,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 +1805,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 +1832,11 @@ static const int included_patches[] = {
670,
669,
668,
- 667,
+ // 667,
666,
- 665,
+ // 665,
664,
- 663,
+ // 663,
662,
661,
660,
@@ -1266,8 +1844,8 @@ static const int included_patches[] = {
658,
657,
656,
- 655,
- 654,
+ // 655,
+ // 654,
653,
652,
651,
@@ -1278,16 +1856,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 +1887,7 @@ static const int included_patches[] = {
615,
614,
613,
- 612,
+ // 612,
611,
610,
609,
@@ -1320,7 +1898,7 @@ static const int included_patches[] = {
604,
603,
602,
- 601,
+ // 601,
600,
599,
598,
@@ -1339,10 +1917,10 @@ static const int included_patches[] = {
585,
584,
583,
- 582,
+ // 582,
581,
580,
- 579,
+ // 579,
578,
577,
576,
@@ -1496,7 +2074,7 @@ static const int included_patches[] = {
428,
427,
426,
- 425,
+ // 425,
424,
423,
422,
@@ -1554,7 +2132,7 @@ static const int included_patches[] = {
370,
369,
368,
- 367,
+ // 367,
366,
365,
364,
@@ -1593,7 +2171,7 @@ static const int included_patches[] = {
331,
330,
329,
- 328,
+ // 328,
327,
326,
325,
@@ -1609,7 +2187,7 @@ static const int included_patches[] = {
315,
314,
313,
- 312,
+ // 312,
311,
310,
309,
@@ -1617,7 +2195,7 @@ static const int included_patches[] = {
307,
306,
305,
- 304,
+ // 304,
303,
302,
301,
@@ -1763,7 +2341,7 @@ static const int included_patches[] = {
161,
160,
159,
- 158,
+ // 158,
157,
156,
155,
@@ -1834,7 +2412,7 @@ static const int included_patches[] = {
90,
89,
88,
- 87,
+ // 87,
86,
85,
84,
@@ -1849,21 +2427,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 +2449,8 @@ static const int included_patches[] = {
53,
52,
51,
- 50,
- 49,
+ // 50,
+ // 49,
48,
47,
46,
@@ -1921,7 +2499,7 @@ static const int included_patches[] = {
3,
2,
1,
- 0,
+ // 0,
};
// clang-format on
@@ -2228,6 +2806,9 @@ void intro_message(int colon)
N_("type :q<Enter> to exit "),
N_("type :help<Enter> for help "),
"",
+ N_("type :help news<Enter> to see changes in")
+ " v" STR(NVIM_VERSION_MAJOR) "." STR(NVIM_VERSION_MINOR),
+ "",
N_("Help poor children in Uganda!"),
N_("type :help iccf<Enter> for information "),
};
@@ -2236,7 +2817,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) {
@@ -2324,6 +2905,7 @@ static void do_intro_line(long row, char *mesg, int attr)
/// @param eap
void ex_intro(exarg_T *eap)
{
+ // TODO(bfredl): use msg_grid instead!
screenclear();
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 ec8d6ab153..0ab9d96173 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,7 @@ enum {
EXPAND_MAPCLEAR,
EXPAND_ARGLIST,
EXPAND_DIFF_BUFFERS,
+ EXPAND_BREAKPOINT,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};
@@ -196,17 +196,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
@@ -218,7 +212,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))
@@ -231,7 +225,6 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
#endif
#define STRCAT(d, s) strcat((char *)(d), (char *)(s)) // NOLINT(runtime/printf)
-#define STRLCAT(d, s, n) xstrlcat((char *)(d), (char *)(s), (size_t)(n))
// Character used as separated in autoload function/variable names.
#define AUTOLOAD_CHAR '#'
@@ -242,27 +235,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)]
@@ -271,13 +245,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..0b19526fa0 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,7 +1825,7 @@ 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),
+ const size_t special_len = trans_special(&p, (size_t)(e - p),
(char_u *)v_p, flags, false, NULL);
if (special_len != 0) {
v_p += special_len;
@@ -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
@@ -2710,14 +2713,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 +2738,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 +2908,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 55f54cedbe..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
@@ -152,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
@@ -178,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
@@ -200,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 74ea1172ee..2bcbef14b0 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2,21 +2,32 @@
// 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 "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/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 +38,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 +59,18 @@
#include "nvim/option.h"
#include "nvim/optionstr.h"
#include "nvim/os/os.h"
-#include "nvim/os_unix.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 +142,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 { \
@@ -236,8 +248,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,6 +262,7 @@ newwindow:
win_goto(wp);
}
break;
+ }
// close all but current window
case Ctrl_O:
@@ -269,13 +282,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 +359,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 +430,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 +494,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 = (char *)grab_file_name(Prenum1, &lnum);
if (ptr != NULL) {
tabpage_T *oldtab = curtab;
win_T *oldwin = curwin;
@@ -508,6 +525,7 @@ wingotofile:
xfree(ptr);
}
break;
+ }
// Go to the first occurrence of the identifier under cursor along path in a
// new window -- webb
@@ -516,8 +534,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 +545,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 +573,7 @@ wingotofile:
no_mapping--;
allow_keys--;
(void)add_to_showcmd(xchar);
+
switch (xchar) {
case '}':
xchar = Ctrl_RSB;
@@ -573,6 +595,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 +603,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;
@@ -624,7 +648,7 @@ wingotofile:
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(bufp + len, bufsize - len, "%" PRId64, Prenum);
@@ -635,11 +659,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();
}
@@ -735,7 +761,7 @@ void win_set_minimal_style(win_T *wp)
}
// TODO(bfredl): this could use a highlight namespace directly,
- // and avoid pecularities around window options
+ // and avoid peculiarities around window options
char_u *old = (char_u *)wp->w_p_winhl;
wp->w_p_winhl = ((*old == NUL)
? xstrdup("EndOfBuffer:")
@@ -744,7 +770,7 @@ void win_set_minimal_style(win_T *wp)
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 = xstrdup("auto");
}
@@ -760,6 +786,12 @@ void win_set_minimal_style(win_T *wp)
free_string_option(wp->w_p_cc);
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("");
+ }
}
void win_config_float(win_T *wp, FloatConfig fconfig)
@@ -772,13 +804,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;
@@ -819,16 +860,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 {
@@ -864,13 +905,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 +949,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,7 +967,6 @@ 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);
@@ -968,21 +1013,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 +1055,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 +1078,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 +1091,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 +1163,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 +1176,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 +1260,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 +1273,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 +1310,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)) {
@@ -1310,7 +1341,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 +1359,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 {
@@ -1487,13 +1519,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 +1559,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,8 +1595,14 @@ 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) {
@@ -1589,10 +1625,8 @@ 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.
@@ -1627,11 +1661,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 +1712,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 +1733,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,6 +1762,8 @@ 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--) {
if (vertical) {
@@ -1744,17 +1786,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 +1800,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 +1819,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 +1845,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;
@@ -1845,11 +1877,6 @@ static void win_exchange(long Prenum)
// 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 +1889,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 +1897,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 +1932,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;
@@ -1925,9 +1956,7 @@ static void win_rotate(bool upwards, int count)
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 +1966,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 +2000,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 +2019,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 +2036,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;
@@ -2074,7 +2099,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
@@ -2087,6 +2112,9 @@ void win_equal(win_T *next_curwin, bool current, int dir)
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 +2133,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.
@@ -2134,7 +2158,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 +2168,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 +2186,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;
@@ -2204,8 +2227,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
}
+ 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 +2241,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 +2281,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 +2293,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 +2305,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 +2313,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;
@@ -2334,8 +2354,10 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
}
+ 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 +2368,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--;
}
@@ -2463,14 +2481,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 +2500,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,11 +2522,6 @@ 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.
@@ -2521,14 +2534,14 @@ 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`
+/// Check if current tab page contains no more than one window other than `aucmd_win[]`.
+/// @param counted_float counted even if floating, but not if it is `aucmd_win[]`
bool one_window(win_T *counted_float) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
bool seen_one = false;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp != aucmd_win && (!wp->w_floating || wp == counted_float)) {
+ if (!is_aucmd_win(wp) && (!wp->w_floating || wp == counted_float)) {
if (seen_one) {
return false;
}
@@ -2558,7 +2571,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 +2605,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));
+ snprintf((char *)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 +2676,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 +2689,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 +2720,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 +2730,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 +2748,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 +2779,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 +2803,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 +2824,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 +2860,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 +2894,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 +2922,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 +2945,10 @@ int win_close(win_T *win, bool free_buf, bool force)
}
curwin->w_pos_changed = true;
- redraw_all_later(UPD_NOT_VALID);
+ if (!was_floating) {
+ // TODO(bfredl): how about no?
+ redraw_all_later(UPD_NOT_VALID);
+ }
return OK;
}
@@ -2953,19 +2966,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 +2992,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 +3022,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 +3031,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 +3047,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 +3058,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 +3074,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,8 +3112,6 @@ 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;
@@ -3102,18 +3122,27 @@ void win_free_all(void)
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 +3161,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 +3180,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 +3209,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 +3249,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 +3275,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 +3308,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 +3355,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 +3402,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 +3418,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 +3442,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 +3465,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 +3568,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 +3601,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 +3616,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 +3632,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 +3655,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,10 +3695,8 @@ 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--;
@@ -3720,10 +3724,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 +3748,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 +3766,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 +3787,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 +3797,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 +3814,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 +3830,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 +3840,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 +3858,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 +3874,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 +3907,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 +3991,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 +4001,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 +4016,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;
@@ -4033,11 +4035,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
@@ -4064,15 +4064,13 @@ void free_tabpage(tabpage_T *tp)
int win_new_tabpage(int after, char_u *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 +4095,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,6 +4108,7 @@ 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;
@@ -4136,11 +4135,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,26 +4150,22 @@ 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();
+ int todo;
for (todo = count - 1; todo > 0; todo--) {
if (win_new_tabpage(0, NULL) == FAIL) {
break;
@@ -4237,9 +4230,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 +4242,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 +4285,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 +4312,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);
@@ -4354,17 +4344,26 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
// 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;
@@ -4410,16 +4409,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();
@@ -4434,6 +4427,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) {
@@ -4444,8 +4439,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;
@@ -4479,6 +4474,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)) {
@@ -4489,6 +4485,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.
@@ -4502,10 +4499,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);
@@ -4517,16 +4512,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++;
}
@@ -4536,7 +4530,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) {
@@ -4568,13 +4562,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;
@@ -4601,9 +4593,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) {
@@ -4625,22 +4615,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;
@@ -4656,9 +4641,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;
@@ -4708,22 +4691,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;
@@ -4739,9 +4717,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;
@@ -4833,7 +4809,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) {
@@ -4850,7 +4828,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();
@@ -4912,7 +4894,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;
}
@@ -4962,12 +4944,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;
}
}
@@ -4975,7 +4956,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)
{
@@ -5034,9 +5015,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);
}
@@ -5066,8 +5045,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;
}
@@ -5086,9 +5064,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);
@@ -5120,7 +5095,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);
}
@@ -5128,16 +5103,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;
@@ -5165,6 +5143,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);
@@ -5175,7 +5156,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) {
@@ -5200,18 +5181,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;
@@ -5248,9 +5222,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;
@@ -5261,9 +5233,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;
@@ -5276,9 +5246,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) {
@@ -5299,7 +5267,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;
@@ -5335,6 +5303,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.
@@ -5355,42 +5327,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_skipcol != wp->w_skipcol
- || 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;
+}
+
+/// 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".
- */
+// Save the size of all windows in "gap".
void win_size_save(garray_T *gap)
{
ga_init(gap, (int)sizeof(int), 1);
@@ -5457,20 +5677,13 @@ 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) {
@@ -5485,8 +5698,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
@@ -5498,19 +5712,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.
@@ -5520,7 +5730,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, UPD_NOT_VALID);
+ redraw_later(win, UPD_VALID);
} else {
frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
@@ -5540,34 +5750,29 @@ void win_setheight_win(int height, win_T *win)
curtab->tp_ch_used = p_ch;
msg_row = row;
msg_col = 0;
+
+ 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;
@@ -5575,11 +5780,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);
@@ -5587,25 +5796,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
@@ -5637,14 +5848,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.
@@ -5666,24 +5875,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) {
@@ -5716,10 +5920,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);
@@ -5752,22 +5954,13 @@ void win_setwidth_win(int width, win_T *wp)
}
}
-/*
- * 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;
@@ -5781,23 +5974,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
@@ -5821,11 +6016,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.
@@ -5838,24 +6031,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) {
@@ -5931,22 +6119,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
@@ -5971,8 +6151,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) {
@@ -5989,7 +6171,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) {
@@ -6014,10 +6195,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);
}
@@ -6027,11 +6206,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);
@@ -6045,7 +6222,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;
@@ -6053,26 +6230,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;
+
+ 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) {
@@ -6097,8 +6271,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;
@@ -6110,7 +6286,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) {
@@ -6145,7 +6320,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);
@@ -6177,6 +6352,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
@@ -6199,8 +6466,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:
@@ -6212,17 +6477,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.
@@ -6235,11 +6498,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) {
@@ -6271,10 +6532,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;
@@ -6297,9 +6556,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, UPD_SOME_VALID);
- wp->w_redr_status = true;
invalidate_botline_win(wp);
}
@@ -6318,7 +6575,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();
@@ -6332,14 +6589,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, UPD_NOT_VALID); // UPD_SOME_VALID??
+ redraw_later(wp, UPD_SOME_VALID);
}
if (width != wp->w_width_inner) {
@@ -6349,8 +6606,10 @@ 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, UPD_NOT_VALID);
@@ -6364,6 +6623,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)
@@ -6380,17 +6640,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;
}
@@ -6401,13 +6659,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
@@ -6415,11 +6669,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
@@ -6429,7 +6680,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;
}
@@ -6452,7 +6703,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;
}
@@ -6486,10 +6737,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);
@@ -6502,11 +6751,9 @@ 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.
- */
+// 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)
{
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
@@ -6522,27 +6769,25 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
*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 (char_u *)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"
- */
+// 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 file_name_in_line(get_cursor_line_ptr(),
+ return file_name_in_line((char_u *)get_cursor_line_ptr(),
curwin->w_cursor.col, options, count, (char_u *)curbuf->b_ffname,
file_lnum);
}
@@ -6554,16 +6799,9 @@ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum)
char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname,
linenr_T *file_lnum)
{
- char *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 = (char *)line + col;
+ while (*ptr != NUL && !vim_isfilec((uint8_t)(*ptr))) {
MB_PTR_ADV(ptr);
}
if (*ptr == NUL) { // nothing found
@@ -6573,26 +6811,27 @@ 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'.
- */
+ 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 ((char_u *)ptr > line) {
- if ((len = (size_t)(utf_head_off(line, (char_u *)ptr - 1))) > 0) {
+ if ((len = (size_t)(utf_head_off((char *)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)) {
// After type:// we also include :, ?, & and = as valid characters, so that
@@ -6613,28 +6852,25 @@ 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 there is trailing punctuation, remove it.
+ // But don't remove "..", could be a directory name.
if (len > 2 && vim_strchr(".,:;!", 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);
}
@@ -6649,7 +6885,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
}
}
- return find_file_name_in_path((char_u *)ptr, len, options, count, rel_fname);
+ return (char_u *)find_file_name_in_path(ptr, len, options, count, (char *)rel_fname);
}
/// Add or remove a status line from window(s), according to the
@@ -6674,7 +6910,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;
@@ -6733,23 +6969,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) {
@@ -6763,6 +6995,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
@@ -6773,9 +7009,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(UPD_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);
}
@@ -6806,11 +7042,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;
@@ -6896,7 +7131,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++;
}
}
@@ -6970,21 +7205,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);
@@ -7008,9 +7239,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]);
@@ -7062,13 +7291,11 @@ 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);
@@ -7095,15 +7322,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;
@@ -7113,13 +7337,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;
}
@@ -7127,114 +7351,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
@@ -7288,17 +7404,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 = 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;
@@ -7347,6 +7460,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]) {
@@ -7359,43 +7473,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;
-}
-
void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
{
*tabnr = 0;
@@ -7416,109 +7493,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..f348f102c9 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -2,10 +2,14 @@
#define NVIM_WINDOW_H
#include <stdbool.h>
+#include <stddef.h>
+#include "nvim/buffer.h"
#include "nvim/buffer_defs.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 +21,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/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 440e93da0e..d0fb26edc7 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -114,6 +114,7 @@ describe('nvim_create_user_command', function()
]]
eq({
+ name = "CommandWithLuaCallback",
args = [[this\ is a\ test]],
fargs = {"this ", "is", "a test"},
bang = false,
@@ -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,
@@ -149,6 +151,7 @@ describe('nvim_create_user_command', function()
]=])
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..541ac0ab63 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,
@@ -354,4 +357,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..833d54396b 100644
--- a/test/functional/api/server_notifications_spec.lua
+++ b/test/functional/api/server_notifications_spec.lua
@@ -5,8 +5,9 @@ local eq, clear, eval, command, nvim, 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
describe('notify', function()
local channel
@@ -78,13 +79,7 @@ describe('notify', function()
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 24d0b6da45..aa2f46bb59 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
@@ -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)
@@ -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)
@@ -1903,11 +1913,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 +2124,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 +2140,7 @@ describe('API', function()
stream = 'job',
id = 4,
argv = (
- iswin() and {
+ is_os('win') and {
eval('&shell'),
'/s',
'/c',
@@ -2131,7 +2162,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 +2285,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 +2323,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 +2519,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 +2723,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 +2757,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 +3042,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 +3126,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({
@@ -3176,9 +3219,6 @@ describe('API', function()
cmd = 'echo',
args = { 'foo' },
bang = false,
- range = {},
- count = -1,
- reg = '',
addr = 'none',
magic = {
file = false,
@@ -3195,6 +3235,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3205,7 +3246,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3218,8 +3259,6 @@ describe('API', function()
args = { '/math.random/math.max/' },
bang = false,
range = { 4, 6 },
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = false,
@@ -3236,6 +3275,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3246,7 +3286,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3260,7 +3300,6 @@ describe('API', function()
bang = false,
range = { 1 },
count = 1,
- reg = '',
addr = 'buf',
magic = {
file = false,
@@ -3277,6 +3316,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3287,7 +3327,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3300,7 +3340,6 @@ describe('API', function()
args = {},
bang = false,
range = {},
- count = -1,
reg = '+',
addr = 'line',
magic = {
@@ -3318,6 +3357,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3328,12 +3368,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 +3438,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3369,7 +3449,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3382,8 +3462,6 @@ describe('API', function()
args = {},
bang = true,
range = {},
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = true,
@@ -3400,6 +3478,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3410,7 +3489,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3423,8 +3502,6 @@ describe('API', function()
args = { 'foo.txt' },
bang = false,
range = {},
- count = -1,
- reg = '',
addr = '?',
magic = {
file = true,
@@ -3441,6 +3518,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = true,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3451,19 +3529,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 +3556,7 @@ describe('API', function()
force = true
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3495,7 +3572,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 +3581,6 @@ describe('API', function()
args = { 'test', 'it' },
bang = true,
range = { 4, 6 },
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = false,
@@ -3522,6 +3597,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3532,7 +3608,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3545,8 +3621,6 @@ describe('API', function()
args = { 'a.txt' },
bang = false,
range = {},
- count = -1,
- reg = '',
addr = 'arg',
magic = {
file = true,
@@ -3563,6 +3637,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3573,7 +3648,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3586,9 +3661,6 @@ describe('API', function()
cmd = 'MyCommand',
args = { 'test it' },
bang = false,
- range = {},
- count = -1,
- reg = '',
addr = 'none',
magic = {
file = false,
@@ -3605,6 +3677,7 @@ describe('API', function()
force = false
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3615,7 +3688,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3679,8 +3752,6 @@ describe('API', function()
args = {'x'},
bang = true,
range = {3, 4},
- count = -1,
- reg = '',
addr = 'line',
magic = {
file = false,
@@ -3697,6 +3768,7 @@ describe('API', function()
force = false,
},
hide = false,
+ horizontal = false,
keepalt = false,
keepjumps = false,
keepmarks = false,
@@ -3707,7 +3779,7 @@ describe('API', function()
sandbox = false,
silent = false,
split = "",
- tab = 0,
+ tab = -1,
unsilent = false,
verbose = -1,
vertical = false,
@@ -3717,6 +3789,11 @@ describe('API', function()
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 ()
@@ -3794,15 +3871,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 } } },
@@ -3810,6 +3900,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([[
@@ -3890,11 +3987,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()
@@ -3927,5 +4036,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/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua
index 8ec06dc148..60c29170e2 100644
--- a/test/functional/autocmd/cmdline_spec.lua
+++ b/test/functional/autocmd/cmdline_spec.lua
@@ -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..4717b1fa2e 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('^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 12b8e7c42d..0000000000
--- a/test/functional/autocmd/winscrolled_spec.lua
+++ /dev/null
@@ -1,99 +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 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'))
- feed('0')
- eq(2, eval('g:scrolled'))
- 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('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/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index a4d22685e8..bbf2202f0d 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)
@@ -188,9 +300,7 @@ describe('tmpdir', function()
end)
-- "…/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)
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/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..9dabcd28b3 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()
@@ -46,6 +48,34 @@ describe('startup', function()
assert_log("require%('vim%._editor'%)", testfile, 100)
end)
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)
describe('startup', function()
@@ -57,6 +87,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 +213,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 +225,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 +244,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 +261,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 +279,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 +299,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 +391,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 +419,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 +510,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 +620,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 +687,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 +719,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 +808,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 +854,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 +882,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/completion_spec.lua b/test/functional/editor/completion_spec.lua
index 6cdac3c079..c503d7ebfb 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -1029,7 +1029,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 +1129,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..f300fea3a0 100644
--- a/test/functional/editor/mark_spec.lua
+++ b/test/functional/editor/mark_spec.lua
@@ -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/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/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 cd1f43f9fd..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()
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index 69404039ff..8eed00c973 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -1,12 +1,14 @@
local Screen = require('test.functional.ui.screen')
local helpers = require('test.functional.helpers')(after_each)
local lfs = require('lfs')
+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
@@ -100,7 +102,7 @@ describe('swapfile detection', function()
-- attempt to create a swapfile in different directory.
local init = [[
set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[//
- set swapfile fileformat=unix undolevels=-1 hidden
+ set swapfile fileformat=unix nomodified undolevels=-1 nohidden
]]
before_each(function()
nvim0 = spawn(new_argv())
@@ -263,4 +265,79 @@ describe('swapfile detection', function()
]])
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 93fb0f245e..4e8bf1a323 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -29,6 +29,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 +41,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 +54,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 +111,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 +243,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 +251,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 +278,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 +302,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 +356,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 +387,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 +397,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(
@@ -435,8 +469,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 +566,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 +579,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 +614,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 +709,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,41 +824,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
- module.exec_lua [[vim._ts_remove_language 'c']]
- 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)
@@ -781,17 +850,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
@@ -805,13 +863,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
@@ -819,7 +877,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
@@ -846,7 +904,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
@@ -864,7 +922,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')
@@ -897,14 +955,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..c0ff3ed17a 100644
--- a/test/functional/legacy/055_list_and_dict_types_spec.lua
+++ b/test/functional/legacy/055_list_and_dict_types_spec.lua
@@ -907,7 +907,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/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
index 99d2d0f30e..2fceb6a132 100644
--- a/test/functional/legacy/cmdline_spec.lua
+++ b/test/functional/legacy/cmdline_spec.lua
@@ -5,6 +5,7 @@ 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()
@@ -140,6 +141,104 @@ 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()
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/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/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 4226bcebac..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')]]))
@@ -148,6 +154,33 @@ describe('vim.diagnostic', function()
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()
@@ -1151,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()
@@ -1767,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 = {
@@ -1974,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)
@@ -2001,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/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 f2fb661b70..90eccc49c8 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -24,6 +24,7 @@ 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()
@@ -788,6 +803,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 +916,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 +1026,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 +1049,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 +1065,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 +1074,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 +1090,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 +1133,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 +1157,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 +1173,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 +1182,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 +1198,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 +1217,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 +1236,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 +1255,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 +1271,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 +1280,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 +1296,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 +1315,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 +1327,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 +1348,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 +1364,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 +1373,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 +1389,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 +1397,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 +1444,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 +1465,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 +2189,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 +2237,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 +2253,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 +2697,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()
@@ -2754,6 +2868,24 @@ describe('lua stdlib', function()
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()
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..c0d820f40e 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -12,13 +12,13 @@ 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
describe('startup defaults', function()
describe(':filetype', function()
@@ -220,7 +220,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 +240,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)
@@ -277,7 +279,7 @@ describe('XDG defaults', 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,24 +327,26 @@ 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={
+ 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)
@@ -405,13 +409,15 @@ 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={
+ 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)
@@ -478,18 +484,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 +549,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 +704,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 fa731f6faf..93fada8a50 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,8 +3408,31 @@ 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)
end)
-
describe('cmd', function()
it('can connect to lsp server via rpc.connect', function()
local result = exec_lua [[
@@ -3209,4 +3460,75 @@ describe('LSP', function()
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 9304aa6da9..c6c7d2b03d 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -1,10 +1,15 @@
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
@@ -50,7 +55,7 @@ describe(':Man', function()
|
]]}
- eval('man#init_pager()')
+ exec_lua[[require'man'.init_pager()]]
screen:expect([[
^this {b:is} {b:a} test |
@@ -74,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} |
@@ -89,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 |
@@ -105,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} |
@@ -121,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:·} |
@@ -138,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 |
@@ -155,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 4f444316c3..228aaa6b23 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -5,7 +5,6 @@
-- 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
@@ -14,6 +13,8 @@ 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 +22,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 +54,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).
@@ -86,24 +97,6 @@ describe('TUI', function()
assert_alive()
end)
- it('resize at startup', function()
- -- Issues: #17285 #15044 #11330
- screen:try_resize(50, 10)
- feed_command([[call termopen([v:progpath, '--clean', '--cmd', 'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile']) | sleep 500m | vs new]])
- screen:expect([[
- {1: } │ |
- {4:~ }│{4:~ }|
- {4:~ }│{4:~ }|
- {4:~ }│{4:~ }|
- {4:~ }│{4:~ }|
- {4:~ }│{5:[No Name] 0,0-1 All}|
- {4:~ }│ |
- {5:new }{MATCH:<.*[/\]nvim }|
- |
- {3:-- TERMINAL --} |
- ]])
- end)
-
it('accepts resize while pager is active', function()
child_session:request("nvim_command", [[
set more
@@ -633,37 +626,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()
@@ -767,12 +806,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 |
@@ -1034,8 +1072,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([[
@@ -1154,6 +1191,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~')
@@ -1170,6 +1216,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~')
@@ -1195,6 +1250,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: } |
@@ -1275,9 +1339,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({
@@ -1288,12 +1351,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 |
|
@@ -1332,7 +1392,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 --} |
@@ -1340,18 +1400,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] [+] }|
@@ -1361,23 +1417,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)
@@ -1387,6 +1503,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"')
@@ -1405,6 +1550,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([[
@@ -1431,6 +1585,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: } |
@@ -1451,61 +1614,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)
@@ -1542,6 +1732,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(':')
@@ -1600,9 +1799,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 |
@@ -1610,7 +1818,7 @@ describe('TUI FocusGained/FocusLost', function()
msg5 |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
- ]])
+ ]], unchanged=true}
end)
end)
@@ -1618,7 +1826,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={}})
@@ -1721,7 +1928,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)
@@ -1729,7 +1936,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)
@@ -1892,8 +2099,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()
@@ -1919,11 +2124,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")
@@ -1981,7 +2186,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)
@@ -2105,3 +2310,165 @@ 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))
+
+ screen:try_resize(60, 7)
+
+ 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 4b0bd1eb50..2a2311c0fa 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -5,7 +5,6 @@ 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
@@ -107,12 +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 |
@@ -276,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")
@@ -351,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};
}
@@ -417,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))
@@ -479,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))
@@ -520,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 [[
@@ -577,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))
@@ -594,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:~ }|
@@ -619,14 +605,12 @@ describe('treesitter highlighting', function()
}}
eq({
- {capture='Error', priority='101'};
- {capture='type'};
- }, exec_lua [[ return vim.treesitter.get_captures_at_position(0, 0, 2) ]])
+ {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?";
]])
@@ -708,7 +692,6 @@ describe('treesitter highlighting', function()
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.
diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua
index 8e9941d797..1db8381a93 100644
--- a/test/functional/treesitter/language_spec.lua
+++ b/test/functional/treesitter/language_spec.lua
@@ -6,7 +6,6 @@ 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)
@@ -14,29 +13,25 @@ before_each(clear)
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: 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')"))
- if not pending_c_parser(pending) then
- matches("Error executing lua: Failed to load parser: uv_dlsym: .+",
- pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")'))
- end
+ 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 = {}, {}
@@ -76,18 +71,16 @@ describe('treesitter language 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 ()
- if pending_c_parser(pending) then return end
insert([[
int main() {
int x = 3;
@@ -102,7 +95,6 @@ describe('treesitter language API', function()
end)
it('retrieve the node given a range', function ()
- if pending_c_parser(pending) then return end
insert([[
int main() {
int x = 3;
diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua
index 87ce1b973c..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) {
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
index 4f4c18a748..7f5a864c3d 100644
--- a/test/functional/treesitter/utils_spec.lua
+++ b/test/functional/treesitter/utils_spec.lua
@@ -4,7 +4,6 @@ local clear = helpers.clear
local insert = helpers.insert
local eq = helpers.eq
local exec_lua = helpers.exec_lua
-local pending_c_parser = helpers.pending_c_parser
before_each(clear)
@@ -12,7 +11,6 @@ describe('treesitter utils', function()
before_each(clear)
it('can find an ancestor', function()
- if pending_c_parser(pending) then return end
insert([[
int main() {
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..33e375760e 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([[
|
@@ -409,7 +409,7 @@ describe('Command-line coloring', function()
]])
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..bfa7167100 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -4,9 +4,10 @@ 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 assert_alive = helpers.assert_alive
@@ -198,50 +199,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 +1670,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 +1717,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',
@@ -1911,4 +1868,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 5ffe9ddaad..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 ')
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 522c9ccba2..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()
@@ -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 78a1e8c677..71adeb42a4 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -2381,8 +2381,52 @@ describe('ext_multigrid', function()
end)
it('with winbar', function()
- command 'split'
- command 'setlocal winbar=very\\ bar'
+ command('split')
+ 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};
+ }}
+
+ -- 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:-----------------------------------------------------]|
@@ -2408,7 +2452,7 @@ describe('ext_multigrid', function()
## grid 3
|
## grid 4
- {7:very bar }|
+ {7:very bar}|
^ |
{1:~ }|
{1:~ }|
@@ -2418,6 +2462,45 @@ describe('ext_multigrid', function()
[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 dcd4ad3d9a..85e6a03872 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>')
@@ -802,6 +967,7 @@ 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
})
end)
@@ -2222,8 +2388,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")
@@ -2320,7 +2608,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')
@@ -2417,6 +2705,29 @@ describe('builtin popupmenu', function()
]]}
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({
@@ -2755,7 +3066,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: }|
@@ -2792,7 +3103,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:~ }|
@@ -2801,7 +3112,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:~ }|
@@ -2811,7 +3122,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: }|
@@ -2820,7 +3131,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: }|
@@ -2829,7 +3140,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:~ }|
@@ -2839,6 +3150,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)
@@ -3047,5 +3540,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..a9d796c10b
--- /dev/null
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -0,0 +1,409 @@
+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 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 \'statuscolumn\'', 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:wrap?'':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 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:wrap?'':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:wrap?'':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 |
+ |
+ ]])
+ 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('fits maximum multibyte foldcolumn #21759', function()
+ command('set stc=%C fdc=9 fillchars=foldsep:𒀀')
+ for _ = 0,8 do command('norm zfjzo') end
+ screen:expect([[
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ --------- ^aaaaa |
+ 𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀 aaaaa |
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ aaaaa |
+ |
+ ]])
+ end)
+
+end)
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
index add5144e1b..549ebbde06 100644
--- a/test/functional/ui/statusline_spec.lua
+++ b/test/functional/ui/statusline_spec.lua
@@ -178,6 +178,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 +399,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 +545,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..0e35a03557 100644
--- a/test/functional/ui/tabline_spec.lua
+++ b/test/functional/ui/tabline_spec.lua
@@ -84,3 +84,39 @@ 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)
+end)
diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua
index 58ffa3bda8..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([[
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/eval_spec.lua b/test/functional/vimscript/eval_spec.lua
index d4fa7afe89..a8a901042b 100644
--- a/test/functional/vimscript/eval_spec.lua
+++ b/test/functional/vimscript/eval_spec.lua
@@ -16,12 +16,16 @@ local lfs = require('lfs')
local clear = helpers.clear
local eq = helpers.eq
local exc_exec = helpers.exc_exec
-local exec = helpers.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
@@ -67,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
@@ -147,79 +151,6 @@ describe('List support code', function()
end)
end)
--- oldtest: Test_deep_nest()
-it('Error when if/for/while/try/function is nested too deep',function()
- clear()
- 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)
-
describe("uncaught exception", function()
before_each(clear)
after_each(function()
@@ -247,3 +178,45 @@ describe("uncaught exception", function()
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/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..14c87d9d93 100644
--- a/test/functional/vimscript/server_spec.lua
+++ b/test/functional/vimscript/server_spec.lua
@@ -1,11 +1,11 @@
local helpers = require('test.functional.helpers')(after_each)
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
local 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 function clear_serverlist()
for _, server in pairs(funcs.serverlist()) do
@@ -19,7 +19,7 @@ describe('server', function()
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 +65,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'))
@@ -130,7 +130,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 +164,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..3fe4322501 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -1,4 +1,4 @@
-require('vim.compat')
+require('test.compat')
local shared = require('vim.shared')
local assert = require('luassert')
local luv = require('luv')
@@ -114,25 +114,13 @@ 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 +138,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 +269,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 +314,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 +361,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 +783,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
index b4da4c0611..0d30736091 100644
--- a/test/includes/CMakeLists.txt
+++ b/test/includes/CMakeLists.txt
@@ -23,7 +23,6 @@ foreach(hfile ${PRE_HEADERS})
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()
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..34dbf592a5 100644
--- a/test/unit/eval/typval_spec.lua
+++ b/test/unit/eval/typval_spec.lua
@@ -2253,8 +2253,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({})
@@ -2721,8 +2721,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/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 eb23a3cff1..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'
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..15b019edd1 100644
--- a/test/unit/tui_spec.lua
+++ b/test/unit/tui_spec.lua
@@ -33,7 +33,7 @@ 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))
+ eq(0, multiqueue.multiqueue_size(events))
local event = multiqueue.multiqueue_get(events)
local bg_event = ffi.cast("Event*", event.argv[1])