aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
committerJosh Rahm <joshuarahm@gmail.com>2025-02-05 23:09:29 +0000
commitd5f194ce780c95821a855aca3c19426576d28ae0 (patch)
treed45f461b19f9118ad2bb1f440a7a08973ad18832
parentc5d770d311841ea5230426cc4c868e8db27300a8 (diff)
parent44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff)
downloadrneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.gz
rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.tar.bz2
rneovim-d5f194ce780c95821a855aca3c19426576d28ae0.zip
Merge remote-tracking branch 'upstream/master' into mix_20240309HEADrahm
-rw-r--r--.editorconfig2
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml2
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yml2
-rw-r--r--.github/ISSUE_TEMPLATE/lsp_bug_report.yml3
-rwxr-xr-x.github/scripts/install_deps.sh2
-rw-r--r--.github/scripts/reviewers_add.js1
-rw-r--r--.github/workflows/build.yml4
-rw-r--r--.github/workflows/news.yml14
-rw-r--r--.github/workflows/notes.md42
-rw-r--r--.github/workflows/release.yml79
-rw-r--r--.github/workflows/test.yml51
-rw-r--r--.github/workflows/vim_patches.yml8
-rw-r--r--.luarc.json3
-rw-r--r--BUILD.md24
-rw-r--r--CMakeLists.txt12
-rw-r--r--CMakePresets.json6
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--INSTALL.md29
-rw-r--r--MAINTAIN.md4
-rw-r--r--Makefile3
-rw-r--r--cmake.deps/deps.txt36
-rw-r--r--cmake.packaging/CMakeLists.txt6
-rw-r--r--cmake/Deps.cmake2
-rw-r--r--contrib/gdb/neovim_gdb.vim6
-rw-r--r--contrib/local.mk.example2
-rw-r--r--runtime/autoload/gzip.vim5
-rw-r--r--runtime/autoload/netrw.vim11941
-rw-r--r--runtime/autoload/netrwFileHandlers.vim0
-rw-r--r--runtime/autoload/netrwSettings.vim249
-rw-r--r--runtime/autoload/provider/clipboard.vim10
-rw-r--r--runtime/autoload/spotbugs.vim354
-rw-r--r--runtime/autoload/typst.vim5
-rw-r--r--runtime/colors/blue.vim6
-rw-r--r--runtime/colors/darkblue.vim4
-rw-r--r--runtime/colors/delek.vim4
-rw-r--r--runtime/colors/desert.vim4
-rw-r--r--runtime/colors/evening.vim5
-rw-r--r--runtime/colors/habamax.vim4
-rw-r--r--runtime/colors/industry.vim5
-rw-r--r--runtime/colors/lunaperche.vim4
-rw-r--r--runtime/colors/morning.vim4
-rw-r--r--runtime/colors/murphy.vim4
-rw-r--r--runtime/colors/pablo.vim4
-rw-r--r--runtime/colors/peachpuff.vim4
-rw-r--r--runtime/colors/quiet.vim3
-rw-r--r--runtime/colors/retrobox.vim3
-rw-r--r--runtime/colors/shine.vim4
-rw-r--r--runtime/colors/slate.vim4
-rw-r--r--runtime/colors/sorbet.vim3
-rw-r--r--runtime/colors/torte.vim4
-rw-r--r--runtime/colors/unokai.vim522
-rw-r--r--runtime/colors/vim.lua1
-rw-r--r--runtime/colors/wildcharm.vim4
-rw-r--r--runtime/colors/zaibatsu.vim6
-rw-r--r--runtime/colors/zellner.vim4
-rw-r--r--runtime/compiler/bash.vim12
-rw-r--r--runtime/compiler/cppcheck.vim4
-rw-r--r--runtime/compiler/eslint.vim7
-rw-r--r--runtime/compiler/groff.vim4
-rw-r--r--runtime/compiler/javac.vim8
-rw-r--r--runtime/compiler/maven.vim2
-rw-r--r--runtime/compiler/mypy.vim4
-rw-r--r--runtime/compiler/pandoc.vim4
-rw-r--r--runtime/compiler/powershell.vim7
-rw-r--r--runtime/compiler/pylint.vim3
-rw-r--r--runtime/compiler/pytest.vim103
-rw-r--r--runtime/compiler/ruff.vim3
-rw-r--r--runtime/compiler/spotbugs.vim254
-rw-r--r--runtime/compiler/tex.vim7
-rw-r--r--runtime/compiler/typst.vim5
-rw-r--r--runtime/doc/api.txt257
-rw-r--r--runtime/doc/autocmd.txt4
-rw-r--r--runtime/doc/builtin.txt326
-rw-r--r--runtime/doc/change.txt15
-rw-r--r--runtime/doc/channel.txt13
-rw-r--r--runtime/doc/cmdline.txt8
-rw-r--r--runtime/doc/credits.txt (renamed from runtime/doc/backers.txt)111
-rw-r--r--runtime/doc/deprecated.txt315
-rw-r--r--runtime/doc/dev_arch.txt10
-rw-r--r--runtime/doc/dev_vimpatch.txt1
-rw-r--r--runtime/doc/develop.txt21
-rw-r--r--runtime/doc/diagnostic.txt91
-rw-r--r--runtime/doc/digraph.txt5
-rw-r--r--runtime/doc/eval.txt3
-rw-r--r--runtime/doc/filetype.txt6
-rw-r--r--runtime/doc/fold.txt54
-rw-r--r--runtime/doc/gui.txt659
-rw-r--r--runtime/doc/health.txt19
-rw-r--r--runtime/doc/help.txt1
-rw-r--r--runtime/doc/helphelp.txt1
-rw-r--r--runtime/doc/index.txt18
-rw-r--r--runtime/doc/insert.txt3
-rw-r--r--runtime/doc/intro.txt971
-rw-r--r--runtime/doc/lsp.txt673
-rw-r--r--runtime/doc/lua-guide.txt2
-rw-r--r--runtime/doc/lua.txt158
-rw-r--r--runtime/doc/luvref.txt394
-rw-r--r--runtime/doc/map.txt18
-rw-r--r--runtime/doc/message.txt3
-rw-r--r--runtime/doc/motion.txt6
-rw-r--r--runtime/doc/news.txt196
-rw-r--r--runtime/doc/nvim.txt36
-rw-r--r--runtime/doc/options.txt166
-rw-r--r--runtime/doc/pattern.txt1
-rw-r--r--runtime/doc/pi_tar.txt2
-rw-r--r--runtime/doc/provider.txt34
-rw-r--r--runtime/doc/quickfix.txt241
-rw-r--r--runtime/doc/repeat.txt2
-rw-r--r--runtime/doc/sign.txt2
-rw-r--r--runtime/doc/starting.txt52
-rw-r--r--runtime/doc/support.txt7
-rw-r--r--runtime/doc/syntax.txt22
-rw-r--r--runtime/doc/terminal.txt14
-rw-r--r--runtime/doc/treesitter.txt210
-rw-r--r--runtime/doc/tui.txt188
-rw-r--r--runtime/doc/ui.txt31
-rw-r--r--runtime/doc/usr_02.txt7
-rw-r--r--runtime/doc/usr_05.txt2
-rw-r--r--runtime/doc/usr_10.txt2
-rw-r--r--runtime/doc/usr_25.txt6
-rw-r--r--runtime/doc/usr_41.txt3
-rw-r--r--runtime/doc/various.txt21
-rw-r--r--runtime/doc/vietnamese.txt73
-rw-r--r--runtime/doc/vim_diff.txt64
-rw-r--r--runtime/doc/vvars.txt17
-rw-r--r--runtime/doc/windows.txt14
-rw-r--r--runtime/filetype.lua4
-rw-r--r--runtime/ftplugin/c.vim2
-rw-r--r--runtime/ftplugin/c3.vim14
-rw-r--r--runtime/ftplugin/checkhealth.vim1
-rw-r--r--runtime/ftplugin/dockerfile.vim6
-rw-r--r--runtime/ftplugin/editorconfig.vim6
-rw-r--r--runtime/ftplugin/gel.vim13
-rw-r--r--runtime/ftplugin/graphql.vim17
-rw-r--r--runtime/ftplugin/help.lua53
-rw-r--r--runtime/ftplugin/java.vim282
-rw-r--r--runtime/ftplugin/jjdescription.vim (renamed from runtime/ftplugin/jj.vim)0
-rw-r--r--runtime/ftplugin/just.vim17
-rw-r--r--runtime/ftplugin/karel.vim16
-rw-r--r--runtime/ftplugin/kconfig.vim3
-rw-r--r--runtime/ftplugin/lnk.vim14
-rw-r--r--runtime/ftplugin/lnkmap.vim16
-rw-r--r--runtime/ftplugin/opencl.vim12
-rw-r--r--runtime/ftplugin/proto.vim18
-rw-r--r--runtime/ftplugin/ptx.vim16
-rw-r--r--runtime/ftplugin/python.vim11
-rw-r--r--runtime/ftplugin/query.lua3
-rw-r--r--runtime/ftplugin/sh.vim18
-rw-r--r--runtime/ftplugin/shaderslang.vim54
-rw-r--r--runtime/ftplugin/tiasm.vim18
-rw-r--r--runtime/ftplugin/typst.vim13
-rw-r--r--runtime/ftplugin/vim.vim8
-rw-r--r--runtime/indent/graphql.vim92
-rw-r--r--runtime/indent/just.vim51
-rw-r--r--runtime/indent/query.lua1
-rw-r--r--runtime/indent/typst.vim5
-rw-r--r--runtime/lua/_vim9script.lua2
-rw-r--r--runtime/lua/coxpcall.lua19
-rw-r--r--runtime/lua/editorconfig.lua6
-rw-r--r--runtime/lua/man.lua701
-rw-r--r--runtime/lua/tohtml.lua10
-rw-r--r--runtime/lua/vim/_defaults.lua75
-rw-r--r--runtime/lua/vim/_editor.lua28
-rw-r--r--runtime/lua/vim/_inspector.lua27
-rw-r--r--runtime/lua/vim/_meta/api.lua206
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua102
-rw-r--r--runtime/lua/vim/_meta/api_keysets_extra.lua66
-rw-r--r--runtime/lua/vim/_meta/builtin.lua5
-rw-r--r--runtime/lua/vim/_meta/json.lua15
-rw-r--r--runtime/lua/vim/_meta/options.lua209
-rw-r--r--runtime/lua/vim/_meta/vimfn.lua315
-rw-r--r--runtime/lua/vim/_meta/vvars.lua34
-rw-r--r--runtime/lua/vim/_meta/vvars_extra.lua77
-rw-r--r--runtime/lua/vim/_options.lua13
-rw-r--r--runtime/lua/vim/_system.lua129
-rw-r--r--runtime/lua/vim/diagnostic.lua517
-rw-r--r--runtime/lua/vim/filetype.lua68
-rw-r--r--runtime/lua/vim/filetype/detect.lua61
-rw-r--r--runtime/lua/vim/fs.lua158
-rw-r--r--runtime/lua/vim/func.lua14
-rw-r--r--runtime/lua/vim/func/_memoize.lua56
-rw-r--r--runtime/lua/vim/glob.lua1
-rw-r--r--runtime/lua/vim/health.lua65
-rw-r--r--runtime/lua/vim/health/health.lua13
-rw-r--r--runtime/lua/vim/hl.lua45
-rw-r--r--runtime/lua/vim/inspect.lua1
-rw-r--r--runtime/lua/vim/loader.lua71
-rw-r--r--runtime/lua/vim/lsp.lua692
-rw-r--r--runtime/lua/vim/lsp/_changetracking.lua18
-rw-r--r--runtime/lua/vim/lsp/_folding_range.lua373
-rw-r--r--runtime/lua/vim/lsp/_meta.lua4
-rw-r--r--runtime/lua/vim/lsp/_snippet_grammar.lua1
-rw-r--r--runtime/lua/vim/lsp/_tagfunc.lua18
-rw-r--r--runtime/lua/vim/lsp/_transport.lua182
-rw-r--r--runtime/lua/vim/lsp/_watchfiles.lua3
-rw-r--r--runtime/lua/vim/lsp/buf.lua98
-rw-r--r--runtime/lua/vim/lsp/client.lua411
-rw-r--r--runtime/lua/vim/lsp/codelens.lua14
-rw-r--r--runtime/lua/vim/lsp/completion.lua43
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua29
-rw-r--r--runtime/lua/vim/lsp/handlers.lua20
-rw-r--r--runtime/lua/vim/lsp/health.lua77
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua29
-rw-r--r--runtime/lua/vim/lsp/protocol.lua15
-rw-r--r--runtime/lua/vim/lsp/rpc.lua290
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua35
-rw-r--r--runtime/lua/vim/lsp/sync.lua52
-rw-r--r--runtime/lua/vim/lsp/util.lua222
-rw-r--r--runtime/lua/vim/provider/health.lua34
-rw-r--r--runtime/lua/vim/re.lua1
-rw-r--r--runtime/lua/vim/shared.lua74
-rw-r--r--runtime/lua/vim/snippet.lua4
-rw-r--r--runtime/lua/vim/text.lua24
-rw-r--r--runtime/lua/vim/treesitter.lua57
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua325
-rw-r--r--runtime/lua/vim/treesitter/_meta/misc.lua8
-rw-r--r--runtime/lua/vim/treesitter/_meta/tsnode.lua16
-rw-r--r--runtime/lua/vim/treesitter/_query_linter.lua6
-rw-r--r--runtime/lua/vim/treesitter/dev.lua25
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua30
-rw-r--r--runtime/lua/vim/treesitter/language.lua7
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua356
-rw-r--r--runtime/lua/vim/treesitter/query.lua418
-rw-r--r--runtime/lua/vim/uri.lua7
-rw-r--r--runtime/lua/vim/version.lua3
-rw-r--r--runtime/lua/vim/vimhelp.lua2
-rw-r--r--runtime/optwin.vim6
-rw-r--r--runtime/pack/dist/opt/matchit/autoload/matchit.vim10
-rw-r--r--runtime/pack/dist/opt/matchit/doc/matchit.txt2
-rw-r--r--runtime/pack/dist/opt/netrw/LICENSE.txt16
-rw-r--r--runtime/pack/dist/opt/netrw/README.md544
-rw-r--r--runtime/pack/dist/opt/netrw/autoload/netrw.vim11927
-rw-r--r--runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim242
-rw-r--r--runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim (renamed from runtime/autoload/netrw_gitignore.vim)13
-rw-r--r--runtime/pack/dist/opt/netrw/doc/netrw.txt (renamed from runtime/doc/pi_netrw.txt)653
-rw-r--r--runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim214
-rw-r--r--runtime/pack/dist/opt/netrw/syntax/netrw.vim149
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim8
-rw-r--r--runtime/plugin/editorconfig.lua2
-rw-r--r--runtime/plugin/man.lua13
-rw-r--r--runtime/plugin/matchparen.vim4
-rw-r--r--runtime/plugin/netrwPlugin.vim234
-rw-r--r--runtime/plugin/osc52.lua31
-rw-r--r--runtime/queries/c/highlights.scm13
-rw-r--r--runtime/queries/lua/highlights.scm2
-rw-r--r--runtime/queries/markdown_inline/highlights.scm13
-rw-r--r--runtime/queries/query/highlights.scm10
-rw-r--r--runtime/queries/vim/highlights.scm1
-rw-r--r--runtime/syntax/apache.vim15
-rw-r--r--runtime/syntax/apkbuild.vim17
-rw-r--r--runtime/syntax/asm.vim7
-rw-r--r--runtime/syntax/c.vim138
-rw-r--r--runtime/syntax/checkhealth.vim1
-rw-r--r--runtime/syntax/chordpro.vim3
-rw-r--r--runtime/syntax/cmacro.vim77
-rw-r--r--runtime/syntax/dircolors.vim3
-rw-r--r--runtime/syntax/dockerfile.vim3
-rw-r--r--runtime/syntax/gel.vim19
-rw-r--r--runtime/syntax/graphql.vim90
-rw-r--r--runtime/syntax/hyprlang.vim59
-rw-r--r--runtime/syntax/java.vim33
-rw-r--r--runtime/syntax/jjdescription.vim (renamed from runtime/syntax/jj.vim)6
-rw-r--r--runtime/syntax/just.vim406
-rw-r--r--runtime/syntax/karel.vim112
-rw-r--r--runtime/syntax/kconfig.vim1392
-rw-r--r--runtime/syntax/lnk.vim40
-rw-r--r--runtime/syntax/lnkmap.vim35
-rw-r--r--runtime/syntax/lyrics.vim4
-rw-r--r--runtime/syntax/netrw.vim148
-rw-r--r--runtime/syntax/opencl.vim13
-rw-r--r--runtime/syntax/po.vim56
-rw-r--r--runtime/syntax/ptx.vim52
-rw-r--r--runtime/syntax/query.lua1
-rw-r--r--runtime/syntax/sh.vim130
-rw-r--r--runtime/syntax/shaderslang.vim360
-rw-r--r--runtime/syntax/tex.vim7
-rw-r--r--runtime/syntax/tiasm.vim102
-rw-r--r--runtime/syntax/typst.vim5
-rw-r--r--runtime/syntax/vim.vim72
-rw-r--r--runtime/syntax/xf86conf.vim8
-rw-r--r--runtime/syntax/zsh.vim25
-rwxr-xr-xscripts/bump_deps.lua476
-rwxr-xr-xscripts/gen_eval_files.lua101
-rw-r--r--scripts/gen_help_html.lua7
-rwxr-xr-xscripts/gen_vimdoc.lua34
-rwxr-xr-xscripts/genappimage.sh33
-rw-r--r--scripts/luacats_grammar.lua4
-rw-r--r--scripts/luacats_parser.lua25
-rwxr-xr-xscripts/vimpatch.lua2
-rw-r--r--src/cjson/lua_cjson.c121
-rwxr-xr-xsrc/clint.py14
-rw-r--r--src/klib/kvec.h4
-rw-r--r--src/mpack/conv.h4
-rw-r--r--src/nvim/CMakeLists.txt22
-rw-r--r--src/nvim/api/autocmd.c104
-rw-r--r--src/nvim/api/buffer.c351
-rw-r--r--src/nvim/api/command.c8
-rw-r--r--src/nvim/api/deprecated.c223
-rw-r--r--src/nvim/api/extmark.c157
-rw-r--r--src/nvim/api/extmark.h1
-rw-r--r--src/nvim/api/keysets_defs.h114
-rw-r--r--src/nvim/api/options.c60
-rw-r--r--src/nvim/api/private/converter.c3
-rw-r--r--src/nvim/api/private/defs.h2
-rw-r--r--src/nvim/api/private/helpers.c89
-rw-r--r--src/nvim/api/private/helpers.h16
-rw-r--r--src/nvim/api/tabpage.c13
-rw-r--r--src/nvim/api/ui.c4
-rw-r--r--src/nvim/api/ui.h1
-rw-r--r--src/nvim/api/ui_events.in.h6
-rw-r--r--src/nvim/api/vim.c416
-rw-r--r--src/nvim/api/vimscript.c70
-rw-r--r--src/nvim/api/win_config.c35
-rw-r--r--src/nvim/api/window.c77
-rw-r--r--src/nvim/arglist.c5
-rw-r--r--src/nvim/ascii_defs.h9
-rw-r--r--src/nvim/autocmd.c153
-rw-r--r--src/nvim/autocmd_defs.h4
-rw-r--r--src/nvim/buffer.c233
-rw-r--r--src/nvim/buffer_defs.h14
-rw-r--r--src/nvim/buffer_updates.c3
-rw-r--r--src/nvim/bufwrite.c15
-rw-r--r--src/nvim/change.c3
-rw-r--r--src/nvim/channel.c3
-rw-r--r--src/nvim/charset.c8
-rw-r--r--src/nvim/cmdexpand.c13
-rw-r--r--src/nvim/cmdhist.c1
-rw-r--r--src/nvim/cmdhist.h4
-rw-r--r--src/nvim/context.c1
-rw-r--r--src/nvim/context.h2
-rw-r--r--src/nvim/cursor.c9
-rw-r--r--src/nvim/cursor_shape.c3
-rw-r--r--src/nvim/cursor_shape.h3
-rw-r--r--src/nvim/debugger.c9
-rw-r--r--src/nvim/decoration.c342
-rw-r--r--src/nvim/decoration.h47
-rw-r--r--src/nvim/decoration_defs.h5
-rw-r--r--src/nvim/decoration_provider.c12
-rw-r--r--src/nvim/diff.c48
-rw-r--r--src/nvim/digraph.c30
-rw-r--r--src/nvim/drawline.c240
-rw-r--r--src/nvim/drawscreen.c128
-rw-r--r--src/nvim/drawscreen.h1
-rw-r--r--src/nvim/edit.c65
-rw-r--r--src/nvim/errors.h1
-rw-r--r--src/nvim/eval.c27
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua237
-rw-r--r--src/nvim/eval/buffer.c6
-rw-r--r--src/nvim/eval/decode.c3
-rw-r--r--src/nvim/eval/decode.h2
-rw-r--r--src/nvim/eval/deprecated.c158
-rw-r--r--src/nvim/eval/deprecated.h8
-rw-r--r--src/nvim/eval/executor.c1
-rw-r--r--src/nvim/eval/funcs.c397
-rw-r--r--src/nvim/eval/typval.c38
-rw-r--r--src/nvim/eval/typval_defs.h7
-rw-r--r--src/nvim/eval/userfunc.c793
-rw-r--r--src/nvim/eval/vars.c35
-rw-r--r--src/nvim/eval/window.c2
-rw-r--r--src/nvim/event/proc.c3
-rw-r--r--src/nvim/event/proc.h5
-rw-r--r--src/nvim/event/rstream.c6
-rw-r--r--src/nvim/event/socket.c2
-rw-r--r--src/nvim/event/stream.c1
-rw-r--r--src/nvim/ex_cmds.c47
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_cmds2.c2
-rw-r--r--src/nvim/ex_cmds_defs.h29
-rw-r--r--src/nvim/ex_docmd.c63
-rw-r--r--src/nvim/ex_eval.c7
-rw-r--r--src/nvim/ex_eval_defs.h2
-rw-r--r--src/nvim/ex_getln.c272
-rw-r--r--src/nvim/ex_getln_defs.h2
-rw-r--r--src/nvim/ex_session.c87
-rw-r--r--src/nvim/extmark.c63
-rw-r--r--src/nvim/file_search.c4
-rw-r--r--src/nvim/fileio.c8
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/garray.c3
-rw-r--r--src/nvim/generators/c_grammar.lua333
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua26
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua2
-rw-r--r--src/nvim/generators/gen_declarations.lua468
-rw-r--r--src/nvim/generators/gen_eval.lua1
-rw-r--r--src/nvim/generators/gen_options.lua589
-rw-r--r--src/nvim/generators/gen_vimvim.lua8
-rw-r--r--src/nvim/generators/hashy.lua2
-rw-r--r--src/nvim/getchar.c106
-rw-r--r--src/nvim/getchar.h1
-rw-r--r--src/nvim/globals.h3
-rw-r--r--src/nvim/grid.c9
-rw-r--r--src/nvim/hashtab.c1
-rw-r--r--src/nvim/highlight.c7
-rw-r--r--src/nvim/highlight.h1
-rw-r--r--src/nvim/highlight_defs.h1
-rw-r--r--src/nvim/highlight_group.c26
-rw-r--r--src/nvim/indent.c7
-rw-r--r--src/nvim/indent_c.c81
-rw-r--r--src/nvim/input.c147
-rw-r--r--src/nvim/insexpand.c721
-rw-r--r--src/nvim/keycodes.c2
-rw-r--r--src/nvim/linematch.c16
-rw-r--r--src/nvim/linematch.h2
-rw-r--r--src/nvim/lua/api_wrappers.c30
-rw-r--r--src/nvim/lua/converter.c1
-rw-r--r--src/nvim/lua/executor.c10
-rw-r--r--src/nvim/lua/executor.h2
-rw-r--r--src/nvim/lua/secure.c2
-rw-r--r--src/nvim/lua/spell.c2
-rw-r--r--src/nvim/lua/stdlib.c51
-rw-r--r--src/nvim/lua/treesitter.c37
-rw-r--r--src/nvim/lua/xdiff.c1
-rw-r--r--src/nvim/main.c31
-rw-r--r--src/nvim/main.h1
-rw-r--r--src/nvim/mapping.c8
-rw-r--r--src/nvim/mark.c5
-rw-r--r--src/nvim/marktree.c3
-rw-r--r--src/nvim/marktree.h1
-rw-r--r--src/nvim/math.c6
-rw-r--r--src/nvim/mbyte.c12
-rw-r--r--src/nvim/mbyte.h1
-rw-r--r--src/nvim/mbyte_defs.h5
-rw-r--r--src/nvim/memline.c6
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/memory.h1
-rw-r--r--src/nvim/menu.c1
-rw-r--r--src/nvim/message.c478
-rw-r--r--src/nvim/mouse.c190
-rw-r--r--src/nvim/move.c23
-rw-r--r--src/nvim/msgpack_rpc/channel.c10
-rw-r--r--src/nvim/msgpack_rpc/packer.c9
-rw-r--r--src/nvim/msgpack_rpc/packer.h6
-rw-r--r--src/nvim/msgpack_rpc/server.c1
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c1
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h2
-rw-r--r--src/nvim/normal.c78
-rw-r--r--src/nvim/ops.c188
-rw-r--r--src/nvim/ops.h2
-rw-r--r--src/nvim/option.c274
-rw-r--r--src/nvim/option.h4
-rw-r--r--src/nvim/option_defs.h11
-rw-r--r--src/nvim/option_vars.h190
-rw-r--r--src/nvim/options.lua1202
-rw-r--r--src/nvim/optionstr.c1021
-rw-r--r--src/nvim/os/env.c3
-rw-r--r--src/nvim/os/fileio.c7
-rw-r--r--src/nvim/os/fs.c6
-rw-r--r--src/nvim/os/input.c4
-rw-r--r--src/nvim/os/nvim.rc2
-rw-r--r--src/nvim/os/pty_proc_unix.c1
-rw-r--r--src/nvim/os/shell.c22
-rw-r--r--src/nvim/os/signal.c3
-rw-r--r--src/nvim/path.c18
-rw-r--r--src/nvim/po/af.po4
-rw-r--r--src/nvim/po/ca.po4
-rw-r--r--src/nvim/po/cs.cp1250.po4
-rw-r--r--src/nvim/po/cs.po4
-rw-r--r--src/nvim/po/da.po4
-rw-r--r--src/nvim/po/de.po4
-rw-r--r--src/nvim/po/en_GB.po4
-rw-r--r--src/nvim/po/eo.po4
-rw-r--r--src/nvim/po/es.po4
-rw-r--r--src/nvim/po/fi.po4
-rw-r--r--src/nvim/po/fr.po4
-rw-r--r--src/nvim/po/ga.po4
-rw-r--r--src/nvim/po/it.po4
-rw-r--r--src/nvim/po/ja.euc-jp.po4
-rw-r--r--src/nvim/po/ja.po4
-rw-r--r--src/nvim/po/ko.UTF-8.po4
-rw-r--r--src/nvim/po/nb.po4
-rw-r--r--src/nvim/po/nl.po4
-rw-r--r--src/nvim/po/no.po4
-rw-r--r--src/nvim/po/pl.UTF-8.po4
-rw-r--r--src/nvim/po/pt_BR.po4
-rw-r--r--src/nvim/po/ru.po4
-rw-r--r--src/nvim/po/sk.cp1250.po4
-rw-r--r--src/nvim/po/sk.po4
-rw-r--r--src/nvim/po/sr.po4
-rw-r--r--src/nvim/po/sv.po4
-rw-r--r--src/nvim/po/tr.po4
-rw-r--r--src/nvim/po/uk.po4
-rw-r--r--src/nvim/po/vi.po6622
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po4
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po4
-rw-r--r--src/nvim/popupmenu.c368
-rw-r--r--src/nvim/profile.c1
-rw-r--r--src/nvim/quickfix.c9
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/runtime.c151
-rw-r--r--src/nvim/search.c79
-rw-r--r--src/nvim/search.h3
-rw-r--r--src/nvim/shada.c9
-rw-r--r--src/nvim/shada.h2
-rw-r--r--src/nvim/sign.c3
-rw-r--r--src/nvim/spell.c6
-rw-r--r--src/nvim/spellsuggest.c17
-rw-r--r--src/nvim/state.c16
-rw-r--r--src/nvim/state.h2
-rw-r--r--src/nvim/statusline.c172
-rw-r--r--src/nvim/statusline_defs.h66
-rw-r--r--src/nvim/strings.c18
-rw-r--r--src/nvim/strings.h2
-rw-r--r--src/nvim/syntax.c1
-rw-r--r--src/nvim/tag.c19
-rw-r--r--src/nvim/terminal.c309
-rw-r--r--src/nvim/textformat.c8
-rw-r--r--src/nvim/textobject.c1
-rw-r--r--src/nvim/tui/input.c53
-rw-r--r--src/nvim/tui/input.h2
-rw-r--r--src/nvim/tui/termkey/driver-csi.c57
-rw-r--r--src/nvim/tui/termkey/driver-csi.h2
-rw-r--r--src/nvim/tui/termkey/driver-ti.c6
-rw-r--r--src/nvim/tui/termkey/driver-ti.h2
-rw-r--r--src/nvim/tui/termkey/termkey-internal.h2
-rw-r--r--src/nvim/tui/termkey/termkey.c55
-rw-r--r--src/nvim/tui/termkey/termkey.h6
-rw-r--r--src/nvim/tui/termkey/termkey_defs.h9
-rw-r--r--src/nvim/tui/tui.c63
-rw-r--r--src/nvim/tui/tui_defs.h1
-rw-r--r--src/nvim/ui.c17
-rw-r--r--src/nvim/ui_client.c1
-rw-r--r--src/nvim/ui_compositor.c35
-rw-r--r--src/nvim/undo.c18
-rw-r--r--src/nvim/usercmd.c2
-rw-r--r--src/nvim/version.c3
-rw-r--r--src/nvim/vterm/LICENSE (renamed from src/vterm/LICENSE)0
-rw-r--r--src/nvim/vterm/README.md1
-rw-r--r--src/nvim/vterm/encoding.c271
-rw-r--r--src/nvim/vterm/encoding.h10
-rw-r--r--src/nvim/vterm/keyboard.c318
-rw-r--r--src/nvim/vterm/keyboard.h10
-rw-r--r--src/nvim/vterm/mouse.c124
-rw-r--r--src/nvim/vterm/mouse.h10
-rw-r--r--src/nvim/vterm/parser.c411
-rw-r--r--src/nvim/vterm/parser.h9
-rw-r--r--src/nvim/vterm/pen.c644
-rw-r--r--src/nvim/vterm/pen.h9
-rw-r--r--src/nvim/vterm/screen.c1115
-rw-r--r--src/nvim/vterm/screen.h9
-rw-r--r--src/nvim/vterm/state.c2467
-rw-r--r--src/nvim/vterm/state.h7
-rw-r--r--src/nvim/vterm/vterm.c335
-rw-r--r--src/nvim/vterm/vterm.h161
-rw-r--r--src/nvim/vterm/vterm_defs.h322
-rw-r--r--src/nvim/vterm/vterm_internal_defs.h292
-rw-r--r--src/nvim/vterm/vterm_keycodes_defs.h (renamed from src/vterm/vterm_keycodes.h)13
-rw-r--r--src/nvim/vvars.lua26
-rw-r--r--src/nvim/window.c358
-rw-r--r--src/nvim/window.h3
-rw-r--r--src/nvim/winfloat.c10
-rw-r--r--src/vterm/encoding.c230
-rw-r--r--src/vterm/encoding/DECdrawing.inc36
-rw-r--r--src/vterm/encoding/uk.inc6
-rw-r--r--src/vterm/fullwidth.inc111
-rw-r--r--src/vterm/keyboard.c225
-rw-r--r--src/vterm/mouse.c99
-rw-r--r--src/vterm/parser.c408
-rw-r--r--src/vterm/pen.c678
-rw-r--r--src/vterm/rect.h56
-rw-r--r--src/vterm/screen.c1202
-rw-r--r--src/vterm/state.c2347
-rw-r--r--src/vterm/unicode.c313
-rw-r--r--src/vterm/vterm.c938
-rw-r--r--src/vterm/vterm.h676
-rw-r--r--src/vterm/vterm_internal.h298
-rw-r--r--test/README.md160
-rw-r--r--test/benchmark/decor_spec.lua140
-rw-r--r--test/benchmark/text_spec.lua52
-rw-r--r--test/client/rpc_stream.lua (renamed from test/client/msgpack_rpc_stream.lua)49
-rw-r--r--test/client/session.lua58
-rw-r--r--test/client/uv_stream.lua105
-rw-r--r--test/functional/api/autocmd_spec.lua133
-rw-r--r--test/functional/api/buffer_updates_spec.lua3
-rw-r--r--test/functional/api/command_spec.lua9
-rw-r--r--test/functional/api/deprecated_spec.lua21
-rw-r--r--test/functional/api/extmark_spec.lua20
-rw-r--r--test/functional/api/highlight_spec.lua23
-rw-r--r--test/functional/api/server_requests_spec.lua16
-rw-r--r--test/functional/api/version_spec.lua18
-rw-r--r--test/functional/api/vim_spec.lua160
-rw-r--r--test/functional/api/window_spec.lua62
-rw-r--r--test/functional/autocmd/autocmd_spec.lua24
-rw-r--r--test/functional/autocmd/completedone_spec.lua2
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua9
-rw-r--r--test/functional/autocmd/focus_spec.lua8
-rw-r--r--test/functional/autocmd/termxx_spec.lua4
-rw-r--r--test/functional/core/channels_spec.lua7
-rw-r--r--test/functional/core/exit_spec.lua6
-rw-r--r--test/functional/core/fileio_spec.lua13
-rw-r--r--test/functional/core/job_spec.lua84
-rw-r--r--test/functional/core/log_spec.lua2
-rw-r--r--test/functional/core/main_spec.lua117
-rw-r--r--test/functional/core/remote_spec.lua18
-rw-r--r--test/functional/core/startup_spec.lua129
-rw-r--r--test/functional/editor/completion_spec.lua69
-rw-r--r--test/functional/editor/defaults_spec.lua99
-rw-r--r--test/functional/editor/mode_normal_spec.lua8
-rw-r--r--test/functional/ex_cmds/swapfile_preserve_recover_spec.lua43
-rw-r--r--test/functional/ex_cmds/wundo_spec.lua16
-rw-r--r--test/functional/fixtures/CMakeLists.txt3
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua15
-rw-r--r--test/functional/fixtures/printargs-test.c2
-rw-r--r--test/functional/fixtures/shell-test.c17
-rw-r--r--test/functional/fixtures/streams-test.c3
-rw-r--r--test/functional/legacy/cmdline_spec.lua70
-rw-r--r--test/functional/legacy/highlight_spec.lua2
-rw-r--r--test/functional/legacy/messages_spec.lua41
-rw-r--r--test/functional/legacy/signs_spec.lua9
-rw-r--r--test/functional/legacy/substitute_spec.lua6
-rw-r--r--test/functional/legacy/window_cmd_spec.lua4
-rw-r--r--test/functional/lua/diagnostic_spec.lua249
-rw-r--r--test/functional/lua/filetype_spec.lua14
-rw-r--r--test/functional/lua/fs_spec.lua228
-rw-r--r--test/functional/lua/func_memoize_spec.lua142
-rw-r--r--test/functional/lua/hl_spec.lua31
-rw-r--r--test/functional/lua/json_spec.lua39
-rw-r--r--test/functional/lua/loader_spec.lua18
-rw-r--r--test/functional/lua/system_spec.lua48
-rw-r--r--test/functional/lua/text_spec.lua16
-rw-r--r--test/functional/lua/thread_spec.lua20
-rw-r--r--test/functional/lua/ui_event_spec.lua177
-rw-r--r--test/functional/lua/uri_spec.lua8
-rw-r--r--test/functional/lua/vim_spec.lua13
-rw-r--r--test/functional/lua/watch_spec.lua3
-rw-r--r--test/functional/lua/with_spec.lua17
-rw-r--r--test/functional/options/defaults_spec.lua16
-rw-r--r--test/functional/options/winfixbuf_spec.lua74
-rw-r--r--test/functional/plugin/health_spec.lua12
-rw-r--r--test/functional/plugin/lsp/completion_spec.lua54
-rw-r--r--test/functional/plugin/lsp/diagnostic_spec.lua61
-rw-r--r--test/functional/plugin/lsp/folding_range_spec.lua647
-rw-r--r--test/functional/plugin/lsp/handler_spec.lua42
-rw-r--r--test/functional/plugin/lsp/incremental_sync_spec.lua10
-rw-r--r--test/functional/plugin/lsp/semantic_tokens_spec.lua2
-rw-r--r--test/functional/plugin/lsp/testutil.lua13
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua62
-rw-r--r--test/functional/plugin/lsp_spec.lua419
-rw-r--r--test/functional/plugin/man_spec.lua26
-rw-r--r--test/functional/provider/clipboard_spec.lua2
-rw-r--r--test/functional/script/luacats_grammar_spec.lua4
-rw-r--r--test/functional/shada/marks_spec.lua10
-rw-r--r--test/functional/shada/shada_spec.lua18
-rw-r--r--test/functional/terminal/altscreen_spec.lua12
-rw-r--r--test/functional/terminal/api_spec.lua8
-rw-r--r--test/functional/terminal/buffer_spec.lua136
-rw-r--r--test/functional/terminal/channel_spec.lua14
-rw-r--r--test/functional/terminal/clipboard_spec.lua2
-rw-r--r--test/functional/terminal/cursor_spec.lua403
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua12
-rw-r--r--test/functional/terminal/highlight_spec.lua105
-rw-r--r--test/functional/terminal/mouse_spec.lua82
-rw-r--r--test/functional/terminal/scrollback_spec.lua44
-rw-r--r--test/functional/terminal/tui_spec.lua375
-rw-r--r--test/functional/terminal/window_spec.lua30
-rw-r--r--test/functional/terminal/window_split_tab_spec.lua10
-rw-r--r--test/functional/testnvim.lua281
-rw-r--r--test/functional/testnvim/exec_lua.lua148
-rw-r--r--test/functional/testterm.lua12
-rw-r--r--test/functional/treesitter/fold_spec.lua76
-rw-r--r--test/functional/treesitter/highlight_spec.lua823
-rw-r--r--test/functional/treesitter/inspect_tree_spec.lua86
-rw-r--r--test/functional/treesitter/language_spec.lua4
-rw-r--r--test/functional/treesitter/node_spec.lua28
-rw-r--r--test/functional/treesitter/parser_spec.lua347
-rw-r--r--test/functional/treesitter/query_spec.lua69
-rw-r--r--test/functional/treesitter/testutil.lua25
-rw-r--r--test/functional/ui/cmdline_spec.lua122
-rw-r--r--test/functional/ui/cursor_spec.lua56
-rw-r--r--test/functional/ui/decorations_spec.lua401
-rw-r--r--test/functional/ui/diff_spec.lua20
-rw-r--r--test/functional/ui/float_spec.lua384
-rw-r--r--test/functional/ui/hlstate_spec.lua10
-rw-r--r--test/functional/ui/inccommand_user_spec.lua44
-rw-r--r--test/functional/ui/input_spec.lua4
-rw-r--r--test/functional/ui/linematch_spec.lua28
-rw-r--r--test/functional/ui/messages_spec.lua798
-rw-r--r--test/functional/ui/mode_spec.lua40
-rw-r--r--test/functional/ui/multigrid_spec.lua2
-rw-r--r--test/functional/ui/output_spec.lua4
-rw-r--r--test/functional/ui/popupmenu_spec.lua604
-rw-r--r--test/functional/ui/screen.lua58
-rw-r--r--test/functional/ui/screen_basic_spec.lua59
-rw-r--r--test/functional/ui/sign_spec.lua202
-rw-r--r--test/functional/ui/statuscolumn_spec.lua177
-rw-r--r--test/functional/ui/statusline_spec.lua505
-rw-r--r--test/functional/ui/syntax_conceal_spec.lua33
-rw-r--r--test/functional/ui/title_spec.lua57
-rw-r--r--test/functional/vimscript/ctx_functions_spec.lua7
-rw-r--r--test/functional/vimscript/getchar_spec.lua95
-rw-r--r--test/functional/vimscript/null_spec.lua2
-rw-r--r--test/functional/vimscript/timer_spec.lua65
-rw-r--r--test/old/testdir/gen_opt_test.vim39
-rw-r--r--test/old/testdir/runnvim.vim3
-rw-r--r--test/old/testdir/runtest.vim4
-rw-r--r--test/old/testdir/setup.vim3
-rw-r--r--test/old/testdir/test_autocmd.vim28
-rw-r--r--test/old/testdir/test_bufwintabinfo.vim12
-rw-r--r--test/old/testdir/test_cmdline.vim33
-rw-r--r--test/old/testdir/test_compiler.vim637
-rw-r--r--test/old/testdir/test_debugger.vim5
-rw-r--r--test/old/testdir/test_diffmode.vim479
-rw-r--r--test/old/testdir/test_digraph.vim10
-rw-r--r--test/old/testdir/test_filetype.vim151
-rw-r--r--test/old/testdir/test_functions.vim138
-rw-r--r--test/old/testdir/test_highlight.vim4
-rw-r--r--test/old/testdir/test_indent.vim76
-rw-r--r--test/old/testdir/test_ins_complete.vim308
-rw-r--r--test/old/testdir/test_let.vim18
-rw-r--r--test/old/testdir/test_matchfuzzy.vim62
-rw-r--r--test/old/testdir/test_messages.vim47
-rw-r--r--test/old/testdir/test_normal.vim21
-rw-r--r--test/old/testdir/test_options.vim59
-rw-r--r--test/old/testdir/test_perl.vim5
-rw-r--r--test/old/testdir/test_popup.vim230
-rw-r--r--test/old/testdir/test_preview.vim59
-rw-r--r--test/old/testdir/test_python3.vim5
-rw-r--r--test/old/testdir/test_pyx3.vim5
-rw-r--r--test/old/testdir/test_ruby.vim11
-rw-r--r--test/old/testdir/test_shift.vim807
-rw-r--r--test/old/testdir/test_spell.vim12
-rw-r--r--test/old/testdir/test_stacktrace.vim142
-rw-r--r--test/old/testdir/test_statusline.vim4
-rw-r--r--test/old/testdir/test_tagjump.vim4
-rw-r--r--test/old/testdir/test_termdebug.vim6
-rw-r--r--test/old/testdir/test_user_func.vim74
-rw-r--r--test/old/testdir/test_visual.vim72
-rw-r--r--test/old/testdir/test_window_cmd.vim51
-rw-r--r--test/old/testdir/test_winfixbuf.vim18
-rw-r--r--test/testutil.lua35
-rw-r--r--test/unit/fixtures/multiqueue.c5
-rw-r--r--test/unit/fixtures/multiqueue.h4
-rw-r--r--test/unit/fixtures/posix.h6
-rw-r--r--test/unit/fixtures/vterm_test.c789
-rw-r--r--test/unit/fixtures/vterm_test.h45
-rw-r--r--test/unit/mbyte_spec.lua6
-rw-r--r--test/unit/optionstr_spec.lua4
-rw-r--r--test/unit/strings_spec.lua26
-rw-r--r--test/unit/vterm_spec.lua392
739 files changed, 55780 insertions, 41195 deletions
diff --git a/.editorconfig b/.editorconfig
index 07993e25b9..0d54d6cb33 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,6 +13,6 @@ max_line_length = 100
[*.py]
indent_size = 4
-[{Makefile,**/Makefile,runtime/doc/*.txt}]
+[{Makefile,**/Makefile,*.mk,runtime/doc/*.txt}]
indent_style = tab
indent_size = 8
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index ce0dbd40de..8cbd3d4ef5 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,6 +1,6 @@
name: Bug Report
description: Report a problem in Nvim
-labels: [bug]
+type: 'bug'
body:
- type: markdown
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 49bd8158bf..f7cf5980b3 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,6 +1,6 @@
name: Feature request
description: Request an enhancement for Nvim
-labels: [enhancement]
+type: 'enhancement'
body:
- type: markdown
diff --git a/.github/ISSUE_TEMPLATE/lsp_bug_report.yml b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
index 277fabca5e..a186a1ee98 100644
--- a/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml
@@ -1,7 +1,8 @@
name: Language server (LSP) client bug
description: Report an issue with Nvim LSP
title: "LSP: "
-labels: [bug, lsp]
+type: bug
+labels: [lsp]
body:
- type: markdown
diff --git a/.github/scripts/install_deps.sh b/.github/scripts/install_deps.sh
index 2aec8ea553..5e2c88c7af 100755
--- a/.github/scripts/install_deps.sh
+++ b/.github/scripts/install_deps.sh
@@ -30,7 +30,7 @@ if [[ $os == Linux ]]; then
fi
if [[ -n $TEST ]]; then
- sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb inotify-tools
+ sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb inotify-tools xdg-utils
# Use default CC to avoid compilation problems when installing Python modules
CC=cc python3 -m pip -q install --user --upgrade --break-system-packages pynvim
diff --git a/.github/scripts/reviewers_add.js b/.github/scripts/reviewers_add.js
index 08b4e85b74..73e1ece516 100644
--- a/.github/scripts/reviewers_add.js
+++ b/.github/scripts/reviewers_add.js
@@ -57,7 +57,6 @@ module.exports = async ({ github, context }) => {
if (labels.includes("lsp")) {
reviewers.add("MariaSolOs");
- reviewers.add("mfussenegger");
}
if (labels.includes("netrw")) {
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ab313729b9..3211216c85 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -89,7 +89,9 @@ jobs:
for d in *; do (cd "$d"; rm -rf ./autom4te.cache; make clean || true; make distclean || true); done
- name: Re-build bundled dependencies with no network access
- run: unshare --map-root-user --net make deps DEPS_CMAKE_FLAGS=-DUSE_EXISTING_SRC_DIR=ON
+ run: |
+ sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0
+ unshare --map-root-user --net make deps DEPS_CMAKE_FLAGS=-DUSE_EXISTING_SRC_DIR=ON
- name: Build
run: make CMAKE_FLAGS="-D CI_BUILD=ON"
diff --git a/.github/workflows/news.yml b/.github/workflows/news.yml
index 8d21b86e8e..e2d9a058c3 100644
--- a/.github/workflows/news.yml
+++ b/.github/workflows/news.yml
@@ -19,15 +19,15 @@ jobs:
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 ||
+ if [[ "$type" == "feat" ]] || [[ "$type" == "perf" ]] || [[ "$breaking" == "breaking-change" ]]; then
+ ! git diff HEAD~${{ github.event.pull_request.commits }}..HEAD --quiet runtime/doc/news.txt runtime/doc/deprecated.txt ||
{
echo "
- Pull request includes a new feature or a breaking change, but
- news.txt hasn't been updated yet. This is just a reminder
- that news.txt may need to be updated. You can ignore this CI
- failure if you think the change won't be of interest to
- users."
+ Pull request includes a new feature, performance improvement
+ or a breaking change, but news.txt hasn't been updated yet.
+ This is just a reminder that news.txt may need to be updated.
+ You can ignore this CI failure if you think the change won't
+ be of interest to users."
exit 1
}
fi
diff --git a/.github/workflows/notes.md b/.github/workflows/notes.md
index 25f4a5fb32..092bab2720 100644
--- a/.github/workflows/notes.md
+++ b/.github/workflows/notes.md
@@ -34,38 +34,46 @@ Note: On Windows "Server" you may need to [install vcruntime140.dll](https://lea
3. Extract: `tar xzvf nvim-macos-arm64.tar.gz`
4. Run `./nvim-macos-arm64/bin/nvim`
-### Linux (x64)
+### Linux (x86_64)
glibc 2.31 or newer is required. Or you may try the (unsupported) [builds for older glibc](https://github.com/neovim/neovim-releases).
#### AppImage
-1. Download **nvim.appimage**
-2. Run `chmod u+x nvim.appimage && ./nvim.appimage`
+1. Download **nvim-linux-x86_64.appimage**
+2. Run `chmod u+x nvim-linux-x86_64.appimage && ./nvim-linux-x86_64.appimage`
- If your system does not have FUSE you can [extract the appimage](https://github.com/AppImage/AppImageKit/wiki/FUSE#type-2-appimage):
```
- ./nvim.appimage --appimage-extract
+ ./nvim-linux-x86_64.appimage --appimage-extract
./squashfs-root/usr/bin/nvim
```
#### Tarball
-1. Download **nvim-linux64.tar.gz**
-2. Extract: `tar xzvf nvim-linux64.tar.gz`
-3. Run `./nvim-linux64/bin/nvim`
+1. Download **nvim-linux-x86_64.tar.gz**
+2. Extract: `tar xzvf nvim-linux-x86_64.tar.gz`
+3. Run `./nvim-linux-x86_64/bin/nvim`
+
+### Linux (arm64)
+
+#### AppImage
+
+1. Download **nvim-linux-arm64.appimage**
+2. Run `chmod u+x nvim-linux-arm64.appimage && ./nvim-linux-arm64.appimage`
+ - If your system does not have FUSE you can [extract the appimage](https://github.com/AppImage/AppImageKit/wiki/FUSE#type-2-appimage):
+ ```
+ ./nvim-linux-arm64.appimage --appimage-extract
+ ./squashfs-root/usr/bin/nvim
+ ```
+
+#### Tarball
+
+1. Download **nvim-linux-arm64.tar.gz**
+2. Extract: `tar xzvf nvim-linux-arm64.tar.gz`
+3. Run `./nvim-linux-arm64/bin/nvim`
### Other
- Install by [package manager](https://github.com/neovim/neovim/blob/master/INSTALL.md#install-from-package)
## SHA256 Checksums
-
-```
-${SHA_LINUX_64_TAR}
-${SHA_APP_IMAGE}
-${SHA_APP_IMAGE_ZSYNC}
-${SHA_MACOS_X86_64}
-${SHA_MACOS_ARM64}
-${SHA_WIN_64_ZIP}
-${SHA_WIN_64_MSI}
-```
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index de90a077ff..9f26e667f7 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,10 +39,21 @@ jobs:
printf "appimage_tag=${APPIMAGE_TAG}\n" >> $GITHUB_OUTPUT
linux:
- runs-on: ubuntu-20.04
needs: setup
+ strategy:
+ fail-fast: false
+ matrix:
+ runner: [ ubuntu-20.04, ubuntu-24.04-arm ]
+ include:
+ - runner: ubuntu-20.04
+ arch: x86_64
+ cc: gcc-10
+ - runner: ubuntu-24.04-arm
+ arch: arm64
+ runs-on: ${{ matrix.runner }}
env:
- CC: gcc-10
+ CC: ${{ matrix.cc }}
+ LDAI_NO_APPSTREAM: 1 # skip checking (broken) AppStream metadata for issues
outputs:
version: ${{ steps.build.outputs.version }}
steps:
@@ -52,22 +63,25 @@ jobs:
fetch-depth: 0
- run: ./.github/scripts/install_deps.sh
- run: echo "CMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}" >> $GITHUB_ENV
+ - if: matrix.arch == 'arm64'
+ run: sudo apt-get update && sudo apt-get install -y libfuse2t64
- name: appimage
- run: ./scripts/genappimage.sh ${{ needs.setup.outputs.appimage_tag }}
+ run: |
+ ./scripts/genappimage.sh ${{ needs.setup.outputs.appimage_tag }}
- name: tar.gz
run: cpack --config build/CPackConfig.cmake -G TGZ
- uses: actions/upload-artifact@v4
with:
- name: appimage
+ name: nvim-appimage-${{ matrix.arch }}
path: |
- build/bin/nvim.appimage
- build/bin/nvim.appimage.zsync
+ build/bin/nvim-linux-${{ matrix.arch }}.appimage
+ build/bin/nvim-linux-${{ matrix.arch }}.appimage.zsync
retention-days: 1
- uses: actions/upload-artifact@v4
with:
- name: nvim-linux64
+ name: nvim-linux-${{ matrix.arch }}
path: |
- build/nvim-linux64.tar.gz
+ build/nvim-linux-${{ matrix.arch }}.tar.gz
retention-days: 1
- name: Export version
id: build
@@ -75,7 +89,6 @@ jobs:
printf 'version<<END\n' >> $GITHUB_OUTPUT
./build/bin/nvim --version | head -n 3 >> $GITHUB_OUTPUT
printf 'END\n' >> $GITHUB_OUTPUT
-
macos:
needs: setup
strategy:
@@ -104,7 +117,6 @@ jobs:
-D CMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} \
-D CMAKE_FIND_FRAMEWORK=NEVER
cmake --build .deps
-
- name: Build neovim
run: |
cmake -B build -G Ninja \
@@ -112,7 +124,6 @@ jobs:
-D ENABLE_LIBINTL=OFF \
-D CMAKE_FIND_FRAMEWORK=NEVER
cmake --build build
-
- name: Package
run: cpack --config build/CPackConfig.cmake
@@ -187,45 +198,23 @@ jobs:
git push origin :stable || true
# `sha256sum` outputs <sha> <path>, so we cd into each dir to drop the
# containing folder from the output.
- - name: Generate Linux64 SHA256 checksums
- run: |
- cd ./nvim-linux64
- sha256sum nvim-linux64.tar.gz > nvim-linux64.tar.gz.sha256sum
- echo "SHA_LINUX_64_TAR=$(cat nvim-linux64.tar.gz.sha256sum)" >> $GITHUB_ENV
- - name: Generate App Image SHA256 checksums
- run: |
- cd ./appimage
- sha256sum nvim.appimage > nvim.appimage.sha256sum
- echo "SHA_APP_IMAGE=$(cat nvim.appimage.sha256sum)" >> $GITHUB_ENV
- - name: Generate App Image Zsync SHA256 checksums
- run: |
- cd ./appimage
- sha256sum nvim.appimage.zsync > nvim.appimage.zsync.sha256sum
- echo "SHA_APP_IMAGE_ZSYNC=$(cat nvim.appimage.zsync.sha256sum)" >> $GITHUB_ENV
- - name: Generate macos x86_64 SHA256 checksums
- run: |
- cd ./nvim-macos-x86_64
- sha256sum nvim-macos-x86_64.tar.gz > nvim-macos-x86_64.tar.gz.sha256sum
- echo "SHA_MACOS_X86_64=$(cat nvim-macos-x86_64.tar.gz.sha256sum)" >> $GITHUB_ENV
- - name: Generate macos arm64 SHA256 checksums
- run: |
- cd ./nvim-macos-arm64
- sha256sum nvim-macos-arm64.tar.gz > nvim-macos-arm64.tar.gz.sha256sum
- echo "SHA_MACOS_ARM64=$(cat nvim-macos-arm64.tar.gz.sha256sum)" >> $GITHUB_ENV
- - name: Generate Win64 SHA256 checksums
- run: |
- cd ./nvim-win64
- sha256sum nvim-win64.zip > nvim-win64.zip.sha256sum
- echo "SHA_WIN_64_ZIP=$(cat nvim-win64.zip.sha256sum)" >> $GITHUB_ENV
- sha256sum nvim-win64.msi > nvim-win64.msi.sha256sum
- echo "SHA_WIN_64_MSI=$(cat nvim-win64.msi.sha256sum)" >> $GITHUB_ENV
+ - run: |
+ for i in nvim-*; do
+ (
+ cd $i || exit
+ sha256sum * >> $GITHUB_WORKSPACE/shasum.txt
+ )
+ done
- name: Publish release
env:
NVIM_VERSION: ${{ needs.linux.outputs.version }}
DEBUG: api
run: |
envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md"
+ echo '```' >> "$RUNNER_TEMP/notes.md"
+ cat shasum.txt >> "$RUNNER_TEMP/notes.md"
+ echo '```' >> "$RUNNER_TEMP/notes.md"
if [ "$TAG_NAME" != "nightly" ]; then
- gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux64/* appimage/* nvim-win64/*
+ gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/* shasum.txt
fi
- gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux64/* appimage/* nvim-win64/*
+ gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux-x86_64/* nvim-linux-arm64/* nvim-appimage-x86_64/* nvim-appimage-arm64/* nvim-win64/* shasum.txt
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 0885efddd5..a366dea082 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -106,7 +106,8 @@ jobs:
[
{ runner: ubuntu-24.04, os: ubuntu, flavor: asan, cc: clang, flags: -D ENABLE_ASAN_UBSAN=ON },
{ runner: ubuntu-24.04, os: ubuntu, flavor: tsan, cc: clang, flags: -D ENABLE_TSAN=ON },
- { runner: ubuntu-24.04, os: ubuntu, cc: gcc },
+ { runner: ubuntu-24.04, os: ubuntu, flavor: release, cc: gcc, flags: -D CMAKE_BUILD_TYPE=Release },
+ { runner: ubuntu-24.04-arm, os: ubuntu, flavor: arm, cc: gcc, flags: -D CMAKE_BUILD_TYPE=RelWithDebInfo },
{ runner: macos-13, os: macos, flavor: intel, cc: clang, flags: -D CMAKE_FIND_FRAMEWORK=NEVER, deps_flags: -D CMAKE_FIND_FRAMEWORK=NEVER },
{ runner: macos-15, os: macos, flavor: arm, cc: clang, flags: -D CMAKE_FIND_FRAMEWORK=NEVER, deps_flags: -D CMAKE_FIND_FRAMEWORK=NEVER },
{ runner: ubuntu-24.04, os: ubuntu, flavor: puc-lua, cc: gcc, deps_flags: -D USE_BUNDLED_LUAJIT=OFF -D USE_BUNDLED_LUA=ON, flags: -D PREFER_LUA=ON },
@@ -145,6 +146,10 @@ jobs:
sudo cpanm -n Neovim::Ext || cat "$HOME/.cpanm/build.log"
perl -W -e 'use Neovim::Ext; print $Neovim::Ext::VERSION'
+ - name: Remove .git directory
+ if: ${{ matrix.build.os == 'ubuntu' }}
+ run: cmake -E rm -rf -- .git
+
- name: Build third-party deps
run: |
cmake -S cmake.deps --preset ci -D CMAKE_BUILD_TYPE=Debug ${{ matrix.build.deps_flags }}
@@ -155,9 +160,15 @@ jobs:
cmake --preset ci -D CMAKE_BUILD_TYPE=Debug -D CMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX ${{ matrix.build.flags }}
cmake --build build
- - name: ${{ matrix.test }}
+ - if: ${{ matrix.test == 'oldtest' }}
+ name: ${{ matrix.test }}
+ timeout-minutes: 20
+ run: make -C test/old/testdir NVIM_PRG=$(realpath build)/bin/nvim
+
+ - if: ${{ matrix.test != 'oldtest' }}
+ name: ${{ matrix.test }}
timeout-minutes: 20
- run: make ${{ matrix.test }}
+ run: cmake --build build --target ${{ matrix.test }}
- name: Install
run: |
@@ -193,40 +204,6 @@ jobs:
windows:
uses: ./.github/workflows/test_windows.yml
- # This job tests the following things:
- # - Check if Release, MinSizeRel and RelWithDebInfo compiles correctly.
- # - Test the above build types with the GCC compiler specifically.
- # Empirically the difference in warning levels between GCC and other
- # compilers is particularly big.
- # - Test if the build works with multi-config generators. We mostly use
- # single-config generators so it's nice to have a small sanity check for
- # multi-config.
- build-types:
- runs-on: ubuntu-24.04
- timeout-minutes: 10
- env:
- CC: gcc
- steps:
- - uses: actions/checkout@v4
- - uses: ./.github/actions/setup
-
- - name: Build third-party deps
- run: |
- cmake -S cmake.deps -B .deps -G "Ninja Multi-Config"
- cmake --build .deps
-
- - name: Configure
- run: cmake --preset ci -G "Ninja Multi-Config"
-
- - name: Release
- run: cmake --build build --config Release
-
- - name: RelWithDebInfo
- run: cmake --build build --config RelWithDebInfo
-
- - name: MinSizeRel
- run: cmake --build build --config MinSizeRel
-
with-external-deps:
runs-on: ubuntu-24.04
timeout-minutes: 10
diff --git a/.github/workflows/vim_patches.yml b/.github/workflows/vim_patches.yml
index b0be01089f..05af39f19c 100644
--- a/.github/workflows/vim_patches.yml
+++ b/.github/workflows/vim_patches.yml
@@ -28,10 +28,10 @@ jobs:
- run: sudo apt-get install libfuse2
- run: |
- gh release download -R neovim/neovim -p nvim.appimage
- chmod a+x nvim.appimage
+ gh release download -R neovim/neovim -p nvim-linux-x86_64.appimage
+ chmod a+x nvim-linux-x86_64.appimage
mkdir -p $HOME/.local/bin
- mv nvim.appimage $HOME/.local/bin/nvim
+ mv nvim-linux-x86_64.appimage $HOME/.local/bin/nvim
printf '%s\n' "$HOME/.local/bin" >> $GITHUB_PATH
- name: Set up git config
@@ -43,7 +43,7 @@ jobs:
id: update-version
run: |
git checkout -b ${VERSION_BRANCH}
- nvim -V1 -es -i NONE +'luafile scripts/vimpatch.lua' +q
+ nvim -l scripts/vimpatch.lua
printf 'NEW_PATCHES=%s\n' $([ -z "$(git diff)" ]; echo $?) >> $GITHUB_OUTPUT
- name: Automatic PR
diff --git a/.luarc.json b/.luarc.json
index 2bd57d6973..1a3cd5b378 100644
--- a/.luarc.json
+++ b/.luarc.json
@@ -10,7 +10,8 @@
"${3rd}/luv/library"
],
"ignoreDir": [
- "test"
+ "test",
+ "_vim9script.lua"
],
"checkThirdParty": "Disable"
},
diff --git a/BUILD.md b/BUILD.md
index e5b1dcc707..cfa26a4569 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -5,14 +5,15 @@
1. Install [build prerequisites](#build-prerequisites) on your system
2. `git clone https://github.com/neovim/neovim`
-3. `cd neovim && make CMAKE_BUILD_TYPE=RelWithDebInfo`
+3. `cd neovim`
- If you want the **stable release**, also run `git checkout stable`.
+4. `make CMAKE_BUILD_TYPE=RelWithDebInfo`
- If you want to install to a custom location, set `CMAKE_INSTALL_PREFIX`. See also [INSTALL.md](./INSTALL.md#install-from-source).
- On BSD, use `gmake` instead of `make`.
- To build on Windows, see the [Building on Windows](#building-on-windows) section. _MSVC (Visual Studio) is recommended._
-4. `sudo make install`
+5. `sudo make install`
- Default install location is `/usr/local`
- - On Debian/Ubuntu, instead of `sudo make install`, you can try `cd build && cpack -G DEB && sudo dpkg -i nvim-linux64.deb` to build DEB-package and install it. This helps ensure clean removal of installed files. Note: This is an unsupported, "best-effort" feature of the Nvim build.
+ - On Debian/Ubuntu, instead of `sudo make install`, you can try `cd build && cpack -G DEB && sudo dpkg -i nvim-linux-<arch>.deb` (with `<arch>` either `x86_64` or `arm64`) to build DEB-package and install it. This helps ensure clean removal of installed files. Note: This is an unsupported, "best-effort" feature of the Nvim build.
**Notes**:
- From the repository's root directory, running `make` will download and build all the needed dependencies and put the `nvim` executable in `build/bin`.
@@ -131,12 +132,13 @@ https://github.com/cascent/neovim-cygwin was built on Cygwin 2.9.0. Newer `libuv
1. From the MSYS2 shell, install these packages:
```
pacman -S \
- mingw-w64-x86_64-{gcc,cmake,make,ninja,diffutils}
+ mingw-w64-ucrt-x86_64-gcc \
+ mingw-w64-x86_64-{cmake,make,ninja,diffutils}
```
2. From the Windows Command Prompt (`cmd.exe`), set up the `PATH` and build.
```cmd
- set PATH=c:\msys64\mingw64\bin;c:\msys64\usr\bin;%PATH%
+ set PATH=c:\msys64\ucrt64\bin;c:\msys64\usr\bin;%PATH%
```
3. You have two options:
- Build using `cmake` and `Ninja` generator:
@@ -292,13 +294,13 @@ Platform-specific requirements are listed below.
### Ubuntu / Debian
```sh
-sudo apt-get install ninja-build gettext cmake unzip curl build-essential
+sudo apt-get install ninja-build gettext cmake curl build-essential
```
### RHEL / Fedora
```
-sudo dnf -y install ninja-build cmake gcc make unzip gettext curl glibc-gconv-extra
+sudo dnf -y install ninja-build cmake gcc make gettext curl glibc-gconv-extra
```
### openSUSE
@@ -310,13 +312,13 @@ sudo zypper install ninja cmake gcc-c++ gettext-tools curl
### Arch Linux
```
-sudo pacman -S base-devel cmake unzip ninja curl
+sudo pacman -S base-devel cmake ninja curl
```
### Alpine Linux
```
-apk add build-base cmake coreutils curl unzip gettext-tiny-dev
+apk add build-base cmake coreutils curl gettext-tiny-dev
```
### Void Linux
@@ -380,7 +382,7 @@ or a specific SHA1 like `--override-input neovim-src github:neovim/neovim/89dc8f
### FreeBSD
```
-sudo pkg install cmake gmake sha unzip wget gettext curl
+sudo pkg install cmake gmake sha wget gettext curl
```
If you get an error regarding a `sha256sum` mismatch, where the actual SHA-256 hash is `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`, then this is your issue (that's the `sha256sum` of an empty file).
@@ -388,7 +390,7 @@ If you get an error regarding a `sha256sum` mismatch, where the actual SHA-256 h
### OpenBSD
```sh
-doas pkg_add gmake cmake unzip curl gettext-tools
+doas pkg_add gmake cmake curl gettext-tools
```
Build can sometimes fail when using the top level `Makefile`, apparently due to some third-party component (see [#2445-comment](https://github.com/neovim/neovim/issues/2445#issuecomment-108124236)). The following instructions use CMake:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9723de9176..b9ceb05aba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -149,11 +149,6 @@ set(NVIM_API_LEVEL 13) # Bump this after any API/stdlib change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE true)
-# Build-type: RelWithDebInfo
-# /Og means something different in MSVC
-if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
- set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -Og -g")
-endif()
# We _want_ assertions in RelWithDebInfo build-type.
if(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DNDEBUG)
string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
@@ -187,6 +182,7 @@ if(NOT PREFER_LUA)
find_program(LUA_PRG NAMES luajit)
endif()
find_program(LUA_PRG NAMES lua5.1 lua5.2 lua)
+mark_as_advanced(LUA_PRG)
if(NOT LUA_PRG)
message(FATAL_ERROR "Failed to find a Lua 5.1-compatible interpreter")
endif()
@@ -200,6 +196,7 @@ message(STATUS "Using Lua interpreter: ${LUA_PRG}")
if(NOT LUA_GEN_PRG)
set(LUA_GEN_PRG "${LUA_PRG}" CACHE FILEPATH "Path to the lua used for code generation.")
endif()
+mark_as_advanced(LUA_GEN_PRG)
message(STATUS "Using Lua interpreter for code generation: ${LUA_GEN_PRG}")
option(COMPILE_LUA "Pre-compile Lua sources into bytecode (for sources that are included in the binary)" ON)
@@ -219,6 +216,7 @@ if(COMPILE_LUA AND NOT WIN32)
endif()
endif()
endif()
+mark_as_advanced(LUAC_PRG)
if(LUAC_PRG)
message(STATUS "Using Lua compiler: ${LUAC_PRG}")
endif()
@@ -229,7 +227,9 @@ if(CI_LINT)
set(LINT_REQUIRED "REQUIRED")
endif()
find_program(SHELLCHECK_PRG shellcheck ${LINT_REQUIRED})
+mark_as_advanced(SHELLCHECK_PRG)
find_program(STYLUA_PRG stylua ${LINT_REQUIRED})
+mark_as_advanced(STYLUA_PRG)
set(STYLUA_DIRS runtime scripts src test contrib)
@@ -266,7 +266,7 @@ add_custom_target(lintcommit
add_dependencies(lintcommit nvim_bin)
add_custom_target(lint)
-add_dependencies(lint lintc lintlua lintsh lintcommit)
+add_dependencies(lint lintc lintlua lintsh)
# Format
add_glob_target(
diff --git a/CMakePresets.json b/CMakePresets.json
index 1d214d7801..b47f509d5f 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -10,7 +10,7 @@
{
"name": "default",
"displayName": "RelWithDebInfo",
- "description": "Enables optimizations (-Og or -O2) with debug information",
+ "description": "Enables optimizations with debug information",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
},
@@ -19,7 +19,7 @@
{
"name": "debug",
"displayName": "Debug",
- "description": "Disables optimizations (-O0), enables debug information",
+ "description": "No optimizations, enables debug information",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
},
@@ -28,7 +28,7 @@
{
"name": "release",
"displayName": "Release",
- "description": "Same as RelWithDebInfo, but disables debug information",
+ "description": "Optimized for performance, disables debug information",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
},
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f40d4c54b7..324cb3e13a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -83,7 +83,7 @@ a comment.
### Commit messages
Follow the [conventional commits guidelines][conventional_commits] to *make reviews easier* and to make
-the VCS/git logs more valuable. The structure of a commit message is:
+the VCS/git logs more valuable (try `make lintcommit`). The structure of a commit message is:
type(scope): subject
diff --git a/INSTALL.md b/INSTALL.md
index 509213fffc..6c2ec3a273 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -15,9 +15,10 @@ Install from download
Downloads are available on the [Releases](https://github.com/neovim/neovim/releases) page.
* Latest [stable release](https://github.com/neovim/neovim/releases/latest)
- * [macOS x86](https://github.com/neovim/neovim/releases/latest/download/nvim-macos-x86_64.tar.gz)
- * [macOS arm](https://github.com/neovim/neovim/releases/latest/download/nvim-macos-arm64.tar.gz)
- * [Linux](https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz)
+ * [macOS x86_64](https://github.com/neovim/neovim/releases/latest/download/nvim-macos-x86_64.tar.gz)
+ * [macOS arm64](https://github.com/neovim/neovim/releases/latest/download/nvim-macos-arm64.tar.gz)
+ * [Linux x86_64](https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz)
+ * [Linux arm64](https://github.com/neovim/neovim/releases/latest/download/nvim-linux-arm64.tar.gz)
* [Windows](https://github.com/neovim/neovim/releases/latest/download/nvim-win64.msi)
* Latest [development prerelease](https://github.com/neovim/neovim/releases/nightly)
@@ -107,35 +108,35 @@ For arm64:
The [Releases](https://github.com/neovim/neovim/releases) page provides pre-built binaries for Linux systems.
```sh
-curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz
+curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz
sudo rm -rf /opt/nvim
-sudo tar -C /opt -xzf nvim-linux64.tar.gz
+sudo tar -C /opt -xzf nvim-linux-x86_64.tar.gz
```
Then add this to your shell config (`~/.bashrc`, `~/.zshrc`, ...):
- export PATH="$PATH:/opt/nvim-linux64/bin"
+ export PATH="$PATH:/opt/nvim-linux-x86_64/bin"
### AppImage ("universal" Linux package)
-The [Releases](https://github.com/neovim/neovim/releases) page provides an [AppImage](https://appimage.org) that runs on most Linux systems. No installation is needed, just download `nvim.appimage` and run it. (It might not work if your Linux distribution is more than 4 years old.)
+The [Releases](https://github.com/neovim/neovim/releases) page provides an [AppImage](https://appimage.org) that runs on most Linux systems. No installation is needed, just download `nvim-linux-x86_64.appimage` and run it. (It might not work if your Linux distribution is more than 4 years old.) The following instructions assume an `x86_64` architecture; on ARM Linux replace with `arm64`.
- curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim.appimage
- chmod u+x nvim.appimage
- ./nvim.appimage
+ curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage
+ chmod u+x nvim-linux-x86_64.appimage
+ ./nvim-linux-x86_64.appimage
To expose nvim globally:
mkdir -p /opt/nvim
- mv nvim.appimage /opt/nvim/nvim
+ mv nvim-linux-x86_64.appimage /opt/nvim/nvim
And the following line to your shell config (`~/.bashrc`, `~/.zshrc`, ...):
export PATH="$PATH:/opt/nvim/"
-If the `./nvim.appimage` command fails, try:
+If the `./nvim-linux-x86_64.appimage` command fails, try:
```sh
-./nvim.appimage --appimage-extract
+./nvim-linux-x86_64.appimage --appimage-extract
./squashfs-root/AppRun --version
# Optional: exposing nvim globally.
@@ -302,7 +303,7 @@ Neovim nightly and stable are available on the [snap store](https://snapcraft.io
**Stable Builds**
```sh
-sudo snap install --beta nvim --classic
+sudo snap install nvim --classic
```
**Nightly Builds**
diff --git a/MAINTAIN.md b/MAINTAIN.md
index cd3dacb964..eb58664ed2 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -143,11 +143,11 @@ These dependencies are "vendored" (inlined), we must update the sources manually
* `src/mpack/`: [libmpack](https://github.com/libmpack/libmpack)
* send improvements upstream!
+* `src/mpack/lmpack.c`: [libmpack-lua](https://github.com/libmpack/libmpack-lua)
+ * 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/klib/`: [Klib](https://github.com/attractivechaos/klib)
-* `src/vterm/`: [libvterm](https://www.leonerd.org.uk/code/libvterm/),
- [mirror](https://github.com/neovim/libvterm)
* `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.
diff --git a/Makefile b/Makefile
index fe83f302e8..9154cd8782 100644
--- a/Makefile
+++ b/Makefile
@@ -133,6 +133,9 @@ generated-sources benchmark $(FORMAT) $(LINT) $(TEST) doc: | build/.ran-cmake
test: $(TEST)
+# iwyu-fix-includes can be downloaded from
+# https://github.com/include-what-you-use/include-what-you-use/blob/master/fix_includes.py.
+# Create a iwyu-fix-includes shell script in your $PATH that invokes the python script.
iwyu: build/.ran-cmake
$(CMAKE) --preset iwyu
$(CMAKE) --build build > build/iwyu.log
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt
index 1623d0ff57..c85696edef 100644
--- a/cmake.deps/deps.txt
+++ b/cmake.deps/deps.txt
@@ -1,8 +1,8 @@
-LIBUV_URL https://github.com/libuv/libuv/archive/v1.49.2.tar.gz
-LIBUV_SHA256 388ffcf3370d4cf7c4b3a3205504eea06c4be5f9e80d2ab32d19f8235accc1cf
+LIBUV_URL https://github.com/libuv/libuv/archive/v1.50.0.tar.gz
+LIBUV_SHA256 b1ec56444ee3f1e10c8bd3eed16ba47016ed0b94fe42137435aaf2e0bd574579
-LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/fe71d0fb54ceadfb5b5f3b6baf29e486d97f6059.tar.gz
-LUAJIT_SHA256 92325f209b21aaf0a67b099bc73cf9bbac5789a9749bdc3898d4a990abb4f36e
+LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/a4f56a459a588ae768801074b46ba0adcfb49eb1.tar.gz
+LUAJIT_SHA256 b4120332a4191db9c9da2d81f9f11f0d4504fc4cff2dea0f642d3d8f1fcebd0e
LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz
LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
@@ -10,8 +10,8 @@ LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
UNIBILIUM_URL https://github.com/neovim/unibilium/archive/v2.1.2.tar.gz
UNIBILIUM_SHA256 370ecb07fbbc20d91d1b350c55f1c806b06bf86797e164081ccc977fc9b3af7a
-LUV_URL https://github.com/luvit/luv/releases/download/1.48.0-2/luv-1.48.0-2.tar.gz
-LUV_SHA256 2c3a1ddfebb4f6550293a40ee789f7122e97647eede51511f57203de48c03b7a
+LUV_URL https://github.com/luvit/luv/archive/1.50.0-1.tar.gz
+LUV_SHA256 bb4f0570571e40c1d2a7644f6f9c1309a6ccdb19bf4d397e8d7bfd0c6b88e613
LPEG_URL https://github.com/neovim/deps/raw/d495ee6f79e7962a53ad79670cb92488abe0b9b4/opt/lpeg-1.1.0.tar.gz
LPEG_SHA256 4b155d67d2246c1ffa7ad7bc466c1ea899bbc40fef0257cc9c03cecbaed4352a
@@ -35,28 +35,28 @@ GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47f5c
LIBICONV_URL https://github.com/neovim/deps/raw/b9bf36eb31f27e8136d907da38fa23518927737e/opt/libiconv-1.17.tar.gz
LIBICONV_SHA256 8f74213b56238c85a50a5329f77e06198771e70dd9a739779f4c02f65d971313
-UTF8PROC_URL https://github.com/JuliaStrings/utf8proc/archive/3de4596fbe28956855df2ecb3c11c0bbc3535838.tar.gz
-UTF8PROC_SHA256 fb4a16bb659b58afb7f921fcc8928d0b3c1fcab135366c8a4f9ca7de1b1cfada
+UTF8PROC_URL https://github.com/JuliaStrings/utf8proc/archive/v2.10.0.tar.gz
+UTF8PROC_SHA256 6f4f1b639daa6dca9f80bc5db1233e9cbaa31a67790887106160b33ef743f136
-TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.23.0.tar.gz
-TREESITTER_C_SHA256 ee58c925e2e507c23d735aad46bf7fb0af31ca06d6f4f41bc008216d9232b0cb
+TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.23.2.tar.gz
+TREESITTER_C_SHA256 d8b9c1b2ffb6a42caf9bc76e07c52507d4e60b17175ed9beb0e779be8db1200c
TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.2.0.tar.gz
TREESITTER_LUA_SHA256 6c41227cd0a59047b19d31f0031d4d901f08bfd78d6fc7f55c89e5b8374c794e
TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.4.0.tar.gz
TREESITTER_VIM_SHA256 9f856f8b4a10ab43348550fa2d3cb2846ae3d8e60f45887200549c051c66f9d5
TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v3.0.0.tar.gz
TREESITTER_VIMDOC_SHA256 a639bf92bf57bfa1cdc90ca16af27bfaf26a9779064776dd4be34c1ef1453f6c
-TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.4.0.tar.gz
-TREESITTER_QUERY_SHA256 d3a423ab66dc62b2969625e280116678a8a22582b5ff087795222108db2f6a6e
+TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.5.0.tar.gz
+TREESITTER_QUERY_SHA256 e33907fd334350e32e49b3875c36bcf070defe490357632fac9398e6d4540a80
TREESITTER_MARKDOWN_URL https://github.com/tree-sitter-grammars/tree-sitter-markdown/archive/v0.3.2.tar.gz
TREESITTER_MARKDOWN_SHA256 5dac48a6d971eb545aab665d59a18180d21963afc781bbf40f9077c06cb82ae5
-TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/e3c82633389256ccc2c5ab2e509046cbf20453d3.tar.gz
-TREESITTER_SHA256 61a21f5d83cfe256472bfa941123a6941fb45073784ee7ec0bc32fdd52f7a4e4
+TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.25.1.tar.gz
+TREESITTER_SHA256 99a2446075c2edf60e82755c48415d5f6e40f2d9aacb3423c6ca56809b70fe59
-WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v25.0.2.tar.gz
-WASMTIME_SHA256 6d1c17c756b83f29f629963228e5fa208ba9d6578421ba2cd07132b6a120accb
+WASMTIME_URL https://github.com/bytecodealliance/wasmtime/archive/v29.0.1.tar.gz
+WASMTIME_SHA256 b94b6c6fd6aebaf05d4c69c1b12b5dc217b0d42c1a95f435b33af63dddfa5304
UNCRUSTIFY_URL https://github.com/uncrustify/uncrustify/archive/uncrustify-0.80.1.tar.gz
UNCRUSTIFY_SHA256 0e2616ec2f78e12816388c513f7060072ff7942b42f1175eb28b24cb75aaec48
-LUA_DEV_DEPS_URL https://github.com/neovim/deps/raw/5a1f71cceb24990a0b15fd9a472a5f549f019248/opt/lua-dev-deps.tar.gz
-LUA_DEV_DEPS_SHA256 27db2495f5eddc7fc191701ec9b291486853530c6125609d3197d03481e8d5a2
+LUA_DEV_DEPS_URL https://github.com/neovim/deps/raw/06ef2b58b0876f8de1a3f5a710473dcd7afff251/opt/lua-dev-deps.tar.gz
+LUA_DEV_DEPS_SHA256 49f8399e453103064a23c65534f266f3067cda716b6502f016bfafeed5799354
diff --git a/cmake.packaging/CMakeLists.txt b/cmake.packaging/CMakeLists.txt
index 8c158c39dc..dc611e9560 100644
--- a/cmake.packaging/CMakeLists.txt
+++ b/cmake.packaging/CMakeLists.txt
@@ -1,3 +1,7 @@
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
+ set(CMAKE_SYSTEM_PROCESSOR arm64)
+endif()
+
set(CPACK_PACKAGE_NAME "Neovim")
set(CPACK_PACKAGE_VENDOR "neovim.io")
set(CPACK_PACKAGE_FILE_NAME "nvim")
@@ -53,7 +57,7 @@ elseif(APPLE)
set(CPACK_GENERATOR TGZ)
set(CPACK_PACKAGE_ICON ${CMAKE_CURRENT_LIST_DIR}/neovim.icns)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
- set(CPACK_PACKAGE_FILE_NAME "nvim-linux64")
+ set(CPACK_PACKAGE_FILE_NAME "nvim-linux-${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_GENERATOR TGZ DEB)
set(CPACK_DEBIAN_PACKAGE_NAME "Neovim") # required
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Neovim.io") # required
diff --git a/cmake/Deps.cmake b/cmake/Deps.cmake
index 519826654f..5902ca6970 100644
--- a/cmake/Deps.cmake
+++ b/cmake/Deps.cmake
@@ -19,6 +19,7 @@ if(APPLE)
endif()
find_program(CACHE_PRG NAMES ccache sccache)
+mark_as_advanced(CACHE_PRG)
if(CACHE_PRG)
set(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env CCACHE_SLOPPINESS=pch_defines,time_macros ${CACHE_PRG})
list(APPEND DEPS_CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER})
@@ -27,6 +28,7 @@ endif()
# MAKE_PRG
if(UNIX)
find_program(MAKE_PRG NAMES gmake make)
+ mark_as_advanced(MAKE_PRG)
if(NOT MAKE_PRG)
message(FATAL_ERROR "GNU Make is required to build the dependencies.")
else()
diff --git a/contrib/gdb/neovim_gdb.vim b/contrib/gdb/neovim_gdb.vim
index d61e7bc0cc..e0be6cb803 100644
--- a/contrib/gdb/neovim_gdb.vim
+++ b/contrib/gdb/neovim_gdb.vim
@@ -167,13 +167,15 @@ function! s:Spawn(server_cmd, client_cmd, server_addr, reconnect)
if type(a:server_cmd) == type('')
" spawn gdbserver in a vertical split
let server = s:GdbServer.new(gdb)
- vsp | enew | let gdb._server_id = termopen(a:server_cmd, server)
+ server.term = v:true
+ vsp | enew | let gdb._server_id = jobstart(a:server_cmd, server)
let gdb._jump_window = 2
let gdb._server_buf = bufnr('%')
endif
" go to the bottom window and spawn gdb client
wincmd j
- enew | let gdb._client_id = termopen(a:client_cmd, gdb)
+ gdb.term = v:true
+ enew | let gdb._client_id = jobstart(a:client_cmd, gdb)
let gdb._client_buf = bufnr('%')
tnoremap <silent> <f8> <c-\><c-n>:GdbContinue<cr>i
tnoremap <silent> <f10> <c-\><c-n>:GdbNext<cr>i
diff --git a/contrib/local.mk.example b/contrib/local.mk.example
index 718bf9f2a3..f3024a147c 100644
--- a/contrib/local.mk.example
+++ b/contrib/local.mk.example
@@ -14,7 +14,7 @@
#
# - Debug: Disables optimizations (-O0), enables debug information.
#
-# - RelWithDebInfo: Enables optimizations (-Og or -O2) with debug information.
+# - RelWithDebInfo: Enables optimizations (-O2) with debug information.
#
# - MinSizeRel: Enables all -O2 optimization that do not typically
# increase code size, and performs further optimizations
diff --git a/runtime/autoload/gzip.vim b/runtime/autoload/gzip.vim
index 26b1cda034..a6fbe2c336 100644
--- a/runtime/autoload/gzip.vim
+++ b/runtime/autoload/gzip.vim
@@ -1,6 +1,6 @@
" Vim autoload file for editing compressed files.
" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2023 Aug 10
+" Last Change: 2024 Nov 25
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" These functions are used by the gzip plugin.
@@ -148,6 +148,9 @@ fun gzip#read(cmd)
else
let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<")
endif
+ if filereadable(undofile(expand("%")))
+ exe "sil rundo " . fnameescape(undofile(expand("%")))
+ endif
if &verbose >= 8
execute "doau BufReadPost " . fname
else
diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim
deleted file mode 100644
index 1df545278f..0000000000
--- a/runtime/autoload/netrw.vim
+++ /dev/null
@@ -1,11941 +0,0 @@
-" netrw.vim: Handles file transfer and remote directory listing across
-" AUTOLOAD SECTION
-" Maintainer: This runtime file is looking for a new maintainer.
-" Date: May 03, 2023
-" Version: 173a
-" Last Change: {{{1
-" 2023 Nov 21 by Vim Project: ignore wildignore when expanding $COMSPEC (v173a)
-" 2023 Nov 22 by Vim Project: fix handling of very long filename on longlist style (v173a)
-" 2024 Feb 19 by Vim Project: (announce adoption)
-" 2024 Feb 29 by Vim Project: handle symlinks in tree mode correctly
-" 2024 Apr 03 by Vim Project: detect filetypes for remote edited files
-" 2024 May 08 by Vim Project: cleanup legacy Win9X checks
-" 2024 May 09 by Vim Project: remove hard-coded private.ppk
-" 2024 May 10 by Vim Project: recursively delete directories by default
-" 2024 May 13 by Vim Project: prefer scp over pscp
-" 2024 Jun 04 by Vim Project: set bufhidden if buffer changed, nohidden is set and buffer shall be switched (#14915)
-" 2024 Jun 13 by Vim Project: glob() on Windows fails when a directory name contains [] (#14952)
-" 2024 Jun 23 by Vim Project: save ad restore registers when liststyle = WIDELIST (#15077, #15114)
-" 2024 Jul 22 by Vim Project: avoid endless recursion (#15318)
-" 2024 Jul 23 by Vim Project: escape filename before trying to delete it (#15330)
-" 2024 Jul 30 by Vim Project: handle mark-copy to same target directory (#12112)
-" 2024 Aug 02 by Vim Project: honor g:netrw_alt{o,v} for :{S,H,V}explore (#15417)
-" 2024 Aug 15 by Vim Project: style changes, prevent E121 (#15501)
-" 2024 Aug 22 by Vim Project: fix mf-selection highlight (#15551)
-" 2024 Aug 22 by Vim Project: adjust echo output of mx command (#15550)
-" 2024 Sep 15 by Vim Project: more strict confirmation dialog (#15680)
-" 2024 Sep 19 by Vim Project: mf-selection highlight uses wrong pattern (#15700)
-" 2024 Sep 21 by Vim Project: remove extraneous closing bracket (#15718)
-" 2024 Oct 21 by Vim Project: remove netrwFileHandlers (#15895)
-" 2024 Oct 27 by Vim Project: clean up gx mapping (#15721)
-" 2024 Oct 30 by Vim Project: fix filetype detection for remote files (#15961)
-" 2024 Oct 30 by Vim Project: fix x mapping on cygwin (#13687)
-" 2024 Oct 31 by Vim Project: add netrw#Launch() and netrw#Open() (#15962)
-" 2024 Oct 31 by Vim Project: fix E874 when browsing remote dir (#15964)
-" 2024 Nov 07 by Vim Project: use keeppatterns to prevent polluting the search history
-" 2024 Nov 07 by Vim Project: fix a few issues with netrw tree listing (#15996)
-" 2024 Nov 10 by Vim Project: directory symlink not resolved in tree view (#16020)
-" 2024 Nov 14 by Vim Project: small fixes to netrw#BrowseX (#16056)
-" }}}
-" Former Maintainer: Charles E Campbell
-" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
-" Copyright: Copyright (C) 2016 Charles E. Campbell {{{1
-" Permission is hereby granted to use and distribute this code,
-" with or without modifications, provided that this copyright
-" notice is copied with it. Like anything else that's free,
-" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided
-" *as is* and come with no warranty of any kind, either
-" expressed or implied. By using this plugin, you agree that
-" in no event will the copyright holder be liable for any damages
-" resulting from the use of this software.
-"
-" Note: the code here was started in 1999 under a much earlier version of vim. The directory browsing
-" code was written using vim v6, which did not have Lists (Lists were first offered with vim-v7).
-"
-"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore()
-"
-" But be doers of the Word, and not only hearers, deluding your own selves {{{1
-" (James 1:22 RSV)
-" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-" Load Once: {{{1
-if &cp || exists("g:loaded_netrw")
- finish
-endif
-
-" Check that vim has patches that netrw requires.
-" Patches needed for v7.4: 1557, and 213.
-" (netrw will benefit from vim's having patch#656, too)
-let s:needspatches=[1557,213]
-if exists("s:needspatches")
- for ptch in s:needspatches
- if v:version < 704 || (v:version == 704 && !has("patch".ptch))
- if !exists("s:needpatch{ptch}")
- unsilent echomsg "***sorry*** this version of netrw requires vim v7.4 with patch#".ptch
- endif
- let s:needpatch{ptch}= 1
- finish
- endif
- endfor
-endif
-
-let g:loaded_netrw = "v173"
-
-let s:keepcpo= &cpo
-setl cpo&vim
-"DechoFuncName 1
-"DechoRemOn
-"call Decho("doing autoload/netrw.vim version ".g:loaded_netrw,'~'.expand("<slnum>"))
-
-" ======================
-" Netrw Variables: {{{1
-" ======================
-
-" ---------------------------------------------------------------------
-" netrw#ErrorMsg: {{{2
-" 0=note = s:NOTE
-" 1=warning = s:WARNING
-" 2=error = s:ERROR
-" Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number)
-" netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number)
-" (this function can optionally take a list of messages)
-" Dec 2, 2019 : max errnum currently is 106
-fun! netrw#ErrorMsg(level,msg,errnum)
-" call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow)
-
- if a:level < g:netrw_errorlvl
-" call Dret("netrw#ErrorMsg : suppressing level=".a:level." since g:netrw_errorlvl=".g:netrw_errorlvl)
- return
- endif
-
- if a:level == 1
- let level= "**warning** (netrw) "
- elseif a:level == 2
- let level= "**error** (netrw) "
- else
- let level= "**note** (netrw) "
- endif
-" call Decho("level=".level,'~'.expand("<slnum>"))
-
- if g:netrw_use_errorwindow == 2 && exists("*popup_atcursor")
- " use popup window
- if type(a:msg) == 3
- let msg = [level]+a:msg
- else
- let msg= level.a:msg
- endif
- let s:popuperr_id = popup_atcursor(msg,{})
- let s:popuperr_text= ""
- elseif g:netrw_use_errorwindow
- " (default) netrw creates a one-line window to show error/warning
- " messages (reliably displayed)
-
- " record current window number
- let s:winBeforeErr= winnr()
-" call Decho("s:winBeforeErr=".s:winBeforeErr,'~'.expand("<slnum>"))
-
- " getting messages out reliably is just plain difficult!
- " This attempt splits the current window, creating a one line window.
- if bufexists("NetrwMessage") && bufwinnr("NetrwMessage") > 0
-" call Decho("write to NetrwMessage buffer",'~'.expand("<slnum>"))
- exe bufwinnr("NetrwMessage")."wincmd w"
-" call Decho("setl ma noro",'~'.expand("<slnum>"))
- setl ma noro
- if type(a:msg) == 3
- for msg in a:msg
- NetrwKeepj call setline(line("$")+1,level.msg)
- endfor
- else
- NetrwKeepj call setline(line("$")+1,level.a:msg)
- endif
- NetrwKeepj $
- else
-" call Decho("create a NetrwMessage buffer window",'~'.expand("<slnum>"))
- bo 1split
- sil! call s:NetrwEnew()
- sil! NetrwKeepj call s:NetrwOptionsSafe(1)
- setl bt=nofile
- NetrwKeepj file NetrwMessage
-" call Decho("setl ma noro",'~'.expand("<slnum>"))
- setl ma noro
- if type(a:msg) == 3
- for msg in a:msg
- NetrwKeepj call setline(line("$")+1,level.msg)
- endfor
- else
- NetrwKeepj call setline(line("$"),level.a:msg)
- endif
- NetrwKeepj $
- endif
-" call Decho("wrote msg<".level.a:msg."> to NetrwMessage win#".winnr(),'~'.expand("<slnum>"))
- if &fo !~ '[ta]'
- syn clear
- syn match netrwMesgNote "^\*\*note\*\*"
- syn match netrwMesgWarning "^\*\*warning\*\*"
- syn match netrwMesgError "^\*\*error\*\*"
- hi link netrwMesgWarning WarningMsg
- hi link netrwMesgError Error
- endif
-" call Decho("setl noma ro bh=wipe",'~'.expand("<slnum>"))
- setl ro nomod noma bh=wipe
-
- else
- " (optional) netrw will show messages using echomsg. Even if the
- " message doesn't appear, at least it'll be recallable via :messages
-" redraw!
- if a:level == s:WARNING
- echohl WarningMsg
- elseif a:level == s:ERROR
- echohl Error
- endif
-
- if type(a:msg) == 3
- for msg in a:msg
- unsilent echomsg level.msg
- endfor
- else
- unsilent echomsg level.a:msg
- endif
-
-" call Decho("echomsg ***netrw*** ".a:msg,'~'.expand("<slnum>"))
- echohl None
- endif
-
-" call Dret("netrw#ErrorMsg")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwInit: initializes variables if they haven't been defined {{{2
-" Loosely, varname = value.
-fun s:NetrwInit(varname,value)
-" call Decho("varname<".a:varname."> value=".a:value,'~'.expand("<slnum>"))
- if !exists(a:varname)
- if type(a:value) == 0
- exe "let ".a:varname."=".a:value
- elseif type(a:value) == 1 && a:value =~ '^[{[]'
- exe "let ".a:varname."=".a:value
- elseif type(a:value) == 1
- exe "let ".a:varname."="."'".a:value."'"
- else
- exe "let ".a:varname."=".a:value
- endif
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" Netrw Constants: {{{2
-call s:NetrwInit("g:netrw_dirhistcnt",0)
-if !exists("s:LONGLIST")
- call s:NetrwInit("s:THINLIST",0)
- call s:NetrwInit("s:LONGLIST",1)
- call s:NetrwInit("s:WIDELIST",2)
- call s:NetrwInit("s:TREELIST",3)
- call s:NetrwInit("s:MAXLIST" ,4)
-endif
-
-let s:NOTE = 0
-let s:WARNING = 1
-let s:ERROR = 2
-call s:NetrwInit("g:netrw_errorlvl", s:NOTE)
-
-" ---------------------------------------------------------------------
-" Default option values: {{{2
-let g:netrw_localcopycmdopt = ""
-let g:netrw_localcopydircmdopt = ""
-let g:netrw_localmkdiropt = ""
-let g:netrw_localmovecmdopt = ""
-
-" ---------------------------------------------------------------------
-" Default values for netrw's global protocol variables {{{2
-if !exists("g:netrw_use_errorwindow")
- let g:netrw_use_errorwindow = 0
-endif
-
-if !exists("g:netrw_dav_cmd")
- if executable("cadaver")
- let g:netrw_dav_cmd = "cadaver"
- elseif executable("curl")
- let g:netrw_dav_cmd = "curl"
- else
- let g:netrw_dav_cmd = ""
- endif
-endif
-if !exists("g:netrw_fetch_cmd")
- if executable("fetch")
- let g:netrw_fetch_cmd = "fetch -o"
- else
- let g:netrw_fetch_cmd = ""
- endif
-endif
-if !exists("g:netrw_file_cmd")
- if executable("elinks")
- call s:NetrwInit("g:netrw_file_cmd","elinks")
- elseif executable("links")
- call s:NetrwInit("g:netrw_file_cmd","links")
- endif
-endif
-if !exists("g:netrw_ftp_cmd")
- let g:netrw_ftp_cmd = "ftp"
-endif
-let s:netrw_ftp_cmd= g:netrw_ftp_cmd
-if !exists("g:netrw_ftp_options")
- let g:netrw_ftp_options= "-i -n"
-endif
-if !exists("g:netrw_http_cmd")
- if executable("wget")
- let g:netrw_http_cmd = "wget"
- call s:NetrwInit("g:netrw_http_xcmd","-q -O")
- elseif executable("curl")
- let g:netrw_http_cmd = "curl"
- call s:NetrwInit("g:netrw_http_xcmd","-L -o")
- elseif executable("elinks")
- let g:netrw_http_cmd = "elinks"
- call s:NetrwInit("g:netrw_http_xcmd","-source >")
- elseif executable("fetch")
- let g:netrw_http_cmd = "fetch"
- call s:NetrwInit("g:netrw_http_xcmd","-o")
- elseif executable("links")
- let g:netrw_http_cmd = "links"
- call s:NetrwInit("g:netrw_http_xcmd","-http.extra-header ".shellescape("Accept-Encoding: identity", 1)." -source >")
- else
- let g:netrw_http_cmd = ""
- endif
-endif
-call s:NetrwInit("g:netrw_http_put_cmd","curl -T")
-call s:NetrwInit("g:netrw_keepj","keepj")
-call s:NetrwInit("g:netrw_rcp_cmd" , "rcp")
-call s:NetrwInit("g:netrw_rsync_cmd", "rsync")
-call s:NetrwInit("g:netrw_rsync_sep", "/")
-if !exists("g:netrw_scp_cmd")
- if executable("scp")
- call s:NetrwInit("g:netrw_scp_cmd" , "scp -q")
- elseif executable("pscp")
- call s:NetrwInit("g:netrw_scp_cmd", 'pscp -q')
- else
- call s:NetrwInit("g:netrw_scp_cmd" , "scp -q")
- endif
-endif
-call s:NetrwInit("g:netrw_sftp_cmd" , "sftp")
-call s:NetrwInit("g:netrw_ssh_cmd" , "ssh")
-
-if has("win32")
- \ && exists("g:netrw_use_nt_rcp")
- \ && g:netrw_use_nt_rcp
- \ && executable( $SystemRoot .'/system32/rcp.exe')
- let s:netrw_has_nt_rcp = 1
- let s:netrw_rcpmode = '-b'
-else
- let s:netrw_has_nt_rcp = 0
- let s:netrw_rcpmode = ''
-endif
-
-" ---------------------------------------------------------------------
-" Default values for netrw's global variables {{{2
-" Cygwin Detection ------- {{{3
-if !exists("g:netrw_cygwin")
- if has("win32unix") && &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$'
- let g:netrw_cygwin= 1
- else
- let g:netrw_cygwin= 0
- endif
-endif
-" Default values - a-c ---------- {{{3
-call s:NetrwInit("g:netrw_alto" , &sb)
-call s:NetrwInit("g:netrw_altv" , &spr)
-call s:NetrwInit("g:netrw_banner" , 1)
-call s:NetrwInit("g:netrw_browse_split", 0)
-call s:NetrwInit("g:netrw_bufsettings" , "noma nomod nonu nobl nowrap ro nornu")
-call s:NetrwInit("g:netrw_chgwin" , -1)
-call s:NetrwInit("g:netrw_clipboard" , 1)
-call s:NetrwInit("g:netrw_compress" , "gzip")
-call s:NetrwInit("g:netrw_ctags" , "ctags")
-if exists("g:netrw_cursorline") && !exists("g:netrw_cursor")
- call netrw#ErrorMsg(s:NOTE,'g:netrw_cursorline is deprecated; use g:netrw_cursor instead',77)
- let g:netrw_cursor= g:netrw_cursorline
-endif
-call s:NetrwInit("g:netrw_cursor" , 2)
-let s:netrw_usercul = &cursorline
-let s:netrw_usercuc = &cursorcolumn
-"call Decho("(netrw) COMBAK: cuc=".&l:cuc." cul=".&l:cul." initialization of s:netrw_cu[cl]")
-call s:NetrwInit("g:netrw_cygdrive","/cygdrive")
-" Default values - d-g ---------- {{{3
-call s:NetrwInit("s:didstarstar",0)
-call s:NetrwInit("g:netrw_dirhistcnt" , 0)
-call s:NetrwInit("g:netrw_decompress" , '{ ".gz" : "gunzip", ".bz2" : "bunzip2", ".zip" : "unzip", ".tar" : "tar -xf", ".xz" : "unxz" }')
-call s:NetrwInit("g:netrw_dirhistmax" , 10)
-call s:NetrwInit("g:netrw_fastbrowse" , 1)
-call s:NetrwInit("g:netrw_ftp_browse_reject", '^total\s\+\d\+$\|^Trying\s\+\d\+.*$\|^KERBEROS_V\d rejected\|^Security extensions not\|No such file\|: connect to address [0-9a-fA-F:]*: No route to host$')
-if !exists("g:netrw_ftp_list_cmd")
- if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)
- let g:netrw_ftp_list_cmd = "ls -lF"
- let g:netrw_ftp_timelist_cmd = "ls -tlF"
- let g:netrw_ftp_sizelist_cmd = "ls -slF"
- else
- let g:netrw_ftp_list_cmd = "dir"
- let g:netrw_ftp_timelist_cmd = "dir"
- let g:netrw_ftp_sizelist_cmd = "dir"
- endif
-endif
-call s:NetrwInit("g:netrw_ftpmode",'binary')
-" Default values - h-lh ---------- {{{3
-call s:NetrwInit("g:netrw_hide",1)
-if !exists("g:netrw_ignorenetrc")
- if &shell =~ '\c\<\%(cmd\|4nt\)\.exe$'
- let g:netrw_ignorenetrc= 1
- else
- let g:netrw_ignorenetrc= 0
- endif
-endif
-call s:NetrwInit("g:netrw_keepdir",1)
-if !exists("g:netrw_list_cmd")
- if g:netrw_scp_cmd =~ '^pscp' && executable("pscp")
- if exists("g:netrw_list_cmd_options")
- let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME: ".g:netrw_list_cmd_options
- else
- let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME:"
- endif
- elseif executable(g:netrw_ssh_cmd)
- " provide a scp-based default listing command
- if exists("g:netrw_list_cmd_options")
- let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa ".g:netrw_list_cmd_options
- else
- let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa"
- endif
- else
-" call Decho(g:netrw_ssh_cmd." is not executable",'~'.expand("<slnum>"))
- let g:netrw_list_cmd= ""
- endif
-endif
-call s:NetrwInit("g:netrw_list_hide","")
-" Default values - lh-lz ---------- {{{3
-if exists("g:netrw_local_copycmd")
- let g:netrw_localcopycmd= g:netrw_local_copycmd
- call netrw#ErrorMsg(s:NOTE,"g:netrw_local_copycmd is deprecated in favor of g:netrw_localcopycmd",84)
-endif
-if !exists("g:netrw_localcmdshell")
- let g:netrw_localcmdshell= ""
-endif
-if !exists("g:netrw_localcopycmd")
- if has("win32")
- if g:netrw_cygwin
- let g:netrw_localcopycmd= "cp"
- else
- let g:netrw_localcopycmd = expand("$COMSPEC", v:true)
- let g:netrw_localcopycmdopt= " /c copy"
- endif
- elseif has("unix") || has("macunix")
- let g:netrw_localcopycmd= "cp"
- else
- let g:netrw_localcopycmd= ""
- endif
-endif
-if !exists("g:netrw_localcopydircmd")
- if has("win32")
- if g:netrw_cygwin
- let g:netrw_localcopydircmd = "cp"
- let g:netrw_localcopydircmdopt= " -R"
- else
- let g:netrw_localcopydircmd = expand("$COMSPEC", v:true)
- let g:netrw_localcopydircmdopt= " /c xcopy /e /c /h /i /k"
- endif
- elseif has("unix")
- let g:netrw_localcopydircmd = "cp"
- let g:netrw_localcopydircmdopt= " -R"
- elseif has("macunix")
- let g:netrw_localcopydircmd = "cp"
- let g:netrw_localcopydircmdopt= " -R"
- else
- let g:netrw_localcopydircmd= ""
- endif
-endif
-if exists("g:netrw_local_mkdir")
- let g:netrw_localmkdir= g:netrw_local_mkdir
- call netrw#ErrorMsg(s:NOTE,"g:netrw_local_mkdir is deprecated in favor of g:netrw_localmkdir",87)
-endif
-if has("win32")
- if g:netrw_cygwin
- call s:NetrwInit("g:netrw_localmkdir","mkdir")
- else
- let g:netrw_localmkdir = expand("$COMSPEC", v:true)
- let g:netrw_localmkdiropt= " /c mkdir"
- endif
-else
- call s:NetrwInit("g:netrw_localmkdir","mkdir")
-endif
-call s:NetrwInit("g:netrw_remote_mkdir","mkdir")
-if exists("g:netrw_local_movecmd")
- let g:netrw_localmovecmd= g:netrw_local_movecmd
- call netrw#ErrorMsg(s:NOTE,"g:netrw_local_movecmd is deprecated in favor of g:netrw_localmovecmd",88)
-endif
-if !exists("g:netrw_localmovecmd")
- if has("win32")
- if g:netrw_cygwin
- let g:netrw_localmovecmd= "mv"
- else
- let g:netrw_localmovecmd = expand("$COMSPEC", v:true)
- let g:netrw_localmovecmdopt= " /c move"
- endif
- elseif has("unix") || has("macunix")
- let g:netrw_localmovecmd= "mv"
- else
- let g:netrw_localmovecmd= ""
- endif
-endif
-" following serves as an example for how to insert a version&patch specific test
-"if v:version < 704 || (v:version == 704 && !has("patch1107"))
-"endif
-call s:NetrwInit("g:netrw_liststyle" , s:THINLIST)
-" sanity checks
-if g:netrw_liststyle < 0 || g:netrw_liststyle >= s:MAXLIST
- let g:netrw_liststyle= s:THINLIST
-endif
-if g:netrw_liststyle == s:LONGLIST && g:netrw_scp_cmd !~ '^pscp'
- let g:netrw_list_cmd= g:netrw_list_cmd." -l"
-endif
-" Default values - m-r ---------- {{{3
-call s:NetrwInit("g:netrw_markfileesc" , '*./[\~')
-call s:NetrwInit("g:netrw_maxfilenamelen", 32)
-call s:NetrwInit("g:netrw_menu" , 1)
-call s:NetrwInit("g:netrw_mkdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mkdir")
-call s:NetrwInit("g:netrw_mousemaps" , (exists("+mouse") && &mouse =~# '[anh]'))
-call s:NetrwInit("g:netrw_retmap" , 0)
-if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)
- call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME")
-elseif has("win32")
- call s:NetrwInit("g:netrw_chgperm" , "cacls FILENAME /e /p PERM")
-else
- call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME")
-endif
-call s:NetrwInit("g:netrw_preview" , 0)
-call s:NetrwInit("g:netrw_scpport" , "-P")
-call s:NetrwInit("g:netrw_servername" , "NETRWSERVER")
-call s:NetrwInit("g:netrw_sshport" , "-p")
-call s:NetrwInit("g:netrw_rename_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mv")
-call s:NetrwInit("g:netrw_rm_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm")
-call s:NetrwInit("g:netrw_rmdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rmdir")
-call s:NetrwInit("g:netrw_rmf_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm -f ")
-" Default values - q-s ---------- {{{3
-call s:NetrwInit("g:netrw_quickhelp",0)
-let s:QuickHelp= ["-:go up dir D:delete R:rename s:sort-by x:special",
- \ "(create new) %:file d:directory",
- \ "(windows split&open) o:horz v:vert p:preview",
- \ "i:style qf:file info O:obtain r:reverse",
- \ "(marks) mf:mark file mt:set target mm:move mc:copy",
- \ "(bookmarks) mb:make mB:delete qb:list gb:go to",
- \ "(history) qb:list u:go up U:go down",
- \ "(targets) mt:target Tb:use bookmark Th:use history"]
-" g:netrw_sepchr: picking a character that doesn't appear in filenames that can be used to separate priority from filename
-call s:NetrwInit("g:netrw_sepchr" , (&enc == "euc-jp")? "\<Char-0x01>" : "\<Char-0xff>")
-if !exists("g:netrw_keepj") || g:netrw_keepj == "keepj"
- call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil keepj " : "keepj ")
-else
- call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil " : " ")
-endif
-call s:NetrwInit("g:netrw_sort_by" , "name") " alternatives: date , size
-call s:NetrwInit("g:netrw_sort_options" , "")
-call s:NetrwInit("g:netrw_sort_direction", "normal") " alternative: reverse (z y x ...)
-if !exists("g:netrw_sort_sequence")
- if has("unix")
- let g:netrw_sort_sequence= '[\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$'
- else
- let g:netrw_sort_sequence= '[\/]$,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$'
- endif
-endif
-call s:NetrwInit("g:netrw_special_syntax" , 0)
-call s:NetrwInit("g:netrw_ssh_browse_reject", '^total\s\+\d\+$')
-call s:NetrwInit("g:netrw_use_noswf" , 1)
-call s:NetrwInit("g:netrw_sizestyle" ,"b")
-" Default values - t-w ---------- {{{3
-call s:NetrwInit("g:netrw_timefmt","%c")
-if !exists("g:netrw_xstrlen")
- if exists("g:Align_xstrlen")
- let g:netrw_xstrlen= g:Align_xstrlen
- elseif exists("g:drawit_xstrlen")
- let g:netrw_xstrlen= g:drawit_xstrlen
- elseif &enc == "latin1" || !has("multi_byte")
- let g:netrw_xstrlen= 0
- else
- let g:netrw_xstrlen= 1
- endif
-endif
-call s:NetrwInit("g:NetrwTopLvlMenu","Netrw.")
-call s:NetrwInit("g:netrw_winsize",50)
-call s:NetrwInit("g:netrw_wiw",1)
-if g:netrw_winsize > 100|let g:netrw_winsize= 100|endif
-" ---------------------------------------------------------------------
-" Default values for netrw's script variables: {{{2
-call s:NetrwInit("g:netrw_fname_escape",' ?&;%')
-if has("win32")
- call s:NetrwInit("g:netrw_glob_escape",'*?`{[]$')
-else
- call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\')
-endif
-call s:NetrwInit("g:netrw_menu_escape",'.&? \')
-call s:NetrwInit("g:netrw_tmpfile_escape",' &;')
-call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\<C-V>\"")
-if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4')
- let s:treedepthstring= "│ "
-else
- let s:treedepthstring= "| "
-endif
-call s:NetrwInit("s:netrw_posn",'{}')
-
-" BufEnter event ignored by decho when following variable is true
-" Has a side effect that doau BufReadPost doesn't work, so
-" files read by network transfer aren't appropriately highlighted.
-"let g:decho_bufenter = 1 "Decho
-
-" ======================
-" Netrw Initialization: {{{1
-" ======================
-if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on")
-" call Decho("installed beval events",'~'.expand("<slnum>"))
- let &l:bexpr = "netrw#BalloonHelp()"
-" call Decho("&l:bexpr<".&l:bexpr."> buf#".bufnr())
- au FileType netrw setl beval
- au WinLeave * if &ft == "netrw" && exists("s:initbeval")|let &beval= s:initbeval|endif
- au VimEnter * let s:initbeval= &beval
-"else " Decho
-" if v:version < 700 | call Decho("did not install beval events: v:version=".v:version." < 700","~".expand("<slnum>")) | endif
-" if !has("balloon_eval") | call Decho("did not install beval events: does not have balloon_eval","~".expand("<slnum>")) | endif
-" if exists("s:initbeval") | call Decho("did not install beval events: s:initbeval exists","~".expand("<slnum>")) | endif
-" if exists("g:netrw_nobeval") | call Decho("did not install beval events: g:netrw_nobeval exists","~".expand("<slnum>")) | endif
-" if !has("syntax") | call Decho("did not install beval events: does not have syntax highlighting","~".expand("<slnum>")) | endif
-" if exists("g:syntax_on") | call Decho("did not install beval events: g:syntax_on exists","~".expand("<slnum>")) | endif
-endif
-au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif
-
-if g:netrw_keepj =~# "keepj"
- com! -nargs=* NetrwKeepj keepj <args>
-else
- let g:netrw_keepj= ""
- com! -nargs=* NetrwKeepj <args>
-endif
-
-" ==============================
-" Netrw Utility Functions: {{{1
-" ==============================
-
-" ---------------------------------------------------------------------
-" netrw#BalloonHelp: {{{2
-if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_on") && !exists("g:netrw_nobeval")
-" call Decho("loading netrw#BalloonHelp()",'~'.expand("<slnum>"))
- fun! netrw#BalloonHelp()
- if &ft != "netrw"
- return ""
- endif
- if exists("s:popuperr_id") && popup_getpos(s:popuperr_id) != {}
- " popup error window is still showing
- " s:pouperr_id and s:popuperr_text are set up in netrw#ErrorMsg()
- if exists("s:popuperr_text") && s:popuperr_text != "" && v:beval_text != s:popuperr_text
- " text under mouse hasn't changed; only close window when it changes
- call popup_close(s:popuperr_id)
- unlet s:popuperr_text
- else
- let s:popuperr_text= v:beval_text
- endif
- let mesg= ""
- elseif !exists("w:netrw_bannercnt") || v:beval_lnum >= w:netrw_bannercnt || (exists("g:netrw_nobeval") && g:netrw_nobeval)
- let mesg= ""
- elseif v:beval_text == "Netrw" || v:beval_text == "Directory" || v:beval_text == "Listing"
- let mesg = "i: thin-long-wide-tree gh: quick hide/unhide of dot-files qf: quick file info %:open new file"
- elseif getline(v:beval_lnum) =~ '^"\s*/'
- let mesg = "<cr>: edit/enter o: edit/enter in horiz window t: edit/enter in new tab v:edit/enter in vert window"
- elseif v:beval_text == "Sorted" || v:beval_text == "by"
- let mesg = 's: sort by name, time, file size, extension r: reverse sorting order mt: mark target'
- elseif v:beval_text == "Sort" || v:beval_text == "sequence"
- let mesg = "S: edit sorting sequence"
- elseif v:beval_text == "Hiding" || v:beval_text == "Showing"
- let mesg = "a: hiding-showing-all ctrl-h: editing hiding list mh: hide/show by suffix"
- elseif v:beval_text == "Quick" || v:beval_text == "Help"
- let mesg = "Help: press <F1>"
- elseif v:beval_text == "Copy/Move" || v:beval_text == "Tgt"
- let mesg = "mt: mark target mc: copy marked file to target mm: move marked file to target"
- else
- let mesg= ""
- endif
- return mesg
- endfun
-"else " Decho
-" if v:version < 700 |call Decho("did not load netrw#BalloonHelp(): vim version ".v:version." < 700 -","~".expand("<slnum>"))|endif
-" if !has("balloon_eval") |call Decho("did not load netrw#BalloonHelp(): does not have balloon eval","~".expand("<slnum>")) |endif
-" if !has("syntax") |call Decho("did not load netrw#BalloonHelp(): syntax disabled","~".expand("<slnum>")) |endif
-" if !exists("g:syntax_on") |call Decho("did not load netrw#BalloonHelp(): g:syntax_on n/a","~".expand("<slnum>")) |endif
-" if exists("g:netrw_nobeval") |call Decho("did not load netrw#BalloonHelp(): g:netrw_nobeval exists","~".expand("<slnum>")) |endif
-endif
-
-" ------------------------------------------------------------------------
-" netrw#Explore: launch the local browser in the directory of the current file {{{2
-" indx: == -1: Nexplore
-" == -2: Pexplore
-" == +: this is overloaded:
-" * If Nexplore/Pexplore is in use, then this refers to the
-" indx'th item in the w:netrw_explore_list[] of items which
-" matched the */pattern **/pattern *//pattern **//pattern
-" * If Hexplore or Vexplore, then this will override
-" g:netrw_winsize to specify the qty of rows or columns the
-" newly split window should have.
-" dosplit==0: the window will be split iff the current file has been modified and hidden not set
-" dosplit==1: the window will be split before running the local browser
-" style == 0: Explore style == 1: Explore!
-" == 2: Hexplore style == 3: Hexplore!
-" == 4: Vexplore style == 5: Vexplore!
-" == 6: Texplore
-fun! netrw#Explore(indx,dosplit,style,...)
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
- endif
-
- " record current file for Rexplore's benefit
- if &ft != "netrw"
- let w:netrw_rexfile= expand("%:p")
- endif
-
- " record current directory
- let curdir = simplify(b:netrw_curdir)
- let curfiledir = substitute(expand("%:p"),'^\(.*[/\\]\)[^/\\]*$','\1','e')
- if !exists("g:netrw_cygwin") && has("win32")
- let curdir= substitute(curdir,'\','/','g')
- endif
-
- " using completion, directories with spaces in their names (thanks, Bill Gates, for a truly dumb idea)
- " will end up with backslashes here. Solution: strip off backslashes that precede white space and
- " try Explore again.
- if a:0 > 0
- if a:1 =~ "\\\s" && !filereadable(s:NetrwFile(a:1)) && !isdirectory(s:NetrwFile(a:1))
- let a1 = substitute(a:1, '\\\(\s\)', '\1', 'g')
- if a1 != a:1
- call netrw#Explore(a:indx, a:dosplit, a:style, a1)
- return
- endif
- endif
- endif
-
- " save registers
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
-" call Decho("(netrw#Explore) save @* and @+",'~'.expand("<slnum>"))
- sil! let keepregstar = @*
- sil! let keepregplus = @+
- endif
- sil! let keepregslash= @/
-
- " if dosplit
- " -or- file has been modified AND file not hidden when abandoned
- " -or- Texplore used
- if a:dosplit || (&modified && &hidden == 0 && &bufhidden != "hide") || a:style == 6
- call s:SaveWinVars()
- let winsz= g:netrw_winsize
- if a:indx > 0
- let winsz= a:indx
- endif
-
- if a:style == 0 " Explore, Sexplore
- let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz
- if winsz == 0|let winsz= ""|endif
- exe "noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s"
-
- elseif a:style == 1 " Explore!, Sexplore!
- let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz
- if winsz == 0|let winsz= ""|endif
- exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v"
-
- elseif a:style == 2 " Hexplore
- let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz
- if winsz == 0|let winsz= ""|endif
- exe "keepalt noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s"
-
- elseif a:style == 3 " Hexplore!
- let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz
- if winsz == 0|let winsz= ""|endif
- exe "keepalt noswapfile ".(!g:netrw_alto ? "below " : "above ").winsz."wincmd s"
-
- elseif a:style == 4 " Vexplore
- let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz
- if winsz == 0|let winsz= ""|endif
- exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v"
-
- elseif a:style == 5 " Vexplore!
- let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz
- if winsz == 0|let winsz= ""|endif
- exe "keepalt noswapfile ".(!g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v"
-
- elseif a:style == 6 " Texplore
- call s:SaveBufVars()
- exe "keepalt tabnew ".fnameescape(curdir)
- call s:RestoreBufVars()
- endif
- call s:RestoreWinVars()
- endif
- NetrwKeepj norm! 0
-
- if a:0 > 0
- if a:1 =~ '^\~' && (has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin))
- let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),''))
- elseif a:1 == '.'
- let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd())
- if dirname !~ '/$'
- let dirname= dirname."/"
- endif
- elseif a:1 =~ '\$'
- let dirname= simplify(expand(a:1))
- elseif a:1 !~ '^\*\{1,2}/' && a:1 !~ '^\a\{3,}://'
- let dirname= simplify(a:1)
- else
- let dirname= a:1
- endif
- else
- " clear explore
- call s:NetrwClearExplore()
- return
- endif
-
- if dirname =~ '\.\./\=$'
- let dirname= simplify(fnamemodify(dirname,':p:h'))
- elseif dirname =~ '\.\.' || dirname == '.'
- let dirname= simplify(fnamemodify(dirname,':p'))
- endif
-
- if dirname =~ '^\*//'
- " starpat=1: Explore *//pattern (current directory only search for files containing pattern)
- let pattern= substitute(dirname,'^\*//\(.*\)$','\1','')
- let starpat= 1
- if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
-
- elseif dirname =~ '^\*\*//'
- " starpat=2: Explore **//pattern (recursive descent search for files containing pattern)
- let pattern= substitute(dirname,'^\*\*//','','')
- let starpat= 2
-
- elseif dirname =~ '/\*\*/'
- " handle .../**/.../filepat
- let prefixdir= substitute(dirname,'^\(.\{-}\)\*\*.*$','\1','')
- if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && has("win32"))
- let b:netrw_curdir = prefixdir
- else
- let b:netrw_curdir= getcwd().'/'.prefixdir
- endif
- let dirname= substitute(dirname,'^.\{-}\(\*\*/.*\)$','\1','')
- let starpat= 4
-
- elseif dirname =~ '^\*/'
- " case starpat=3: Explore */filepat (search in current directory for filenames matching filepat)
- let starpat= 3
-
- elseif dirname=~ '^\*\*/'
- " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat)
- let starpat= 4
-
- else
- let starpat= 0
- endif
-
- if starpat == 0 && a:indx >= 0
- " [Explore Hexplore Vexplore Sexplore] [dirname]
- if dirname == ""
- let dirname= curfiledir
- endif
- if dirname =~# '^scp://' || dirname =~ '^ftp://'
- call netrw#Nread(2,dirname)
- else
- if dirname == ""
- let dirname= getcwd()
- elseif has("win32") && !g:netrw_cygwin
- " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo',
- " depending on whether backslashes have been converted to forward slashes by earlier code).
- if dirname !~ '^[a-zA-Z]:' && dirname !~ '^\\\\\w\+' && dirname !~ '^//\w\+'
- let dirname= b:netrw_curdir."/".dirname
- endif
- elseif dirname !~ '^/'
- let dirname= b:netrw_curdir."/".dirname
- endif
- call netrw#LocalBrowseCheck(dirname)
- endif
- if exists("w:netrw_bannercnt")
- " done to handle P08-Ingelrest. :Explore will _Always_ go to the line just after the banner.
- " If one wants to return the same place in the netrw window, use :Rex instead.
- exe w:netrw_bannercnt
- endif
-
-
- " starpat=1: Explore *//pattern (current directory only search for files containing pattern)
- " starpat=2: Explore **//pattern (recursive descent search for files containing pattern)
- " starpat=3: Explore */filepat (search in current directory for filenames matching filepat)
- " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat)
- elseif a:indx <= 0
- " Nexplore, Pexplore, Explore: handle starpat
- if !mapcheck("<s-up>","n") && !mapcheck("<s-down>","n") && exists("b:netrw_curdir")
- let s:didstarstar= 1
- nnoremap <buffer> <silent> <s-up> :Pexplore<cr>
- nnoremap <buffer> <silent> <s-down> :Nexplore<cr>
- endif
-
- if has("path_extra")
- if !exists("w:netrw_explore_indx")
- let w:netrw_explore_indx= 0
- endif
-
- let indx = a:indx
-
- if indx == -1
- " Nexplore
- if !exists("w:netrw_explore_list") " sanity check
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40)
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- sil! let @/ = keepregslash
- return
- endif
- let indx= w:netrw_explore_indx
- if indx < 0 | let indx= 0 | endif
- if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif
- let curfile= w:netrw_explore_list[indx]
- while indx < w:netrw_explore_listlen && curfile == w:netrw_explore_list[indx]
- let indx= indx + 1
- endwhile
- if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif
-
- elseif indx == -2
- " Pexplore
- if !exists("w:netrw_explore_list") " sanity check
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41)
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- sil! let @/ = keepregslash
- return
- endif
- let indx= w:netrw_explore_indx
- if indx < 0 | let indx= 0 | endif
- if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif
- let curfile= w:netrw_explore_list[indx]
- while indx >= 0 && curfile == w:netrw_explore_list[indx]
- let indx= indx - 1
- endwhile
- if indx < 0 | let indx= 0 | endif
-
- else
- " Explore -- initialize
- " build list of files to Explore with Nexplore/Pexplore
- NetrwKeepj keepalt call s:NetrwClearExplore()
- let w:netrw_explore_indx= 0
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
- endif
-
- " switch on starpat to build the w:netrw_explore_list of files
- if starpat == 1
- " starpat=1: Explore *//pattern (current directory only search for files containing pattern)
- try
- exe "NetrwKeepj noautocmd vimgrep /".pattern."/gj ".fnameescape(b:netrw_curdir)."/*"
- catch /^Vim\%((\a\+)\)\=:E480/
- keepalt call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pattern.">",76)
- return
- endtry
- let w:netrw_explore_list = s:NetrwExploreListUniq(map(getqflist(),'bufname(v:val.bufnr)'))
- if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
-
- elseif starpat == 2
- " starpat=2: Explore **//pattern (recursive descent search for files containing pattern)
- try
- exe "sil NetrwKeepj noautocmd keepalt vimgrep /".pattern."/gj "."**/*"
- catch /^Vim\%((\a\+)\)\=:E480/
- keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45)
- if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- sil! let @/ = keepregslash
- return
- endtry
- let s:netrw_curdir = b:netrw_curdir
- let w:netrw_explore_list = getqflist()
- let w:netrw_explore_list = s:NetrwExploreListUniq(map(w:netrw_explore_list,'s:netrw_curdir."/".bufname(v:val.bufnr)'))
- if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
-
- elseif starpat == 3
- " starpat=3: Explore */filepat (search in current directory for filenames matching filepat)
- let filepat= substitute(dirname,'^\*/','','')
- let filepat= substitute(filepat,'^[%#<]','\\&','')
- let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".filepat),'\n'))
- if &hls | let keepregslash= s:ExplorePatHls(filepat) | endif
-
- elseif starpat == 4
- " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat)
- let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".dirname),'\n'))
- if &hls | let keepregslash= s:ExplorePatHls(dirname) | endif
- endif " switch on starpat to build w:netrw_explore_list
-
- let w:netrw_explore_listlen = len(w:netrw_explore_list)
-
- if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/')
- keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42)
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- sil! let @/ = keepregslash
- return
- endif
- endif " if indx ... endif
-
- " NetrwStatusLine support - for exploring support
- let w:netrw_explore_indx= indx
-
- " wrap the indx around, but issue a note
- if indx >= w:netrw_explore_listlen || indx < 0
- let indx = (indx < 0)? ( w:netrw_explore_listlen - 1 ) : 0
- let w:netrw_explore_indx= indx
- keepalt NetrwKeepj call netrw#ErrorMsg(s:NOTE,"no more files match Explore pattern",43)
- endif
-
- exe "let dirfile= w:netrw_explore_list[".indx."]"
- let newdir= substitute(dirfile,'/[^/]*$','','e')
-
- call netrw#LocalBrowseCheck(newdir)
- if !exists("w:netrw_liststyle")
- let w:netrw_liststyle= g:netrw_liststyle
- endif
- if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:LONGLIST
- keepalt NetrwKeepj call search('^'.substitute(dirfile,"^.*/","","").'\>',"W")
- else
- keepalt NetrwKeepj call search('\<'.substitute(dirfile,"^.*/","","").'\>',"w")
- endif
- let w:netrw_explore_mtchcnt = indx + 1
- let w:netrw_explore_bufnr = bufnr("%")
- let w:netrw_explore_line = line(".")
- keepalt NetrwKeepj call s:SetupNetrwStatusLine('%f %h%m%r%=%9*%{NetrwStatusLine()}')
-
- else
- if !exists("g:netrw_quiet")
- keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44)
- endif
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- sil! let @/ = keepregslash
- return
- endif
-
- else
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && dirname =~ '/'
- sil! unlet w:netrw_treedict
- sil! unlet w:netrw_treetop
- endif
- let newdir= dirname
- if !exists("b:netrw_curdir")
- NetrwKeepj call netrw#LocalBrowseCheck(getcwd())
- else
- NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,newdir,0))
- endif
- endif
-
- " visual display of **/ **// */ Exploration files
- if exists("w:netrw_explore_indx") && exists("b:netrw_curdir")
- if !exists("s:explore_prvdir") || s:explore_prvdir != b:netrw_curdir
- " only update match list when current directory isn't the same as before
- let s:explore_prvdir = b:netrw_curdir
- let s:explore_match = ""
- let dirlen = strlen(b:netrw_curdir)
- if b:netrw_curdir !~ '/$'
- let dirlen= dirlen + 1
- endif
- let prvfname= ""
- for fname in w:netrw_explore_list
- if fname =~ '^'.b:netrw_curdir
- if s:explore_match == ""
- let s:explore_match= '\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>'
- else
- let s:explore_match= s:explore_match.'\|\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>'
- endif
- elseif fname !~ '^/' && fname != prvfname
- if s:explore_match == ""
- let s:explore_match= '\<'.escape(fname,g:netrw_markfileesc).'\>'
- else
- let s:explore_match= s:explore_match.'\|\<'.escape(fname,g:netrw_markfileesc).'\>'
- endif
- endif
- let prvfname= fname
- endfor
- if has("syntax") && exists("g:syntax_on") && g:syntax_on
- exe "2match netrwMarkFile /".s:explore_match."/"
- endif
- endif
- echo "<s-up>==Pexplore <s-down>==Nexplore"
- else
- 2match none
- if exists("s:explore_match") | unlet s:explore_match | endif
- if exists("s:explore_prvdir") | unlet s:explore_prvdir | endif
- endif
-
- " since Explore may be used to initialize netrw's browser,
- " there's no danger of a late FocusGained event on initialization.
- " Consequently, set s:netrw_events to 2.
- let s:netrw_events= 2
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- sil! let @/ = keepregslash
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Lexplore: toggle Explorer window, keeping it on the left of the current tab {{{2
-" Uses g:netrw_chgwin : specifies the window where Lexplore files are to be opened
-" t:netrw_lexposn : winsaveview() output (used on Lexplore window)
-" t:netrw_lexbufnr: the buffer number of the Lexplore buffer (internal to this function)
-" s:lexplore_win : window number of Lexplore window (serves to indicate which window is a Lexplore window)
-" w:lexplore_buf : buffer number of Lexplore window (serves to indicate which window is a Lexplore window)
-fun! netrw#Lexplore(count,rightside,...)
-" call Dfunc("netrw#Lexplore(count=".a:count." rightside=".a:rightside.",...) a:0=".a:0." ft=".&ft)
- let curwin= winnr()
-
- if a:0 > 0 && a:1 != ""
- " if a netrw window is already on the left-side of the tab
- " and a directory has been specified, explore with that
- " directory.
- let a1 = expand(a:1)
- exe "1wincmd w"
- if &ft == "netrw"
- exe "Explore ".fnameescape(a1)
- exe curwin."wincmd w"
- let s:lexplore_win= curwin
- let w:lexplore_buf= bufnr("%")
- if exists("t:netrw_lexposn")
- unlet t:netrw_lexposn
- endif
- return
- endif
- exe curwin."wincmd w"
- else
- let a1= ""
- endif
-
- if exists("t:netrw_lexbufnr")
- " check if t:netrw_lexbufnr refers to a netrw window
- let lexwinnr = bufwinnr(t:netrw_lexbufnr)
- else
- let lexwinnr= 0
- endif
-
- if lexwinnr > 0
- " close down netrw explorer window
- exe lexwinnr."wincmd w"
- let g:netrw_winsize = -winwidth(0)
- let t:netrw_lexposn = winsaveview()
- close
- if lexwinnr < curwin
- let curwin= curwin - 1
- endif
- if lexwinnr != curwin
- exe curwin."wincmd w"
- endif
- unlet t:netrw_lexbufnr
-
- else
- " open netrw explorer window
- exe "1wincmd w"
- let keep_altv = g:netrw_altv
- let g:netrw_altv = 0
- if a:count != 0
- let netrw_winsize = g:netrw_winsize
- let g:netrw_winsize = a:count
- endif
- let curfile= expand("%")
- exe (a:rightside? "botright" : "topleft")." vertical ".((g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize) . " new"
- if a:0 > 0 && a1 != ""
- call netrw#Explore(0,0,0,a1)
- exe "Explore ".fnameescape(a1)
- elseif curfile =~ '^\a\{3,}://'
- call netrw#Explore(0,0,0,substitute(curfile,'[^/\\]*$','',''))
- else
- call netrw#Explore(0,0,0,".")
- endif
- if a:count != 0
- let g:netrw_winsize = netrw_winsize
- endif
- setlocal winfixwidth
- let g:netrw_altv = keep_altv
- let t:netrw_lexbufnr = bufnr("%")
- " done to prevent build-up of hidden buffers due to quitting and re-invocation of :Lexplore.
- " Since the intended use of :Lexplore is to have an always-present explorer window, the extra
- " effort to prevent mis-use of :Lex is warranted.
- set bh=wipe
- if exists("t:netrw_lexposn")
- call winrestview(t:netrw_lexposn)
- unlet t:netrw_lexposn
- endif
- endif
-
- " set up default window for editing via <cr>
- if exists("g:netrw_chgwin") && g:netrw_chgwin == -1
- if a:rightside
- let g:netrw_chgwin= 1
- else
- let g:netrw_chgwin= 2
- endif
- endif
-
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Clean: remove netrw {{{2
-" supports :NetrwClean -- remove netrw from first directory on runtimepath
-" :NetrwClean! -- remove netrw from all directories on runtimepath
-fun! netrw#Clean(sys)
-" call Dfunc("netrw#Clean(sys=".a:sys.")")
-
- if a:sys
- let choice= confirm("Remove personal and system copies of netrw?","&Yes\n&No")
- else
- let choice= confirm("Remove personal copy of netrw?","&Yes\n&No")
- endif
-" call Decho("choice=".choice,'~'.expand("<slnum>"))
- let diddel= 0
- let diddir= ""
-
- if choice == 1
- for dir in split(&rtp,',')
- if filereadable(dir."/plugin/netrwPlugin.vim")
-" call Decho("removing netrw-related files from ".dir,'~'.expand("<slnum>"))
- if s:NetrwDelete(dir."/plugin/netrwPlugin.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/plugin/netrwPlugin.vim",55) |endif
- if s:NetrwDelete(dir."/autoload/netrwFileHandlers.vim")|call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwFileHandlers.vim",55)|endif
- if s:NetrwDelete(dir."/autoload/netrwSettings.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwSettings.vim",55) |endif
- if s:NetrwDelete(dir."/autoload/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrw.vim",55) |endif
- if s:NetrwDelete(dir."/syntax/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrw.vim",55) |endif
- if s:NetrwDelete(dir."/syntax/netrwlist.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrwlist.vim",55) |endif
- let diddir= dir
- let diddel= diddel + 1
- if !a:sys|break|endif
- endif
- endfor
- endif
-
- echohl WarningMsg
- if diddel == 0
- echomsg "netrw is either not installed or not removable"
- elseif diddel == 1
- echomsg "removed one copy of netrw from <".diddir.">"
- else
- echomsg "removed ".diddel." copies of netrw"
- endif
- echohl None
-
-" call Dret("netrw#Clean")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#MakeTgt: make a target out of the directory name provided {{{2
-fun! netrw#MakeTgt(dname)
-" call Dfunc("netrw#MakeTgt(dname<".a:dname.">)")
- " simplify the target (eg. /abc/def/../ghi -> /abc/ghi)
- let svpos = winsaveview()
-" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- let s:netrwmftgt_islocal= (a:dname !~ '^\a\{3,}://')
-" call Decho("s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>"))
- if s:netrwmftgt_islocal
- let netrwmftgt= simplify(a:dname)
- else
- let netrwmftgt= a:dname
- endif
- if exists("s:netrwmftgt") && netrwmftgt == s:netrwmftgt
- " re-selected target, so just clear it
- unlet s:netrwmftgt s:netrwmftgt_islocal
- else
- let s:netrwmftgt= netrwmftgt
- endif
- if g:netrw_fastbrowse <= 1
- call s:NetrwRefresh((b:netrw_curdir !~ '\a\{3,}://'),b:netrw_curdir)
- endif
-" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))"
- call winrestview(svpos)
-" call Dret("netrw#MakeTgt")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Obtain: {{{2
-" netrw#Obtain(islocal,fname[,tgtdirectory])
-" islocal=0 obtain from remote source
-" =1 obtain from local source
-" fname : a filename or a list of filenames
-" tgtdir : optional place where files are to go (not present, uses getcwd())
-fun! netrw#Obtain(islocal,fname,...)
-" call Dfunc("netrw#Obtain(islocal=".a:islocal." fname<".((type(a:fname) == 1)? a:fname : string(a:fname)).">) a:0=".a:0)
- " NetrwStatusLine support - for obtaining support
-
- if type(a:fname) == 1
- let fnamelist= [ a:fname ]
- elseif type(a:fname) == 3
- let fnamelist= a:fname
- else
- call netrw#ErrorMsg(s:ERROR,"attempting to use NetrwObtain on something not a filename or a list",62)
-" call Dret("netrw#Obtain")
- return
- endif
-" call Decho("fnamelist<".string(fnamelist).">",'~'.expand("<slnum>"))
- if a:0 > 0
- let tgtdir= a:1
- else
- let tgtdir= getcwd()
- endif
-" call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>"))
-
- if exists("b:netrw_islocal") && b:netrw_islocal
- " obtain a file from local b:netrw_curdir to (local) tgtdir
-" call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>"))
- if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir
- let topath= s:ComposePath(tgtdir,"")
- if has("win32")
- " transfer files one at time
-" call Decho("transfer files one at a time",'~'.expand("<slnum>"))
- for fname in fnamelist
-" call Decho("system(".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath).")",'~'.expand("<slnum>"))
- call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".s:ShellEscape(fname)." ".s:ShellEscape(topath))
- if v:shell_error != 0
- call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80)
-" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath))
- return
- endif
- endfor
- else
- " transfer files with one command
-" call Decho("transfer files with one command",'~'.expand("<slnum>"))
- let filelist= join(map(deepcopy(fnamelist),"s:ShellEscape(v:val)"))
-" call Decho("system(".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath).")",'~'.expand("<slnum>"))
- call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".filelist." ".s:ShellEscape(topath))
- if v:shell_error != 0
- call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80)
-" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath))
- return
- endif
- endif
- elseif !exists("b:netrw_curdir")
- call netrw#ErrorMsg(s:ERROR,"local browsing directory doesn't exist!",36)
- else
- call netrw#ErrorMsg(s:WARNING,"local browsing directory and current directory are identical",37)
- endif
-
- else
- " obtain files from remote b:netrw_curdir to local tgtdir
-" call Decho("obtain a file from remote ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>"))
- if type(a:fname) == 1
- call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname)
- endif
- call s:NetrwMethod(b:netrw_curdir)
-
- if b:netrw_method == 4
- " obtain file using scp
-" call Decho("obtain via scp (method#4)",'~'.expand("<slnum>"))
- if exists("g:netrw_port") && g:netrw_port != ""
- let useport= " ".g:netrw_scpport." ".g:netrw_port
- else
- let useport= ""
- endif
- if b:netrw_fname =~ '/'
- let path= substitute(b:netrw_fname,'^\(.*/\).\{-}$','\1','')
- else
- let path= ""
- endif
- let filelist= join(map(deepcopy(fnamelist),'escape(s:ShellEscape(g:netrw_machine.":".path.v:val,1)," ")'))
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".filelist." ".s:ShellEscape(tgtdir,1))
-
- elseif b:netrw_method == 2
- " obtain file using ftp + .netrc
-" call Decho("obtain via ftp+.netrc (method #2)",'~'.expand("<slnum>"))
- call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars()
- let tmpbufnr= bufnr("%")
- setl ff=unix
- if exists("g:netrw_ftpmode") && g:netrw_ftpmode != ""
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("b:netrw_fname") && b:netrw_fname != ""
- call setline(line("$")+1,'cd "'.b:netrw_fname.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
- for fname in fnamelist
- call setline(line("$")+1,'get "'.fname.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endfor
- if exists("g:netrw_port") && g:netrw_port != ""
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
- else
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
- endif
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
- let debugkeep= &debug
- setl debug=msg
- call netrw#ErrorMsg(s:ERROR,getline(1),4)
- let &debug= debugkeep
- endif
-
- elseif b:netrw_method == 3
- " obtain with ftp + machine, id, passwd, and fname (ie. no .netrc)
-" call Decho("obtain via ftp+mipf (method #3)",'~'.expand("<slnum>"))
- call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars()
- let tmpbufnr= bufnr("%")
- setl ff=unix
-
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- else
- NetrwKeepj put ='open '.g:netrw_machine
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_uid") && g:netrw_uid != ""
- if exists("g:netrw_ftp") && g:netrw_ftp == 1
- NetrwKeepj put =g:netrw_uid
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- if exists("s:netrw_passwd") && s:netrw_passwd != ""
- NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
- endif
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- elseif exists("s:netrw_passwd")
- NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
- endif
-
- if exists("g:netrw_ftpmode") && g:netrw_ftpmode != ""
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("b:netrw_fname") && b:netrw_fname != ""
- NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
- for fname in fnamelist
- NetrwKeepj call setline(line("$")+1,'get "'.fname.'"')
- endfor
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
-
- " perform ftp:
- " -i : turns off interactive prompting from ftp
- " -n unix : DON'T use <.netrc>, even though it exists
- " -n win32: quit being obnoxious about password
- " Note: using "_dd to delete to the black hole register; avoids messing up @@
- NetrwKeepj norm! 1G"_dd
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- if getline(1) !~ "^$"
-" call Decho("error<".getline(1).">",'~'.expand("<slnum>"))
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),5)
- endif
- endif
-
- elseif b:netrw_method == 9
- " obtain file using sftp
-" call Decho("obtain via sftp (method #9)",'~'.expand("<slnum>"))
- if a:fname =~ '/'
- let localfile= substitute(a:fname,'^.*/','','')
- else
- let localfile= a:fname
- endif
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1).s:ShellEscape(localfile)." ".s:ShellEscape(tgtdir))
-
- elseif !exists("b:netrw_method") || b:netrw_method < 0
- " probably a badly formed url; protocol not recognized
-" call Dret("netrw#Obtain : unsupported method")
- return
-
- else
- " protocol recognized but not supported for Obtain (yet?)
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"current protocol not supported for obtaining file",97)
- endif
-" call Dret("netrw#Obtain : current protocol not supported for obtaining file")
- return
- endif
-
- " restore status line
- if type(a:fname) == 1 && exists("s:netrw_users_stl")
- NetrwKeepj call s:SetupNetrwStatusLine(s:netrw_users_stl)
- endif
-
- endif
-
- " cleanup
- if exists("tmpbufnr")
- if bufnr("%") != tmpbufnr
- exe tmpbufnr."bw!"
- else
- q!
- endif
- endif
-
-" call Dret("netrw#Obtain")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Nread: save position, call netrw#NetRead(), and restore position {{{2
-fun! netrw#Nread(mode,fname)
-" call Dfunc("netrw#Nread(mode=".a:mode." fname<".a:fname.">)")
- let svpos= winsaveview()
-" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- call netrw#NetRead(a:mode,a:fname)
-" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- call winrestview(svpos)
-
- if exists("w:netrw_liststyle") && w:netrw_liststyle != s:TREELIST
- if exists("w:netrw_bannercnt")
- " start with cursor just after the banner
- exe w:netrw_bannercnt
- endif
- endif
-" call Dret("netrw#Nread")
-endfun
-
-" ------------------------------------------------------------------------
-" s:NetrwOptionsSave: save options prior to setting to "netrw-buffer-standard" form {{{2
-" Options get restored by s:NetrwOptionsRestore()
-"
-" Option handling:
-" * save user's options (s:NetrwOptionsSave)
-" * set netrw-safe options (s:NetrwOptionsSafe)
-" - change an option only when user option != safe option (s:netrwSetSafeSetting)
-" * restore user's options (s:netrwOPtionsRestore)
-" - restore a user option when != safe option (s:NetrwRestoreSetting)
-" vt: (variable type) normally its either "w:" or "s:"
-fun! s:NetrwOptionsSave(vt)
-" call Dfunc("s:NetrwOptionsSave(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%")).">"." winnr($)=".winnr("$")." mod=".&mod." ma=".&ma)
-" call Decho(a:vt."netrw_optionsave".(exists("{a:vt}netrw_optionsave")? ("=".{a:vt}netrw_optionsave) : " doesn't exist"),'~'.expand("<slnum>"))
-" 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." hid=".&hid,'~'.expand("<slnum>"))
-" call Decho("(s:NetrwOptionsSave) lines=".&lines)
-
- if !exists("{a:vt}netrw_optionsave")
- let {a:vt}netrw_optionsave= 1
- else
-" call Dret("s:NetrwOptionsSave : options already saved")
- return
- endif
-" call Decho("prior to save: fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." diff=".&l:diff,'~'.expand("<slnum>"))
-
- " Save current settings and current directory
-" call Decho("saving current settings and current directory",'~'.expand("<slnum>"))
- let s:yykeep = @@
- if exists("&l:acd")|let {a:vt}netrw_acdkeep = &l:acd|endif
- let {a:vt}netrw_aikeep = &l:ai
- let {a:vt}netrw_awkeep = &l:aw
- let {a:vt}netrw_bhkeep = &l:bh
- let {a:vt}netrw_blkeep = &l:bl
- let {a:vt}netrw_btkeep = &l:bt
- let {a:vt}netrw_bombkeep = &l:bomb
- let {a:vt}netrw_cedit = &cedit
- let {a:vt}netrw_cikeep = &l:ci
- let {a:vt}netrw_cinkeep = &l:cin
- let {a:vt}netrw_cinokeep = &l:cino
- let {a:vt}netrw_comkeep = &l:com
- let {a:vt}netrw_cpokeep = &l:cpo
- let {a:vt}netrw_cuckeep = &l:cuc
- let {a:vt}netrw_culkeep = &l:cul
-" call Decho("(s:NetrwOptionsSave) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
- let {a:vt}netrw_diffkeep = &l:diff
- let {a:vt}netrw_fenkeep = &l:fen
- if !exists("g:netrw_ffkeep") || g:netrw_ffkeep
- let {a:vt}netrw_ffkeep = &l:ff
- endif
- let {a:vt}netrw_fokeep = &l:fo " formatoptions
- let {a:vt}netrw_gdkeep = &l:gd " gdefault
- let {a:vt}netrw_gokeep = &go " guioptions
- let {a:vt}netrw_hidkeep = &l:hidden
- let {a:vt}netrw_imkeep = &l:im
- let {a:vt}netrw_iskkeep = &l:isk
- let {a:vt}netrw_lines = &lines
- let {a:vt}netrw_lskeep = &l:ls
- let {a:vt}netrw_makeep = &l:ma
- let {a:vt}netrw_magickeep = &l:magic
- let {a:vt}netrw_modkeep = &l:mod
- let {a:vt}netrw_nukeep = &l:nu
- let {a:vt}netrw_rnukeep = &l:rnu
- let {a:vt}netrw_repkeep = &l:report
- let {a:vt}netrw_rokeep = &l:ro
- let {a:vt}netrw_selkeep = &l:sel
- let {a:vt}netrw_spellkeep = &l:spell
- if !g:netrw_use_noswf
- let {a:vt}netrw_swfkeep = &l:swf
- endif
- let {a:vt}netrw_tskeep = &l:ts
- let {a:vt}netrw_twkeep = &l:tw " textwidth
- let {a:vt}netrw_wigkeep = &l:wig " wildignore
- let {a:vt}netrw_wrapkeep = &l:wrap
- let {a:vt}netrw_writekeep = &l:write
-
- " save a few selected netrw-related variables
-" call Decho("saving a few selected netrw-related variables",'~'.expand("<slnum>"))
- if g:netrw_keepdir
- let {a:vt}netrw_dirkeep = getcwd()
-" call Decho("saving to ".a:vt."netrw_dirkeep<".{a:vt}netrw_dirkeep.">",'~'.expand("<slnum>"))
- endif
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- sil! let {a:vt}netrw_starkeep = @*
- sil! let {a:vt}netrw_pluskeep = @+
- endif
- sil! let {a:vt}netrw_slashkeep= @/
-
-" call Decho("(s:NetrwOptionsSave) 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>"))
-" call Dret("s:NetrwOptionsSave : tab#".tabpagenr()." win#".winnr())
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwOptionsSafe: sets options to help netrw do its job {{{2
-" Use s:NetrwSaveOptions() to save user settings
-" Use s:NetrwOptionsRestore() to restore user settings
-fun! s:NetrwOptionsSafe(islocal)
-" call Dfunc("s:NetrwOptionsSafe(islocal=".a:islocal.") win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%"))."> winnr($)=".winnr("$"))
-" call Decho("win#".winnr()."'s ft=".&ft,'~'.expand("<slnum>"))
-" 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,'~'.expand("<slnum>"))
- if exists("+acd") | call s:NetrwSetSafeSetting("&l:acd",0)|endif
- call s:NetrwSetSafeSetting("&l:ai",0)
- call s:NetrwSetSafeSetting("&l:aw",0)
- call s:NetrwSetSafeSetting("&l:bl",0)
- call s:NetrwSetSafeSetting("&l:bomb",0)
- if a:islocal
- call s:NetrwSetSafeSetting("&l:bt","nofile")
- else
- call s:NetrwSetSafeSetting("&l:bt","acwrite")
- endif
- call s:NetrwSetSafeSetting("&l:ci",0)
- call s:NetrwSetSafeSetting("&l:cin",0)
- if g:netrw_fastbrowse > a:islocal
- call s:NetrwSetSafeSetting("&l:bh","hide")
- else
- call s:NetrwSetSafeSetting("&l:bh","delete")
- endif
- call s:NetrwSetSafeSetting("&l:cino","")
- call s:NetrwSetSafeSetting("&l:com","")
- if &cpo =~ 'a' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'a','','g')) | endif
- if &cpo =~ 'A' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'A','','g')) | endif
- setl fo=nroql2
- if &go =~ 'a' | set go-=a | endif
- if &go =~ 'A' | set go-=A | endif
- if &go =~ 'P' | set go-=P | endif
- call s:NetrwSetSafeSetting("&l:hid",0)
- call s:NetrwSetSafeSetting("&l:im",0)
- setl isk+=@ isk+=* isk+=/
- call s:NetrwSetSafeSetting("&l:magic",1)
- if g:netrw_use_noswf
- call s:NetrwSetSafeSetting("swf",0)
- endif
- call s:NetrwSetSafeSetting("&l:report",10000)
- call s:NetrwSetSafeSetting("&l:sel","inclusive")
- call s:NetrwSetSafeSetting("&l:spell",0)
- call s:NetrwSetSafeSetting("&l:tw",0)
- call s:NetrwSetSafeSetting("&l:wig","")
- setl cedit&
-
- " set up cuc and cul based on g:netrw_cursor and listing style
- " COMBAK -- cuc cul related
- call s:NetrwCursor(0)
-
- " allow the user to override safe options
-" call Decho("ft<".&ft."> ei=".&ei,'~'.expand("<slnum>"))
- if &ft == "netrw"
-" call Decho("do any netrw FileType autocmds (doau FileType netrw)",'~'.expand("<slnum>"))
- keepalt NetrwKeepj doau FileType netrw
- endif
-
-" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." bh=".&l:bh." bt<".&bt.">",'~'.expand("<slnum>"))
-" 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,'~'.expand("<slnum>"))
-" call Dret("s:NetrwOptionsSafe")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwOptionsRestore: restore options (based on prior s:NetrwOptionsSave) {{{2
-fun! s:NetrwOptionsRestore(vt)
- if !exists("{a:vt}netrw_optionsave")
- " filereadable() returns zero for remote files (e.g. scp://user@localhost//etc/fstab)
- " Note: @ may not be in 'isfname', so '^\w\+://\f\+/' may not match
- if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+'
- filetype detect
- else
- setl ft=netrw
- endif
- return
- endif
- unlet {a:vt}netrw_optionsave
-
- if exists("+acd")
- if exists("{a:vt}netrw_acdkeep")
- let curdir = getcwd()
- let &l:acd = {a:vt}netrw_acdkeep
- unlet {a:vt}netrw_acdkeep
- if &l:acd
- call s:NetrwLcd(curdir)
- endif
- endif
- endif
- call s:NetrwRestoreSetting(a:vt."netrw_aikeep","&l:ai")
- call s:NetrwRestoreSetting(a:vt."netrw_awkeep","&l:aw")
- call s:NetrwRestoreSetting(a:vt."netrw_blkeep","&l:bl")
- call s:NetrwRestoreSetting(a:vt."netrw_btkeep","&l:bt")
- call s:NetrwRestoreSetting(a:vt."netrw_bombkeep","&l:bomb")
- call s:NetrwRestoreSetting(a:vt."netrw_cedit","&cedit")
- call s:NetrwRestoreSetting(a:vt."netrw_cikeep","&l:ci")
- call s:NetrwRestoreSetting(a:vt."netrw_cinkeep","&l:cin")
- call s:NetrwRestoreSetting(a:vt."netrw_cinokeep","&l:cino")
- call s:NetrwRestoreSetting(a:vt."netrw_comkeep","&l:com")
- call s:NetrwRestoreSetting(a:vt."netrw_cpokeep","&l:cpo")
- call s:NetrwRestoreSetting(a:vt."netrw_diffkeep","&l:diff")
- call s:NetrwRestoreSetting(a:vt."netrw_fenkeep","&l:fen")
- if exists("g:netrw_ffkeep") && g:netrw_ffkeep
- call s:NetrwRestoreSetting(a:vt."netrw_ffkeep")","&l:ff")
- endif
- call s:NetrwRestoreSetting(a:vt."netrw_fokeep" ,"&l:fo")
- call s:NetrwRestoreSetting(a:vt."netrw_gdkeep" ,"&l:gd")
- call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&go")
- call s:NetrwRestoreSetting(a:vt."netrw_hidkeep" ,"&l:hidden")
- call s:NetrwRestoreSetting(a:vt."netrw_imkeep" ,"&l:im")
- call s:NetrwRestoreSetting(a:vt."netrw_iskkeep" ,"&l:isk")
- call s:NetrwRestoreSetting(a:vt."netrw_lines" ,"&lines")
- call s:NetrwRestoreSetting(a:vt."netrw_lskeep" ,"&l:ls")
- call s:NetrwRestoreSetting(a:vt."netrw_makeep" ,"&l:ma")
- call s:NetrwRestoreSetting(a:vt."netrw_magickeep","&l:magic")
- call s:NetrwRestoreSetting(a:vt."netrw_modkeep" ,"&l:mod")
- call s:NetrwRestoreSetting(a:vt."netrw_nukeep" ,"&l:nu")
- call s:NetrwRestoreSetting(a:vt."netrw_rnukeep" ,"&l:rnu")
- call s:NetrwRestoreSetting(a:vt."netrw_repkeep" ,"&l:report")
- call s:NetrwRestoreSetting(a:vt."netrw_rokeep" ,"&l:ro")
- call s:NetrwRestoreSetting(a:vt."netrw_selkeep" ,"&l:sel")
- call s:NetrwRestoreSetting(a:vt."netrw_spellkeep","&l:spell")
- call s:NetrwRestoreSetting(a:vt."netrw_twkeep" ,"&l:tw")
- call s:NetrwRestoreSetting(a:vt."netrw_wigkeep" ,"&l:wig")
- call s:NetrwRestoreSetting(a:vt."netrw_wrapkeep" ,"&l:wrap")
- call s:NetrwRestoreSetting(a:vt."netrw_writekeep","&l:write")
- call s:NetrwRestoreSetting("s:yykeep","@@")
- " former problem: start with liststyle=0; press <i> : result, following line resets l:ts.
- " Fixed; in s:PerformListing, when w:netrw_liststyle is s:LONGLIST, will use a printf to pad filename with spaces
- " rather than by appending a tab which previously was using "&ts" to set the desired spacing. (Sep 28, 2018)
- call s:NetrwRestoreSetting(a:vt."netrw_tskeep","&l:ts")
-
- if exists("{a:vt}netrw_swfkeep")
- if &directory == ""
- " user hasn't specified a swapfile directory;
- " netrw will temporarily set the swapfile directory
- " to the current directory as returned by getcwd().
- let &l:directory= getcwd()
- sil! let &l:swf = {a:vt}netrw_swfkeep
- setl directory=
- unlet {a:vt}netrw_swfkeep
- elseif &l:swf != {a:vt}netrw_swfkeep
- if !g:netrw_use_noswf
- " following line causes a Press ENTER in windows -- can't seem to work around it!!!
- sil! let &l:swf= {a:vt}netrw_swfkeep
- endif
- unlet {a:vt}netrw_swfkeep
- endif
- endif
- if exists("{a:vt}netrw_dirkeep") && isdirectory(s:NetrwFile({a:vt}netrw_dirkeep)) && g:netrw_keepdir
- let dirkeep = substitute({a:vt}netrw_dirkeep,'\\','/','g')
- if exists("{a:vt}netrw_dirkeep")
- call s:NetrwLcd(dirkeep)
- unlet {a:vt}netrw_dirkeep
- endif
- endif
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
- call s:NetrwRestoreSetting(a:vt."netrw_starkeep","@*")
- call s:NetrwRestoreSetting(a:vt."netrw_pluskeep","@+")
- endif
- call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/")
-
- " Moved the filetype detect here from NetrwGetFile() because remote files
- " were having their filetype detect-generated settings overwritten by
- " NetrwOptionRestore.
- if &ft != "netrw"
- filetype detect
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSetSafeSetting: sets an option to a safe setting {{{2
-" but only when the options' value and the safe setting differ
-" Doing this means that netrw will not come up as having changed a
-" setting last when it really didn't actually change it.
-"
-" Called from s:NetrwOptionsSafe
-" ex. call s:NetrwSetSafeSetting("&l:sel","inclusive")
-fun! s:NetrwSetSafeSetting(setting,safesetting)
-" call Dfunc("s:NetrwSetSafeSetting(setting<".a:setting."> safesetting<".a:safesetting.">)")
-
- if a:setting =~ '^&'
-" call Decho("fyi: a:setting starts with &")
- exe "let settingval= ".a:setting
-" call Decho("fyi: settingval<".settingval.">")
-
- if settingval != a:safesetting
-" call Decho("set setting<".a:setting."> to option value<".a:safesetting.">")
- if type(a:safesetting) == 0
- exe "let ".a:setting."=".a:safesetting
- elseif type(a:safesetting) == 1
- exe "let ".a:setting."= '".a:safesetting."'"
- else
- call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:setting." with a safesetting of type#".type(a:safesetting),105)
- endif
- endif
- endif
-
-" call Dret("s:NetrwSetSafeSetting")
-endfun
-
-" ------------------------------------------------------------------------
-" s:NetrwRestoreSetting: restores specified setting using associated keepvar, {{{2
-" but only if the setting value differs from the associated keepvar.
-" Doing this means that netrw will not come up as having changed a
-" setting last when it really didn't actually change it.
-"
-" Used by s:NetrwOptionsRestore() to restore each netrw-sensitive setting
-" keepvars are set up by s:NetrwOptionsSave
-fun! s:NetrwRestoreSetting(keepvar,setting)
-""" call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)")
-
- " typically called from s:NetrwOptionsRestore
- " call s:NetrwRestoreSettings(keep-option-variable-name,'associated-option')
- " ex. call s:NetrwRestoreSetting(a:vt."netrw_selkeep","&l:sel")
- " Restores option (but only if different) from a:keepvar
- if exists(a:keepvar)
- exe "let keepvarval= ".a:keepvar
- exe "let setting= ".a:setting
-
-"" call Decho("fyi: a:keepvar<".a:keepvar."> exists")
-"" call Decho("fyi: keepvarval=".keepvarval)
-"" call Decho("fyi: a:setting<".a:setting."> setting<".setting.">")
-
- if setting != keepvarval
-"" call Decho("restore setting<".a:setting."> (currently=".setting.") to keepvarval<".keepvarval.">")
- if type(a:setting) == 0
- exe "let ".a:setting."= ".keepvarval
- elseif type(a:setting) == 1
- exe "let ".a:setting."= '".substitute(keepvarval,"'","''","g")."'"
- else
- call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:keepvar." with a setting of type#".type(a:setting),105)
- endif
- endif
-
- exe "unlet ".a:keepvar
- endif
-
-"" call Dret("s:NetrwRestoreSetting")
-endfun
-
-" ---------------------------------------------------------------------
-" NetrwStatusLine: {{{2
-fun! NetrwStatusLine()
-
-" vvv NetrwStatusLine() debugging vvv
-" let g:stlmsg=""
-" if !exists("w:netrw_explore_bufnr")
-" let g:stlmsg="!X<explore_bufnr>"
-" elseif w:netrw_explore_bufnr != bufnr("%")
-" let g:stlmsg="explore_bufnr!=".bufnr("%")
-" endif
-" if !exists("w:netrw_explore_line")
-" let g:stlmsg=" !X<explore_line>"
-" elseif w:netrw_explore_line != line(".")
-" let g:stlmsg=" explore_line!={line(.)<".line(".").">"
-" endif
-" if !exists("w:netrw_explore_list")
-" let g:stlmsg=" !X<explore_list>"
-" endif
-" ^^^ NetrwStatusLine() debugging ^^^
-
- if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list")
- " restore user's status line
- let &l:stl = s:netrw_users_stl
- let &laststatus = s:netrw_users_ls
- if exists("w:netrw_explore_bufnr")|unlet w:netrw_explore_bufnr|endif
- if exists("w:netrw_explore_line") |unlet w:netrw_explore_line |endif
- return ""
- else
- return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen
- endif
-endfun
-
-" ===============================
-" Netrw Transfer Functions: {{{1
-" ===============================
-
-" ------------------------------------------------------------------------
-" netrw#NetRead: responsible for reading a file over the net {{{2
-" mode: =0 read remote file and insert before current line
-" =1 read remote file and insert after current line
-" =2 replace with remote file
-" =3 obtain file, but leave in temporary format
-fun! netrw#NetRead(mode,...)
-" call Dfunc("netrw#NetRead(mode=".a:mode.",...) a:0=".a:0." ".g:loaded_netrw.((a:0 > 0)? " a:1<".a:1.">" : ""))
-
- " NetRead: save options {{{3
- call s:NetrwOptionsSave("w:")
- call s:NetrwOptionsSafe(0)
- call s:RestoreCursorline()
- " NetrwSafeOptions sets a buffer up for a netrw listing, which includes buflisting off.
- " However, this setting is not wanted for a remote editing session. The buffer should be "nofile", still.
- setl bl
-" call Decho("buf#".bufnr("%")."<".bufname("%")."> bl=".&bl." bt=".&bt." bh=".&bh,'~'.expand("<slnum>"))
-
- " NetRead: interpret mode into a readcmd {{{3
- if a:mode == 0 " read remote file before current line
- let readcmd = "0r"
- elseif a:mode == 1 " read file after current line
- let readcmd = "r"
- elseif a:mode == 2 " replace with remote file
- let readcmd = "%r"
- elseif a:mode == 3 " skip read of file (leave as temporary)
- let readcmd = "t"
- else
- exe a:mode
- let readcmd = "r"
- endif
- let ichoice = (a:0 == 0)? 0 : 1
-" call Decho("readcmd<".readcmd."> ichoice=".ichoice,'~'.expand("<slnum>"))
-
- " NetRead: get temporary filename {{{3
- let tmpfile= s:GetTempfile("")
- if tmpfile == ""
-" call Dret("netrw#NetRead : unable to get a tempfile!")
- return
- endif
-
- while ichoice <= a:0
-
- " attempt to repeat with previous host-file-etc
- if exists("b:netrw_lastfile") && a:0 == 0
-" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>"))
- let choice = b:netrw_lastfile
- let ichoice= ichoice + 1
-
- else
- exe "let choice= a:" . ichoice
-" call Decho("no lastfile: choice<" . choice . ">",'~'.expand("<slnum>"))
-
- if match(choice,"?") == 0
- " give help
- echomsg 'NetRead Usage:'
- echomsg ':Nread machine:path uses rcp'
- echomsg ':Nread "machine path" uses ftp with <.netrc>'
- echomsg ':Nread "machine id password path" uses ftp'
- echomsg ':Nread dav://machine[:port]/path uses cadaver'
- echomsg ':Nread fetch://machine/path uses fetch'
- echomsg ':Nread ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>'
- echomsg ':Nread http://[user@]machine/path uses http wget'
- echomsg ':Nread file:///path uses elinks'
- echomsg ':Nread https://[user@]machine/path uses http wget'
- echomsg ':Nread rcp://[user@]machine/path uses rcp'
- echomsg ':Nread rsync://machine[:port]/path uses rsync'
- echomsg ':Nread scp://[user@]machine[[:#]port]/path uses scp'
- echomsg ':Nread sftp://[user@]machine[[:#]port]/path uses sftp'
- sleep 4
- break
-
- elseif match(choice,'^"') != -1
- " Reconstruct Choice if choice starts with '"'
-" call Decho("reconstructing choice",'~'.expand("<slnum>"))
- if match(choice,'"$') != -1
- " case "..."
- let choice= strpart(choice,1,strlen(choice)-2)
- else
- " case "... ... ..."
- let choice = strpart(choice,1,strlen(choice)-1)
- let wholechoice = ""
-
- while match(choice,'"$') == -1
- let wholechoice = wholechoice . " " . choice
- let ichoice = ichoice + 1
- if ichoice > a:0
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",3)
- endif
-" call Dret("netrw#NetRead :2 getcwd<".getcwd().">")
- return
- endif
- let choice= a:{ichoice}
- endwhile
- let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1)
- endif
- endif
- endif
-
-" call Decho("choice<" . choice . ">",'~'.expand("<slnum>"))
- let ichoice= ichoice + 1
-
- " NetRead: Determine method of read (ftp, rcp, etc) {{{3
- call s:NetrwMethod(choice)
- if !exists("b:netrw_method") || b:netrw_method < 0
-" call Dret("netrw#NetRead : unsupported method")
- return
- endif
- let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix
-
- " Check whether or not NetrwBrowse() should be handling this request
-" call Decho("checking if NetrwBrowse() should handle choice<".choice."> with netrw_list_cmd<".g:netrw_list_cmd.">",'~'.expand("<slnum>"))
- if choice =~ "^.*[\/]$" && b:netrw_method != 5 && choice !~ '^https\=://'
-" call Decho("yes, choice matches '^.*[\/]$'",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwBrowse(0,choice)
-" call Dret("netrw#NetRead :3 getcwd<".getcwd().">")
- return
- endif
-
- " ============
- " NetRead: Perform Protocol-Based Read {{{3
- " ===========================
- if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1
- echo "(netrw) Processing your read request..."
- endif
-
- ".........................................
- " NetRead: (rcp) NetRead Method #1 {{{3
- if b:netrw_method == 1 " read with rcp
-" call Decho("read via rcp (method #1)",'~'.expand("<slnum>"))
- " ER: nothing done with g:netrw_uid yet?
- " ER: on Win2K" rcp machine[.user]:file tmpfile
- " ER: when machine contains '.' adding .user is required (use $USERNAME)
- " ER: the tmpfile is full path: rcp sees C:\... as host C
- if s:netrw_has_nt_rcp == 1
- if exists("g:netrw_uid") && ( g:netrw_uid != "" )
- let uid_machine = g:netrw_machine .'.'. g:netrw_uid
- else
- " Any way needed it machine contains a '.'
- let uid_machine = g:netrw_machine .'.'. $USERNAME
- endif
- else
- if exists("g:netrw_uid") && ( g:netrw_uid != "" )
- let uid_machine = g:netrw_uid .'@'. g:netrw_machine
- else
- let uid_machine = g:netrw_machine
- endif
- endif
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1))
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (ftp + <.netrc>) NetRead Method #2 {{{3
- elseif b:netrw_method == 2 " read with ftp + <.netrc>
-" call Decho("read via ftp+.netrc (method #2)",'~'.expand("<slnum>"))
- let netrw_fname= b:netrw_fname
- NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars()
- let filtbuf= bufnr("%")
- setl ff=unix
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>"))
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>"))
- endif
- call setline(line("$")+1,'get "'.netrw_fname.'" '.tmpfile)
-" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>"))
- if exists("g:netrw_port") && g:netrw_port != ""
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
- else
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
- endif
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
- let debugkeep = &debug
- setl debug=msg
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),4)
- let &debug = debugkeep
- endif
- call s:SaveBufVars()
- keepj bd!
- if bufname("%") == "" && getline("$") == "" && line('$') == 1
- " needed when one sources a file in a nolbl setting window via ftp
- q!
- endif
- call s:RestoreBufVars()
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (ftp + machine,id,passwd,filename) NetRead Method #3 {{{3
- elseif b:netrw_method == 3 " read with ftp + machine, id, passwd, and fname
- " Construct execution string (four lines) which will be passed through filter
-" call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>"))
- let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape)
- NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars()
- let filtbuf= bufnr("%")
- setl ff=unix
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- else
- NetrwKeepj put ='open '.g:netrw_machine
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_uid") && g:netrw_uid != ""
- if exists("g:netrw_ftp") && g:netrw_ftp == 1
- NetrwKeepj put =g:netrw_uid
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- if exists("s:netrw_passwd")
- NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
- endif
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- elseif exists("s:netrw_passwd")
- NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- endif
-
- if exists("g:netrw_ftpmode") && g:netrw_ftpmode != ""
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- NetrwKeepj put ='get \"'.netrw_fname.'\" '.tmpfile
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
-
- " perform ftp:
- " -i : turns off interactive prompting from ftp
- " -n unix : DON'T use <.netrc>, even though it exists
- " -n win32: quit being obnoxious about password
- NetrwKeepj norm! 1G"_dd
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- if getline(1) !~ "^$"
-" call Decho("error<".getline(1).">",'~'.expand("<slnum>"))
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,getline(1),5)
- endif
- endif
- call s:SaveBufVars()|keepj bd!|call s:RestoreBufVars()
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (scp) NetRead Method #4 {{{3
- elseif b:netrw_method == 4 " read with scp
-" call Decho("read via scp (method #4)",'~'.expand("<slnum>"))
- if exists("g:netrw_port") && g:netrw_port != ""
- let useport= " ".g:netrw_scpport." ".g:netrw_port
- else
- let useport= ""
- endif
- " 'C' in 'C:\path\to\file' is handled as hostname on windows.
- " This is workaround to avoid mis-handle windows local-path:
- if g:netrw_scp_cmd =~ '^scp' && has("win32")
- let tmpfile_get = substitute(tr(tmpfile, '\', '/'), '^\(\a\):[/\\]\(.*\)$', '/\1/\2', '')
- else
- let tmpfile_get = tmpfile
- endif
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".escape(s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1),' ')." ".s:ShellEscape(tmpfile_get,1))
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (http) NetRead Method #5 (wget) {{{3
- elseif b:netrw_method == 5
-" call Decho("read via http (method #5)",'~'.expand("<slnum>"))
- if g:netrw_http_cmd == ""
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,"neither the wget nor the fetch command is available",6)
- endif
-" call Dret("netrw#NetRead :4 getcwd<".getcwd().">")
- return
- endif
-
- if match(b:netrw_fname,"#") == -1 || exists("g:netrw_http_xcmd")
- " using g:netrw_http_cmd (usually elinks, links, curl, wget, or fetch)
-" call Decho('using '.g:netrw_http_cmd.' (# not in b:netrw_fname<'.b:netrw_fname.">)",'~'.expand("<slnum>"))
- if exists("g:netrw_http_xcmd")
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".s:ShellEscape(tmpfile,1))
- else
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1))
- endif
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
-
- else
- " wget/curl/fetch plus a jump to an in-page marker (ie. http://abc/def.html#aMarker)
-" call Decho("wget/curl plus jump (# in b:netrw_fname<".b:netrw_fname.">)",'~'.expand("<slnum>"))
- let netrw_html= substitute(b:netrw_fname,"#.*$","","")
- let netrw_tag = substitute(b:netrw_fname,"^.*#","","")
-" call Decho("netrw_html<".netrw_html.">",'~'.expand("<slnum>"))
-" call Decho("netrw_tag <".netrw_tag.">",'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.netrw_html,1))
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
-" call Decho('<\s*a\s*name=\s*"'.netrw_tag.'"/','~'.expand("<slnum>"))
- exe 'NetrwKeepj norm! 1G/<\s*a\s*name=\s*"'.netrw_tag.'"/'."\<CR>"
- endif
- let b:netrw_lastfile = choice
-" call Decho("setl ro",'~'.expand("<slnum>"))
- setl ro nomod
-
- ".........................................
- " NetRead: (dav) NetRead Method #6 {{{3
- elseif b:netrw_method == 6
-" call Decho("read via cadaver (method #6)",'~'.expand("<slnum>"))
-
- if !executable(g:netrw_dav_cmd)
- call netrw#ErrorMsg(s:ERROR,g:netrw_dav_cmd." is not executable",73)
-" call Dret("netrw#NetRead : ".g:netrw_dav_cmd." not executable")
- return
- endif
- if g:netrw_dav_cmd =~ "curl"
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".s:ShellEscape("dav://".g:netrw_machine.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1))
- else
- " Construct execution string (four lines) which will be passed through filter
- let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape)
- new
- setl ff=unix
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
- else
- NetrwKeepj put ='open '.g:netrw_machine
- endif
- if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != ""
- NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd
- endif
- NetrwKeepj put ='get '.netrw_fname.' '.tmpfile
- NetrwKeepj put ='quit'
-
- " perform cadaver operation:
- NetrwKeepj norm! 1G"_dd
- call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd)
- keepj bd!
- endif
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (rsync) NetRead Method #7 {{{3
- elseif b:netrw_method == 7
-" call Decho("read via rsync (method #7)",'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1))
- let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (fetch) NetRead Method #8 {{{3
- " fetch://[user@]host[:http]/path
- elseif b:netrw_method == 8
-" call Decho("read via fetch (method #8)",'~'.expand("<slnum>"))
- if g:netrw_fetch_cmd == ""
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"fetch command not available",7)
- endif
-" call Dret("NetRead")
- return
- endif
- if exists("g:netrw_option") && g:netrw_option =~ ":https\="
- let netrw_option= "http"
- else
- let netrw_option= "ftp"
- endif
-" call Decho("read via fetch for ".netrw_option,'~'.expand("<slnum>"))
-
- if exists("g:netrw_uid") && g:netrw_uid != "" && exists("s:netrw_passwd") && s:netrw_passwd != ""
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1))
- else
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1))
- endif
-
- let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-" call Decho("setl ro",'~'.expand("<slnum>"))
- setl ro nomod
-
- ".........................................
- " NetRead: (sftp) NetRead Method #9 {{{3
- elseif b:netrw_method == 9
-" call Decho("read via sftp (method #9)",'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile)
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: (file) NetRead Method #10 {{{3
- elseif b:netrw_method == 10 && exists("g:netrw_file_cmd")
-" " call Decho("read via ".b:netrw_file_cmd." (method #10)",'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_file_cmd." ".s:ShellEscape(b:netrw_fname,1)." ".tmpfile)
- let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetRead: Complain {{{3
- else
- call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",8)
- endif
- endwhile
-
- " NetRead: cleanup {{{3
- if exists("b:netrw_method")
-" call Decho("cleanup b:netrw_method and b:netrw_fname",'~'.expand("<slnum>"))
- unlet b:netrw_method
- unlet b:netrw_fname
- endif
- if s:FileReadable(tmpfile) && tmpfile !~ '.tar.bz2$' && tmpfile !~ '.tar.gz$' && tmpfile !~ '.zip' && tmpfile !~ '.tar' && readcmd != 't' && tmpfile !~ '.tar.xz$' && tmpfile !~ '.txz'
-" call Decho("cleanup by deleting tmpfile<".tmpfile.">",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwDelete(tmpfile)
- endif
- NetrwKeepj call s:NetrwOptionsRestore("w:")
-
-" call Dret("netrw#NetRead :5 getcwd<".getcwd().">")
-endfun
-
-" ------------------------------------------------------------------------
-" netrw#NetWrite: responsible for writing a file over the net {{{2
-fun! netrw#NetWrite(...) range
-" call Dfunc("netrw#NetWrite(a:0=".a:0.") ".g:loaded_netrw)
-
- " NetWrite: option handling {{{3
- let mod= 0
- call s:NetrwOptionsSave("w:")
- call s:NetrwOptionsSafe(0)
-
- " NetWrite: Get Temporary Filename {{{3
- let tmpfile= s:GetTempfile("")
- if tmpfile == ""
-" call Dret("netrw#NetWrite : unable to get a tempfile!")
- return
- endif
-
- if a:0 == 0
- let ichoice = 0
- else
- let ichoice = 1
- endif
-
- let curbufname= expand("%")
-" call Decho("curbufname<".curbufname.">",'~'.expand("<slnum>"))
- if &binary
- " For binary writes, always write entire file.
- " (line numbers don't really make sense for that).
- " Also supports the writing of tar and zip files.
-" call Decho("(write entire file) sil exe w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>"))
- exe "sil NetrwKeepj w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile)
- elseif g:netrw_cygwin
- " write (selected portion of) file to temporary
- let cygtmpfile= substitute(tmpfile,g:netrw_cygdrive.'/\(.\)','\1:','')
-" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile),'~'.expand("<slnum>"))
- exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile)
- else
- " write (selected portion of) file to temporary
-" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>"))
- exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile)
- endif
-
- if curbufname == ""
- " when the file is [No Name], and one attempts to Nwrite it, the buffer takes
- " on the temporary file's name. Deletion of the temporary file during
- " cleanup then causes an error message.
- 0file!
- endif
-
- " NetWrite: while choice loop: {{{3
- while ichoice <= a:0
-
- " Process arguments: {{{4
- " attempt to repeat with previous host-file-etc
- if exists("b:netrw_lastfile") && a:0 == 0
-" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>"))
- let choice = b:netrw_lastfile
- let ichoice= ichoice + 1
- else
- exe "let choice= a:" . ichoice
-
- " Reconstruct Choice when choice starts with '"'
- if match(choice,"?") == 0
- echomsg 'NetWrite Usage:"'
- echomsg ':Nwrite machine:path uses rcp'
- echomsg ':Nwrite "machine path" uses ftp with <.netrc>'
- echomsg ':Nwrite "machine id password path" uses ftp'
- echomsg ':Nwrite dav://[user@]machine/path uses cadaver'
- echomsg ':Nwrite fetch://[user@]machine/path uses fetch'
- echomsg ':Nwrite ftp://machine[#port]/path uses ftp (autodetects <.netrc>)'
- echomsg ':Nwrite rcp://machine/path uses rcp'
- echomsg ':Nwrite rsync://[user@]machine/path uses rsync'
- echomsg ':Nwrite scp://[user@]machine[[:#]port]/path uses scp'
- echomsg ':Nwrite sftp://[user@]machine/path uses sftp'
- sleep 4
- break
-
- elseif match(choice,"^\"") != -1
- if match(choice,"\"$") != -1
- " case "..."
- let choice=strpart(choice,1,strlen(choice)-2)
- else
- " case "... ... ..."
- let choice = strpart(choice,1,strlen(choice)-1)
- let wholechoice = ""
-
- while match(choice,"\"$") == -1
- let wholechoice= wholechoice . " " . choice
- let ichoice = ichoice + 1
- if choice > a:0
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",13)
- endif
-" call Dret("netrw#NetWrite")
- return
- endif
- let choice= a:{ichoice}
- endwhile
- let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1)
- endif
- endif
- endif
- let ichoice= ichoice + 1
-" call Decho("choice<" . choice . "> ichoice=".ichoice,'~'.expand("<slnum>"))
-
- " Determine method of write (ftp, rcp, etc) {{{4
- NetrwKeepj call s:NetrwMethod(choice)
- if !exists("b:netrw_method") || b:netrw_method < 0
-" call Dfunc("netrw#NetWrite : unsupported method")
- return
- endif
-
- " =============
- " NetWrite: Perform Protocol-Based Write {{{3
- " ============================
- if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1
- echo "(netrw) Processing your write request..."
-" call Decho("Processing your write request...",'~'.expand("<slnum>"))
- endif
-
- ".........................................
- " NetWrite: (rcp) NetWrite Method #1 {{{3
- if b:netrw_method == 1
-" call Decho("write via rcp (method #1)",'~'.expand("<slnum>"))
- if s:netrw_has_nt_rcp == 1
- if exists("g:netrw_uid") && ( g:netrw_uid != "" )
- let uid_machine = g:netrw_machine .'.'. g:netrw_uid
- else
- let uid_machine = g:netrw_machine .'.'. $USERNAME
- endif
- else
- if exists("g:netrw_uid") && ( g:netrw_uid != "" )
- let uid_machine = g:netrw_uid .'@'. g:netrw_machine
- else
- let uid_machine = g:netrw_machine
- endif
- endif
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1))
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetWrite: (ftp + <.netrc>) NetWrite Method #2 {{{3
- elseif b:netrw_method == 2
-" call Decho("write via ftp+.netrc (method #2)",'~'.expand("<slnum>"))
- let netrw_fname = b:netrw_fname
-
- " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
- let bhkeep = &l:bh
- let curbuf = bufnr("%")
- setl bh=hide
- keepj keepalt enew
-
-" call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
- setl ff=unix
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>"))
- endif
- NetrwKeepj call setline(line("$")+1,'put "'.tmpfile.'" "'.netrw_fname.'"')
-" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>"))
- if exists("g:netrw_port") && g:netrw_port != ""
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
- else
-" call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
- endif
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- if getline(1) !~ "^$"
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),14)
- endif
- let mod=1
- endif
-
- " remove enew buffer (quietly)
- let filtbuf= bufnr("%")
- exe curbuf."b!"
- let &l:bh = bhkeep
- exe filtbuf."bw!"
-
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetWrite: (ftp + machine, id, passwd, filename) NetWrite Method #3 {{{3
- elseif b:netrw_method == 3
- " Construct execution string (three or more lines) which will be passed through filter
-" call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>"))
- let netrw_fname = b:netrw_fname
- let bhkeep = &l:bh
-
- " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
- let curbuf = bufnr("%")
- setl bh=hide
- keepj keepalt enew
- setl ff=unix
-
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- else
- NetrwKeepj put ='open '.g:netrw_machine
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- if exists("g:netrw_uid") && g:netrw_uid != ""
- if exists("g:netrw_ftp") && g:netrw_ftp == 1
- NetrwKeepj put =g:netrw_uid
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- if exists("s:netrw_passwd") && s:netrw_passwd != ""
- NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
- endif
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- elseif exists("s:netrw_passwd") && s:netrw_passwd != ""
- NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- endif
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>"))
- endif
- NetrwKeepj put ='put \"'.tmpfile.'\" \"'.netrw_fname.'\"'
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- " save choice/id/password for future use
- let b:netrw_lastfile = choice
-
- " perform ftp:
- " -i : turns off interactive prompting from ftp
- " -n unix : DON'T use <.netrc>, even though it exists
- " -n win32: quit being obnoxious about password
- NetrwKeepj norm! 1G"_dd
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- if getline(1) !~ "^$"
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,getline(1),15)
- endif
- let mod=1
- endif
-
- " remove enew buffer (quietly)
- let filtbuf= bufnr("%")
- exe curbuf."b!"
- let &l:bh= bhkeep
- exe filtbuf."bw!"
-
- ".........................................
- " NetWrite: (scp) NetWrite Method #4 {{{3
- elseif b:netrw_method == 4
-" call Decho("write via scp (method #4)",'~'.expand("<slnum>"))
- if exists("g:netrw_port") && g:netrw_port != ""
- let useport= " ".g:netrw_scpport." ".fnameescape(g:netrw_port)
- else
- let useport= ""
- endif
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1))
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetWrite: (http) NetWrite Method #5 {{{3
- elseif b:netrw_method == 5
-" call Decho("write via http (method #5)",'~'.expand("<slnum>"))
- let curl= substitute(g:netrw_http_put_cmd,'\s\+.*$',"","")
- if executable(curl)
- let url= g:netrw_choice
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(url,1) )
- elseif !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd.">",16)
- endif
-
- ".........................................
- " NetWrite: (dav) NetWrite Method #6 (cadaver) {{{3
- elseif b:netrw_method == 6
-" call Decho("write via cadaver (method #6)",'~'.expand("<slnum>"))
-
- " Construct execution string (four lines) which will be passed through filter
- let netrw_fname = escape(b:netrw_fname,g:netrw_fname_escape)
- let bhkeep = &l:bh
-
- " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
- let curbuf = bufnr("%")
- setl bh=hide
- keepj keepalt enew
-
- setl ff=unix
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
- else
- NetrwKeepj put ='open '.g:netrw_machine
- endif
- if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != ""
- NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd
- endif
- NetrwKeepj put ='put '.tmpfile.' '.netrw_fname
-
- " perform cadaver operation:
- NetrwKeepj norm! 1G"_dd
- call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd)
-
- " remove enew buffer (quietly)
- let filtbuf= bufnr("%")
- exe curbuf."b!"
- let &l:bh = bhkeep
- exe filtbuf."bw!"
-
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetWrite: (rsync) NetWrite Method #7 {{{3
- elseif b:netrw_method == 7
-" call Decho("write via rsync (method #7)",'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1))
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetWrite: (sftp) NetWrite Method #9 {{{3
- elseif b:netrw_method == 9
-" call Decho("write via sftp (method #9)",'~'.expand("<slnum>"))
- let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape)
- if exists("g:netrw_uid") && ( g:netrw_uid != "" )
- let uid_machine = g:netrw_uid .'@'. g:netrw_machine
- else
- let uid_machine = g:netrw_machine
- endif
-
- " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
- let bhkeep = &l:bh
- let curbuf = bufnr("%")
- setl bh=hide
- keepj keepalt enew
-
- setl ff=unix
- call setline(1,'put "'.escape(tmpfile,'\').'" '.netrw_fname)
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- let sftpcmd= substitute(g:netrw_sftp_cmd,"%TEMPFILE%",escape(tmpfile,'\'),"g")
- call s:NetrwExe(s:netrw_silentxfer."%!".sftpcmd.' '.s:ShellEscape(uid_machine,1))
- let filtbuf= bufnr("%")
- exe curbuf."b!"
- let &l:bh = bhkeep
- exe filtbuf."bw!"
- let b:netrw_lastfile = choice
-
- ".........................................
- " NetWrite: Complain {{{3
- else
- call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",17)
- let leavemod= 1
- endif
- endwhile
-
- " NetWrite: Cleanup: {{{3
-" call Decho("cleanup",'~'.expand("<slnum>"))
- if s:FileReadable(tmpfile)
-" call Decho("tmpfile<".tmpfile."> readable, will now delete it",'~'.expand("<slnum>"))
- call s:NetrwDelete(tmpfile)
- endif
- call s:NetrwOptionsRestore("w:")
-
- if a:firstline == 1 && a:lastline == line("$")
- " restore modifiability; usually equivalent to set nomod
- let &l:mod= mod
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- elseif !exists("leavemod")
- " indicate that the buffer has not been modified since last written
-" call Decho("set nomod",'~'.expand("<slnum>"))
- setl nomod
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- endif
-
-" call Dret("netrw#NetWrite")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#NetSource: source a remotely hosted vim script {{{2
-" uses NetRead to get a copy of the file into a temporarily file,
-" then sources that file,
-" then removes that file.
-fun! netrw#NetSource(...)
-" call Dfunc("netrw#NetSource() a:0=".a:0)
- if a:0 > 0 && a:1 == '?'
- " give help
- echomsg 'NetSource Usage:'
- echomsg ':Nsource dav://machine[:port]/path uses cadaver'
- echomsg ':Nsource fetch://machine/path uses fetch'
- echomsg ':Nsource ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>'
- echomsg ':Nsource http[s]://[user@]machine/path uses http wget'
- echomsg ':Nsource rcp://[user@]machine/path uses rcp'
- echomsg ':Nsource rsync://machine[:port]/path uses rsync'
- echomsg ':Nsource scp://[user@]machine[[:#]port]/path uses scp'
- echomsg ':Nsource sftp://[user@]machine[[:#]port]/path uses sftp'
- sleep 4
- else
- let i= 1
- while i <= a:0
- call netrw#NetRead(3,a:{i})
-" call Decho("s:netread_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>"))
- if s:FileReadable(s:netrw_tmpfile)
-" call Decho("exe so ".fnameescape(s:netrw_tmpfile),'~'.expand("<slnum>"))
- exe "so ".fnameescape(s:netrw_tmpfile)
-" call Decho("delete(".s:netrw_tmpfile.")",'~'.expand("<slnum>"))
- if delete(s:netrw_tmpfile)
- call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".s:netrw_tmpfile.">!",103)
- endif
- unlet s:netrw_tmpfile
- else
- call netrw#ErrorMsg(s:ERROR,"unable to source <".a:{i}.">!",48)
- endif
- let i= i + 1
- endwhile
- endif
-" call Dret("netrw#NetSource")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#SetTreetop: resets the tree top to the current directory/specified directory {{{2
-" (implements the :Ntree command)
-fun! netrw#SetTreetop(iscmd,...)
-
- " iscmd==0: netrw#SetTreetop called using gn mapping
- " iscmd==1: netrw#SetTreetop called using :Ntree from the command line
- " clear out the current tree
- if exists("w:netrw_treetop")
- let inittreetop= w:netrw_treetop
- unlet w:netrw_treetop
- endif
- if exists("w:netrw_treedict")
- unlet w:netrw_treedict
- endif
-
- if (a:iscmd == 0 || a:1 == "") && exists("inittreetop")
- let treedir = s:NetrwTreePath(inittreetop)
- else
- if isdirectory(s:NetrwFile(a:1))
- let treedir = a:1
- let s:netrw_treetop = treedir
- elseif exists("b:netrw_curdir") && (isdirectory(s:NetrwFile(b:netrw_curdir."/".a:1)) || a:1 =~ '^\a\{3,}://')
- let treedir = b:netrw_curdir."/".a:1
- let s:netrw_treetop = treedir
- else
- " normally the cursor is left in the message window.
- " However, here this results in the directory being listed in the message window, which is not wanted.
- let netrwbuf= bufnr("%")
- call netrw#ErrorMsg(s:ERROR,"sorry, ".a:1." doesn't seem to be a directory!",95)
- exe bufwinnr(netrwbuf)."wincmd w"
- let treedir = "."
- let s:netrw_treetop = getcwd()
- endif
- endif
-
- " determine if treedir is remote or local
- let islocal= expand("%") !~ '^\a\{3,}://'
-
- " browse the resulting directory
- if islocal
- call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(islocal,treedir,0))
- else
- call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,treedir,0))
- endif
-
-endfun
-
-" ===========================================
-" s:NetrwGetFile: Function to read temporary file "tfile" with command "readcmd". {{{2
-" readcmd == %r : replace buffer with newly read file
-" == 0r : read file at top of buffer
-" == r : read file after current line
-" == t : leave file in temporary form (ie. don't read into buffer)
-fun! s:NetrwGetFile(readcmd, tfile, method)
-" call Dfunc("NetrwGetFile(readcmd<".a:readcmd.">,tfile<".a:tfile."> method<".a:method.">)")
-
- " readcmd=='t': simply do nothing
- if a:readcmd == 't'
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
-" call Dret("NetrwGetFile : skip read of tfile<".a:tfile.">")
- return
- endif
-
- " get name of remote filename (ie. url and all)
- let rfile= bufname("%")
-" call Decho("rfile<".rfile.">",'~'.expand("<slnum>"))
-
- if exists("*NetReadFixup")
- " for the use of NetReadFixup (not otherwise used internally)
- let line2= line("$")
- endif
-
- if a:readcmd[0] == '%'
- " get file into buffer
-" call Decho("get file into buffer",'~'.expand("<slnum>"))
-
- " rename the current buffer to the temp file (ie. tfile)
- if g:netrw_cygwin
- let tfile= substitute(a:tfile,g:netrw_cygdrive.'/\(.\)','\1:','')
- else
- let tfile= a:tfile
- endif
- call s:NetrwBufRename(tfile)
-
- " edit temporary file (ie. read the temporary file in)
- if rfile =~ '\.zip$'
-" call Decho("handling remote zip file with zip#Browse(tfile<".tfile.">)",'~'.expand("<slnum>"))
- call zip#Browse(tfile)
- elseif rfile =~ '\.tar$'
-" call Decho("handling remote tar file with tar#Browse(tfile<".tfile.">)",'~'.expand("<slnum>"))
- call tar#Browse(tfile)
- elseif rfile =~ '\.tar\.gz$'
-" call Decho("handling remote gzip-compressed tar file",'~'.expand("<slnum>"))
- call tar#Browse(tfile)
- elseif rfile =~ '\.tar\.bz2$'
-" call Decho("handling remote bz2-compressed tar file",'~'.expand("<slnum>"))
- call tar#Browse(tfile)
- elseif rfile =~ '\.tar\.xz$'
-" call Decho("handling remote xz-compressed tar file",'~'.expand("<slnum>"))
- call tar#Browse(tfile)
- elseif rfile =~ '\.txz$'
-" call Decho("handling remote xz-compressed tar file (.txz)",'~'.expand("<slnum>"))
- call tar#Browse(tfile)
- else
-" call Decho("edit temporary file",'~'.expand("<slnum>"))
- NetrwKeepj e!
- endif
-
- " rename buffer back to remote filename
- call s:NetrwBufRename(rfile)
-
- " Jan 19, 2022: COMBAK -- bram problem with https://github.com/vim/vim/pull/9554.diff filetype
- " Detect filetype of local version of remote file.
- " Note that isk must not include a "/" for scripts.vim
- " to process this detection correctly.
-" call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>"))
-" call Decho("..did_filetype()=".did_filetype())
-" setl ft=
-" call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">")
- let iskkeep= &isk
- setl isk-=/
- filetype detect
-" call Decho("..local filetype<".&ft."> for buf#".bufnr()."<".bufname().">")
- let &l:isk= iskkeep
-" call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)")
- let line1 = 1
- let line2 = line("$")
-
- elseif !&ma
- " attempting to read a file after the current line in the file, but the buffer is not modifiable
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"attempt to read<".a:tfile."> into a non-modifiable buffer!",94)
-" call Dret("NetrwGetFile : attempt to read<".a:tfile."> into a non-modifiable buffer!")
- return
-
- elseif s:FileReadable(a:tfile)
- " read file after current line
-" call Decho("read file<".a:tfile."> after current line",'~'.expand("<slnum>"))
- let curline = line(".")
- let lastline= line("$")
-" call Decho("exe<".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)."> line#".curline,'~'.expand("<slnum>"))
- exe "NetrwKeepj ".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)
- let line1= curline + 1
- let line2= line("$") - lastline + 1
-
- else
- " not readable
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
-" call Decho("tfile<".a:tfile."> not readable",'~'.expand("<slnum>"))
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"file <".a:tfile."> not readable",9)
-" call Dret("NetrwGetFile : tfile<".a:tfile."> not readable")
- return
- endif
-
- " User-provided (ie. optional) fix-it-up command
- if exists("*NetReadFixup")
-" call Decho("calling NetReadFixup(method<".a:method."> line1=".line1." line2=".line2.")",'~'.expand("<slnum>"))
- NetrwKeepj call NetReadFixup(a:method, line1, line2)
-" else " Decho
-" call Decho("NetReadFixup() not called, doesn't exist (line1=".line1." line2=".line2.")",'~'.expand("<slnum>"))
- endif
-
- if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
- " update the Buffers menu
- NetrwKeepj call s:UpdateBuffersMenu()
- endif
-
-" call Decho("readcmd<".a:readcmd."> cmdarg<".v:cmdarg."> tfile<".a:tfile."> readable=".s:FileReadable(a:tfile),'~'.expand("<slnum>"))
-
- " make sure file is being displayed
-" redraw!
-
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
-" call Dret("NetrwGetFile")
-endfun
-
-" ------------------------------------------------------------------------
-" s:NetrwMethod: determine method of transfer {{{2
-" Input:
-" choice = url [protocol:]//[userid@]hostname[:port]/[path-to-file]
-" Output:
-" b:netrw_method= 1: rcp
-" 2: ftp + <.netrc>
-" 3: ftp + machine, id, password, and [path]filename
-" 4: scp
-" 5: http[s] (wget)
-" 6: dav
-" 7: rsync
-" 8: fetch
-" 9: sftp
-" 10: file
-" g:netrw_machine= hostname
-" b:netrw_fname = filename
-" g:netrw_port = optional port number (for ftp)
-" g:netrw_choice = copy of input url (choice)
-fun! s:NetrwMethod(choice)
-" call Dfunc("s:NetrwMethod(a:choice<".a:choice.">)")
-
- " sanity check: choice should have at least three slashes in it
- if strlen(substitute(a:choice,'[^/]','','g')) < 3
- call netrw#ErrorMsg(s:ERROR,"not a netrw-style url; netrw uses protocol://[user@]hostname[:port]/[path])",78)
- let b:netrw_method = -1
-" call Dret("s:NetrwMethod : incorrect url format<".a:choice.">")
- return
- endif
-
- " record current g:netrw_machine, if any
- " curmachine used if protocol == ftp and no .netrc
- if exists("g:netrw_machine")
- let curmachine= g:netrw_machine
-" call Decho("curmachine<".curmachine.">",'~'.expand("<slnum>"))
- else
- let curmachine= "N O T A HOST"
- endif
- if exists("g:netrw_port")
- let netrw_port= g:netrw_port
- endif
-
- " insure that netrw_ftp_cmd starts off every method determination
- " with the current g:netrw_ftp_cmd
- let s:netrw_ftp_cmd= g:netrw_ftp_cmd
-
- " initialization
- let b:netrw_method = 0
- let g:netrw_machine = ""
- let b:netrw_fname = ""
- let g:netrw_port = ""
- let g:netrw_choice = a:choice
-
- " Patterns:
- " mipf : a:machine a:id password filename Use ftp
- " mf : a:machine filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd
- " ftpurm : ftp://[user@]host[[#:]port]/filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd
- " rcpurm : rcp://[user@]host/filename Use rcp
- " rcphf : [user@]host:filename Use rcp
- " scpurm : scp://[user@]host[[#:]port]/filename Use scp
- " httpurm : http[s]://[user@]host/filename Use wget
- " davurm : dav[s]://host[:port]/path Use cadaver/curl
- " rsyncurm : rsync://host[:port]/path Use rsync
- " fetchurm : fetch://[user@]host[:http]/filename Use fetch (defaults to ftp, override for http)
- " sftpurm : sftp://[user@]host/filename Use scp
- " fileurm : file://[user@]host/filename Use elinks or links
- let mipf = '^\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)$'
- let mf = '^\(\S\+\)\s\+\(\S\+\)$'
- let ftpurm = '^ftp://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\([#:]\d\+\)\=/\(.*\)$'
- let rcpurm = '^rcp://\%(\([^/]*\)@\)\=\([^/]\{-}\)/\(.*\)$'
- let rcphf = '^\(\(\h\w*\)@\)\=\(\h\w*\):\([^@]\+\)$'
- let scpurm = '^scp://\([^/#:]\+\)\%([#:]\(\d\+\)\)\=/\(.*\)$'
- let httpurm = '^https\=://\([^/]\{-}\)\(/.*\)\=$'
- let davurm = '^davs\=://\([^/]\+\)/\(.*/\)\([-_.~[:alnum:]]\+\)$'
- let rsyncurm = '^rsync://\([^/]\{-}\)/\(.*\)\=$'
- let fetchurm = '^fetch://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\(:http\)\=/\(.*\)$'
- let sftpurm = '^sftp://\([^/]\{-}\)/\(.*\)\=$'
- let fileurm = '^file\=://\(.*\)$'
-
-" call Decho("determine method:",'~'.expand("<slnum>"))
- " Determine Method
- " Method#1: rcp://user@hostname/...path-to-file {{{3
- if match(a:choice,rcpurm) == 0
-" call Decho("rcp://...",'~'.expand("<slnum>"))
- let b:netrw_method = 1
- let userid = substitute(a:choice,rcpurm,'\1',"")
- let g:netrw_machine = substitute(a:choice,rcpurm,'\2',"")
- let b:netrw_fname = substitute(a:choice,rcpurm,'\3',"")
- if userid != ""
- let g:netrw_uid= userid
- endif
-
- " Method#4: scp://user@hostname/...path-to-file {{{3
- elseif match(a:choice,scpurm) == 0
-" call Decho("scp://...",'~'.expand("<slnum>"))
- let b:netrw_method = 4
- let g:netrw_machine = substitute(a:choice,scpurm,'\1',"")
- let g:netrw_port = substitute(a:choice,scpurm,'\2',"")
- let b:netrw_fname = substitute(a:choice,scpurm,'\3',"")
-
- " Method#5: http[s]://user@hostname/...path-to-file {{{3
- elseif match(a:choice,httpurm) == 0
-" call Decho("http[s]://...",'~'.expand("<slnum>"))
- let b:netrw_method = 5
- let g:netrw_machine= substitute(a:choice,httpurm,'\1',"")
- let b:netrw_fname = substitute(a:choice,httpurm,'\2',"")
- let b:netrw_http = (a:choice =~ '^https:')? "https" : "http"
-
- " Method#6: dav://hostname[:port]/..path-to-file.. {{{3
- elseif match(a:choice,davurm) == 0
-" call Decho("dav://...",'~'.expand("<slnum>"))
- let b:netrw_method= 6
- if a:choice =~ 'davs:'
- let g:netrw_machine= 'https://'.substitute(a:choice,davurm,'\1/\2',"")
- else
- let g:netrw_machine= 'http://'.substitute(a:choice,davurm,'\1/\2',"")
- endif
- let b:netrw_fname = substitute(a:choice,davurm,'\3',"")
-
- " Method#7: rsync://user@hostname/...path-to-file {{{3
- elseif match(a:choice,rsyncurm) == 0
-" call Decho("rsync://...",'~'.expand("<slnum>"))
- let b:netrw_method = 7
- let g:netrw_machine= substitute(a:choice,rsyncurm,'\1',"")
- let b:netrw_fname = substitute(a:choice,rsyncurm,'\2',"")
-
- " Methods 2,3: ftp://[user@]hostname[[:#]port]/...path-to-file {{{3
- elseif match(a:choice,ftpurm) == 0
-" call Decho("ftp://...",'~'.expand("<slnum>"))
- let userid = substitute(a:choice,ftpurm,'\2',"")
- let g:netrw_machine= substitute(a:choice,ftpurm,'\3',"")
- let g:netrw_port = substitute(a:choice,ftpurm,'\4',"")
- let b:netrw_fname = substitute(a:choice,ftpurm,'\5',"")
-" call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>"))
- if userid != ""
- let g:netrw_uid= userid
- endif
-
- if curmachine != g:netrw_machine
- if exists("s:netrw_hup[".g:netrw_machine."]")
- call NetUserPass("ftp:".g:netrw_machine)
- elseif exists("s:netrw_passwd")
- " if there's a change in hostname, require password re-entry
- unlet s:netrw_passwd
- endif
- if exists("netrw_port")
- unlet netrw_port
- endif
- endif
-
- if exists("g:netrw_uid") && exists("s:netrw_passwd")
- let b:netrw_method = 3
- else
- let host= substitute(g:netrw_machine,'\..*$','','')
- if exists("s:netrw_hup[host]")
- call NetUserPass("ftp:".host)
-
- elseif has("win32") && s:netrw_ftp_cmd =~# '-[sS]:'
-" call Decho("has -s: : s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>"))
-" call Decho(" g:netrw_ftp_cmd<".g:netrw_ftp_cmd.">",'~'.expand("<slnum>"))
- if g:netrw_ftp_cmd =~# '-[sS]:\S*MACHINE\>'
- let s:netrw_ftp_cmd= substitute(g:netrw_ftp_cmd,'\<MACHINE\>',g:netrw_machine,'')
-" call Decho("s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>"))
- endif
- let b:netrw_method= 2
- elseif s:FileReadable(expand("$HOME/.netrc")) && !g:netrw_ignorenetrc
-" call Decho("using <".expand("$HOME/.netrc")."> (readable)",'~'.expand("<slnum>"))
- let b:netrw_method= 2
- else
- if !exists("g:netrw_uid") || g:netrw_uid == ""
- call NetUserPass()
- elseif !exists("s:netrw_passwd") || s:netrw_passwd == ""
- call NetUserPass(g:netrw_uid)
- " else just use current g:netrw_uid and s:netrw_passwd
- endif
- let b:netrw_method= 3
- endif
- endif
-
- " Method#8: fetch {{{3
- elseif match(a:choice,fetchurm) == 0
-" call Decho("fetch://...",'~'.expand("<slnum>"))
- let b:netrw_method = 8
- let g:netrw_userid = substitute(a:choice,fetchurm,'\2',"")
- let g:netrw_machine= substitute(a:choice,fetchurm,'\3',"")
- let b:netrw_option = substitute(a:choice,fetchurm,'\4',"")
- let b:netrw_fname = substitute(a:choice,fetchurm,'\5',"")
-
- " Method#3: Issue an ftp : "machine id password [path/]filename" {{{3
- elseif match(a:choice,mipf) == 0
-" call Decho("(ftp) host id pass file",'~'.expand("<slnum>"))
- let b:netrw_method = 3
- let g:netrw_machine = substitute(a:choice,mipf,'\1',"")
- let g:netrw_uid = substitute(a:choice,mipf,'\2',"")
- let s:netrw_passwd = substitute(a:choice,mipf,'\3',"")
- let b:netrw_fname = substitute(a:choice,mipf,'\4',"")
- call NetUserPass(g:netrw_machine,g:netrw_uid,s:netrw_passwd)
-
- " Method#3: Issue an ftp: "hostname [path/]filename" {{{3
- elseif match(a:choice,mf) == 0
-" call Decho("(ftp) host file",'~'.expand("<slnum>"))
- if exists("g:netrw_uid") && exists("s:netrw_passwd")
- let b:netrw_method = 3
- let g:netrw_machine = substitute(a:choice,mf,'\1',"")
- let b:netrw_fname = substitute(a:choice,mf,'\2',"")
-
- elseif s:FileReadable(expand("$HOME/.netrc"))
- let b:netrw_method = 2
- let g:netrw_machine = substitute(a:choice,mf,'\1',"")
- let b:netrw_fname = substitute(a:choice,mf,'\2',"")
- endif
-
- " Method#9: sftp://user@hostname/...path-to-file {{{3
- elseif match(a:choice,sftpurm) == 0
-" call Decho("sftp://...",'~'.expand("<slnum>"))
- let b:netrw_method = 9
- let g:netrw_machine= substitute(a:choice,sftpurm,'\1',"")
- let b:netrw_fname = substitute(a:choice,sftpurm,'\2',"")
-
- " Method#1: Issue an rcp: hostname:filename" (this one should be last) {{{3
- elseif match(a:choice,rcphf) == 0
-" call Decho("(rcp) [user@]host:file) rcphf<".rcphf.">",'~'.expand("<slnum>"))
- let b:netrw_method = 1
- let userid = substitute(a:choice,rcphf,'\2',"")
- let g:netrw_machine = substitute(a:choice,rcphf,'\3',"")
- let b:netrw_fname = substitute(a:choice,rcphf,'\4',"")
-" call Decho('\1<'.substitute(a:choice,rcphf,'\1',"").">",'~'.expand("<slnum>"))
-" call Decho('\2<'.substitute(a:choice,rcphf,'\2',"").">",'~'.expand("<slnum>"))
-" call Decho('\3<'.substitute(a:choice,rcphf,'\3',"").">",'~'.expand("<slnum>"))
-" call Decho('\4<'.substitute(a:choice,rcphf,'\4',"").">",'~'.expand("<slnum>"))
- if userid != ""
- let g:netrw_uid= userid
- endif
-
- " Method#10: file://user@hostname/...path-to-file {{{3
- elseif match(a:choice,fileurm) == 0 && exists("g:netrw_file_cmd")
-" call Decho("http[s]://...",'~'.expand("<slnum>"))
- let b:netrw_method = 10
- let b:netrw_fname = substitute(a:choice,fileurm,'\1',"")
-" call Decho('\1<'.substitute(a:choice,fileurm,'\1',"").">",'~'.expand("<slnum>"))
-
- " Cannot Determine Method {{{3
- else
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:WARNING,"cannot determine method (format: protocol://[user@]hostname[:port]/[path])",45)
- endif
- let b:netrw_method = -1
- endif
- "}}}3
-
- if g:netrw_port != ""
- " remove any leading [:#] from port number
- let g:netrw_port = substitute(g:netrw_port,'[#:]\+','','')
- elseif exists("netrw_port")
- " retain port number as implicit for subsequent ftp operations
- let g:netrw_port= netrw_port
- endif
-
-" call Decho("a:choice <".a:choice.">",'~'.expand("<slnum>"))
-" call Decho("b:netrw_method <".b:netrw_method.">",'~'.expand("<slnum>"))
-" call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>"))
-" call Decho("g:netrw_port <".g:netrw_port.">",'~'.expand("<slnum>"))
-" if exists("g:netrw_uid") "Decho
-" call Decho("g:netrw_uid <".g:netrw_uid.">",'~'.expand("<slnum>"))
-" endif "Decho
-" if exists("s:netrw_passwd") "Decho
-" call Decho("s:netrw_passwd <".s:netrw_passwd.">",'~'.expand("<slnum>"))
-" endif "Decho
-" call Decho("b:netrw_fname <".b:netrw_fname.">",'~'.expand("<slnum>"))
-" call Dret("s:NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port)
-endfun
-
-" ---------------------------------------------------------------------
-" NetUserPass: set username and password for subsequent ftp transfer {{{2
-" Usage: :call NetUserPass() -- will prompt for userid and password
-" :call NetUserPass("uid") -- will prompt for password
-" :call NetUserPass("uid","password") -- sets global userid and password
-" :call NetUserPass("ftp:host") -- looks up userid and password using hup dictionary
-" :call NetUserPass("host","uid","password") -- sets hup dictionary with host, userid, password
-fun! NetUserPass(...)
-
-" call Dfunc("NetUserPass() a:0=".a:0)
-
- if !exists('s:netrw_hup')
- let s:netrw_hup= {}
- endif
-
- if a:0 == 0
- " case: no input arguments
-
- " change host and username if not previously entered; get new password
- if !exists("g:netrw_machine")
- let g:netrw_machine= input('Enter hostname: ')
- endif
- if !exists("g:netrw_uid") || g:netrw_uid == ""
- " get username (user-id) via prompt
- let g:netrw_uid= input('Enter username: ')
- endif
- " get password via prompting
- let s:netrw_passwd= inputsecret("Enter Password: ")
-
- " set up hup database
- let host = substitute(g:netrw_machine,'\..*$','','')
- if !exists('s:netrw_hup[host]')
- let s:netrw_hup[host]= {}
- endif
- let s:netrw_hup[host].uid = g:netrw_uid
- let s:netrw_hup[host].passwd = s:netrw_passwd
-
- elseif a:0 == 1
- " case: one input argument
-
- if a:1 =~ '^ftp:'
- " get host from ftp:... url
- " access userid and password from hup (host-user-passwd) dictionary
-" call Decho("case a:0=1: a:1<".a:1."> (get host from ftp:... url)",'~'.expand("<slnum>"))
- let host = substitute(a:1,'^ftp:','','')
- let host = substitute(host,'\..*','','')
- if exists("s:netrw_hup[host]")
- let g:netrw_uid = s:netrw_hup[host].uid
- let s:netrw_passwd = s:netrw_hup[host].passwd
-" call Decho("get s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>"))
-" call Decho("get s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>"))
- else
- let g:netrw_uid = input("Enter UserId: ")
- let s:netrw_passwd = inputsecret("Enter Password: ")
- endif
-
- else
- " case: one input argument, not an url. Using it as a new user-id.
-" call Decho("case a:0=1: a:1<".a:1."> (get host from input argument, not an url)",'~'.expand("<slnum>"))
- if exists("g:netrw_machine")
- if g:netrw_machine =~ '[0-9.]\+'
- let host= g:netrw_machine
- else
- let host= substitute(g:netrw_machine,'\..*$','','')
- endif
- else
- let g:netrw_machine= input('Enter hostname: ')
- endif
- let g:netrw_uid = a:1
-" call Decho("set g:netrw_uid= <".g:netrw_uid.">",'~'.expand("<slnum>"))
- if exists("g:netrw_passwd")
- " ask for password if one not previously entered
- let s:netrw_passwd= g:netrw_passwd
- else
- let s:netrw_passwd = inputsecret("Enter Password: ")
- endif
- endif
-
-" call Decho("host<".host.">",'~'.expand("<slnum>"))
- if exists("host")
- if !exists('s:netrw_hup[host]')
- let s:netrw_hup[host]= {}
- endif
- let s:netrw_hup[host].uid = g:netrw_uid
- let s:netrw_hup[host].passwd = s:netrw_passwd
- endif
-
- elseif a:0 == 2
- let g:netrw_uid = a:1
- let s:netrw_passwd = a:2
-
- elseif a:0 == 3
- " enter hostname, user-id, and password into the hup dictionary
- let host = substitute(a:1,'^\a\+:','','')
- let host = substitute(host,'\..*$','','')
- if !exists('s:netrw_hup[host]')
- let s:netrw_hup[host]= {}
- endif
- let s:netrw_hup[host].uid = a:2
- let s:netrw_hup[host].passwd = a:3
- let g:netrw_uid = s:netrw_hup[host].uid
- let s:netrw_passwd = s:netrw_hup[host].passwd
-" call Decho("set s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>"))
-" call Decho("set s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>"))
- endif
-
-" call Dret("NetUserPass : uid<".g:netrw_uid."> passwd<".s:netrw_passwd.">")
-endfun
-
-" =================================
-" Shared Browsing Support: {{{1
-" =================================
-
-" ---------------------------------------------------------------------
-" s:ExplorePatHls: converts an Explore pattern into a regular expression search pattern {{{2
-fun! s:ExplorePatHls(pattern)
-" call Dfunc("s:ExplorePatHls(pattern<".a:pattern.">)")
- let repat= substitute(a:pattern,'^**/\{1,2}','','')
-" call Decho("repat<".repat.">",'~'.expand("<slnum>"))
- let repat= escape(repat,'][.\')
-" call Decho("repat<".repat.">",'~'.expand("<slnum>"))
- let repat= '\<'.substitute(repat,'\*','\\(\\S\\+ \\)*\\S\\+','g').'\>'
-" call Dret("s:ExplorePatHls repat<".repat.">")
- return repat
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBookHistHandler: {{{2
-" 0: (user: <mb>) bookmark current directory
-" 1: (user: <gb>) change to the bookmarked directory
-" 2: (user: <qb>) list bookmarks
-" 3: (browsing) records current directory history
-" 4: (user: <u>) go up (previous) directory, using history
-" 5: (user: <U>) go down (next) directory, using history
-" 6: (user: <mB>) delete bookmark
-fun! s:NetrwBookHistHandler(chg,curdir)
-" call Dfunc("s:NetrwBookHistHandler(chg=".a:chg." curdir<".a:curdir.">) cnt=".v:count." histcnt=".g:netrw_dirhistcnt." histmax=".g:netrw_dirhistmax)
- if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0
-" " call Dret("s:NetrwBookHistHandler - suppressed due to g:netrw_dirhistmax")
- return
- endif
-
- let ykeep = @@
- let curbufnr = bufnr("%")
-
- if a:chg == 0
- " bookmark the current directory
-" call Decho("(user: <b>) bookmark the current directory",'~'.expand("<slnum>"))
- if exists("s:netrwmarkfilelist_{curbufnr}")
- call s:NetrwBookmark(0)
- echo "bookmarked marked files"
- else
- call s:MakeBookmark(a:curdir)
- echo "bookmarked the current directory"
- endif
-
- try
- call s:NetrwBookHistSave()
- catch
- endtry
-
- elseif a:chg == 1
- " change to the bookmarked directory
-" call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>"))
- if exists("g:netrw_bookmarklist[v:count-1]")
-" call Decho("(user: <".v:count."gb>) bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>"))
- exe "NetrwKeepj e ".fnameescape(g:netrw_bookmarklist[v:count-1])
- else
- echomsg "Sorry, bookmark#".v:count." doesn't exist!"
- endif
-
- elseif a:chg == 2
-" redraw!
- let didwork= 0
- " list user's bookmarks
-" call Decho("(user: <q>) list user's bookmarks",'~'.expand("<slnum>"))
- if exists("g:netrw_bookmarklist")
-" call Decho('list '.len(g:netrw_bookmarklist).' bookmarks','~'.expand("<slnum>"))
- let cnt= 1
- for bmd in g:netrw_bookmarklist
-" call Decho("Netrw Bookmark#".cnt.": ".g:netrw_bookmarklist[cnt-1],'~'.expand("<slnum>"))
- echo printf("Netrw Bookmark#%-2d: %s",cnt,g:netrw_bookmarklist[cnt-1])
- let didwork = 1
- let cnt = cnt + 1
- endfor
- endif
-
- " list directory history
- " Note: history is saved only when PerformListing is done;
- " ie. when netrw can re-use a netrw buffer, the current directory is not saved in the history.
- let cnt = g:netrw_dirhistcnt
- let first = 1
- let histcnt = 0
- if g:netrw_dirhistmax > 0
- while ( first || cnt != g:netrw_dirhistcnt )
-" call Decho("first=".first." cnt=".cnt." dirhistcnt=".g:netrw_dirhistcnt,'~'.expand("<slnum>"))
- if exists("g:netrw_dirhist_{cnt}")
-" call Decho("Netrw History#".histcnt.": ".g:netrw_dirhist_{cnt},'~'.expand("<slnum>"))
- echo printf("Netrw History#%-2d: %s",histcnt,g:netrw_dirhist_{cnt})
- let didwork= 1
- endif
- let histcnt = histcnt + 1
- let first = 0
- let cnt = ( cnt - 1 ) % g:netrw_dirhistmax
- if cnt < 0
- let cnt= cnt + g:netrw_dirhistmax
- endif
- endwhile
- else
- let g:netrw_dirhistcnt= 0
- endif
- if didwork
- call inputsave()|call input("Press <cr> to continue")|call inputrestore()
- endif
-
- elseif a:chg == 3
- " saves most recently visited directories (when they differ)
-" call Decho("(browsing) record curdir history",'~'.expand("<slnum>"))
- if !exists("g:netrw_dirhistcnt") || !exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") || g:netrw_dirhist_{g:netrw_dirhistcnt} != a:curdir
- if g:netrw_dirhistmax > 0
- let g:netrw_dirhistcnt = ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax
- let g:netrw_dirhist_{g:netrw_dirhistcnt} = a:curdir
- endif
-" call Decho("save dirhist#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>"))
- endif
-
- elseif a:chg == 4
- " u: change to the previous directory stored on the history list
-" call Decho("(user: <u>) chg to prev dir from history",'~'.expand("<slnum>"))
- if g:netrw_dirhistmax > 0
- let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - v:count1 ) % g:netrw_dirhistmax
- if g:netrw_dirhistcnt < 0
- let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax
- endif
- else
- let g:netrw_dirhistcnt= 0
- endif
- if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}")
-" call Decho("changedir u#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>"))
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
- setl ma noro
-" call Decho("setl ma noro",'~'.expand("<slnum>"))
- sil! NetrwKeepj %d _
- setl nomod
-" call Decho("setl nomod",'~'.expand("<slnum>"))
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- endif
-" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>"))
- exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt})
- else
- if g:netrw_dirhistmax > 0
- let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + v:count1 ) % g:netrw_dirhistmax
- else
- let g:netrw_dirhistcnt= 0
- endif
- echo "Sorry, no predecessor directory exists yet"
- endif
-
- elseif a:chg == 5
- " U: change to the subsequent directory stored on the history list
-" call Decho("(user: <U>) chg to next dir from history",'~'.expand("<slnum>"))
- if g:netrw_dirhistmax > 0
- let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax
- if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}")
-" call Decho("changedir U#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>"))
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
-" call Decho("setl ma noro",'~'.expand("<slnum>"))
- setl ma noro
- sil! NetrwKeepj %d _
-" call Decho("removed all lines from buffer (%d)",'~'.expand("<slnum>"))
-" call Decho("setl nomod",'~'.expand("<slnum>"))
- setl nomod
-" call Decho("(set nomod) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- endif
-" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>"))
- exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt})
- else
- let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - 1 ) % g:netrw_dirhistmax
- if g:netrw_dirhistcnt < 0
- let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax
- endif
- echo "Sorry, no successor directory exists yet"
- endif
- else
- let g:netrw_dirhistcnt= 0
- echo "Sorry, no successor directory exists yet (g:netrw_dirhistmax is ".g:netrw_dirhistmax.")"
- endif
-
- elseif a:chg == 6
-" call Decho("(user: <mB>) delete bookmark'd directory",'~'.expand("<slnum>"))
- if exists("s:netrwmarkfilelist_{curbufnr}")
- call s:NetrwBookmark(1)
- echo "removed marked files from bookmarks"
- else
- " delete the v:count'th bookmark
- let iremove = v:count
- let dremove = g:netrw_bookmarklist[iremove - 1]
-" call Decho("delete bookmark#".iremove."<".g:netrw_bookmarklist[iremove - 1].">",'~'.expand("<slnum>"))
- call s:MergeBookmarks()
-" call Decho("remove g:netrw_bookmarklist[".(iremove-1)."]<".g:netrw_bookmarklist[(iremove-1)].">",'~'.expand("<slnum>"))
- NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1)
- echo "removed ".dremove." from g:netrw_bookmarklist"
-" call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>"))
- endif
-" call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>"))
-
- try
- call s:NetrwBookHistSave()
- catch
- endtry
- endif
- call s:NetrwBookmarkMenu()
- call s:NetrwTgtMenu()
- let @@= ykeep
-" call Dret("s:NetrwBookHistHandler")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBookHistRead: this function reads bookmarks and history {{{2
-" Will source the history file (.netrwhist) only if the g:netrw_disthistmax is > 0.
-" Sister function: s:NetrwBookHistSave()
-fun! s:NetrwBookHistRead()
-" call Dfunc("s:NetrwBookHistRead()")
- if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0
-" call Dret("s:NetrwBookHistRead - nothing read (suppressed due to dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a").")")
- return
- endif
- let ykeep= @@
-
- " read bookmarks
- if !exists("s:netrw_initbookhist")
- let home = s:NetrwHome()
- let savefile= home."/.netrwbook"
- if filereadable(s:NetrwFile(savefile))
-" call Decho("sourcing .netrwbook",'~'.expand("<slnum>"))
- exe "keepalt NetrwKeepj so ".savefile
- endif
-
- " read history
- if g:netrw_dirhistmax > 0
- let savefile= home."/.netrwhist"
- if filereadable(s:NetrwFile(savefile))
-" call Decho("sourcing .netrwhist",'~'.expand("<slnum>"))
- exe "keepalt NetrwKeepj so ".savefile
- endif
- let s:netrw_initbookhist= 1
- au VimLeave * call s:NetrwBookHistSave()
- endif
- endif
-
- let @@= ykeep
-" call Decho("dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a"),'~'.expand("<slnum>"))
-" call Decho("dirhistcnt=".(exists("g:netrw_dirhistcnt")? g:netrw_dirhistcnt : "n/a"),'~'.expand("<slnum>"))
-" call Dret("s:NetrwBookHistRead")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBookHistSave: this function saves bookmarks and history to files {{{2
-" Sister function: s:NetrwBookHistRead()
-" I used to do this via viminfo but that appears to
-" be unreliable for long-term storage
-" If g:netrw_dirhistmax is <= 0, no history or bookmarks
-" will be saved.
-" (s:NetrwBookHistHandler(3,...) used to record history)
-fun! s:NetrwBookHistSave()
-" call Dfunc("s:NetrwBookHistSave() dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt)
- if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0
-" call Dret("s:NetrwBookHistSave : nothing saved (dirhistmax=".g:netrw_dirhistmax.")")
- return
- endif
-
- let savefile= s:NetrwHome()."/.netrwhist"
-" call Decho("savefile<".savefile.">",'~'.expand("<slnum>"))
- 1split
-
- " setting up a new buffer which will become .netrwhist
- call s:NetrwEnew()
-" call Decho("case g:netrw_use_noswf=".g:netrw_use_noswf.(exists("+acd")? " +acd" : " -acd"),'~'.expand("<slnum>"))
- if g:netrw_use_noswf
- setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000 noswf
- else
- setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000
- endif
- setl nocin noai noci magic nospell nohid wig= noaw
- setl ma noro write
- if exists("+acd") | setl noacd | endif
- sil! NetrwKeepj keepalt %d _
-
- " rename enew'd file: .netrwhist -- no attempt to merge
- " record dirhistmax and current dirhistcnt
- " save history
-" call Decho("saving history: dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt." lastline=".line("$"),'~'.expand("<slnum>"))
- sil! keepalt file .netrwhist
- call setline(1,"let g:netrw_dirhistmax =".g:netrw_dirhistmax)
- call setline(2,"let g:netrw_dirhistcnt =".g:netrw_dirhistcnt)
- if g:netrw_dirhistmax > 0
- let lastline = line("$")
- let cnt = g:netrw_dirhistcnt
- let first = 1
- while ( first || cnt != g:netrw_dirhistcnt )
- let lastline= lastline + 1
- if exists("g:netrw_dirhist_{cnt}")
- call setline(lastline,'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'")
-" call Decho("..".lastline.'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'",'~'.expand("<slnum>"))
- endif
- let first = 0
- let cnt = ( cnt - 1 ) % g:netrw_dirhistmax
- if cnt < 0
- let cnt= cnt + g:netrw_dirhistmax
- endif
- endwhile
- exe "sil! w! ".savefile
-" call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>"))
- endif
-
- " save bookmarks
- sil NetrwKeepj %d _
- if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != []
-" call Decho("saving bookmarks",'~'.expand("<slnum>"))
- " merge and write .netrwbook
- let savefile= s:NetrwHome()."/.netrwbook"
-
- if filereadable(s:NetrwFile(savefile))
- let booklist= deepcopy(g:netrw_bookmarklist)
- exe "sil NetrwKeepj keepalt so ".savefile
- for bdm in booklist
- if index(g:netrw_bookmarklist,bdm) == -1
- call add(g:netrw_bookmarklist,bdm)
- endif
- endfor
- call sort(g:netrw_bookmarklist)
- endif
-
- " construct and save .netrwbook
- call setline(1,"let g:netrw_bookmarklist= ".string(g:netrw_bookmarklist))
- exe "sil! w! ".savefile
-" call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>"))
- endif
-
- " cleanup -- remove buffer used to construct history
- let bgone= bufnr("%")
- q!
- exe "keepalt ".bgone."bwipe!"
-
-" call Dret("s:NetrwBookHistSave")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBrowse: This function uses the command in g:netrw_list_cmd to provide a {{{2
-" list of the contents of a local or remote directory. It is assumed that the
-" g:netrw_list_cmd has a string, USEPORT HOSTNAME, that needs to be substituted
-" with the requested remote hostname first.
-" Often called via: Explore/e dirname/etc -> netrw#LocalBrowseCheck() -> s:NetrwBrowse()
-fun! s:NetrwBrowse(islocal,dirname)
- if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif
-
- " save alternate-file's filename if w:netrw_rexlocal doesn't exist
- " This is useful when one edits a local file, then :e ., then :Rex
- if a:islocal && !exists("w:netrw_rexfile") && bufname("#") != ""
- let w:netrw_rexfile= bufname("#")
- endif
-
- " s:NetrwBrowse : initialize history {{{3
- if !exists("s:netrw_initbookhist")
- NetrwKeepj call s:NetrwBookHistRead()
- endif
-
- " s:NetrwBrowse : simplify the dirname (especially for ".."s in dirnames) {{{3
- if a:dirname !~ '^\a\{3,}://'
- let dirname= simplify(a:dirname)
- else
- let dirname= a:dirname
- endif
-
- " repoint t:netrw_lexbufnr if appropriate
- if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr
- let repointlexbufnr= 1
- endif
-
- " s:NetrwBrowse : sanity checks: {{{3
- if exists("s:netrw_skipbrowse")
- unlet s:netrw_skipbrowse
- return
- endif
- if !exists("*shellescape")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing shellescape()",69)
- return
- endif
- if !exists("*fnameescape")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing fnameescape()",70)
- return
- endif
-
- " s:NetrwBrowse : save options: {{{3
- call s:NetrwOptionsSave("w:")
-
- " s:NetrwBrowse : re-instate any marked files {{{3
- if has("syntax") && exists("g:syntax_on") && g:syntax_on
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
- exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/"
- endif
- endif
-
- if a:islocal && exists("w:netrw_acdkeep") && w:netrw_acdkeep
- " s:NetrwBrowse : set up "safe" options for local directory/file {{{3
- if s:NetrwLcd(dirname)
- return
- endif
-
- elseif !a:islocal && dirname !~ '[\/]$' && dirname !~ '^"'
- " s:NetrwBrowse : remote regular file handler {{{3
- if bufname(dirname) != ""
- exe "NetrwKeepj b ".bufname(dirname)
- else
- " attempt transfer of remote regular file
-
- " remove any filetype indicator from end of dirname, except for the
- " "this is a directory" indicator (/).
- " There shouldn't be one of those here, anyway.
- let path= substitute(dirname,'[*=@|]\r\=$','','e')
- call s:RemotePathAnalysis(dirname)
-
- " s:NetrwBrowse : remote-read the requested file into current buffer {{{3
- call s:NetrwEnew(dirname)
- call s:NetrwOptionsSafe(a:islocal)
- setl ma noro
- let b:netrw_curdir = dirname
- let url = s:method."://".((s:user == "")? "" : s:user."@").s:machine.(s:port ? ":".s:port : "")."/".s:path
- call s:NetrwBufRename(url)
- exe "sil! NetrwKeepj keepalt doau BufReadPre ".fnameescape(s:fname)
- sil call netrw#NetRead(2,url)
- " netrw.vim and tar.vim have already handled decompression of the tarball; avoiding gzip.vim error
- if s:path =~ '.bz2'
- exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.bz2$','',''))
- elseif s:path =~ '.gz'
- exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.gz$','',''))
- elseif s:path =~ '.gz'
- exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.txz$','',''))
- else
- exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(s:fname)
- endif
- endif
-
- " s:NetrwBrowse : save certain window-oriented variables into buffer-oriented variables {{{3
- call s:SetBufWinVars()
- call s:NetrwOptionsRestore("w:")
- setl ma nomod noro
- return
- endif
-
- " use buffer-oriented WinVars if buffer variables exist but associated window variables don't {{{3
- call s:UseBufWinVars()
-
- " set up some variables {{{3
- let b:netrw_browser_active = 1
- let dirname = dirname
- let s:last_sort_by = g:netrw_sort_by
-
- " set up menu {{{3
- NetrwKeepj call s:NetrwMenu(1)
-
- " get/set-up buffer {{{3
- let svpos = winsaveview()
-
- " NetrwGetBuffer might change buffers but s:rexposn_X was set for the
- " previous buffer
- let prevbufnr = bufnr('%')
- let reusing= s:NetrwGetBuffer(a:islocal,dirname)
- if exists("s:rexposn_".prevbufnr)
- let s:rexposn_{bufnr('%')} = s:rexposn_{prevbufnr}
- endif
-
- " maintain markfile highlighting
- if has("syntax") && exists("g:syntax_on") && g:syntax_on
- if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != ""
- exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/"
- else
- 2match none
- endif
- endif
- if reusing && line("$") > 1
- call s:NetrwOptionsRestore("w:")
- setl noma nomod nowrap
- return
- endif
-
- " set b:netrw_curdir to the new directory name {{{3
- let b:netrw_curdir= dirname
- if b:netrw_curdir =~ '[/\\]$'
- let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e')
- endif
- if b:netrw_curdir =~ '\a:$' && has("win32")
- let b:netrw_curdir= b:netrw_curdir."/"
- endif
- if b:netrw_curdir == ''
- if has("amiga")
- " On the Amiga, the empty string connotes the current directory
- let b:netrw_curdir= getcwd()
- else
- " under unix, when the root directory is encountered, the result
- " from the preceding substitute is an empty string.
- let b:netrw_curdir= '/'
- endif
- endif
- if !a:islocal && b:netrw_curdir !~ '/$'
- let b:netrw_curdir= b:netrw_curdir.'/'
- endif
-
- " ------------
- " (local only) {{{3
- " ------------
- if a:islocal
- " Set up ShellCmdPost handling. Append current buffer to browselist
- call s:LocalFastBrowser()
-
- " handle g:netrw_keepdir: set vim's current directory to netrw's notion of the current directory {{{3
- if !g:netrw_keepdir
- if !exists("&l:acd") || !&l:acd
- if s:NetrwLcd(b:netrw_curdir)
- return
- endif
- endif
- endif
-
- " --------------------------------
- " remote handling: {{{3
- " --------------------------------
- else
-
- " analyze dirname and g:netrw_list_cmd {{{3
- if dirname =~# "^NetrwTreeListing\>"
- let dirname= b:netrw_curdir
- elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
- let dirname= substitute(b:netrw_curdir,'\\','/','g')
- if dirname !~ '/$'
- let dirname= dirname.'/'
- endif
- let b:netrw_curdir = dirname
- else
- let dirname = substitute(dirname,'\\','/','g')
- endif
-
- let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/]\+\)/\(.*\)$'
- if dirname !~ dirpat
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw doesn't understand your dirname<".dirname.">",20)
- endif
- NetrwKeepj call s:NetrwOptionsRestore("w:")
- setl noma nomod nowrap
- return
- endif
- let b:netrw_curdir= dirname
- endif " (additional remote handling)
-
- " -------------------------------
- " Perform Directory Listing: {{{3
- " -------------------------------
- NetrwKeepj call s:NetrwMaps(a:islocal)
- NetrwKeepj call s:NetrwCommands(a:islocal)
- NetrwKeepj call s:PerformListing(a:islocal)
-
- " restore option(s)
- call s:NetrwOptionsRestore("w:")
-
- " If there is a rexposn: restore position with rexposn
- " Otherwise : set rexposn
- if exists("s:rexposn_".bufnr("%"))
- NetrwKeepj call winrestview(s:rexposn_{bufnr('%')})
- if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt
- NetrwKeepj exe w:netrw_bannercnt
- endif
- else
- NetrwKeepj call s:SetRexDir(a:islocal,b:netrw_curdir)
- endif
- if v:version >= 700 && has("balloon_eval") && &beval == 0 && &l:bexpr == "" && !exists("g:netrw_nobeval")
- let &l:bexpr= "netrw#BalloonHelp()"
- setl beval
- endif
-
- " repoint t:netrw_lexbufnr if appropriate
- if exists("repointlexbufnr")
- let t:netrw_lexbufnr= bufnr("%")
- endif
-
- " restore position
- if reusing
- call winrestview(svpos)
- endif
-
- " The s:LocalBrowseRefresh() function is called by an autocmd
- " installed by s:LocalFastBrowser() when g:netrw_fastbrowse <= 1 (ie. slow or medium speed).
- " However, s:NetrwBrowse() causes the FocusGained event to fire the first time.
- return
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwFile: because of g:netrw_keepdir, isdirectory(), type(), etc may or {{{2
-" may not apply correctly; ie. netrw's idea of the current directory may
-" differ from vim's. This function insures that netrw's idea of the current
-" directory is used.
-" Returns a path to the file specified by a:fname
-fun! s:NetrwFile(fname)
-" "" call Dfunc("s:NetrwFile(fname<".a:fname.">) win#".winnr())
-" "" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a'),'~'.expand("<slnum>"))
-" "" call Decho("g:netrw_cygwin =".(exists("g:netrw_cygwin")? g:netrw_cygwin : 'n/a'),'~'.expand("<slnum>"))
-" "" call Decho("g:netrw_liststyle=".(exists("g:netrw_liststyle")? g:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
-" "" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
-
- " clean up any leading treedepthstring
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- let fname= substitute(a:fname,'^'.s:treedepthstring.'\+','','')
-" "" call Decho("clean up any leading treedepthstring: fname<".fname.">",'~'.expand("<slnum>"))
- else
- let fname= a:fname
- endif
-
- if g:netrw_keepdir
- " vim's idea of the current directory possibly may differ from netrw's
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
- endif
-
- if !exists("g:netrw_cygwin") && has("win32")
- if fname =~ '^\' || fname =~ '^\a:\'
- " windows, but full path given
- let ret= fname
-" "" call Decho("windows+full path: isdirectory(".fname.")",'~'.expand("<slnum>"))
- else
- " windows, relative path given
- let ret= s:ComposePath(b:netrw_curdir,fname)
-" "" call Decho("windows+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>"))
- endif
-
- elseif fname =~ '^/'
- " not windows, full path given
- let ret= fname
-" "" call Decho("unix+full path: isdirectory(".fname.")",'~'.expand("<slnum>"))
- else
- " not windows, relative path given
- let ret= s:ComposePath(b:netrw_curdir,fname)
-" "" call Decho("unix+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>"))
- endif
- else
- " vim and netrw agree on the current directory
- let ret= fname
-" "" call Decho("vim and netrw agree on current directory (g:netrw_keepdir=".g:netrw_keepdir.")",'~'.expand("<slnum>"))
-" "" call Decho("vim directory: ".getcwd(),'~'.expand("<slnum>"))
-" "" call Decho("netrw directory: ".(exists("b:netrw_curdir")? b:netrw_curdir : 'n/a'),'~'.expand("<slnum>"))
- endif
-
-" "" call Dret("s:NetrwFile ".ret)
- return ret
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwFileInfo: supports qf (query for file information) {{{2
-fun! s:NetrwFileInfo(islocal,fname)
-" call Dfunc("s:NetrwFileInfo(islocal=".a:islocal." fname<".a:fname.">) b:netrw_curdir<".b:netrw_curdir.">")
- let ykeep= @@
- if a:islocal
- let lsopt= "-lsad"
- if g:netrw_sizestyle =~# 'H'
- let lsopt= "-lsadh"
- elseif g:netrw_sizestyle =~# 'h'
- let lsopt= "-lsadh --si"
- endif
-" call Decho("(s:NetrwFileInfo) lsopt<".lsopt.">")
- if (has("unix") || has("macunix")) && executable("/bin/ls")
-
- if getline(".") == "../"
- echo system("/bin/ls ".lsopt." ".s:ShellEscape(".."))
-" call Decho("#1: echo system(/bin/ls -lsad ".s:ShellEscape(..).")",'~'.expand("<slnum>"))
-
- elseif w:netrw_liststyle == s:TREELIST && getline(".") !~ '^'.s:treedepthstring
- echo system("/bin/ls ".lsopt." ".s:ShellEscape(b:netrw_curdir))
-" call Decho("#2: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir).")",'~'.expand("<slnum>"))
-
- elseif exists("b:netrw_curdir")
- echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,a:fname)))
-" call Decho("#3: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir.a:fname).")",'~'.expand("<slnum>"))
-
- else
-" call Decho('using ls '.a:fname." using cwd<".getcwd().">",'~'.expand("<slnum>"))
- echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:NetrwFile(a:fname)))
-" call Decho("#5: echo system(/bin/ls -lsad ".s:ShellEscape(a:fname).")",'~'.expand("<slnum>"))
- endif
- else
- " use vim functions to return information about file below cursor
-" call Decho("using vim functions to query for file info",'~'.expand("<slnum>"))
- if !isdirectory(s:NetrwFile(a:fname)) && !filereadable(s:NetrwFile(a:fname)) && a:fname =~ '[*@/]'
- let fname= substitute(a:fname,".$","","")
- else
- let fname= a:fname
- endif
- let t = getftime(s:NetrwFile(fname))
- let sz = getfsize(s:NetrwFile(fname))
- if g:netrw_sizestyle =~# "[hH]"
- let sz= s:NetrwHumanReadable(sz)
- endif
- echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname)))
-" call Decho("fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname)),'~'.expand("<slnum>"))
- endif
- else
- echo "sorry, \"qf\" not supported yet for remote files"
- endif
- let @@= ykeep
-" call Dret("s:NetrwFileInfo")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwFullPath: returns the full path to a directory and/or file {{{2
-fun! s:NetrwFullPath(filename)
-" " call Dfunc("s:NetrwFullPath(filename<".a:filename.">)")
- let filename= a:filename
- if filename !~ '^/'
- let filename= resolve(getcwd().'/'.filename)
- endif
- if filename != "/" && filename =~ '/$'
- let filename= substitute(filename,'/$','','')
- endif
-" " call Dret("s:NetrwFullPath <".filename.">")
- return filename
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwGetBuffer: [get a new|find an old netrw] buffer for a netrw listing {{{2
-" returns 0=cleared buffer
-" 1=re-used buffer (buffer not cleared)
-" Nov 09, 2020: tst952 shows that when user does :set hidden that NetrwGetBuffer will come up with a [No Name] buffer (hid fix)
-fun! s:NetrwGetBuffer(islocal,dirname)
-" call Dfunc("s:NetrwGetBuffer(islocal=".a:islocal." dirname<".a:dirname.">) liststyle=".g:netrw_liststyle)
-" 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." hid=".&hid,'~'.expand("<slnum>"))
-" call Decho("netrwbuf dictionary=".(exists("s:netrwbuf")? string(s:netrwbuf) : 'n/a'),'~'.expand("<slnum>"))
-" call Dredir("ls!","s:NetrwGetBuffer")
- let dirname= a:dirname
-
- " re-use buffer if possible {{{3
-" call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>"))
- if !exists("s:netrwbuf")
-" call Decho(" s:netrwbuf initialized to {}",'~'.expand("<slnum>"))
- let s:netrwbuf= {}
- endif
-" call Decho(" s:netrwbuf =".string(s:netrwbuf),'~'.expand("<slnum>"))
-" call Decho(" w:netrw_liststyle =".(exists("w:netrw_liststyle")? w:netrw_liststyle : "n/a"),'~'.expand("<slnum>"))
-
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- let bufnum = -1
-
- if !empty(s:netrwbuf) && has_key(s:netrwbuf,s:NetrwFullPath(dirname))
- if has_key(s:netrwbuf,"NetrwTreeListing")
- let bufnum= s:netrwbuf["NetrwTreeListing"]
- else
- let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)]
- endif
-" call Decho(" NetrwTreeListing: bufnum#".bufnum,'~'.expand("<slnum>"))
- if !bufexists(bufnum)
- call remove(s:netrwbuf,"NetrwTreeListing")
- let bufnum= -1
- endif
- elseif bufnr("NetrwTreeListing") != -1
- let bufnum= bufnr("NetrwTreeListing")
-" call Decho(" NetrwTreeListing".": bufnum#".bufnum,'~'.expand("<slnum>"))
- else
-" call Decho(" did not find a NetrwTreeListing buffer",'~'.expand("<slnum>"))
- let bufnum= -1
- endif
-
- elseif has_key(s:netrwbuf,s:NetrwFullPath(dirname))
- let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)]
-" call Decho(" lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."]=".bufnum,'~'.expand("<slnum>"))
- if !bufexists(bufnum)
- call remove(s:netrwbuf,s:NetrwFullPath(dirname))
- let bufnum= -1
- endif
-
- else
-" call Decho(" lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."] not a key",'~'.expand("<slnum>"))
- let bufnum= -1
- endif
-" call Decho(" bufnum#".bufnum,'~'.expand("<slnum>"))
-
- " highjack the current buffer
- " IF the buffer already has the desired name
- " AND it is empty
- let curbuf = bufname("%")
- if curbuf == '.'
- let curbuf = getcwd()
- endif
-" call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)")
-" call Decho("deciding if netrw may highjack the current buffer#".bufnr("%")."<".curbuf.">",'~'.expand("<slnum>"))
-" call Decho("..dirname<".dirname."> IF dirname == bufname",'~'.expand("<slnum>"))
-" call Decho("..curbuf<".curbuf.">",'~'.expand("<slnum>"))
-" call Decho("..line($)=".line("$")." AND this is 1",'~'.expand("<slnum>"))
-" call Decho("..getline(%)<".getline("%")."> AND this line is empty",'~'.expand("<slnum>"))
- if dirname == curbuf && line("$") == 1 && getline("%") == ""
-" call Dret("s:NetrwGetBuffer 0<cleared buffer> : highjacking buffer#".bufnr("%"))
- return 0
- else " DEBUG
-" call Decho("..did NOT highjack buffer",'~'.expand("<slnum>"))
- endif
- " Aug 14, 2021: was thinking about looking for a [No Name] buffer here and using it, but that might cause problems
-
- " get enew buffer and name it -or- re-use buffer {{{3
- if bufnum < 0 " get enew buffer and name it
-" call Decho("--get enew buffer and name it (bufnum#".bufnum."<0 OR bufexists(".bufnum.")=".bufexists(bufnum)."==0)",'~'.expand("<slnum>"))
- call s:NetrwEnew(dirname)
-" call Decho(" got enew buffer#".bufnr("%")." (altbuf<".expand("#").">)",'~'.expand("<slnum>"))
- " name the buffer
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- " Got enew buffer; transform into a NetrwTreeListing
-" call Decho("--transform enew buffer#".bufnr("%")." into a NetrwTreeListing --",'~'.expand("<slnum>"))
- let w:netrw_treebufnr = bufnr("%")
- call s:NetrwBufRename("NetrwTreeListing")
- if g:netrw_use_noswf
- setl nobl bt=nofile noswf
- else
- setl nobl bt=nofile
- endif
- nnoremap <silent> <buffer> [[ :sil call <SID>TreeListMove('[[')<cr>
- nnoremap <silent> <buffer> ]] :sil call <SID>TreeListMove(']]')<cr>
- nnoremap <silent> <buffer> [] :sil call <SID>TreeListMove('[]')<cr>
- nnoremap <silent> <buffer> ][ :sil call <SID>TreeListMove('][')<cr>
-" call Decho(" tree listing bufnr=".w:netrw_treebufnr,'~'.expand("<slnum>"))
- else
- call s:NetrwBufRename(dirname)
- " enter the new buffer into the s:netrwbuf dictionary
- let s:netrwbuf[s:NetrwFullPath(dirname)]= bufnr("%")
-" call Decho("update netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."]=".bufnr("%"),'~'.expand("<slnum>"))
-" call Decho("netrwbuf dictionary=".string(s:netrwbuf),'~'.expand("<slnum>"))
- endif
-" call Decho(" named enew buffer#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
-
- else " Re-use the buffer
-" call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>"))
- " ignore all events
- let eikeep= &ei
- setl ei=all
-
- if &ft == "netrw"
-" call Decho("buffer type is netrw; not using keepalt with b ".bufnum)
- exe "sil! NetrwKeepj noswapfile b ".bufnum
-" call Dredir("ls!","one")
- else
-" call Decho("buffer type is not netrw; using keepalt with b ".bufnum)
- call s:NetrwEditBuf(bufnum)
-" call Dredir("ls!","two")
- endif
-" call Decho(" line($)=".line("$"),'~'.expand("<slnum>"))
- if bufname("%") == '.'
- call s:NetrwBufRename(getcwd())
- endif
-
- " restore ei
- let &ei= eikeep
-
- if line("$") <= 1 && getline(1) == ""
- " empty buffer
- NetrwKeepj call s:NetrwListSettings(a:islocal)
-" 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,'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Dret("s:NetrwGetBuffer 0<buffer empty> : re-using buffer#".bufnr("%").", but its empty, so refresh it")
- return 0
-
- elseif g:netrw_fastbrowse == 0 || (a:islocal && g:netrw_fastbrowse == 1)
-" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse." a:islocal=".a:islocal.": clear buffer",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwListSettings(a:islocal)
- sil NetrwKeepj %d _
-" 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,'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Dret("s:NetrwGetBuffer 0<cleared buffer> : re-using buffer#".bufnr("%").", but refreshing due to g:netrw_fastbrowse=".g:netrw_fastbrowse)
- return 0
-
- elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
-" call Decho("--re-use tree listing--",'~'.expand("<slnum>"))
-" call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
- setl ma
- sil NetrwKeepj %d _
- NetrwKeepj call s:NetrwListSettings(a:islocal)
-" 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,'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Dret("s:NetrwGetBuffer 0<cleared buffer> : re-using buffer#".bufnr("%").", but treelist mode always needs a refresh")
- return 0
-
- else
-" 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,'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Dret("s:NetrwGetBuffer 1<buffer not cleared>")
- return 1
- endif
- endif
-
- " do netrw settings: make this buffer not-a-file, modifiable, not line-numbered, etc {{{3
- " fastbrowse Local Remote Hiding a buffer implies it may be re-used (fast)
- " slow 0 D D Deleting a buffer implies it will not be re-used (slow)
- " med 1 D H
- " fast 2 H H
-" call Decho("--do netrw settings: make this buffer#".bufnr("%")." not-a-file, modifiable, not line-numbered, etc--",'~'.expand("<slnum>"))
- let fname= expand("%")
- NetrwKeepj call s:NetrwListSettings(a:islocal)
- call s:NetrwBufRename(fname)
-
- " delete all lines from buffer {{{3
-" call Decho("--delete all lines from buffer--",'~'.expand("<slnum>"))
-" call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
- sil! keepalt NetrwKeepj %d _
-
-" 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,'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Dret("s:NetrwGetBuffer 0<cleared buffer>")
- return 0
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwGetcwd: get the current directory. {{{2
-" Change backslashes to forward slashes, if any.
-" If doesc is true, escape certain troublesome characters
-fun! s:NetrwGetcwd(doesc)
-" call Dfunc("NetrwGetcwd(doesc=".a:doesc.")")
- let curdir= substitute(getcwd(),'\\','/','ge')
- if curdir !~ '[\/]$'
- let curdir= curdir.'/'
- endif
- if a:doesc
- let curdir= fnameescape(curdir)
- endif
-" call Dret("NetrwGetcwd <".curdir.">")
- return curdir
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwGetWord: it gets the directory/file named under the cursor {{{2
-fun! s:NetrwGetWord()
-" call Dfunc("s:NetrwGetWord() liststyle=".s:ShowStyle()." virtcol=".virtcol("."))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
- let keepsol= &l:sol
- setl nosol
-
- call s:UseBufWinVars()
-
- " insure that w:netrw_liststyle is set up
- if !exists("w:netrw_liststyle")
- if exists("g:netrw_liststyle")
- let w:netrw_liststyle= g:netrw_liststyle
- else
- let w:netrw_liststyle= s:THINLIST
- endif
-" call Decho("w:netrw_liststyle=".w:netrw_liststyle,'~'.expand("<slnum>"))
- endif
-
- if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt
- " Active Banner support
-" call Decho("active banner handling",'~'.expand("<slnum>"))
- NetrwKeepj norm! 0
- let dirname= "./"
- let curline= getline('.')
-
- if curline =~# '"\s*Sorted by\s'
- NetrwKeepj norm! "_s
- let s:netrw_skipbrowse= 1
- echo 'Pressing "s" also works'
-
- elseif curline =~# '"\s*Sort sequence:'
- let s:netrw_skipbrowse= 1
- echo 'Press "S" to edit sorting sequence'
-
- elseif curline =~# '"\s*Quick Help:'
- NetrwKeepj norm! ?
- let s:netrw_skipbrowse= 1
-
- elseif curline =~# '"\s*\%(Hiding\|Showing\):'
- NetrwKeepj norm! a
- let s:netrw_skipbrowse= 1
- echo 'Pressing "a" also works'
-
- elseif line("$") > w:netrw_bannercnt
- exe 'sil NetrwKeepj '.w:netrw_bannercnt
- endif
-
- elseif w:netrw_liststyle == s:THINLIST
-" call Decho("thin column handling",'~'.expand("<slnum>"))
- NetrwKeepj norm! 0
- let dirname= substitute(getline('.'),'\t -->.*$','','')
-
- elseif w:netrw_liststyle == s:LONGLIST
-" call Decho("long column handling",'~'.expand("<slnum>"))
- NetrwKeepj norm! 0
- let dirname= substitute(getline('.'),'^\(\%(\S\+ \)*\S\+\).\{-}$','\1','e')
-
- elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
-" call Decho("treelist handling",'~'.expand("<slnum>"))
- let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e')
- let dirname= substitute(dirname,'\t -->.*$','','')
-
- else
-" call Decho("obtain word from wide listing",'~'.expand("<slnum>"))
- let dirname= getline('.')
-
- if !exists("b:netrw_cpf")
- let b:netrw_cpf= 0
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif'
- call histdel("/",-1)
-" "call Decho("computed cpf=".b:netrw_cpf,'~'.expand("<slnum>"))
- endif
-
-" call Decho("buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
- let filestart = (virtcol(".")/b:netrw_cpf)*b:netrw_cpf
-" call Decho("filestart= ([virtcol=".virtcol(".")."]/[b:netrw_cpf=".b:netrw_cpf."])*b:netrw_cpf=".filestart." bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>"))
-" call Decho("1: dirname<".dirname.">",'~'.expand("<slnum>"))
- if filestart == 0
- NetrwKeepj norm! 0ma
- else
- call cursor(line("."),filestart+1)
- NetrwKeepj norm! ma
- endif
-
- let dict={}
- " save the unnamed register and register 0-9 and a
- let dict.a=[getreg('a'), getregtype('a')]
- for i in range(0, 9)
- let dict[i] = [getreg(i), getregtype(i)]
- endfor
- let dict.unnamed = [getreg(''), getregtype('')]
-
- let eofname= filestart + b:netrw_cpf + 1
- if eofname <= col("$")
- call cursor(line("."),filestart+b:netrw_cpf+1)
- NetrwKeepj norm! "ay`a
- else
- NetrwKeepj norm! "ay$
- endif
-
- let dirname = @a
- call s:RestoreRegister(dict)
-
-" call Decho("2: dirname<".dirname.">",'~'.expand("<slnum>"))
- let dirname= substitute(dirname,'\s\+$','','e')
-" call Decho("3: dirname<".dirname.">",'~'.expand("<slnum>"))
- endif
-
- " symlinks are indicated by a trailing "@". Remove it before further processing.
- let dirname= substitute(dirname,"@$","","")
-
- " executables are indicated by a trailing "*". Remove it before further processing.
- let dirname= substitute(dirname,"\*$","","")
-
- let &l:sol= keepsol
-
-" call Dret("s:NetrwGetWord <".dirname.">")
- return dirname
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwListSettings: make standard settings for making a netrw listing {{{2
-" g:netrw_bufsettings will be used after the listing is produced.
-" Called by s:NetrwGetBuffer()
-fun! s:NetrwListSettings(islocal)
-" call Dfunc("s:NetrwListSettings(islocal=".a:islocal.")")
-" 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,'~'.expand("<slnum>"))
- let fname= bufname("%")
-" " call Decho("setl bt=nofile nobl ma nonu nowrap noro nornu",'~'.expand("<slnum>"))
- " nobl noma nomod nonu noma nowrap ro nornu (std g:netrw_bufsettings)
- setl bt=nofile nobl ma nonu nowrap noro nornu
- call s:NetrwBufRename(fname)
- if g:netrw_use_noswf
- setl noswf
- endif
-" call Dredir("ls!","s:NetrwListSettings")
-" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>"))
- exe "setl ts=".(g:netrw_maxfilenamelen+1)
- setl isk+=.,~,-
- if g:netrw_fastbrowse > a:islocal
- setl bh=hide
- else
- setl bh=delete
- 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,'~'.expand("<slnum>"))
-" call Dret("s:NetrwListSettings")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwListStyle: change list style (thin - long - wide - tree) {{{2
-" islocal=0: remote browsing
-" =1: local browsing
-fun! s:NetrwListStyle(islocal)
- let ykeep = @@
- let fname = s:NetrwGetWord()
- if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif
- let svpos = winsaveview()
- let w:netrw_liststyle = (w:netrw_liststyle + 1) % s:MAXLIST
-
- " repoint t:netrw_lexbufnr if appropriate
- if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr
- let repointlexbufnr= 1
- endif
-
- if w:netrw_liststyle == s:THINLIST
- " use one column listing
- let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
-
- elseif w:netrw_liststyle == s:LONGLIST
- " use long list
- let g:netrw_list_cmd = g:netrw_list_cmd." -l"
-
- elseif w:netrw_liststyle == s:WIDELIST
- " give wide list
- let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
-
- elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
-
- else
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"bad value for g:netrw_liststyle (=".w:netrw_liststyle.")",46)
- let g:netrw_liststyle = s:THINLIST
- let w:netrw_liststyle = g:netrw_liststyle
- let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
- endif
- setl ma noro
-
- " clear buffer - this will cause NetrwBrowse/LocalBrowseCheck to do a refresh
- sil! NetrwKeepj %d _
- " following prevents tree listing buffer from being marked "modified"
- setl nomod
-
- " refresh the listing
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call s:NetrwCursor(0)
-
- " repoint t:netrw_lexbufnr if appropriate
- if exists("repointlexbufnr")
- let t:netrw_lexbufnr= bufnr("%")
- endif
-
- " restore position; keep cursor on the filename
-" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(svpos)
- let @@= ykeep
-
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBannerCtrl: toggles the display of the banner {{{2
-fun! s:NetrwBannerCtrl(islocal)
- let ykeep= @@
- " toggle the banner (enable/suppress)
- let g:netrw_banner= !g:netrw_banner
-
- " refresh the listing
- let svpos= winsaveview()
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
-
- " keep cursor on the filename
- if g:netrw_banner && exists("w:netrw_bannercnt") && line(".") >= w:netrw_bannercnt
- let fname= s:NetrwGetWord()
- sil NetrwKeepj $
- let result= search('\%(^\%(|\+\s\)\=\|\s\{2,}\)\zs'.escape(fname,'.\[]*$^').'\%(\s\{2,}\|$\)','bc')
-" " call Decho("search result=".result." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'N/A'),'~'.expand("<slnum>"))
- if result <= 0 && exists("w:netrw_bannercnt")
- exe "NetrwKeepj ".w:netrw_bannercnt
- endif
- endif
- let @@= ykeep
-" call Dret("s:NetrwBannerCtrl : g:netrw_banner=".g:netrw_banner)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBookmark: supports :NetrwMB[!] [file]s {{{2
-"
-" No bang: enters files/directories into Netrw's bookmark system
-" No argument and in netrw buffer:
-" if there are marked files: bookmark marked files
-" otherwise : bookmark file/directory under cursor
-" No argument and not in netrw buffer: bookmarks current open file
-" Has arguments: globs them individually and bookmarks them
-"
-" With bang: deletes files/directories from Netrw's bookmark system
-fun! s:NetrwBookmark(del,...)
- if a:0 == 0
- if &ft == "netrw"
- let curbufnr = bufnr("%")
-
- if exists("s:netrwmarkfilelist_{curbufnr}")
- " for every filename in the marked list
- let svpos = winsaveview()
- let islocal= expand("%") !~ '^\a\{3,}://'
- for fname in s:netrwmarkfilelist_{curbufnr}
- if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif
- endfor
- let curdir = exists("b:netrw_curdir")? b:netrw_curdir : getcwd()
- call s:NetrwUnmarkList(curbufnr,curdir)
- NetrwKeepj call s:NetrwRefresh(islocal,s:NetrwBrowseChgDir(islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- else
- let fname= s:NetrwGetWord()
- if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif
- endif
-
- else
- " bookmark currently open file
- let fname= expand("%")
- if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif
- endif
-
- else
- " bookmark specified files
- " attempts to infer if working remote or local
- " by deciding if the current file begins with an url
- " Globbing cannot be done remotely.
- let islocal= expand("%") !~ '^\a\{3,}://'
- let i = 1
- while i <= a:0
- if islocal
- if v:version > 704 || (v:version == 704 && has("patch656"))
- let mbfiles= glob(fnameescape(a:{i}),0,1,1)
- else
- let mbfiles= glob(fnameescape(a:{i}),0,1)
- endif
- else
- let mbfiles= [a:{i}]
- endif
- for mbfile in mbfiles
- if a:del|call s:DeleteBookmark(mbfile)|else|call s:MakeBookmark(mbfile)|endif
- endfor
- let i= i + 1
- endwhile
- endif
-
- " update the menu
- call s:NetrwBookmarkMenu()
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBookmarkMenu: Uses menu priorities {{{2
-" .2.[cnt] for bookmarks, and
-" .3.[cnt] for history
-" (see s:NetrwMenu())
-fun! s:NetrwBookmarkMenu()
- if !exists("s:netrw_menucnt")
- return
- endif
-" call Dfunc("NetrwBookmarkMenu() histcnt=".g:netrw_dirhistcnt." menucnt=".s:netrw_menucnt)
-
- " the following test assures that gvim is running, has menus available, and has menus enabled.
- if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
- if exists("g:NetrwTopLvlMenu")
-" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>"))
- exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks'
- exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete'
- endif
- if !exists("s:netrw_initbookhist")
- call s:NetrwBookHistRead()
- endif
-
- " show bookmarked places
- if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0
- let cnt= 1
- for bmd in g:netrw_bookmarklist
-" call Decho('sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmark.'.bmd.' :e '.bmd,'~'.expand("<slnum>"))
- let bmd= escape(bmd,g:netrw_menu_escape)
-
- " show bookmarks for goto menu
- exe 'sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks.'.bmd.' :e '.bmd."\<cr>"
-
- " show bookmarks for deletion menu
- exe 'sil! menu '.g:NetrwMenuPriority.".8.2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete.'.bmd.' '.cnt."mB"
- let cnt= cnt + 1
- endfor
-
- endif
-
- " show directory browsing history
- if g:netrw_dirhistmax > 0
- let cnt = g:netrw_dirhistcnt
- let first = 1
- let histcnt = 0
- while ( first || cnt != g:netrw_dirhistcnt )
- let histcnt = histcnt + 1
- let priority = g:netrw_dirhistcnt + histcnt
- if exists("g:netrw_dirhist_{cnt}")
- let histdir= escape(g:netrw_dirhist_{cnt},g:netrw_menu_escape)
-" call Decho('sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir,'~'.expand("<slnum>"))
- exe 'sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir."\<cr>"
- endif
- let first = 0
- let cnt = ( cnt - 1 ) % g:netrw_dirhistmax
- if cnt < 0
- let cnt= cnt + g:netrw_dirhistmax
- endif
- endwhile
- endif
-
- endif
-" call Dret("NetrwBookmarkMenu")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBrowseChgDir: constructs a new directory based on the current {{{2
-" directory and a new directory name. Also, if the
-" "new directory name" is actually a file,
-" NetrwBrowseChgDir() edits the file.
-" cursor=0: newdir is relative to b:netrw_curdir
-" =1: newdir is relative to the path to the word under the cursor in
-" tree view
-fun! s:NetrwBrowseChgDir(islocal,newdir,cursor,...)
- let ykeep= @@
- if !exists("b:netrw_curdir")
- " Don't try to change-directory: this can happen, for example, when netrw#ErrorMsg has been called
- " and the current window is the NetrwMessage window.
- let @@= ykeep
- return
- endif
-
- " NetrwBrowseChgDir; save options and initialize {{{3
- call s:SavePosn(s:netrw_posn)
- NetrwKeepj call s:NetrwOptionsSave("s:")
- NetrwKeepj call s:NetrwOptionsSafe(a:islocal)
-
- let newdir = a:newdir
- if a:cursor && exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop")
- " dirname is the path to the word under the cursor
- let dirname = s:NetrwTreePath(w:netrw_treetop)
- " newdir resolves to a directory and points to a directory in dirname
- " /tmp/test/folder_symlink/ -> /tmp/test/original_folder/
- if a:islocal && fnamemodify(dirname, ':t') == newdir && isdirectory(resolve(dirname)) && resolve(dirname) == resolve(newdir)
- let dirname = fnamemodify(resolve(dirname), ':p:h:h')
- let newdir = fnamemodify(resolve(newdir), ':t')
- endif
- " Remove trailing "/"
- let dirname = substitute(dirname, "/$", "", "")
-
- " If the word under the cursor is a directory (except for ../), NetrwTreePath
- " returns the full path, including the word under the cursor, remove it
- if newdir =~ "/$" && newdir != "../"
- let dirname = fnamemodify(dirname, ":h")
- endif
- else
- let dirname = b:netrw_curdir
- endif
- if has("win32")
- let dirname = substitute(dirname,'\\','/','ge')
- endif
- let dolockout = 0
- let dorestore = 1
-
- " ignore <cr>s when done in the banner
- if g:netrw_banner
- if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt
- if getline(".") =~# 'Quick Help'
- let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp)
- setl ma noro nowrap
- NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp])
- setl noma nomod nowrap
- NetrwKeepj call s:NetrwOptionsRestore("s:")
- endif
- endif
- endif
-
- " set up o/s-dependent directory recognition pattern
- if has("amiga")
- let dirpat= '[\/:]$'
- else
- let dirpat= '[\/]$'
- endif
-
- if dirname !~ dirpat
- " apparently vim is "recognizing" that it is in a directory and
- " is removing the trailing "/". Bad idea, so let's put it back.
- let dirname= dirname.'/'
- endif
-
- if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir))))
- " ------------------------------
- " NetrwBrowseChgDir: edit a file {{{3
- " ------------------------------
-
- " save position for benefit of Rexplore
- let s:rexposn_{bufnr("%")}= winsaveview()
-
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") && newdir !~ '^\(/\|\a:\)'
- if dirname =~ '/$'
- let dirname= dirname.newdir
- else
- let dirname= dirname."/".newdir
- endif
- elseif newdir =~ '^\(/\|\a:\)'
- let dirname= newdir
- else
- let dirname= s:ComposePath(dirname,newdir)
- endif
- " this lets netrw#BrowseX avoid the edit
- if a:0 < 1
- NetrwKeepj call s:NetrwOptionsRestore("s:")
- let curdir= b:netrw_curdir
- if !exists("s:didsplit")
- if type(g:netrw_browse_split) == 3
- " open file in server
- " Note that g:netrw_browse_split is a List: [servername,tabnr,winnr]
- call s:NetrwServerEdit(a:islocal,dirname)
- return
-
- elseif g:netrw_browse_split == 1
- " horizontally splitting the window first
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
- exe "keepalt ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
- if !&ea
- keepalt wincmd _
- endif
- call s:SetRexDir(a:islocal,curdir)
-
- elseif g:netrw_browse_split == 2
- " vertically splitting the window first
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
- exe "keepalt ".(g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s"
- if !&ea
- keepalt wincmd |
- endif
- call s:SetRexDir(a:islocal,curdir)
-
- elseif g:netrw_browse_split == 3
- " open file in new tab
- keepalt tabnew
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
- endif
- call s:SetRexDir(a:islocal,curdir)
-
- elseif g:netrw_browse_split == 4
- " act like "P" (ie. open previous window)
- if s:NetrwPrevWinOpen(2) == 3
- let @@= ykeep
- return
- endif
- call s:SetRexDir(a:islocal,curdir)
-
- else
- " handling a file, didn't split, so remove menu
- call s:NetrwMenu(0)
- " optional change to window
- if g:netrw_chgwin >= 1
- if winnr("$")+1 == g:netrw_chgwin
- " if g:netrw_chgwin is set to one more than the last window, then
- " vertically split the last window to make that window available.
- let curwin= winnr()
- exe "NetrwKeepj keepalt ".winnr("$")."wincmd w"
- vs
- exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd ".curwin
- endif
- exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w"
- endif
- call s:SetRexDir(a:islocal,curdir)
- endif
-
- endif
-
- " the point where netrw actually edits the (local) file
- " if its local only: LocalBrowseCheck() doesn't edit a file, but NetrwBrowse() will
- " use keepalt to support :e # to return to a directory listing
- if !&mod
- " if e the new file would fail due to &mod, then don't change any of the flags
- let dolockout= 1
- endif
- if a:islocal
- " some like c-^ to return to the last edited file
- " others like c-^ to return to the netrw buffer
- " Apr 30, 2020: used to have e! here. That can cause loss of a modified file,
- " so emit error E37 instead.
- call s:NetrwEditFile("e","",dirname)
- call s:NetrwCursor(1)
- if &hidden || &bufhidden == "hide"
- " file came from vim's hidden storage. Don't "restore" options with it.
- let dorestore= 0
- endif
- else
- endif
-
- " handle g:Netrw_funcref -- call external-to-netrw functions
- " This code will handle g:Netrw_funcref as an individual function reference
- " or as a list of function references. It will ignore anything that's not
- " a function reference. See :help Funcref for information about function references.
- if exists("g:Netrw_funcref")
- if type(g:Netrw_funcref) == 2
- NetrwKeepj call g:Netrw_funcref()
- elseif type(g:Netrw_funcref) == 3
- for Fncref in g:Netrw_funcref
- if type(Fncref) == 2
- NetrwKeepj call Fncref()
- endif
- endfor
- endif
- endif
- endif
-
- elseif newdir =~ '^/'
- " ----------------------------------------------------
- " NetrwBrowseChgDir: just go to the new directory spec {{{3
- " ----------------------------------------------------
- let dirname = newdir
- NetrwKeepj call s:SetRexDir(a:islocal,dirname)
- NetrwKeepj call s:NetrwOptionsRestore("s:")
- norm! m`
-
- elseif newdir == './'
- " ---------------------------------------------
- " NetrwBrowseChgDir: refresh the directory list {{{3
- " ---------------------------------------------
- NetrwKeepj call s:SetRexDir(a:islocal,dirname)
- norm! m`
-
- elseif newdir == '../'
- " --------------------------------------
- " NetrwBrowseChgDir: go up one directory {{{3
- " --------------------------------------
-
- if w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
- " force a refresh
- setl noro ma
- NetrwKeepj %d _
- endif
-
- if has("amiga")
- " amiga
- if a:islocal
- let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+$\)','\1','')
- let dirname= substitute(dirname,'/$','','')
- else
- let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+/$\)','\1','')
- endif
-
- elseif !g:netrw_cygwin && has("win32")
- " windows
- if a:islocal
- let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','')
- if dirname == ""
- let dirname= '/'
- endif
- else
- let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','')
- endif
- if dirname =~ '^\a:$'
- let dirname= dirname.'/'
- endif
-
- else
- " unix or cygwin
- if a:islocal
- let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','')
- if dirname == ""
- let dirname= '/'
- endif
- else
- let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','')
- endif
- endif
- NetrwKeepj call s:SetRexDir(a:islocal,dirname)
- norm! m`
-
- elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
- " --------------------------------------
- " NetrwBrowseChgDir: Handle Tree Listing {{{3
- " --------------------------------------
- " force a refresh (for TREELIST, NetrwTreeDir() will force the refresh)
- setl noro ma
- if !(exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir"))
- NetrwKeepj %d _
- endif
- let treedir = s:NetrwTreeDir(a:islocal)
- let s:treecurpos = winsaveview()
- let haskey = 0
-
- " search treedict for tree dir as-is
- if has_key(w:netrw_treedict,treedir)
- let haskey= 1
- else
- endif
-
- " search treedict for treedir with a [/@] appended
- if !haskey && treedir !~ '[/@]$'
- if has_key(w:netrw_treedict,treedir."/")
- let treedir= treedir."/"
- let haskey = 1
- else
- endif
- endif
-
- " search treedict for treedir with any trailing / elided
- if !haskey && treedir =~ '/$'
- let treedir= substitute(treedir,'/$','','')
- if has_key(w:netrw_treedict,treedir)
- let haskey = 1
- else
- endif
- endif
-
- if haskey
- " close tree listing for selected subdirectory
- call remove(w:netrw_treedict,treedir)
- let dirname= w:netrw_treetop
- else
- " go down one directory
- let dirname= substitute(treedir,'/*$','/','')
- endif
- NetrwKeepj call s:SetRexDir(a:islocal,dirname)
- let s:treeforceredraw = 1
-
- else
- " ----------------------------------------
- " NetrwBrowseChgDir: Go down one directory {{{3
- " ----------------------------------------
- let dirname = s:ComposePath(dirname,newdir)
- NetrwKeepj call s:SetRexDir(a:islocal,dirname)
- norm! m`
- endif
-
- " --------------------------------------
- " NetrwBrowseChgDir: Restore and Cleanup {{{3
- " --------------------------------------
- if dorestore
- " dorestore is zero'd when a local file was hidden or bufhidden;
- " in such a case, we want to keep whatever settings it may have.
- NetrwKeepj call s:NetrwOptionsRestore("s:")
- endif
- if dolockout && dorestore
- if filewritable(dirname)
- setl ma noro nomod
- else
- setl ma ro nomod
- endif
- endif
- call s:RestorePosn(s:netrw_posn)
- let @@= ykeep
-
- return dirname
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBrowseUpDir: implements the "-" mappings {{{2
-" for thin, long, and wide: cursor placed just after banner
-" for tree, keeps cursor on current filename
-fun! s:NetrwBrowseUpDir(islocal)
- if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt-1
- " this test needed because occasionally this function seems to be incorrectly called
- " when multiple leftmouse clicks are taken when atop the one line help in the banner.
- " I'm allowing the very bottom line to permit a "-" exit so that one may escape empty
- " directories.
- return
- endif
-
- norm! 0
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
- let curline= getline(".")
- let swwline= winline() - 1
- if exists("w:netrw_treetop")
- let b:netrw_curdir= w:netrw_treetop
- elseif exists("b:netrw_curdir")
- let w:netrw_treetop= b:netrw_curdir
- else
- let w:netrw_treetop= getcwd()
- let b:netrw_curdir = w:netrw_treetop
- endif
- let curfile = getline(".")
- let curpath = s:NetrwTreePath(w:netrw_treetop)
- if a:islocal
- call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0))
- else
- call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0))
- endif
- if w:netrw_treetop == '/'
- keepj call search('^\M'.curfile,"w")
- elseif curfile == '../'
- keepj call search('^\M'.curfile,"wb")
- else
- while 1
- keepj call search('^\M'.s:treedepthstring.curfile,"wb")
- let treepath= s:NetrwTreePath(w:netrw_treetop)
- if treepath == curpath
- break
- endif
- endwhile
- endif
-
- else
- call s:SavePosn(s:netrw_posn)
- if exists("b:netrw_curdir")
- let curdir= b:netrw_curdir
- else
- let curdir= expand(getcwd())
- endif
- if a:islocal
- call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0))
- else
- call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0))
- endif
- call s:RestorePosn(s:netrw_posn)
- let curdir= substitute(curdir,'^.*[\/]','','')
- let curdir= '\<'. escape(curdir, '~'). '/'
- call search(curdir,'wc')
- endif
-endfun
-
-func s:redir()
- " set up redirection (avoids browser messages)
- " by default if not set, g:netrw_suppress_gx_mesg is true
- if get(g:, 'netrw_suppress_gx_mesg', 1)
- if &srr =~# "%s"
- return printf(&srr, has("win32") ? "nul" : "/dev/null")
- else
- return &srr .. (has("win32") ? "nul" : "/dev/null")
- endif
- endif
- return ''
-endfunc
-
-if has('unix')
- if has('win32unix')
- " Cygwin provides cygstart
- if executable('cygstart')
- fun! netrw#Launch(args)
- exe 'silent ! cygstart --hide' a:args s:redir() | redraw!
- endfun
- elseif !empty($MSYSTEM) && executable('start')
- " MSYS2/Git Bash comes by default without cygstart; see
- " https://www.msys2.org/wiki/How-does-MSYS2-differ-from-Cygwin
- " Instead it provides /usr/bin/start script running `cmd.exe //c start`
- " Adding "" //b` sets void title, hides cmd window and blocks path conversion
- " of /b to \b\ " by MSYS2; see https://www.msys2.org/docs/filesystem-paths/
- fun! netrw#Launch(args)
- exe 'silent !start "" //b' a:args s:redir() | redraw!
- endfun
- else
- " imitate /usr/bin/start script for other environments and hope for the best
- fun! netrw#Launch(args)
- exe 'silent !cmd //c start "" //b' a:args s:redir() | redraw!
- endfun
- endif
- elseif exists('$WSL_DISTRO_NAME') " use cmd.exe to start GUI apps in WSL
- fun! netrw#Launch(args)
- let args = a:args
- exe 'silent !' ..
- \ ((args =~? '\v<\f+\.(exe|com|bat|cmd)>') ?
- \ 'cmd.exe /c start /b ' .. args :
- \ 'nohup ' .. args .. ' ' .. s:redir() .. ' &')
- \ | redraw!
- endfun
- else
- fun! netrw#Launch(args)
- exe ':silent ! nohup' a:args s:redir() '&' | redraw!
- endfun
- endif
-elseif has('win32')
- fun! netrw#Launch(args)
- exe 'silent !' .. (&shell =~? '\<cmd\.exe\>' ? '' : 'cmd.exe /c')
- \ 'start "" /b' a:args s:redir() | redraw!
- endfun
-else
- fun! netrw#Launch(dummy)
- echom 'No common launcher found'
- endfun
-endif
-
-" Git Bash
-if has('win32unix')
- " (cyg)start suffices
- let s:os_viewer = ''
-" Windows / WSL
-elseif executable('explorer.exe')
- let s:os_viewer = 'explorer.exe'
-" Linux / BSD
-elseif executable('xdg-open')
- let s:os_viewer = 'xdg-open'
-" MacOS
-elseif executable('open')
- let s:os_viewer = 'open'
-endif
-
-fun! s:viewer()
- if exists('g:netrw_browsex_viewer') && executable(g:netrw_browsex_viewer)
- " extract any viewing options. Assumes that they're set apart by spaces.
- " call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>"))
- if g:netrw_browsex_viewer =~ '\s'
- let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','')
- let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." "
- let oviewer = ''
- let cnt = 1
- while !executable(viewer) && viewer != oviewer
- let viewer = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\1','')
- let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." "
- let cnt = cnt + 1
- let oviewer = viewer
- " call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>"))
- endwhile
- else
- let viewer = g:netrw_browsex_viewer
- let viewopt = ""
- endif
- " call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>"))
- return viewer .. ' ' .. viewopt
- else
- if !exists('s:os_viewer')
- call netrw#ErrorMsg(s:ERROR,"No program to open this path found. See :help Open for more information.",106)
- else
- return s:os_viewer
- endif
- endif
-endfun
-
-fun! netrw#Open(file) abort
- call netrw#Launch(s:viewer() .. ' ' .. shellescape(a:file, 1))
-endf
-
-if !exists('g:netrw_regex_url')
- let g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}'
-endif
-
-" ---------------------------------------------------------------------
-" netrw#BrowseX: (implements "x" and "gx") executes a special "viewer" script or program for the {{{2
-" given filename; typically this means given their extension.
-" 0=local, 1=remote
-fun! netrw#BrowseX(fname,remote)
- if a:remote == 1 && a:fname !~ '^https\=:' && a:fname =~ '/$'
- " remote directory, not a webpage access, looks like an attempt to do a directory listing
- norm! gf
- endif
-
- if exists("g:netrw_browsex_viewer") && exists("g:netrw_browsex_support_remote") && !g:netrw_browsex_support_remote
- let remote = a:remote
- else
- let remote = 0
- endif
-
- let ykeep = @@
- let screenposn = winsaveview()
-
- " need to save and restore aw setting as gx can invoke this function from non-netrw buffers
- let awkeep = &aw
- set noaw
-
- " special core dump handler
- if a:fname =~ '/core\(\.\d\+\)\=$'
- if exists("g:Netrw_corehandler")
- if type(g:Netrw_corehandler) == 2
- " g:Netrw_corehandler is a function reference (see :help Funcref)
- call g:Netrw_corehandler(s:NetrwFile(a:fname))
- elseif type(g:Netrw_corehandler) == 3
- " g:Netrw_corehandler is a List of function references (see :help Funcref)
- for Fncref in g:Netrw_corehandler
- if type(Fncref) == 2
- call Fncref(a:fname)
- endif
- endfor
- endif
- call winrestview(screenposn)
- let @@= ykeep
- let &aw= awkeep
- return
- endif
- endif
-
- " set up the filename
- " (lower case the extension, make a local copy of a remote file)
- let exten= substitute(a:fname,'.*\.\(.\{-}\)','\1','e')
- if has("win32")
- let exten= substitute(exten,'^.*$','\L&\E','')
- endif
- if exten =~ "[\\/]"
- let exten= ""
- endif
-
- if remote == 1
- " create a local copy
- setl bh=delete
- call netrw#NetRead(3,a:fname)
- " attempt to rename tempfile
- let basename= substitute(a:fname,'^\(.*\)/\(.*\)\.\([^.]*\)$','\2','')
- let newname = substitute(s:netrw_tmpfile,'^\(.*\)/\(.*\)\.\([^.]*\)$','\1/'.basename.'.\3','')
- if s:netrw_tmpfile != newname && newname != ""
- if rename(s:netrw_tmpfile,newname) == 0
- " renaming succeeded
- let fname= newname
- else
- " renaming failed
- let fname= s:netrw_tmpfile
- endif
- else
- let fname= s:netrw_tmpfile
- endif
- else
- let fname= a:fname
- " special ~ handler for local
- if fname =~ '^\~' && expand("$HOME") != ""
- let fname= s:NetrwFile(substitute(fname,'^\~',expand("$HOME"),''))
- endif
- endif
-
- " although shellescape(..., 1) is used in netrw#Open(), it's insufficient
- call netrw#Open(escape(fname, '#%'))
-
- " cleanup: remove temporary file,
- " delete current buffer if success with handler,
- " return to prior buffer (directory listing)
- " Feb 12, 2008: had to de-activate removal of
- " temporary file because it wasn't getting seen.
-" if remote == 1 && fname != a:fname
-" call s:NetrwDelete(fname)
-" endif
-
- if remote == 1
- setl bh=delete bt=nofile
- if g:netrw_use_noswf
- setl noswf
- endif
- exe "sil! NetrwKeepj norm! \<c-o>"
- endif
- call winrestview(screenposn)
- let @@ = ykeep
- let &aw= awkeep
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#GX: gets word under cursor for gx support {{{2
-" See also: netrw#BrowseXVis
-" netrw#BrowseX
-fun! netrw#GX()
-" call Dfunc("netrw#GX()")
- if &ft == "netrw"
- let fname= s:NetrwGetWord()
- else
- let fname= exists("g:netrw_gx")? expand(g:netrw_gx) : s:GetURL()
- endif
-" call Dret("netrw#GX <".fname.">")
- return fname
-endfun
-
-fun! s:GetURL() abort
- let URL = ''
- if exists('*Netrw_get_URL_' .. &filetype)
- let URL = call('Netrw_get_URL_' .. &filetype, [])
- endif
- if !empty(URL) | return URL | endif
- " URLs end in letter, digit or forward slash
- let URL = matchstr(expand("<cWORD>"), '\<' .. g:netrw_regex_url .. '\ze[^A-Za-z0-9/]*$')
- if !empty(URL) | return URL | endif
-
- " Is it a file in the current work dir ...
- let file = expand("<cfile>")
- if filereadable(file) | return file | endif
- " ... or in that of the current buffer?
- let path = fnamemodify(expand('%'), ':p')
- if isdirectory(path)
- let dir = path
- elseif filereadable(path)
- let dir = fnamemodify(path, ':h')
- endif
- if exists('dir') && filereadable(dir..'/'..file) | return dir..'/'..file | endif
-
- return ''
-endf
-
-" ---------------------------------------------------------------------
-" netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2
-fun! netrw#BrowseXVis()
- let dict={}
- let dict.a=[getreg('a'), getregtype('a')]
- norm! gv"ay
- let gxfile= @a
- call s:RestoreRegister(dict)
- call netrw#BrowseX(gxfile,netrw#CheckIfRemote(gxfile))
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBufRename: renames a buffer without the side effect of retaining an unlisted buffer having the old name {{{2
-" Using the file command on a "[No Name]" buffer does not seem to cause the old "[No Name]" buffer
-" to become an unlisted buffer, so in that case don't bwipe it.
-fun! s:NetrwBufRename(newname)
-" call Dfunc("s:NetrwBufRename(newname<".a:newname.">) buf(%)#".bufnr("%")."<".bufname(bufnr("%")).">")
-" call Dredir("ls!","s:NetrwBufRename (before rename)")
- let oldbufname= bufname(bufnr("%"))
-" call Decho("buf#".bufnr("%").": oldbufname<".oldbufname.">",'~'.expand("<slnum>"))
-
- if oldbufname != a:newname
-" call Decho("do buffer rename: oldbufname<".oldbufname."> ≠ a:newname<".a:newname.">",'~'.expand("<slnum>"))
- let b:junk= 1
-" call Decho("rename buffer: sil! keepj keepalt file ".fnameescape(a:newname),'~'.expand("<slnum>"))
- exe 'sil! keepj keepalt file '.fnameescape(a:newname)
-" call Dredir("ls!","s:NetrwBufRename (before bwipe)~".expand("<slnum>"))
- let oldbufnr= bufnr(oldbufname)
-" call Decho("oldbufname<".oldbufname."> oldbufnr#".oldbufnr,'~'.expand("<slnum>"))
-" call Decho("bufnr(%)=".bufnr("%"),'~'.expand("<slnum>"))
- if oldbufname != "" && oldbufnr != -1 && oldbufnr != bufnr("%")
-" call Decho("bwipe ".oldbufnr,'~'.expand("<slnum>"))
- exe "bwipe! ".oldbufnr
-" else " Decho
-" call Decho("did *not* bwipe buf#".oldbufnr,'~'.expand("<slnum>"))
-" call Decho("..reason: if oldbufname<".oldbufname."> is empty",'~'.expand("<slnum>"))"
-" call Decho("..reason: if oldbufnr#".oldbufnr." is -1",'~'.expand("<slnum>"))"
-" call Decho("..reason: if oldbufnr#".oldbufnr." != bufnr(%)#".bufnr("%"),'~'.expand("<slnum>"))"
- endif
-" call Dredir("ls!","s:NetrwBufRename (after rename)")
-" else " Decho
-" call Decho("oldbufname<".oldbufname."> == a:newname: did *not* rename",'~'.expand("<slnum>"))
- endif
-
-" call Dret("s:NetrwBufRename : buf#".bufnr("%").": oldname<".oldbufname."> newname<".a:newname."> expand(%)<".expand("%").">")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#CheckIfRemote: returns 1 if current file looks like an url, 0 else {{{2
-fun! netrw#CheckIfRemote(...)
-" call Dfunc("netrw#CheckIfRemote() a:0=".a:0)
- if a:0 > 0
- let curfile= a:1
- else
- let curfile= expand("%")
- endif
-
- " Ignore terminal buffers
- if &buftype ==# 'terminal'
- return 0
- endif
-" call Decho("curfile<".curfile.">")
- if curfile =~ '^\a\{3,}://'
-" call Dret("netrw#CheckIfRemote 1")
- return 1
- else
-" call Dret("netrw#CheckIfRemote 0")
- return 0
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwChgPerm: (implements "gp") change file permission {{{2
-fun! s:NetrwChgPerm(islocal,curdir)
- let ykeep = @@
- call inputsave()
- let newperm= input("Enter new permission: ")
- call inputrestore()
- let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',s:ShellEscape(expand("<cfile>")),'')
- let chgperm= substitute(chgperm,'\<PERM\>',s:ShellEscape(newperm),'')
- call system(chgperm)
- if v:shell_error != 0
- NetrwKeepj call netrw#ErrorMsg(1,"changing permission on file<".expand("<cfile>")."> seems to have failed",75)
- endif
- if a:islocal
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- endif
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:CheckIfKde: checks if kdeinit is running {{{2
-" Returns 0: kdeinit not running
-" 1: kdeinit is running
-fun! s:CheckIfKde()
-" call Dfunc("s:CheckIfKde()")
- " seems kde systems often have gnome-open due to dependencies, even though
- " gnome-open's subsidiary display tools are largely absent. Kde systems
- " usually have "kdeinit" running, though... (tnx Mikolaj Machowski)
- if !exists("s:haskdeinit")
- if has("unix") && executable("ps") && !has("win32unix")
- let s:haskdeinit= system("ps -e") =~ '\<kdeinit'
- if v:shell_error
- let s:haskdeinit = 0
- endif
- else
- let s:haskdeinit= 0
- endif
-" call Decho("setting s:haskdeinit=".s:haskdeinit,'~'.expand("<slnum>"))
- endif
-
-" call Dret("s:CheckIfKde ".s:haskdeinit)
- return s:haskdeinit
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwClearExplore: clear explore variables (if any) {{{2
-fun! s:NetrwClearExplore()
-" call Dfunc("s:NetrwClearExplore()")
- 2match none
- if exists("s:explore_match") |unlet s:explore_match |endif
- if exists("s:explore_indx") |unlet s:explore_indx |endif
- if exists("s:netrw_explore_prvdir") |unlet s:netrw_explore_prvdir |endif
- if exists("s:dirstarstar") |unlet s:dirstarstar |endif
- if exists("s:explore_prvdir") |unlet s:explore_prvdir |endif
- if exists("w:netrw_explore_indx") |unlet w:netrw_explore_indx |endif
- if exists("w:netrw_explore_listlen")|unlet w:netrw_explore_listlen|endif
- if exists("w:netrw_explore_list") |unlet w:netrw_explore_list |endif
- if exists("w:netrw_explore_bufnr") |unlet w:netrw_explore_bufnr |endif
-" redraw!
-" call Dret("s:NetrwClearExplore")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwEditBuf: decides whether or not to use keepalt to edit a buffer {{{2
-fun! s:NetrwEditBuf(bufnum)
-" call Dfunc("s:NetrwEditBuf(fname<".a:bufnum.">)")
- if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw"
-" call Decho("exe sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum))
- exe "sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum)
- else
-" call Decho("exe sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum))
- exe "sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum)
- endif
-" call Dret("s:NetrwEditBuf")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwEditFile: decides whether or not to use keepalt to edit a file {{{2
-" NetrwKeepj [keepalt] <OPT> <CMD> <FILENAME>
-fun! s:NetrwEditFile(cmd,opt,fname)
-" call Dfunc("s:NetrwEditFile(cmd<".a:cmd.">,opt<".a:opt.">,fname<".a:fname.">) ft<".&ft.">")
- if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw"
-" call Decho("exe NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname))
- exe "NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname)
- else
-" call Decho("exe NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname))
- if a:cmd =~# 'e\%[new]!' && !&hidden && getbufvar(bufname('%'), '&modified', 0)
- call setbufvar(bufname('%'), '&bufhidden', 'hide')
- endif
- exe "NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname)
- endif
-" call Dret("s:NetrwEditFile")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwExploreListUniq: {{{2
-fun! s:NetrwExploreListUniq(explist)
- " this assumes that the list is already sorted
- let newexplist= []
- for member in a:explist
- if !exists("uniqmember") || member != uniqmember
- let uniqmember = member
- let newexplist = newexplist + [ member ]
- endif
- endfor
- return newexplist
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwForceChgDir: (gd support) Force treatment as a directory {{{2
-fun! s:NetrwForceChgDir(islocal,newdir)
- let ykeep= @@
- if a:newdir !~ '/$'
- " ok, looks like force is needed to get directory-style treatment
- if a:newdir =~ '@$'
- let newdir= substitute(a:newdir,'@$','/','')
- elseif a:newdir =~ '[*=|\\]$'
- let newdir= substitute(a:newdir,'.$','/','')
- else
- let newdir= a:newdir.'/'
- endif
- else
- " should already be getting treatment as a directory
- let newdir= a:newdir
- endif
- let newdir= s:NetrwBrowseChgDir(a:islocal,newdir,0)
- call s:NetrwBrowse(a:islocal,newdir)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwGlob: does glob() if local, remote listing otherwise {{{2
-" direntry: this is the name of the directory. Will be fnameescape'd to prevent wildcard handling by glob()
-" expr : this is the expression to follow the directory. Will use s:ComposePath()
-" pare =1: remove the current directory from the resulting glob() filelist
-" =0: leave the current directory in the resulting glob() filelist
-fun! s:NetrwGlob(direntry,expr,pare)
-" call Dfunc("s:NetrwGlob(direntry<".a:direntry."> expr<".a:expr."> pare=".a:pare.")")
- if netrw#CheckIfRemote()
- keepalt 1sp
- keepalt enew
- let keep_liststyle = w:netrw_liststyle
- let w:netrw_liststyle = s:THINLIST
- if s:NetrwRemoteListing() == 0
- keepj keepalt %s@/@@
- let filelist= getline(1,$)
- q!
- else
- " remote listing error -- leave treedict unchanged
- let filelist= w:netrw_treedict[a:direntry]
- endif
- let w:netrw_liststyle= keep_liststyle
- else
- let path= s:ComposePath(fnameescape(a:direntry), a:expr)
- if has("win32")
- " escape [ so it is not detected as wildcard character, see :h wildcard
- let path= substitute(path, '[', '[[]', 'g')
- endif
- if v:version > 704 || (v:version == 704 && has("patch656"))
- let filelist= glob(path,0,1,1)
- else
- let filelist= glob(path,0,1)
- endif
- if a:pare
- let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")')
- endif
- endif
- return filelist
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwForceFile: (gf support) Force treatment as a file {{{2
-fun! s:NetrwForceFile(islocal,newfile)
- if a:newfile =~ '[/@*=|\\]$'
- let newfile= substitute(a:newfile,'.$','','')
- else
- let newfile= a:newfile
- endif
- if a:islocal
- call s:NetrwBrowseChgDir(a:islocal,newfile,0)
- else
- call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,newfile,0))
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwHide: this function is invoked by the "a" map for browsing {{{2
-" and switches the hiding mode. The actual hiding is done by
-" s:NetrwListHide().
-" g:netrw_hide= 0: show all
-" 1: show not-hidden files
-" 2: show hidden files only
-fun! s:NetrwHide(islocal)
- let ykeep= @@
- let svpos= winsaveview()
-
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
-
- " hide the files in the markfile list
- for fname in s:netrwmarkfilelist_{bufnr("%")}
- if match(g:netrw_list_hide,'\<'.fname.'\>') != -1
- " remove fname from hiding list
- let g:netrw_list_hide= substitute(g:netrw_list_hide,'..\<'.escape(fname,g:netrw_fname_escape).'\>..','','')
- let g:netrw_list_hide= substitute(g:netrw_list_hide,',,',',','g')
- let g:netrw_list_hide= substitute(g:netrw_list_hide,'^,\|,$','','')
- else
- " append fname to hiding list
- if exists("g:netrw_list_hide") && g:netrw_list_hide != ""
- let g:netrw_list_hide= g:netrw_list_hide.',\<'.escape(fname,g:netrw_fname_escape).'\>'
- else
- let g:netrw_list_hide= '\<'.escape(fname,g:netrw_fname_escape).'\>'
- endif
- endif
- endfor
- NetrwKeepj call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir)
- let g:netrw_hide= 1
-
- else
-
- " switch between show-all/show-not-hidden/show-hidden
- let g:netrw_hide=(g:netrw_hide+1)%3
- exe "NetrwKeepj norm! 0"
- if g:netrw_hide && g:netrw_list_hide == ""
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your hiding list is empty!",49)
- let @@= ykeep
- return
- endif
- endif
-
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwHideEdit: allows user to edit the file/directory hiding list {{{2
-fun! s:NetrwHideEdit(islocal)
- let ykeep= @@
- " save current cursor position
- let svpos= winsaveview()
-
- " get new hiding list from user
- call inputsave()
- let newhide= input("Edit Hiding List: ",g:netrw_list_hide)
- call inputrestore()
- let g:netrw_list_hide= newhide
-
- " refresh the listing
- sil NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,"./",0))
-
- " restore cursor position
- call winrestview(svpos)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwHidden: invoked by "gh" {{{2
-fun! s:NetrwHidden(islocal)
- let ykeep= @@
- " save current position
- let svpos = winsaveview()
-
- if g:netrw_list_hide =~ '\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+'
- " remove .file pattern from hiding list
- let g:netrw_list_hide= substitute(g:netrw_list_hide,'\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+','','')
- elseif s:Strlen(g:netrw_list_hide) >= 1
- let g:netrw_list_hide= g:netrw_list_hide . ',\(^\|\s\s\)\zs\.\S\+'
- else
- let g:netrw_list_hide= '\(^\|\s\s\)\zs\.\S\+'
- endif
- if g:netrw_list_hide =~ '^,'
- let g:netrw_list_hide= strpart(g:netrw_list_hide,1)
- endif
-
- " refresh screen and return to saved position
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwHome: this function determines a "home" for saving bookmarks and history {{{2
-fun! s:NetrwHome()
- if exists("g:netrw_home")
- let home= expand(g:netrw_home)
- else
- let home = stdpath('data')
- endif
- " insure that the home directory exists
- if g:netrw_dirhistmax > 0 && !isdirectory(s:NetrwFile(home))
-" call Decho("insure that the home<".home."> directory exists")
- if exists("g:netrw_mkdir")
-" call Decho("call system(".g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home)).")")
- call system(g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home)))
- else
-" call Decho("mkdir(".home.")")
- call mkdir(home)
- endif
- endif
- let g:netrw_home= home
- return home
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwLeftmouse: handles the <leftmouse> when in a netrw browsing window {{{2
-fun! s:NetrwLeftmouse(islocal)
- if exists("s:netrwdrag")
- return
- endif
- if &ft != "netrw"
- return
- endif
-
- let ykeep= @@
- " check if the status bar was clicked on instead of a file/directory name
- while getchar(0) != 0
- "clear the input stream
- endwhile
- call feedkeys("\<LeftMouse>")
- let c = getchar()
- let mouse_lnum = v:mouse_lnum
- let wlastline = line('w$')
- let lastline = line('$')
- if mouse_lnum >= wlastline + 1 || v:mouse_win != winnr()
- " appears to be a status bar leftmouse click
- let @@= ykeep
- return
- endif
- " Dec 04, 2013: following test prevents leftmouse selection/deselection of directories and files in treelist mode
- " Windows are separated by vertical separator bars - but the mouse seems to be doing what it should when dragging that bar
- " without this test when its disabled.
- " May 26, 2014: edit file, :Lex, resize window -- causes refresh. Reinstated a modified test. See if problems develop.
- if v:mouse_col > virtcol('.')
- let @@= ykeep
- return
- endif
-
- if a:islocal
- if exists("b:netrw_curdir")
- NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1))
- endif
- else
- if exists("b:netrw_curdir")
- NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
- endif
- endif
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwCLeftmouse: used to select a file/directory for a target {{{2
-fun! s:NetrwCLeftmouse(islocal)
- if &ft != "netrw"
- return
- endif
- call s:NetrwMarkFileTgt(a:islocal)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwServerEdit: edit file in a server gvim, usually NETRWSERVER (implements <c-r>){{{2
-" a:islocal=0 : <c-r> not used, remote
-" a:islocal=1 : <c-r> not used, local
-" a:islocal=2 : <c-r> used, remote
-" a:islocal=3 : <c-r> used, local
-fun! s:NetrwServerEdit(islocal,fname)
-" call Dfunc("s:NetrwServerEdit(islocal=".a:islocal.",fname<".a:fname.">)")
- let islocal = a:islocal%2 " =0: remote =1: local
- let ctrlr = a:islocal >= 2 " =0: <c-r> not used =1: <c-r> used
-
- if (islocal && isdirectory(s:NetrwFile(a:fname))) || (!islocal && a:fname =~ '/$')
- " handle directories in the local window -- not in the remote vim server
- " user must have closed the NETRWSERVER window. Treat as normal editing from netrw.
- let g:netrw_browse_split= 0
- if exists("s:netrw_browse_split") && exists("s:netrw_browse_split_".winnr())
- let g:netrw_browse_split= s:netrw_browse_split_{winnr()}
- unlet s:netrw_browse_split_{winnr()}
- endif
- call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0))
- return
- endif
-
- if has("clientserver") && executable("gvim")
-
- if exists("g:netrw_browse_split") && type(g:netrw_browse_split) == 3
- let srvrname = g:netrw_browse_split[0]
- let tabnum = g:netrw_browse_split[1]
- let winnum = g:netrw_browse_split[2]
-
- if serverlist() !~ '\<'.srvrname.'\>'
- if !ctrlr
- " user must have closed the server window and the user did not use <c-r>, but
- " used something like <cr>.
- if exists("g:netrw_browse_split")
- unlet g:netrw_browse_split
- endif
- let g:netrw_browse_split= 0
- if exists("s:netrw_browse_split_".winnr())
- let g:netrw_browse_split= s:netrw_browse_split_{winnr()}
- endif
- call s:NetrwBrowseChgDir(islocal,a:fname,0)
- return
-
- elseif has("win32") && executable("start")
- " start up remote netrw server under windows
- call system("start gvim --servername ".srvrname)
-
- else
- " start up remote netrw server under linux
- call system("gvim --servername ".srvrname)
- endif
- endif
-
- call remote_send(srvrname,":tabn ".tabnum."\<cr>")
- call remote_send(srvrname,":".winnum."wincmd w\<cr>")
- call remote_send(srvrname,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>")
- else
-
- if serverlist() !~ '\<'.g:netrw_servername.'\>'
-
- if !ctrlr
- if exists("g:netrw_browse_split")
- unlet g:netrw_browse_split
- endif
- let g:netrw_browse_split= 0
- call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0))
- return
-
- else
- if has("win32") && executable("start")
- " start up remote netrw server under windows
- call system("start gvim --servername ".g:netrw_servername)
- else
- " start up remote netrw server under linux
- call system("gvim --servername ".g:netrw_servername)
- endif
- endif
- endif
-
- while 1
- try
- call remote_send(g:netrw_servername,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>")
- break
- catch /^Vim\%((\a\+)\)\=:E241/
- sleep 200m
- endtry
- endwhile
-
- if exists("g:netrw_browse_split")
- if type(g:netrw_browse_split) != 3
- let s:netrw_browse_split_{winnr()}= g:netrw_browse_split
- endif
- unlet g:netrw_browse_split
- endif
- let g:netrw_browse_split= [g:netrw_servername,1,1]
- endif
-
- else
- call netrw#ErrorMsg(s:ERROR,"you need a gui-capable vim and client-server to use <ctrl-r>",98)
- endif
-
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSLeftmouse: marks the file under the cursor. May be dragged to select additional files {{{2
-fun! s:NetrwSLeftmouse(islocal)
- if &ft != "netrw"
- return
- endif
-" call Dfunc("s:NetrwSLeftmouse(islocal=".a:islocal.")")
-
- let s:ngw= s:NetrwGetWord()
- call s:NetrwMarkFile(a:islocal,s:ngw)
-
-" call Dret("s:NetrwSLeftmouse")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSLeftdrag: invoked via a shift-leftmouse and dragging {{{2
-" Used to mark multiple files.
-fun! s:NetrwSLeftdrag(islocal)
-" call Dfunc("s:NetrwSLeftdrag(islocal=".a:islocal.")")
- if !exists("s:netrwdrag")
- let s:netrwdrag = winnr()
- if a:islocal
- nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(1)<cr>
- else
- nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(0)<cr>
- endif
- endif
- let ngw = s:NetrwGetWord()
- if !exists("s:ngw") || s:ngw != ngw
- call s:NetrwMarkFile(a:islocal,ngw)
- endif
- let s:ngw= ngw
-" call Dret("s:NetrwSLeftdrag : s:netrwdrag=".s:netrwdrag." buf#".bufnr("%"))
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSLeftrelease: terminates shift-leftmouse dragging {{{2
-fun! s:NetrwSLeftrelease(islocal)
-" call Dfunc("s:NetrwSLeftrelease(islocal=".a:islocal.") s:netrwdrag=".s:netrwdrag." buf#".bufnr("%"))
- if exists("s:netrwdrag")
- nunmap <s-leftrelease>
- let ngw = s:NetrwGetWord()
- if !exists("s:ngw") || s:ngw != ngw
- call s:NetrwMarkFile(a:islocal,ngw)
- endif
- if exists("s:ngw")
- unlet s:ngw
- endif
- unlet s:netrwdrag
- endif
-" call Dret("s:NetrwSLeftrelease")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwListHide: uses [range]g~...~d to delete files that match {{{2
-" comma-separated patterns given in g:netrw_list_hide
-fun! s:NetrwListHide()
-" call Dfunc("s:NetrwListHide() g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">")
-" call Decho("initial: ".string(getline(w:netrw_bannercnt,'$')))
- let ykeep= @@
-
- " find a character not in the "hide" string to use as a separator for :g and :v commands
- " How-it-works: take the hiding command, convert it into a range.
- " Duplicate characters don't matter.
- " Remove all such characters from the '/~@#...890' string.
- " Use the first character left as a separator character.
-" call Decho("find a character not in the hide string to use as a separator",'~'.expand("<slnum>"))
- let listhide= g:netrw_list_hide
- let sep = strpart(substitute('~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1)
-" call Decho("sep<".sep."> (sep not in hide string)",'~'.expand("<slnum>"))
-
- while listhide != ""
- if listhide =~ ','
- let hide = substitute(listhide,',.*$','','e')
- let listhide = substitute(listhide,'^.\{-},\(.*\)$','\1','e')
- else
- let hide = listhide
- let listhide = ""
- endif
-" call Decho("..extracted pattern from listhide: hide<".hide."> g:netrw_sort_by<".g:netrw_sort_by.'>','~'.expand("<slnum>"))
- if g:netrw_sort_by =~ '^[ts]'
- if hide =~ '^\^'
-" call Decho("..modify hide to handle a \"^...\" pattern",'~'.expand("<slnum>"))
- let hide= substitute(hide,'^\^','^\(\\d\\+/\)','')
- elseif hide =~ '^\\(\^'
- let hide= substitute(hide,'^\\(\^','\\(^\\(\\d\\+/\\)','')
- endif
-" call Decho("..hide<".hide."> listhide<".listhide.'>','~'.expand("<slnum>"))
- endif
-
- " Prune the list by hiding any files which match
-" call Decho("..prune the list by hiding any files which ".((g:netrw_hide == 1)? "" : "don't")."match hide<".hide.">")
- if g:netrw_hide == 1
-" call Decho("..hiding<".hide.">",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d'
- elseif g:netrw_hide == 2
-" call Decho("..showing<".hide.">",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'s@^@ /-KEEP-/ @'
- endif
-" call Decho("..result: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>"))
- endwhile
-
- if g:netrw_hide == 2
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$v@^ /-KEEP-/ @d'
-" call Decho("..v KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s@^\%( /-KEEP-/ \)\+@@e'
-" call Decho("..g KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>"))
- endif
-
- " remove any blank lines that have somehow remained.
- " This seems to happen under Windows.
- exe 'sil! NetrwKeepj 1,$g@^\s*$@d'
-
- let @@= ykeep
-" call Dret("s:NetrwListHide")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMakeDir: this function makes a directory (both local and remote) {{{2
-" implements the "d" mapping.
-fun! s:NetrwMakeDir(usrhost)
-
- let ykeep= @@
- " get name of new directory from user. A bare <CR> will skip.
- " if its currently a directory, also request will be skipped, but with
- " a message.
- call inputsave()
- let newdirname= input("Please give directory name: ")
- call inputrestore()
-
- if newdirname == ""
- let @@= ykeep
- return
- endif
-
- if a:usrhost == ""
-
- " Local mkdir:
- " sanity checks
- let fullnewdir= b:netrw_curdir.'/'.newdirname
- if isdirectory(s:NetrwFile(fullnewdir))
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a directory!",24)
- endif
- let @@= ykeep
- return
- endif
- if s:FileReadable(fullnewdir)
- if !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a file!",25)
- endif
- let @@= ykeep
- return
- endif
-
- " requested new local directory is neither a pre-existing file or
- " directory, so make it!
- if exists("*mkdir")
- if has("unix")
- call mkdir(fullnewdir,"p",xor(0777, system("umask")))
- else
- call mkdir(fullnewdir,"p")
- endif
- else
- let netrw_origdir= s:NetrwGetcwd(1)
- if s:NetrwLcd(b:netrw_curdir)
- return
- endif
- call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(newdirname,1))
- if v:shell_error != 0
- let @@= ykeep
- call netrw#ErrorMsg(s:ERROR,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80)
- return
- endif
- if !g:netrw_keepdir
- if s:NetrwLcd(netrw_origdir)
- return
- endif
- endif
- endif
-
- if v:shell_error == 0
- " refresh listing
- let svpos= winsaveview()
- call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
- call winrestview(svpos)
- elseif !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",26)
- endif
-
- elseif !exists("b:netrw_method") || b:netrw_method == 4
- " Remote mkdir: using ssh
- let mkdircmd = s:MakeSshCmd(g:netrw_mkdir_cmd)
- let newdirname= substitute(b:netrw_curdir,'^\%(.\{-}/\)\{3}\(.*\)$','\1','').newdirname
- call s:NetrwExe("sil! !".mkdircmd." ".s:ShellEscape(newdirname,1))
- if v:shell_error == 0
- " refresh listing
- let svpos= winsaveview()
- NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
- NetrwKeepj call winrestview(svpos)
- elseif !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",27)
- endif
-
- elseif b:netrw_method == 2
- " Remote mkdir: using ftp+.netrc
- let svpos= winsaveview()
- if exists("b:netrw_fname")
- let remotepath= b:netrw_fname
- else
- let remotepath= ""
- endif
- call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"')
- NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
- NetrwKeepj call winrestview(svpos)
-
- elseif b:netrw_method == 3
- " Remote mkdir: using ftp + machine, id, passwd, and fname (ie. no .netrc)
- let svpos= winsaveview()
- if exists("b:netrw_fname")
- let remotepath= b:netrw_fname
- else
- let remotepath= ""
- endif
- call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"')
- NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
- NetrwKeepj call winrestview(svpos)
- endif
-
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:TreeSqueezeDir: allows a shift-cr (gvim only) to squeeze the current tree-listing directory {{{2
-fun! s:TreeSqueezeDir(islocal)
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
- " its a tree-listing style
- let curdepth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e')
- let stopline = (exists("w:netrw_bannercnt")? (w:netrw_bannercnt + 1) : 1)
- let depth = strchars(substitute(curdepth,' ','','g'))
- let srch = -1
- if depth >= 2
- NetrwKeepj norm! 0
- let curdepthm1= substitute(curdepth,'^'.s:treedepthstring,'','')
- let srch = search('^'.curdepthm1.'\%('.s:treedepthstring.'\)\@!','bW',stopline)
- elseif depth == 1
- NetrwKeepj norm! 0
- let treedepthchr= substitute(s:treedepthstring,' ','','')
- let srch = search('^[^'.treedepthchr.']','bW',stopline)
- endif
- if srch > 0
- call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,s:NetrwGetWord(),1))
- exe srch
- endif
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMaps: {{{2
-fun! s:NetrwMaps(islocal)
-
- " mouse <Plug> maps: {{{3
- if g:netrw_mousemaps && g:netrw_retmap
-" call Decho("set up Rexplore 2-leftmouse",'~'.expand("<slnum>"))
- if !hasmapto("<Plug>NetrwReturn")
- if maparg("<2-leftmouse>","n") == "" || maparg("<2-leftmouse>","n") =~ '^-$'
- nmap <unique> <silent> <2-leftmouse> <Plug>NetrwReturn
- elseif maparg("<c-leftmouse>","n") == ""
- nmap <unique> <silent> <c-leftmouse> <Plug>NetrwReturn
- endif
- endif
- nno <silent> <Plug>NetrwReturn :Rexplore<cr>
- endif
-
- " generate default <Plug> maps {{{3
- if !hasmapto('<Plug>NetrwHide') |nmap <buffer> <silent> <nowait> a <Plug>NetrwHide_a|endif
- if !hasmapto('<Plug>NetrwBrowseUpDir') |nmap <buffer> <silent> <nowait> - <Plug>NetrwBrowseUpDir|endif
- if !hasmapto('<Plug>NetrwOpenFile') |nmap <buffer> <silent> <nowait> % <Plug>NetrwOpenFile|endif
- if !hasmapto('<Plug>NetrwBadd_cb') |nmap <buffer> <silent> <nowait> cb <Plug>NetrwBadd_cb|endif
- if !hasmapto('<Plug>NetrwBadd_cB') |nmap <buffer> <silent> <nowait> cB <Plug>NetrwBadd_cB|endif
- if !hasmapto('<Plug>NetrwLcd') |nmap <buffer> <silent> <nowait> cd <Plug>NetrwLcd|endif
- if !hasmapto('<Plug>NetrwSetChgwin') |nmap <buffer> <silent> <nowait> C <Plug>NetrwSetChgwin|endif
- if !hasmapto('<Plug>NetrwRefresh') |nmap <buffer> <silent> <nowait> <c-l> <Plug>NetrwRefresh|endif
- if !hasmapto('<Plug>NetrwLocalBrowseCheck') |nmap <buffer> <silent> <nowait> <cr> <Plug>NetrwLocalBrowseCheck|endif
- if !hasmapto('<Plug>NetrwServerEdit') |nmap <buffer> <silent> <nowait> <c-r> <Plug>NetrwServerEdit|endif
- if !hasmapto('<Plug>NetrwMakeDir') |nmap <buffer> <silent> <nowait> d <Plug>NetrwMakeDir|endif
- if !hasmapto('<Plug>NetrwBookHistHandler_gb')|nmap <buffer> <silent> <nowait> gb <Plug>NetrwBookHistHandler_gb|endif
-
- if a:islocal
- " local normal-mode maps {{{3
- nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(1)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(1)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(1)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(1,0)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(1,1)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr>
- nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr>
- nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(3,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <Plug>NetrwMakeDir :<c-u>call <SID>NetrwMakeDir("")<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr>
-" ---------------------------------------------------------------------
- nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(1,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(1,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(1)<cr>
- nnoremap <buffer> <silent> <nowait> gn :<c-u>call netrw#SetTreetop(0,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr>
- nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(1)<cr>
- nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(1,0)<cr>
- nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(1,1)<cr>
- nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(1)<cr>
- nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(1)<cr>
- nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(1)<cr>
- nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr>
- 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>
- nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(1)<cr>
- nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(1)<cr>
- nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr>
- nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(1,0)<cr>
- nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(1,1)<cr>
- nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(1)<cr>
- nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(1)<cr>
- nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(3)<cr>
- nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr>
- nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(1)<cr>
- nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr>
- nnoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(1,getloclist(v:count))<cr>
- nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(1)<cr>
- nnoremap <buffer> <silent> <nowait> S :<c-u>call <SID>NetSortSequence(1)<cr>
- nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt(1,'b',v:count1)<cr>
- nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(4)<cr>
- nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(1,'h',v:count)<cr>
- nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr>
- nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr>
- nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(5)<cr>
- nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,0),0)"<cr>
- nnoremap <buffer> <silent> <nowait> X :<c-u>call <SID>NetrwLocalExecute(expand("<cword>"))"<cr>
-
- nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./',0))<cr>
- if !hasmapto('<Plug>NetrwHideEdit')
- nmap <buffer> <unique> <c-h> <Plug>NetrwHideEdit
- endif
- nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(1)<cr>
- if !hasmapto('<Plug>NetrwRefresh')
- nmap <buffer> <unique> <c-l> <Plug>NetrwRefresh
- endif
- nnoremap <buffer> <silent> <Plug>NetrwRefresh <c-l>:call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,(exists("w:netrw_liststyle") && exists("w:netrw_treetop") && w:netrw_liststyle == 3)? w:netrw_treetop : './',0))<cr>
- if s:didstarstar || !mapcheck("<s-down>","n")
- nnoremap <buffer> <silent> <s-down> :Nexplore<cr>
- endif
- if s:didstarstar || !mapcheck("<s-up>","n")
- nnoremap <buffer> <silent> <s-up> :Pexplore<cr>
- endif
- if !hasmapto('<Plug>NetrwTreeSqueeze')
- nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze
- endif
- nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(1)<cr>
- let mapsafecurdir = escape(b:netrw_curdir, s:netrw_map_escape)
- if g:netrw_mousemaps == 1
- nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse
- nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse
- nmap <buffer> <middlemouse> <Plug>NetrwMiddlemouse
- nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse
- nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag
- nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse
- imap <buffer> <leftmouse> <Plug>ILeftmouse
- imap <buffer> <middlemouse> <Plug>IMiddlemouse
- nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(1)<cr>
- nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(1)<cr>
- nno <buffer> <silent> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(1)<cr>
- nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(1)<cr>
- nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(1)<cr>
- nmap <buffer> <silent> <Plug>Netrw2Leftmouse -
- exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
- exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
- endif
- exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
- exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
- exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>'
- exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("")<cr>'
- exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
- exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
- exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>'
- nnoremap <buffer> <F1> :he netrw-quickhelp<cr>
-
- " support user-specified maps
- call netrw#UserMaps(1)
-
- else
- " remote normal-mode maps {{{3
- call s:RemotePathAnalysis(b:netrw_curdir)
- nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(0)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(0)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(0)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(0,0)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(0,1)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr>
- nnoremap <buffer> <silent> <Plug>NetrwRefresh :<c-u>call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr>
- nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1))<cr>
- nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(2,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr>
-" ---------------------------------------------------------------------
- nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(0,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(0,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(0)<cr>
- nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr>
- nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(0)<cr>
- nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(0,0)<cr>
- nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(0,1)<cr>
- nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(0)<cr>
- nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(0)<cr>
- nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(0)<cr>
- nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr>
- 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>
- nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(0)<cr>
- nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(0)<cr>
- nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr>
- nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(0,0)<cr>
- nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(0,1)<cr>
- nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(0)<cr>
- nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(0)<cr>
- nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(0)<cr>
- nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr>
- nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(0)<cr>
- nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr>
- nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr>
- nnoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(0,getloclist(v:count))<cr>
- nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr>
- nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(0)<cr>
- nnoremap <buffer> <silent> <nowait> S :<c-u>call <SID>NetSortSequence(0)<cr>
- nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt(0,'b',v:count1)<cr>
- nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(1)<cr>
- nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(0,'h',v:count)<cr>
- nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr>
- nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr>
- nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1),1)<cr>
- nmap <buffer> <nowait> gx x
- if !hasmapto('<Plug>NetrwHideEdit')
- nmap <buffer> <c-h> <Plug>NetrwHideEdit
- endif
- nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(0)<cr>
- if !hasmapto('<Plug>NetrwRefresh')
- nmap <buffer> <c-l> <Plug>NetrwRefresh
- endif
- if !hasmapto('<Plug>NetrwTreeSqueeze')
- nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze
- endif
- nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(0)<cr>
-
- let mapsafepath = escape(s:path, s:netrw_map_escape)
- let mapsafeusermach = escape(((s:user == "")? "" : s:user."@").s:machine, s:netrw_map_escape)
-
- nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr>
- if g:netrw_mousemaps == 1
- nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse
- nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(0)<cr>
- nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse
- nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(0)<cr>
- nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse
- nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(0)<cr>
- nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag
- nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(0)<cr>
- nmap <middlemouse> <Plug>NetrwMiddlemouse
- nno <buffer> <silent> <middlemouse> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(0)<cr>
- nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse
- nmap <buffer> <silent> <Plug>Netrw2Leftmouse -
- imap <buffer> <leftmouse> <Plug>ILeftmouse
- imap <buffer> <middlemouse> <Plug>IMiddlemouse
- imap <buffer> <s-leftmouse> <Plug>ISLeftmouse
- exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- endif
- exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>'
- exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
- nnoremap <buffer> <F1> :he netrw-quickhelp<cr>
-
- " support user-specified maps
- call netrw#UserMaps(0)
- endif " }}}3
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwCommands: set up commands {{{2
-" If -buffer, the command is only available from within netrw buffers
-" Otherwise, the command is available from any window, so long as netrw
-" has been used at least once in the session.
-fun! s:NetrwCommands(islocal)
-" call Dfunc("s:NetrwCommands(islocal=".a:islocal.")")
-
- com! -nargs=* -complete=file -bang NetrwMB call s:NetrwBookmark(<bang>0,<f-args>)
- com! -nargs=* NetrwC call s:NetrwSetChgwin(<q-args>)
- com! Rexplore if exists("w:netrw_rexlocal")|call s:NetrwRexplore(w:netrw_rexlocal,exists("w:netrw_rexdir")? w:netrw_rexdir : ".")|else|call netrw#ErrorMsg(s:WARNING,"win#".winnr()." not a former netrw window",79)|endif
- if a:islocal
- com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(1,<f-args>)
- else
- com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(0,<f-args>)
- endif
- com! -buffer -nargs=? -complete=file MT call s:NetrwMarkTarget(<q-args>)
-
-" call Dret("s:NetrwCommands")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFiles: apply s:NetrwMarkFile() to named file(s) {{{2
-" glob()ing only works with local files
-fun! s:NetrwMarkFiles(islocal,...)
-" call Dfunc("s:NetrwMarkFiles(islocal=".a:islocal."...) a:0=".a:0)
- let curdir = s:NetrwGetCurdir(a:islocal)
- let i = 1
- while i <= a:0
- if a:islocal
- if v:version > 704 || (v:version == 704 && has("patch656"))
- let mffiles= glob(a:{i},0,1,1)
- else
- let mffiles= glob(a:{i},0,1)
- endif
- else
- let mffiles= [a:{i}]
- endif
-" call Decho("mffiles".string(mffiles),'~'.expand("<slnum>"))
- for mffile in mffiles
-" call Decho("mffile<".mffile.">",'~'.expand("<slnum>"))
- call s:NetrwMarkFile(a:islocal,mffile)
- endfor
- let i= i + 1
- endwhile
-" call Dret("s:NetrwMarkFiles")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkTarget: implements :MT (mark target) {{{2
-fun! s:NetrwMarkTarget(...)
- if a:0 == 0 || (a:0 == 1 && a:1 == "")
- let curdir = s:NetrwGetCurdir(1)
- let tgt = b:netrw_curdir
- else
- let curdir = s:NetrwGetCurdir((a:1 =~ '^\a\{3,}://')? 0 : 1)
- let tgt = a:1
- endif
- let s:netrwmftgt = tgt
- let s:netrwmftgt_islocal = tgt !~ '^\a\{3,}://'
- let curislocal = b:netrw_curdir !~ '^\a\{3,}://'
- let svpos = winsaveview()
- call s:NetrwRefresh(curislocal,s:NetrwBrowseChgDir(curislocal,'./',0))
- call winrestview(svpos)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFile: (invoked by mf) This function is used to both {{{2
-" mark and unmark files. If a markfile list exists,
-" then the rename and delete functions will use it instead
-" of whatever may happen to be under the cursor at that
-" moment. When the mouse and gui are available,
-" shift-leftmouse may also be used to mark files.
-"
-" Creates two lists
-" s:netrwmarkfilelist -- holds complete paths to all marked files
-" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr())
-"
-" Creates a marked file match string
-" s:netrwmarfilemtch_# -- used with 2match to display marked files
-"
-" Creates a buffer version of islocal
-" b:netrw_islocal
-fun! s:NetrwMarkFile(islocal,fname)
-" call Dfunc("s:NetrwMarkFile(islocal=".a:islocal." fname<".a:fname.">)")
-" call Decho("bufnr(%)=".bufnr("%").": ".bufname("%"),'~'.expand("<slnum>"))
-
- " sanity check
- if empty(a:fname)
-" call Dret("s:NetrwMarkFile : empty fname")
- return
- endif
- let curdir = s:NetrwGetCurdir(a:islocal)
-
- let ykeep = @@
- let curbufnr= bufnr("%")
- let leader= '\%(^\|\s\)\zs'
- if a:fname =~ '\a$'
- let trailer = '\>[@=|\/\*]\=\ze\%( \|\t\|$\)'
- else
- let trailer = '[@=|\/\*]\=\ze\%( \|\t\|$\)'
- endif
-
- if exists("s:netrwmarkfilelist_".curbufnr)
- " markfile list pre-exists
-" call Decho("case s:netrwmarkfilelist_".curbufnr." already exists",'~'.expand("<slnum>"))
-" call Decho("starting s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
-" call Decho("starting s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>"))
- let b:netrw_islocal= a:islocal
-
- if index(s:netrwmarkfilelist_{curbufnr},a:fname) == -1
- " append filename to buffer's markfilelist
-" call Decho("append filename<".a:fname."> to local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
- call add(s:netrwmarkfilelist_{curbufnr},a:fname)
- let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(a:fname,g:netrw_markfileesc).trailer
-
- else
- " remove filename from buffer's markfilelist
-" call Decho("remove filename<".a:fname."> from local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
- call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname')
- if s:netrwmarkfilelist_{curbufnr} == []
- " local markfilelist is empty; remove it entirely
-" call Decho("markfile list now empty",'~'.expand("<slnum>"))
- call s:NetrwUnmarkList(curbufnr,curdir)
- else
- " rebuild match list to display markings correctly
-" call Decho("rebuild s:netrwmarkfilemtch_".curbufnr,'~'.expand("<slnum>"))
- let s:netrwmarkfilemtch_{curbufnr}= ""
- let first = 1
- for fname in s:netrwmarkfilelist_{curbufnr}
- if first
- let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.leader.escape(fname,g:netrw_markfileesc).trailer
- else
- let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(fname,g:netrw_markfileesc).trailer
- endif
- let first= 0
- endfor
-" call Decho("ending s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
- endif
- endif
-
- else
- " initialize new markfilelist
-" call Decho("case: initialize new markfilelist",'~'.expand("<slnum>"))
-
-" call Decho("add fname<".a:fname."> to new markfilelist_".curbufnr,'~'.expand("<slnum>"))
- let s:netrwmarkfilelist_{curbufnr}= []
- call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','',''))
-" call Decho("ending s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
-
- " build initial markfile matching pattern
- if a:fname =~ '/$'
- let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc)
- else
- let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc).trailer
- endif
-" call Decho("ending s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>"))
- endif
-
- " handle global markfilelist
- if exists("s:netrwmarkfilelist")
- let dname= s:ComposePath(b:netrw_curdir,a:fname)
- if index(s:netrwmarkfilelist,dname) == -1
- " append new filename to global markfilelist
- call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname))
-" call Decho("append filename<".a:fname."> to global s:markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
- else
- " remove new filename from global markfilelist
-" call Decho("remove new filename from global s:markfilelist",'~'.expand("<slnum>"))
-" call Decho("..filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>"))
- call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"')
-" call Decho("..ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
- if s:netrwmarkfilelist == []
-" call Decho("s:netrwmarkfilelist is empty; unlet it",'~'.expand("<slnum>"))
- unlet s:netrwmarkfilelist
- endif
- endif
- else
- " initialize new global-directory markfilelist
- let s:netrwmarkfilelist= []
- call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname))
-" call Decho("init s:netrwmarkfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
- endif
-
- " set up 2match'ing to netrwmarkfilemtch_# list
- if has("syntax") && exists("g:syntax_on") && g:syntax_on
- if exists("s:netrwmarkfilemtch_{curbufnr}") && s:netrwmarkfilemtch_{curbufnr} != ""
-" " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/",'~'.expand("<slnum>"))
- if exists("g:did_drchip_netrwlist_syntax")
- exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/"
- endif
- else
-" " call Decho("2match none",'~'.expand("<slnum>"))
- 2match none
- endif
- endif
- let @@= ykeep
-" call Decho("s:netrwmarkfilelist[".(exists("s:netrwmarkfilelist")? string(s:netrwmarkfilelist) : "")."] (avail in all buffers)",'~'.expand("<slnum>"))
-" call Dret("s:NetrwMarkFile : s:netrwmarkfilelist_".curbufnr."<".(exists("s:netrwmarkfilelist_{curbufnr}")? string(s:netrwmarkfilelist_{curbufnr}) : " doesn't exist")."> (buf#".curbufnr."list)")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileArgList: ma: move the marked file list to the argument list (tomflist=0) {{{2
-" mA: move the argument list to marked file list (tomflist=1)
-" Uses the global marked file list
-fun! s:NetrwMarkFileArgList(islocal,tomflist)
- let svpos = winsaveview()
- let curdir = s:NetrwGetCurdir(a:islocal)
- let curbufnr = bufnr("%")
-
- if a:tomflist
- " mA: move argument list to marked file list
- while argc()
- let fname= argv(0)
- exe "argdel ".fnameescape(fname)
- call s:NetrwMarkFile(a:islocal,fname)
- endwhile
-
- else
- " ma: move marked file list to argument list
- if exists("s:netrwmarkfilelist")
-
- " for every filename in the marked list
- for fname in s:netrwmarkfilelist
- exe "argadd ".fnameescape(fname)
- endfor " for every file in the marked list
-
- " unmark list and refresh
- call s:NetrwUnmarkList(curbufnr,curdir)
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- endif
- endif
-
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileCompress: (invoked by mz) This function is used to {{{2
-" compress/decompress files using the programs
-" in g:netrw_compress and g:netrw_uncompress,
-" using g:netrw_compress_suffix to know which to
-" do. By default:
-" g:netrw_compress = "gzip"
-" g:netrw_decompress = { ".gz" : "gunzip" , ".bz2" : "bunzip2" , ".zip" : "unzip" , ".tar" : "tar -xf", ".xz" : "unxz"}
-fun! s:NetrwMarkFileCompress(islocal)
- let svpos = winsaveview()
- let curdir = s:NetrwGetCurdir(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)
- return
- endif
-
- if exists("s:netrwmarkfilelist_{curbufnr}") && exists("g:netrw_compress") && exists("g:netrw_decompress")
-
- " for every filename in the marked list
- for fname in s:netrwmarkfilelist_{curbufnr}
- let sfx= substitute(fname,'^.\{-}\(\.\a\+\)$','\1','')
- if exists("g:netrw_decompress['".sfx."']")
- " fname has a suffix indicating that its compressed; apply associated decompression routine
- let exe= g:netrw_decompress[sfx]
- let exe= netrw#WinPath(exe)
- if a:islocal
- if g:netrw_keepdir
- let fname= s:ShellEscape(s:ComposePath(curdir,fname))
- endif
- call system(exe." ".fname)
- if v:shell_error
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to apply<".exe."> to file<".fname.">",50)
- endif
- else
- let fname= s:ShellEscape(b:netrw_curdir.fname,1)
- NetrwKeepj call s:RemoteSystem(exe." ".fname)
- endif
-
- endif
- unlet sfx
-
- if exists("exe")
- unlet exe
- elseif a:islocal
- " fname not a compressed file, so compress it
- call system(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,fname)))
- if v:shell_error
- call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_compress<".g:netrw_compress."> to something that works",104)
- endif
- else
- " fname not a compressed file, so compress it
- NetrwKeepj call s:RemoteSystem(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(fname))
- endif
- endfor " for every file in the marked list
-
- call s:NetrwUnmarkList(curbufnr,curdir)
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileCopy: (invoked by mc) copy marked files to target {{{2
-" If no marked files, then set up directory as the
-" target. Currently does not support copying entire
-" directories. Uses the local-buffer marked file list.
-" Returns 1=success (used by NetrwMarkFileMove())
-" 0=failure
-fun! s:NetrwMarkFileCopy(islocal,...)
-" call Dfunc("s:NetrwMarkFileCopy(islocal=".a:islocal.") target<".(exists("s:netrwmftgt")? s:netrwmftgt : '---')."> a:0=".a:0)
-
- let curdir = s:NetrwGetCurdir(a:islocal)
- let curbufnr = bufnr("%")
- if b:netrw_curdir !~ '/$'
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= curdir
- endif
- let b:netrw_curdir= b:netrw_curdir."/"
- endif
-
- " 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:NetrwMarkFileCopy")
- return
- endif
-" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
-
- if !exists("s:netrwmftgt")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your marked file target is empty! (:help netrw-mt)",67)
-" call Dret("s:NetrwMarkFileCopy 0")
- return 0
- endif
-" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>"))
-
- if a:islocal && s:netrwmftgt_islocal
- " Copy marked files, local directory to local directory
-" call Decho("copy from local to local",'~'.expand("<slnum>"))
- if !executable(g:netrw_localcopycmd)
- call netrw#ErrorMsg(s:ERROR,"g:netrw_localcopycmd<".g:netrw_localcopycmd."> not executable on your system, aborting",91)
-" call Dfunc("s:NetrwMarkFileMove : g:netrw_localcopycmd<".g:netrw_localcopycmd."> n/a!")
- return
- endif
-
- " copy marked files while within the same directory (ie. allow renaming)
- if s:StripTrailingSlash(simplify(s:netrwmftgt)) == s:StripTrailingSlash(simplify(b:netrw_curdir))
- if len(s:netrwmarkfilelist_{bufnr('%')}) == 1
- " only one marked file
-" call Decho("case: only one marked file",'~'.expand("<slnum>"))
- let args = s:ShellEscape(b:netrw_curdir.s:netrwmarkfilelist_{bufnr('%')}[0])
- let oldname = s:netrwmarkfilelist_{bufnr('%')}[0]
- elseif a:0 == 1
-" call Decho("case: handling one input argument",'~'.expand("<slnum>"))
- " this happens when the next case was used to recursively call s:NetrwMarkFileCopy()
- let args = s:ShellEscape(b:netrw_curdir.a:1)
- let oldname = a:1
- else
- " copy multiple marked files inside the same directory
-" call Decho("case: handling a multiple marked files",'~'.expand("<slnum>"))
- let s:recursive= 1
- for oldname in s:netrwmarkfilelist_{bufnr("%")}
- let ret= s:NetrwMarkFileCopy(a:islocal,oldname)
- if ret == 0
- break
- endif
- endfor
- unlet s:recursive
- call s:NetrwUnmarkList(curbufnr,curdir)
-" call Dret("s:NetrwMarkFileCopy ".ret)
- return ret
- endif
-
- call inputsave()
- let newname= input("Copy ".oldname." to : ",oldname,"file")
- call inputrestore()
- if newname == ""
-" call Dret("s:NetrwMarkFileCopy 0")
- return 0
- endif
- let args= s:ShellEscape(oldname)
- let tgt = s:ShellEscape(s:netrwmftgt.'/'.newname)
- else
- let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"s:ShellEscape(b:netrw_curdir.\"/\".v:val)"))
- let tgt = s:ShellEscape(s:netrwmftgt)
- endif
- if !g:netrw_cygwin && has("win32")
- let args= substitute(args,'/','\\','g')
- let tgt = substitute(tgt, '/','\\','g')
- endif
- if args =~ "'" |let args= substitute(args,"'\\(.*\\)'",'\1','')|endif
- if tgt =~ "'" |let tgt = substitute(tgt ,"'\\(.*\\)'",'\1','')|endif
- if args =~ '//'|let args= substitute(args,'//','/','g')|endif
- if tgt =~ '//'|let tgt = substitute(tgt ,'//','/','g')|endif
-" call Decho("args <".args.">",'~'.expand("<slnum>"))
-" call Decho("tgt <".tgt.">",'~'.expand("<slnum>"))
- if isdirectory(s:NetrwFile(args))
-" call Decho("args<".args."> is a directory",'~'.expand("<slnum>"))
- let copycmd= g:netrw_localcopydircmd
-" call Decho("using copydircmd<".copycmd.">",'~'.expand("<slnum>"))
- if !g:netrw_cygwin && has("win32")
- " window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's
- " contents to a target. One must append the source directory name to the target to get xcopy to
- " do the right thing.
- let tgt= tgt.'\'.substitute(a:1,'^.*[\\/]','','')
-" call Decho("modified tgt for xcopy",'~'.expand("<slnum>"))
- endif
- else
- let copycmd= g:netrw_localcopycmd
- endif
- if g:netrw_localcopycmd =~ '\s'
- let copycmd = substitute(copycmd,'\s.*$','','')
- let copycmdargs = substitute(copycmd,'^.\{-}\(\s.*\)$','\1','')
- let copycmd = netrw#WinPath(copycmd).copycmdargs
- else
- let copycmd = netrw#WinPath(copycmd)
- endif
-" call Decho("args <".args.">",'~'.expand("<slnum>"))
-" call Decho("tgt <".tgt.">",'~'.expand("<slnum>"))
-" call Decho("copycmd<".copycmd.">",'~'.expand("<slnum>"))
-" call Decho("system(".copycmd." '".args."' '".tgt."')",'~'.expand("<slnum>"))
- call system(copycmd.g:netrw_localcopycmdopt." '".args."' '".tgt."'")
- if v:shell_error != 0
- if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && g:netrw_keepdir
- call netrw#ErrorMsg(s:ERROR,"copy failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",101)
- else
- call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localcopycmd<".g:netrw_localcopycmd.">; it doesn't work!",80)
- endif
-" call Dret("s:NetrwMarkFileCopy 0 : failed: system(".g:netrw_localcopycmd." ".args." ".s:ShellEscape(s:netrwmftgt))
- return 0
- endif
-
- elseif a:islocal && !s:netrwmftgt_islocal
- " Copy marked files, local directory to remote directory
-" call Decho("copy from local to remote",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
-
- elseif !a:islocal && s:netrwmftgt_islocal
- " Copy marked files, remote directory to local directory
-" call Decho("copy from remote to local",'~'.expand("<slnum>"))
- NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
-
- elseif !a:islocal && !s:netrwmftgt_islocal
- " Copy marked files, remote directory to remote directory
-" call Decho("copy from remote to remote",'~'.expand("<slnum>"))
- let curdir = getcwd()
- let tmpdir = s:GetTempfile("")
- if tmpdir !~ '/'
- let tmpdir= curdir."/".tmpdir
- endif
- if exists("*mkdir")
- call mkdir(tmpdir)
- else
- call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(tmpdir,1))
- if v:shell_error != 0
- call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80)
-" call Dret("s:NetrwMarkFileCopy : failed: sil! !".g:netrw_localmkdir.' '.s:ShellEscape(tmpdir,1) )
- return
- endif
- endif
- if isdirectory(s:NetrwFile(tmpdir))
- if s:NetrwLcd(tmpdir)
-" call Dret("s:NetrwMarkFileCopy : lcd failure")
- return
- endif
- NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir)
- let localfiles= map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")')
- NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt)
- if getcwd() == tmpdir
- for fname in s:netrwmarkfilelist_{bufnr('%')}
- NetrwKeepj call s:NetrwDelete(fname)
- endfor
- if s:NetrwLcd(curdir)
-" call Dret("s:NetrwMarkFileCopy : lcd failure")
- return
- endif
- if delete(tmpdir,"d")
- call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".tmpdir.">!",103)
- endif
- else
- if s:NetrwLcd(curdir)
-" call Dret("s:NetrwMarkFileCopy : lcd failure")
- return
- endif
- endif
- endif
- endif
-
- " -------
- " cleanup
- " -------
-" call Decho("cleanup",'~'.expand("<slnum>"))
- " remove markings from local buffer
- call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
-" call Decho(" g:netrw_fastbrowse =".g:netrw_fastbrowse,'~'.expand("<slnum>"))
-" call Decho(" s:netrwmftgt =".s:netrwmftgt,'~'.expand("<slnum>"))
-" call Decho(" s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>"))
-" call Decho(" curdir =".curdir,'~'.expand("<slnum>"))
-" call Decho(" a:islocal =".a:islocal,'~'.expand("<slnum>"))
-" call Decho(" curbufnr =".curbufnr,'~'.expand("<slnum>"))
- if exists("s:recursive")
-" call Decho(" s:recursive =".s:recursive,'~'.expand("<slnum>"))
- else
-" call Decho(" s:recursive =n/a",'~'.expand("<slnum>"))
- endif
- " see s:LocalFastBrowser() for g:netrw_fastbrowse interpretation (refreshing done for both slow and medium)
- if g:netrw_fastbrowse <= 1
- NetrwKeepj call s:LocalBrowseRefresh()
- else
- " refresh local and targets for fast browsing
- if !exists("s:recursive")
- " remove markings from local buffer
-" call Decho(" remove markings from local buffer",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir)
- endif
-
- " refresh buffers
- if s:netrwmftgt_islocal
-" call Decho(" refresh s:netrwmftgt=".s:netrwmftgt,'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt)
- endif
- if a:islocal && s:netrwmftgt != curdir
-" call Decho(" refresh curdir=".curdir,'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwRefreshDir(a:islocal,curdir)
- endif
- endif
-
-" call Dret("s:NetrwMarkFileCopy 1")
- return 1
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileDiff: (invoked by md) This function is used to {{{2
-" invoke vim's diff mode on the marked files.
-" Either two or three files can be so handled.
-" Uses the global marked file list.
-fun! s:NetrwMarkFileDiff(islocal)
-" call Dfunc("s:NetrwMarkFileDiff(islocal=".a:islocal.") b:netrw_curdir<".b:netrw_curdir.">")
- 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:NetrwMarkFileDiff")
- return
- endif
- let curdir= s:NetrwGetCurdir(a:islocal)
-" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
-
- if exists("s:netrwmarkfilelist_{".curbufnr."}")
- let cnt = 0
- for fname in s:netrwmarkfilelist
- let cnt= cnt + 1
- if cnt == 1
-" call Decho("diffthis: fname<".fname.">",'~'.expand("<slnum>"))
- exe "NetrwKeepj e ".fnameescape(fname)
- diffthis
- elseif cnt == 2 || cnt == 3
- below vsplit
-" call Decho("diffthis: ".fname,'~'.expand("<slnum>"))
- exe "NetrwKeepj e ".fnameescape(fname)
- diffthis
- else
- break
- endif
- endfor
- call s:NetrwUnmarkList(curbufnr,curdir)
- endif
-
-" call Dret("s:NetrwMarkFileDiff")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileEdit: (invoked by me) put marked files on arg list and start editing them {{{2
-" Uses global markfilelist
-fun! s:NetrwMarkFileEdit(islocal)
-" call Dfunc("s:NetrwMarkFileEdit(islocal=".a:islocal.")")
-
- let curdir = s:NetrwGetCurdir(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:NetrwMarkFileEdit")
- return
- endif
-" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
-
- if exists("s:netrwmarkfilelist_{curbufnr}")
- call s:SetRexDir(a:islocal,curdir)
- let flist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)"))
- " unmark markedfile list
-" call s:NetrwUnmarkList(curbufnr,curdir)
- call s:NetrwUnmarkAll()
-" call Decho("exe sil args ".flist,'~'.expand("<slnum>"))
- exe "sil args ".flist
- endif
- echo "(use :bn, :bp to navigate files; :Rex to return)"
-
-" call Dret("s:NetrwMarkFileEdit")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileQFEL: convert a quickfix-error or location list into a marked file list {{{2
-fun! s:NetrwMarkFileQFEL(islocal,qfel)
-" call Dfunc("s:NetrwMarkFileQFEL(islocal=".a:islocal.",qfel)")
- call s:NetrwUnmarkAll()
- let curbufnr= bufnr("%")
-
- if !empty(a:qfel)
- for entry in a:qfel
- let bufnmbr= entry["bufnr"]
-" call Decho("bufname(".bufnmbr.")<".bufname(bufnmbr)."> line#".entry["lnum"]." text=".entry["text"],'~'.expand("<slnum>"))
- if !exists("s:netrwmarkfilelist_{curbufnr}")
-" call Decho("case: no marked file list",'~'.expand("<slnum>"))
- call s:NetrwMarkFile(a:islocal,bufname(bufnmbr))
- elseif index(s:netrwmarkfilelist_{curbufnr},bufname(bufnmbr)) == -1
- " s:NetrwMarkFile will remove duplicate entries from the marked file list.
- " So, this test lets two or more hits on the same pattern to be ignored.
-" call Decho("case: ".bufname(bufnmbr)." not currently in marked file list",'~'.expand("<slnum>"))
- call s:NetrwMarkFile(a:islocal,bufname(bufnmbr))
- else
-" call Decho("case: ".bufname(bufnmbr)." already in marked file list",'~'.expand("<slnum>"))
- endif
- endfor
- echo "(use me to edit marked files)"
- else
- call netrw#ErrorMsg(s:WARNING,"can't convert quickfix error list; its empty!",92)
- endif
-
-" call Dret("s:NetrwMarkFileQFEL")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileExe: (invoked by mx and mX) execute arbitrary system command on marked files {{{2
-" mx enbloc=0: Uses the local marked-file list, applies command to each file individually
-" mX enbloc=1: Uses the global marked-file list, applies command to entire list
-fun! s:NetrwMarkFileExe(islocal,enbloc)
- let svpos = winsaveview()
- let curdir = s:NetrwGetCurdir(a:islocal)
- let curbufnr = bufnr("%")
-
- if a:enbloc == 0
- " individually apply command to files, one at a time
- " 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)
- return
- endif
-
- if exists("s:netrwmarkfilelist_{curbufnr}")
- " get the command
- call inputsave()
- let cmd= input("Enter command: ","","file")
- call inputrestore()
- if cmd == ""
- return
- endif
-
- " apply command to marked files, individually. Substitute: filename -> %
- " If no %, then append a space and the filename to the command
- for fname in s:netrwmarkfilelist_{curbufnr}
- if a:islocal
- if g:netrw_keepdir
- let fname= s:ShellEscape(netrw#WinPath(s:ComposePath(curdir,fname)))
- endif
- else
- let fname= s:ShellEscape(netrw#WinPath(b:netrw_curdir.fname))
- endif
- if cmd =~ '%'
- let xcmd= substitute(cmd,'%',fname,'g')
- else
- let xcmd= cmd.' '.fname
- endif
- if a:islocal
- let ret= system(xcmd)
- else
- let ret= s:RemoteSystem(xcmd)
- endif
- if v:shell_error < 0
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54)
- break
- else
- if ret !=# ''
- echo "\n"
- " skip trailing new line
- echo ret[0:-2]
- else
- echo ret
- endif
- endif
- endfor
-
- " unmark marked file list
- call s:NetrwUnmarkList(curbufnr,curdir)
-
- " refresh the listing
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- else
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
- endif
-
- else " apply command to global list of files, en bloc
-
- call inputsave()
- let cmd= input("Enter command: ","","file")
- call inputrestore()
- if cmd == ""
- return
- endif
- if cmd =~ '%'
- let cmd= substitute(cmd,'%',join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' '),'g')
- else
- let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' ')
- endif
- if a:islocal
- call system(cmd)
- if v:shell_error < 0
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54)
- endif
- else
- let ret= s:RemoteSystem(cmd)
- endif
- call s:NetrwUnmarkAll()
-
- " refresh the listing
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
-
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix
-" as the marked file(s) (toggles suffix presence)
-" Uses the local marked file list.
-fun! s:NetrwMarkHideSfx(islocal)
- let svpos = winsaveview()
- let curbufnr = bufnr("%")
-
- " s:netrwmarkfilelist_{curbufnr}: the List of marked files
- if exists("s:netrwmarkfilelist_{curbufnr}")
-
- for fname in s:netrwmarkfilelist_{curbufnr}
- " construct suffix pattern
- if fname =~ '\.'
- let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','')
- else
- let sfxpat= '^\%(\%(\.\)\@!.\)*$'
- endif
- " determine if its in the hiding list or not
- let inhidelist= 0
- if g:netrw_list_hide != ""
- let itemnum = 0
- let hidelist= split(g:netrw_list_hide,',')
- for hidepat in hidelist
- if sfxpat == hidepat
- let inhidelist= 1
- break
- endif
- let itemnum= itemnum + 1
- endfor
- endif
- if inhidelist
- " remove sfxpat from list
- call remove(hidelist,itemnum)
- let g:netrw_list_hide= join(hidelist,",")
- elseif g:netrw_list_hide != ""
- " append sfxpat to non-empty list
- let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat
- else
- " set hiding list to sfxpat
- let g:netrw_list_hide= sfxpat
- endif
- endfor
-
- " refresh the listing
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- else
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileVimCmd: (invoked by mv) execute arbitrary vim command on marked files, one at a time {{{2
-" Uses the local marked-file list.
-fun! s:NetrwMarkFileVimCmd(islocal)
- let svpos = winsaveview()
- let curdir = s:NetrwGetCurdir(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)
- return
- endif
-
- if exists("s:netrwmarkfilelist_{curbufnr}")
- " get the command
- call inputsave()
- let cmd= input("Enter vim command: ","","file")
- call inputrestore()
- if cmd == ""
- return
- endif
-
- " apply command to marked files. Substitute: filename -> %
- " If no %, then append a space and the filename to the command
- for fname in s:netrwmarkfilelist_{curbufnr}
- if a:islocal
- 1split
- exe "sil! NetrwKeepj keepalt e ".fnameescape(fname)
- exe cmd
- exe "sil! keepalt wq!"
- else
- echo "sorry, \"mv\" not supported yet for remote files"
- endif
- endfor
-
- " unmark marked file list
- call s:NetrwUnmarkList(curbufnr,curdir)
-
- " refresh the listing
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- else
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix
-" as the marked file(s) (toggles suffix presence)
-" Uses the local marked file list.
-fun! s:NetrwMarkHideSfx(islocal)
- let svpos = winsaveview()
- let curbufnr = bufnr("%")
-
- " s:netrwmarkfilelist_{curbufnr}: the List of marked files
- if exists("s:netrwmarkfilelist_{curbufnr}")
-
- for fname in s:netrwmarkfilelist_{curbufnr}
- " construct suffix pattern
- if fname =~ '\.'
- let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','')
- else
- let sfxpat= '^\%(\%(\.\)\@!.\)*$'
- endif
- " determine if its in the hiding list or not
- let inhidelist= 0
- if g:netrw_list_hide != ""
- let itemnum = 0
- let hidelist= split(g:netrw_list_hide,',')
- for hidepat in hidelist
- if sfxpat == hidepat
- let inhidelist= 1
- break
- endif
- let itemnum= itemnum + 1
- endfor
- endif
- if inhidelist
- " remove sfxpat from list
- call remove(hidelist,itemnum)
- let g:netrw_list_hide= join(hidelist,",")
- elseif g:netrw_list_hide != ""
- " append sfxpat to non-empty list
- let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat
- else
- " set hiding list to sfxpat
- let g:netrw_list_hide= sfxpat
- endif
- endfor
-
- " refresh the listing
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- else
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileGrep: (invoked by mg) This function applies vimgrep to marked files {{{2
-" Uses the global markfilelist
-fun! s:NetrwMarkFileGrep(islocal)
-" call Dfunc("s:NetrwMarkFileGrep(islocal=".a:islocal.")")
- let svpos = winsaveview()
-" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- let curbufnr = bufnr("%")
- let curdir = s:NetrwGetCurdir(a:islocal)
-
- if exists("s:netrwmarkfilelist")
-" call Decho("using s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
- let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)"))
-" call Decho("keeping copy of s:netrwmarkfilelist in function-local variable,'~'.expand("<slnum>"))"
- call s:NetrwUnmarkAll()
- else
-" call Decho('no marked files, using "*"','~'.expand("<slnum>"))
- let netrwmarkfilelist= "*"
- endif
-
- " ask user for pattern
-" call Decho("ask user for search pattern",'~'.expand("<slnum>"))
- call inputsave()
- let pat= input("Enter pattern: ","")
- call inputrestore()
- let patbang = ""
- if pat =~ '^!'
- let patbang = "!"
- let pat = strpart(pat,2)
- endif
- if pat =~ '^\i'
- let pat = escape(pat,'/')
- let pat = '/'.pat.'/'
- else
- let nonisi = pat[0]
- endif
-
- " use vimgrep for both local and remote
-" call Decho("exe vimgrep".patbang." ".pat." ".netrwmarkfilelist,'~'.expand("<slnum>"))
- try
- exe "NetrwKeepj noautocmd vimgrep".patbang." ".pat." ".netrwmarkfilelist
- catch /^Vim\%((\a\+)\)\=:E480/
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pat.">",76)
-" call Dret("s:NetrwMarkFileGrep : unable to find pattern<".pat.">")
- return
- endtry
- echo "(use :cn, :cp to navigate, :Rex to return)"
-
- 2match none
-" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(svpos)
-
- if exists("nonisi")
- " original, user-supplied pattern did not begin with a character from isident
-" call Decho("looking for trailing nonisi<".nonisi."> followed by a j, gj, or jg",'~'.expand("<slnum>"))
- if pat =~# nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$'
- call s:NetrwMarkFileQFEL(a:islocal,getqflist())
- endif
- endif
-
-" call Dret("s:NetrwMarkFileGrep")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileMove: (invoked by mm) execute arbitrary command on marked files, one at a time {{{2
-" uses the global marked file list
-" s:netrwmfloc= 0: target directory is remote
-" = 1: target directory is local
-fun! s:NetrwMarkFileMove(islocal)
-" call Dfunc("s:NetrwMarkFileMove(islocal=".a:islocal.")")
- let curdir = s:NetrwGetCurdir(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:NetrwMarkFileMove")
- return
- endif
-" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
-
- if !exists("s:netrwmftgt")
- NetrwKeepj call netrw#ErrorMsg(2,"your marked file target is empty! (:help netrw-mt)",67)
-" call Dret("s:NetrwMarkFileCopy 0")
- return 0
- endif
-" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>"))
-
- if a:islocal && s:netrwmftgt_islocal
- " move: local -> local
-" call Decho("move from local to local",'~'.expand("<slnum>"))
-" call Decho("local to local move",'~'.expand("<slnum>"))
- if !executable(g:netrw_localmovecmd)
- call netrw#ErrorMsg(s:ERROR,"g:netrw_localmovecmd<".g:netrw_localmovecmd."> not executable on your system, aborting",90)
-" call Dfunc("s:NetrwMarkFileMove : g:netrw_localmovecmd<".g:netrw_localmovecmd."> n/a!")
- return
- endif
- let tgt = s:ShellEscape(s:netrwmftgt)
-" call Decho("tgt<".tgt.">",'~'.expand("<slnum>"))
- if !g:netrw_cygwin && has("win32")
- let tgt= substitute(tgt, '/','\\','g')
-" call Decho("windows exception: tgt<".tgt.">",'~'.expand("<slnum>"))
- if g:netrw_localmovecmd =~ '\s'
- let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','')
- let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','')
- let movecmd = netrw#WinPath(movecmd).movecmdargs
-" call Decho("windows exception: movecmd<".movecmd."> (#1: had a space)",'~'.expand("<slnum>"))
- else
- let movecmd = netrw#WinPath(g:netrw_localmovecmd)
-" call Decho("windows exception: movecmd<".movecmd."> (#2: no space)",'~'.expand("<slnum>"))
- endif
- else
- let movecmd = netrw#WinPath(g:netrw_localmovecmd)
-" call Decho("movecmd<".movecmd."> (#3 linux or cygwin)",'~'.expand("<slnum>"))
- endif
- for fname in s:netrwmarkfilelist_{bufnr("%")}
- if g:netrw_keepdir
- " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1
- let fname= b:netrw_curdir."/".fname
- endif
- if !g:netrw_cygwin && has("win32")
- let fname= substitute(fname,'/','\\','g')
- endif
-" call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>"))
- let ret= system(movecmd.g:netrw_localmovecmdopt." ".s:ShellEscape(fname)." ".tgt)
- if v:shell_error != 0
- if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir
- call netrw#ErrorMsg(s:ERROR,"move failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",100)
- else
- call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localmovecmd<".g:netrw_localmovecmd.">; it doesn't work!",54)
- endif
- break
- endif
- endfor
-
- elseif a:islocal && !s:netrwmftgt_islocal
- " move: local -> remote
-" call Decho("move from local to remote",'~'.expand("<slnum>"))
-" call Decho("copy",'~'.expand("<slnum>"))
- let mflist= s:netrwmarkfilelist_{bufnr("%")}
- NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
-" call Decho("remove",'~'.expand("<slnum>"))
- for fname in mflist
- let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
- let ok = s:NetrwLocalRmFile(b:netrw_curdir,barefname,1)
- endfor
- unlet mflist
-
- elseif !a:islocal && s:netrwmftgt_islocal
- " move: remote -> local
-" call Decho("move from remote to local",'~'.expand("<slnum>"))
-" call Decho("copy",'~'.expand("<slnum>"))
- let mflist= s:netrwmarkfilelist_{bufnr("%")}
- NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
-" call Decho("remove",'~'.expand("<slnum>"))
- for fname in mflist
- let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
- let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1)
- endfor
- unlet mflist
-
- elseif !a:islocal && !s:netrwmftgt_islocal
- " move: remote -> remote
-" call Decho("move from remote to remote",'~'.expand("<slnum>"))
-" call Decho("copy",'~'.expand("<slnum>"))
- let mflist= s:netrwmarkfilelist_{bufnr("%")}
- NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
-" call Decho("remove",'~'.expand("<slnum>"))
- for fname in mflist
- let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
- let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1)
- endfor
- unlet mflist
- endif
-
- " -------
- " cleanup
- " -------
-" call Decho("cleanup",'~'.expand("<slnum>"))
-
- " remove markings from local buffer
- call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
-
- " refresh buffers
- if !s:netrwmftgt_islocal
-" call Decho("refresh netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt)
- endif
- if a:islocal
-" call Decho("refresh b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwRefreshDir(a:islocal,b:netrw_curdir)
- endif
- if g:netrw_fastbrowse <= 1
-" call Decho("since g:netrw_fastbrowse=".g:netrw_fastbrowse.", perform shell cmd refresh",'~'.expand("<slnum>"))
- NetrwKeepj call s:LocalBrowseRefresh()
- endif
-
-" call Dret("s:NetrwMarkFileMove")
-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).
-fun! s:NetrwMarkFileRegexp(islocal)
-" call Dfunc("s:NetrwMarkFileRegexp(islocal=".a:islocal.")")
-
- " get the regular expression
- call inputsave()
- let regexp= input("Enter regexp: ","","file")
- call inputrestore()
-
- if a:islocal
- let curdir= s:NetrwGetCurdir(a:islocal)
-" call Decho("curdir<".fnameescape(curdir).">")
- " get the matching list of files using local glob()
-" call Decho("handle local regexp",'~'.expand("<slnum>"))
- let dirname = escape(b:netrw_curdir,g:netrw_glob_escape)
- if v:version > 704 || (v:version == 704 && has("patch656"))
- let filelist= glob(s:ComposePath(dirname,regexp),0,1,1)
- else
- let files = glob(s:ComposePath(dirname,regexp),0,0)
- let filelist= split(files,"\n")
- endif
-" call Decho("files<".string(filelist).">",'~'.expand("<slnum>"))
-
- " mark the list of files
- for fname in filelist
- if fname =~ '^'.fnameescape(curdir)
-" call Decho("fname<".substitute(fname,'^'.fnameescape(curdir).'/','','').">",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^'.fnameescape(curdir).'/','',''))
- else
-" call Decho("fname<".fname.">",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^.*/','',''))
- endif
- endfor
-
- else
-" call Decho("handle remote regexp",'~'.expand("<slnum>"))
-
- " convert displayed listing into a filelist
- let eikeep = &ei
- let areg = @a
- sil NetrwKeepj %y a
- setl ei=all ma
-" call Decho("setl ei=all ma",'~'.expand("<slnum>"))
- 1split
- NetrwKeepj call s:NetrwEnew()
- NetrwKeepj call s:NetrwOptionsSafe(a:islocal)
- sil NetrwKeepj norm! "ap
- NetrwKeepj 2
- let bannercnt= search('^" =====','W')
- exe "sil NetrwKeepj 1,".bannercnt."d"
- setl bt=nofile
- if g:netrw_liststyle == s:LONGLIST
- sil NetrwKeepj %s/\s\{2,}\S.*$//e
- call histdel("/",-1)
- elseif g:netrw_liststyle == s:WIDELIST
- sil NetrwKeepj %s/\s\{2,}/\r/ge
- call histdel("/",-1)
- elseif g:netrw_liststyle == s:TREELIST
- exe 'sil NetrwKeepj %s/^'.s:treedepthstring.' //e'
- sil! NetrwKeepj g/^ .*$/d
- call histdel("/",-1)
- call histdel("/",-1)
- endif
- " convert regexp into the more usual glob-style format
- let regexp= substitute(regexp,'\*','.*','g')
-" call Decho("regexp<".regexp.">",'~'.expand("<slnum>"))
- exe "sil! NetrwKeepj v/".escape(regexp,'/')."/d"
- call histdel("/",-1)
- let filelist= getline(1,line("$"))
- q!
- for filename in filelist
- NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(filename,'^.*/','',''))
- endfor
- unlet filelist
- let @a = areg
- let &ei = eikeep
- endif
- echo " (use me to edit marked files)"
-
-" call Dret("s:NetrwMarkFileRegexp")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileSource: (invoked by ms) This function sources marked files {{{2
-" Uses the local marked file list.
-fun! s:NetrwMarkFileSource(islocal)
-" call Dfunc("s:NetrwMarkFileSource(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:NetrwMarkFileSource")
- 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_{bufnr("%")}
- 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
- " the autocmds will handle sourcing both local and remote files
-" call Decho("exe so ".fnameescape(fname),'~'.expand("<slnum>"))
- exe "so ".fnameescape(fname)
- endfor
- 2match none
- endif
-" call Dret("s:NetrwMarkFileSource")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileTag: (invoked by mT) This function applies g:netrw_ctags to marked files {{{2
-" Uses the global markfilelist
-fun! s:NetrwMarkFileTag(islocal)
- let svpos = winsaveview()
- let curdir = s:NetrwGetCurdir(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)
- return
- endif
-
- if exists("s:netrwmarkfilelist")
- let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "s:ShellEscape(v:val,".!a:islocal.")"))
- call s:NetrwUnmarkAll()
-
- if a:islocal
-
- call system(g:netrw_ctags." ".netrwmarkfilelist)
- if v:shell_error
- call netrw#ErrorMsg(s:ERROR,"g:netrw_ctags<".g:netrw_ctags."> is not executable!",51)
- endif
-
- else
- let cmd = s:RemoteSystem(g:netrw_ctags." ".netrwmarkfilelist)
- call netrw#Obtain(a:islocal,"tags")
- let curdir= b:netrw_curdir
- 1split
- NetrwKeepj e tags
- let path= substitute(curdir,'^\(.*\)/[^/]*$','\1/','')
- exe 'NetrwKeepj %s/\t\(\S\+\)\t/\t'.escape(path,"/\n\r\\").'\1\t/e'
- call histdel("/",-1)
- wq!
- endif
- 2match none
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- call winrestview(svpos)
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMarkFileTgt: (invoked by mt) This function sets up a marked file target {{{2
-" Sets up two variables,
-" s:netrwmftgt : holds the target directory
-" s:netrwmftgt_islocal : 0=target directory is remote
-" 1=target directory is local
-fun! s:NetrwMarkFileTgt(islocal)
- let svpos = winsaveview()
- let curdir = s:NetrwGetCurdir(a:islocal)
- let hadtgt = exists("s:netrwmftgt")
- if !exists("w:netrw_bannercnt")
- let w:netrw_bannercnt= b:netrw_bannercnt
- endif
-
- " set up target
- if line(".") < w:netrw_bannercnt
- " if cursor in banner region, use b:netrw_curdir for the target unless its already the target
- if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") && s:netrwmftgt == b:netrw_curdir
- unlet s:netrwmftgt s:netrwmftgt_islocal
- if g:netrw_fastbrowse <= 1
- call s:LocalBrowseRefresh()
- endif
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- call winrestview(svpos)
- return
- else
- let s:netrwmftgt= b:netrw_curdir
- endif
-
- else
- " get word under cursor.
- " * If directory, use it for the target.
- " * If file, use b:netrw_curdir for the target
- let curword= s:NetrwGetWord()
- let tgtdir = s:ComposePath(curdir,curword)
- if a:islocal && isdirectory(s:NetrwFile(tgtdir))
- let s:netrwmftgt = tgtdir
- elseif !a:islocal && tgtdir =~ '/$'
- let s:netrwmftgt = tgtdir
- else
- let s:netrwmftgt = curdir
- endif
- endif
- if a:islocal
- " simplify the target (eg. /abc/def/../ghi -> /abc/ghi)
- let s:netrwmftgt= simplify(s:netrwmftgt)
- endif
- if g:netrw_cygwin
- let s:netrwmftgt= substitute(system("cygpath ".s:ShellEscape(s:netrwmftgt)),'\n$','','')
- let s:netrwmftgt= substitute(s:netrwmftgt,'\n$','','')
- endif
- let s:netrwmftgt_islocal= a:islocal
-
- " need to do refresh so that the banner will be updated
- " s:LocalBrowseRefresh handles all local-browsing buffers when not fast browsing
- if g:netrw_fastbrowse <= 1
- call s:LocalBrowseRefresh()
- endif
-" call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,w:netrw_treetop,0))
- else
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- endif
- call winrestview(svpos)
- if !hadtgt
- sil! NetrwKeepj norm! j
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwGetCurdir: gets current directory and sets up b:netrw_curdir if necessary {{{2
-fun! s:NetrwGetCurdir(islocal)
-" call Dfunc("s:NetrwGetCurdir(islocal=".a:islocal.")")
-
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- let b:netrw_curdir = s:NetrwTreePath(w:netrw_treetop)
-" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used s:NetrwTreeDir)",'~'.expand("<slnum>"))
- elseif !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
-" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)",'~'.expand("<slnum>"))
- endif
-
-" call Decho("b:netrw_curdir<".b:netrw_curdir."> ".((b:netrw_curdir !~ '\<\a\{3,}://')? "does not match" : "matches")." url pattern",'~'.expand("<slnum>"))
- if b:netrw_curdir !~ '\<\a\{3,}://'
- let curdir= b:netrw_curdir
-" call Decho("g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>"))
- if g:netrw_keepdir == 0
- call s:NetrwLcd(curdir)
- endif
- endif
-
-" call Dret("s:NetrwGetCurdir <".curdir.">")
- return b:netrw_curdir
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwOpenFile: query user for a filename and open it {{{2
-fun! s:NetrwOpenFile(islocal)
-" call Dfunc("s:NetrwOpenFile(islocal=".a:islocal.")")
- let ykeep= @@
- call inputsave()
- let fname= input("Enter filename: ")
- call inputrestore()
-" call Decho("(s:NetrwOpenFile) fname<".fname.">",'~'.expand("<slnum>"))
-
- " determine if Lexplore is in use
- if exists("t:netrw_lexbufnr")
- " check if t:netrw_lexbufnr refers to a netrw window
-" call Decho("(s:netrwOpenFile) ..t:netrw_lexbufnr=".t:netrw_lexbufnr,'~'.expand("<slnum>"))
- let lexwinnr = bufwinnr(t:netrw_lexbufnr)
- if lexwinnr != -1 && exists("g:netrw_chgwin") && g:netrw_chgwin != -1
-" call Decho("(s:netrwOpenFile) ..Lexplore in use",'~'.expand("<slnum>"))
- exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w"
- exe "NetrwKeepj e ".fnameescape(fname)
- let @@= ykeep
-" call Dret("s:NetrwOpenFile : creating a file with Lexplore mode")
- endif
- endif
-
- " Does the filename contain a path?
- if fname !~ '[/\\]'
- if exists("b:netrw_curdir")
- if exists("g:netrw_quiet")
- let netrw_quiet_keep = g:netrw_quiet
- endif
- let g:netrw_quiet = 1
- " save position for benefit of Rexplore
- let s:rexposn_{bufnr("%")}= winsaveview()
-" call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>"))
- if b:netrw_curdir =~ '/$'
- exe "NetrwKeepj e ".fnameescape(b:netrw_curdir.fname)
- else
- exe "e ".fnameescape(b:netrw_curdir."/".fname)
- endif
- if exists("netrw_quiet_keep")
- let g:netrw_quiet= netrw_quiet_keep
- else
- unlet g:netrw_quiet
- endif
- endif
- else
- exe "NetrwKeepj e ".fnameescape(fname)
- endif
- let @@= ykeep
-" call Dret("s:NetrwOpenFile")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Shrink: shrinks/expands a netrw or Lexplorer window {{{2
-" For the mapping to this function be made via
-" netrwPlugin, you'll need to have had
-" g:netrw_usetab set to non-zero.
-fun! netrw#Shrink()
-" call Dfunc("netrw#Shrink() ft<".&ft."> winwidth=".winwidth(0)." lexbuf#".((exists("t:netrw_lexbufnr"))? t:netrw_lexbufnr : 'n/a'))
- let curwin = winnr()
- let wiwkeep = &wiw
- set wiw=1
-
- if &ft == "netrw"
- if winwidth(0) > g:netrw_wiw
- let t:netrw_winwidth= winwidth(0)
- exe "vert resize ".g:netrw_wiw
- wincmd l
- if winnr() == curwin
- wincmd h
- endif
-" call Decho("vert resize 0",'~'.expand("<slnum>"))
- else
- exe "vert resize ".t:netrw_winwidth
-" call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>"))
- endif
-
- elseif exists("t:netrw_lexbufnr")
- exe bufwinnr(t:netrw_lexbufnr)."wincmd w"
- if winwidth(bufwinnr(t:netrw_lexbufnr)) > g:netrw_wiw
- let t:netrw_winwidth= winwidth(0)
- exe "vert resize ".g:netrw_wiw
- wincmd l
- if winnr() == curwin
- wincmd h
- endif
-" call Decho("vert resize 0",'~'.expand("<slnum>"))
- elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0
- exe "vert resize ".t:netrw_winwidth
-" call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>"))
- else
- call netrw#Lexplore(0,0)
- endif
-
- else
- call netrw#Lexplore(0,0)
- endif
- let wiw= wiwkeep
-
-" call Dret("netrw#Shrink")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetSortSequence: allows user to edit the sorting sequence {{{2
-fun! s:NetSortSequence(islocal)
- let ykeep= @@
- let svpos= winsaveview()
- call inputsave()
- let newsortseq= input("Edit Sorting Sequence: ",g:netrw_sort_sequence)
- call inputrestore()
-
- " refresh the listing
- let g:netrw_sort_sequence= newsortseq
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwUnmarkList: delete local marked file list and remove their contents from the global marked-file list {{{2
-" User access provided by the <mF> mapping. (see :help netrw-mF)
-" Used by many MarkFile functions.
-fun! s:NetrwUnmarkList(curbufnr,curdir)
-" call Dfunc("s:NetrwUnmarkList(curbufnr=".a:curbufnr." curdir<".a:curdir.">)")
-
- " remove all files in local marked-file list from global list
- if exists("s:netrwmarkfilelist")
- for mfile in s:netrwmarkfilelist_{a:curbufnr}
- let dfile = s:ComposePath(a:curdir,mfile) " prepend directory to mfile
- let idx = index(s:netrwmarkfilelist,dfile) " get index in list of dfile
- call remove(s:netrwmarkfilelist,idx) " remove from global list
- endfor
- if s:netrwmarkfilelist == []
- unlet s:netrwmarkfilelist
- endif
-
- " getting rid of the local marked-file lists is easy
- unlet s:netrwmarkfilelist_{a:curbufnr}
- endif
- if exists("s:netrwmarkfilemtch_{a:curbufnr}")
- unlet s:netrwmarkfilemtch_{a:curbufnr}
- endif
- 2match none
-" call Dret("s:NetrwUnmarkList")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwUnmarkAll: remove the global marked file list and all local ones {{{2
-fun! s:NetrwUnmarkAll()
-" call Dfunc("s:NetrwUnmarkAll()")
- if exists("s:netrwmarkfilelist")
- unlet s:netrwmarkfilelist
- endif
- sil call s:NetrwUnmarkAll2()
- 2match none
-" call Dret("s:NetrwUnmarkAll")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwUnmarkAll2: unmark all files from all buffers {{{2
-fun! s:NetrwUnmarkAll2()
-" call Dfunc("s:NetrwUnmarkAll2()")
- redir => netrwmarkfilelist_let
- let
- redir END
- let netrwmarkfilelist_list= split(netrwmarkfilelist_let,'\n') " convert let string into a let list
- call filter(netrwmarkfilelist_list,"v:val =~ '^s:netrwmarkfilelist_'") " retain only those vars that start as s:netrwmarkfilelist_
- call map(netrwmarkfilelist_list,"substitute(v:val,'\\s.*$','','')") " remove what the entries are equal to
- for flist in netrwmarkfilelist_list
- let curbufnr= substitute(flist,'s:netrwmarkfilelist_','','')
- unlet s:netrwmarkfilelist_{curbufnr}
- unlet s:netrwmarkfilemtch_{curbufnr}
- endfor
-" call Dret("s:NetrwUnmarkAll2")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwUnMarkFile: called via mu map; unmarks *all* marked files, both global and buffer-local {{{2
-"
-" Marked files are in two types of lists:
-" s:netrwmarkfilelist -- holds complete paths to all marked files
-" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr())
-"
-" Marked files suitable for use with 2match are in:
-" s:netrwmarkfilemtch_# -- used with 2match to display marked files
-fun! s:NetrwUnMarkFile(islocal)
- let svpos = winsaveview()
- let curbufnr = bufnr("%")
-
- " unmark marked file list
- " (although I expect s:NetrwUpload() to do it, I'm just making sure)
- if exists("s:netrwmarkfilelist")
-" " call Decho("unlet'ing: s:netrwmarkfilelist",'~'.expand("<slnum>"))
- unlet s:netrwmarkfilelist
- endif
-
- let ibuf= 1
- while ibuf < bufnr("$")
- if exists("s:netrwmarkfilelist_".ibuf)
- unlet s:netrwmarkfilelist_{ibuf}
- unlet s:netrwmarkfilemtch_{ibuf}
- endif
- let ibuf = ibuf + 1
- endwhile
- 2match none
-
-" call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
-call winrestview(svpos)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwMenu: generates the menu for gvim and netrw {{{2
-fun! s:NetrwMenu(domenu)
-
- if !exists("g:NetrwMenuPriority")
- let g:NetrwMenuPriority= 80
- endif
-
- if has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
-" call Dfunc("NetrwMenu(domenu=".a:domenu.")")
-
- if !exists("s:netrw_menu_enabled") && a:domenu
-" call Decho("initialize menu",'~'.expand("<slnum>"))
- let s:netrw_menu_enabled= 1
- exe 'sil! menu '.g:NetrwMenuPriority.'.1 '.g:NetrwTopLvlMenu.'Help<tab><F1> <F1>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.5 '.g:NetrwTopLvlMenu.'-Sep1- :'
- exe 'sil! menu '.g:NetrwMenuPriority.'.6 '.g:NetrwTopLvlMenu.'Go\ Up\ Directory<tab>- -'
- exe 'sil! menu '.g:NetrwMenuPriority.'.7 '.g:NetrwTopLvlMenu.'Apply\ Special\ Viewer<tab>x x'
- if g:netrw_dirhistmax > 0
- exe 'sil! menu '.g:NetrwMenuPriority.'.8.1 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Current\ Directory<tab>mb mb'
- exe 'sil! menu '.g:NetrwMenuPriority.'.8.4 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Prev\ Dir\ (History)<tab>u u'
- exe 'sil! menu '.g:NetrwMenuPriority.'.8.5 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Next\ Dir\ (History)<tab>U U'
- exe 'sil! menu '.g:NetrwMenuPriority.'.8.6 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.List<tab>qb qb'
- else
- exe 'sil! menu '.g:NetrwMenuPriority.'.8 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History :echo "(disabled)"'."\<cr>"
- endif
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.1 '.g:NetrwTopLvlMenu.'Browsing\ Control.Horizontal\ Split<tab>o o'
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.2 '.g:NetrwTopLvlMenu.'Browsing\ Control.Vertical\ Split<tab>v v'
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.3 '.g:NetrwTopLvlMenu.'Browsing\ Control.New\ Tab<tab>t t'
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.4 '.g:NetrwTopLvlMenu.'Browsing\ Control.Preview<tab>p p'
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.5 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ File\ Hiding\ List<tab><ctrl-h>'." \<c-h>'"
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.6 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ Sorting\ Sequence<tab>S S'
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.7 '.g:NetrwTopLvlMenu.'Browsing\ Control.Quick\ Hide/Unhide\ Dot\ Files<tab>'."gh gh"
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.8 '.g:NetrwTopLvlMenu.'Browsing\ Control.Refresh\ Listing<tab>'."<ctrl-l> \<c-l>"
- exe 'sil! menu '.g:NetrwMenuPriority.'.9.9 '.g:NetrwTopLvlMenu.'Browsing\ Control.Settings/Options<tab>:NetrwSettings '.":NetrwSettings\<cr>"
- exe 'sil! menu '.g:NetrwMenuPriority.'.10 '.g:NetrwTopLvlMenu.'Delete\ File/Directory<tab>D D'
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Create\ New\ File<tab>% %'
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Current\ Window<tab><cr> '."\<cr>"
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.2 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Preview\ File/Directory<tab>p p'
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.3 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Previous\ Window<tab>P P'
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.4 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Window<tab>o o'
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Tab<tab>t t'
- exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Vertical\ Window<tab>v v'
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.1 '.g:NetrwTopLvlMenu.'Explore.Directory\ Name :Explore '
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (curdir\ only)<tab>:Explore\ */ :Explore */'
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (+subdirs)<tab>:Explore\ **/ :Explore **/'
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.3 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (curdir\ only)<tab>:Explore\ *// :Explore *//'
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (+subdirs)<tab>:Explore\ **// :Explore **//'
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Next\ Match<tab>:Nexplore :Nexplore<cr>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Prev\ Match<tab>:Pexplore :Pexplore<cr>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.13 '.g:NetrwTopLvlMenu.'Make\ Subdirectory<tab>d d'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.1 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ File<tab>mf mf'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.2 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ Files\ by\ Regexp<tab>mr mr'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.3 '.g:NetrwTopLvlMenu.'Marked\ Files.Hide-Show-List\ Control<tab>a a'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.4 '.g:NetrwTopLvlMenu.'Marked\ Files.Copy\ To\ Target<tab>mc mc'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.5 '.g:NetrwTopLvlMenu.'Marked\ Files.Delete<tab>D D'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.6 '.g:NetrwTopLvlMenu.'Marked\ Files.Diff<tab>md md'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.7 '.g:NetrwTopLvlMenu.'Marked\ Files.Edit<tab>me me'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.8 '.g:NetrwTopLvlMenu.'Marked\ Files.Exe\ Cmd<tab>mx mx'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.9 '.g:NetrwTopLvlMenu.'Marked\ Files.Move\ To\ Target<tab>mm mm'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.10 '.g:NetrwTopLvlMenu.'Marked\ Files.Obtain<tab>O O'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.11 '.g:NetrwTopLvlMenu.'Marked\ Files.Print<tab>mp mp'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.12 '.g:NetrwTopLvlMenu.'Marked\ Files.Replace<tab>R R'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.13 '.g:NetrwTopLvlMenu.'Marked\ Files.Set\ Target<tab>mt mt'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.14 '.g:NetrwTopLvlMenu.'Marked\ Files.Tag<tab>mT mT'
- exe 'sil! menu '.g:NetrwMenuPriority.'.14.15 '.g:NetrwTopLvlMenu.'Marked\ Files.Zip/Unzip/Compress/Uncompress<tab>mz mz'
- exe 'sil! menu '.g:NetrwMenuPriority.'.15 '.g:NetrwTopLvlMenu.'Obtain\ File<tab>O O'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.thin<tab>i :let w:netrw_liststyle=0<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.long<tab>i :let w:netrw_liststyle=1<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.wide<tab>i :let w:netrw_liststyle=2<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.tree<tab>i :let w:netrw_liststyle=3<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.1 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Show\ All<tab>a :let g:netrw_hide=0<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.3 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Normal<tab>a :let g:netrw_hide=1<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.2 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Hidden\ Only<tab>a :let g:netrw_hide=2<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.3 '.g:NetrwTopLvlMenu.'Style.Reverse\ Sorting\ Order<tab>'."r r"
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.1 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Name<tab>s :let g:netrw_sort_by="name"<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.2 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Time<tab>s :let g:netrw_sort_by="time"<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Size<tab>s :let g:netrw_sort_by="size"<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Exten<tab>s :let g:netrw_sort_by="exten"<cr><c-L>'
- exe 'sil! menu '.g:NetrwMenuPriority.'.17 '.g:NetrwTopLvlMenu.'Rename\ File/Directory<tab>R R'
- exe 'sil! menu '.g:NetrwMenuPriority.'.18 '.g:NetrwTopLvlMenu.'Set\ Current\ Directory<tab>c c'
- let s:netrw_menucnt= 28
- call s:NetrwBookmarkMenu() " provide some history! uses priorities 2,3, reserves 4, 8.2.x
- call s:NetrwTgtMenu() " let bookmarks and history be easy targets
-
- elseif !a:domenu
- let s:netrwcnt = 0
- let curwin = winnr()
- windo if getline(2) =~# "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif
- exe curwin."wincmd w"
-
- if s:netrwcnt <= 1
-" call Decho("clear menus",'~'.expand("<slnum>"))
- exe 'sil! unmenu '.g:NetrwTopLvlMenu
-" call Decho('exe sil! unmenu '.g:NetrwTopLvlMenu.'*','~'.expand("<slnum>"))
- sil! unlet s:netrw_menu_enabled
- endif
- endif
-" call Dret("NetrwMenu")
- return
- endif
-
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwObtain: obtain file under cursor or from markfile list {{{2
-" Used by the O maps (as <SID>NetrwObtain())
-fun! s:NetrwObtain(islocal)
-" call Dfunc("NetrwObtain(islocal=".a:islocal.")")
-
- let ykeep= @@
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
- let islocal= s:netrwmarkfilelist_{bufnr('%')}[1] !~ '^\a\{3,}://'
- call netrw#Obtain(islocal,s:netrwmarkfilelist_{bufnr('%')})
- call s:NetrwUnmarkList(bufnr('%'),b:netrw_curdir)
- else
- call netrw#Obtain(a:islocal,s:NetrwGetWord())
- endif
- let @@= ykeep
-
-" call Dret("NetrwObtain")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwPrevWinOpen: open file/directory in previous window. {{{2
-" If there's only one window, then the window will first be split.
-" Returns:
-" choice = 0 : didn't have to choose
-" choice = 1 : saved modified file in window first
-" choice = 2 : didn't save modified file, opened window
-" choice = 3 : cancel open
-fun! s:NetrwPrevWinOpen(islocal)
- let ykeep= @@
- " grab a copy of the b:netrw_curdir to pass it along to newly split windows
- let curdir = b:netrw_curdir
-
- " get last window number and the word currently under the cursor
- let origwin = winnr()
- let lastwinnr = winnr("$")
- let curword = s:NetrwGetWord()
- let choice = 0
- let s:prevwinopen= 1 " lets s:NetrwTreeDir() know that NetrwPrevWinOpen called it (s:NetrwTreeDir() will unlet s:prevwinopen)
- let s:treedir = s:NetrwTreeDir(a:islocal)
- let curdir = s:treedir
-
- let didsplit = 0
- if lastwinnr == 1
- " if only one window, open a new one first
- " g:netrw_preview=0: preview window shown in a horizontally split window
- " g:netrw_preview=1: preview window shown in a vertically split window
- if g:netrw_preview
- " vertically split preview window
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
- exe (g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s"
- else
- " horizontally split preview window
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
- exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
- endif
- let didsplit = 1
-
- else
- NetrwKeepj call s:SaveBufVars()
- let eikeep= &ei
- setl ei=all
- wincmd p
-
- if exists("s:lexplore_win") && s:lexplore_win == winnr()
- " whoops -- user trying to open file in the Lexplore window.
- " Use Lexplore's opening-file window instead.
-" exe g:netrw_chgwin."wincmd w"
- wincmd p
- call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
- endif
-
- " prevwinnr: the window number of the "prev" window
- " prevbufnr: the buffer number of the buffer in the "prev" window
- " bnrcnt : the qty of windows open on the "prev" buffer
- let prevwinnr = winnr()
- let prevbufnr = bufnr("%")
- let prevbufname = bufname("%")
- let prevmod = &mod
- let bnrcnt = 0
- NetrwKeepj call s:RestoreBufVars()
-
- " if the previous window's buffer has been changed (ie. its modified flag is set),
- " and it doesn't appear in any other extant window, then ask the
- " user if s/he wants to abandon modifications therein.
- if prevmod
- windo if winbufnr(0) == prevbufnr | let bnrcnt=bnrcnt+1 | endif
- exe prevwinnr."wincmd w"
-
- if bnrcnt == 1 && &hidden == 0
- " only one copy of the modified buffer in a window, and
- " hidden not set, so overwriting will lose the modified file. Ask first...
- let choice = confirm("Save modified buffer<".prevbufname."> first?","&Yes\n&No\n&Cancel")
- let &ei= eikeep
-
- if choice == 1
- " Yes -- write file & then browse
- let v:errmsg= ""
- sil w
- if v:errmsg != ""
- call netrw#ErrorMsg(s:ERROR,"unable to write <".(exists("prevbufname")? prevbufname : 'n/a').">!",30)
- exe origwin."wincmd w"
- let &ei = eikeep
- let @@ = ykeep
- return choice
- endif
-
- elseif choice == 2
- " No -- don't worry about changed file, just browse anyway
- echomsg "**note** changes to ".prevbufname." abandoned"
-
- else
- " Cancel -- don't do this
- exe origwin."wincmd w"
- let &ei= eikeep
- let @@ = ykeep
- return choice
- endif
- endif
- endif
- let &ei= eikeep
- endif
-
- " restore b:netrw_curdir (window split/enew may have lost it)
- let b:netrw_curdir= curdir
- if a:islocal < 2
- if a:islocal
- call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(a:islocal,curword,0))
- else
- call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,curword,0))
- endif
- endif
- let @@= ykeep
- return choice
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwUpload: load fname to tgt (used by NetrwMarkFileCopy()) {{{2
-" Always assumed to be local -> remote
-" call s:NetrwUpload(filename, target)
-" call s:NetrwUpload(filename, target, fromdirectory)
-fun! s:NetrwUpload(fname,tgt,...)
-" call Dfunc("s:NetrwUpload(fname<".((type(a:fname) == 1)? a:fname : string(a:fname))."> tgt<".a:tgt.">) a:0=".a:0)
-
- if a:tgt =~ '^\a\{3,}://'
- let tgtdir= substitute(a:tgt,'^\a\{3,}://[^/]\+/\(.\{-}\)$','\1','')
- else
- let tgtdir= substitute(a:tgt,'^\(.*\)/[^/]*$','\1','')
- endif
-" call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>"))
-
- if a:0 > 0
- let fromdir= a:1
- else
- let fromdir= getcwd()
- endif
-" call Decho("fromdir<".fromdir.">",'~'.expand("<slnum>"))
-
- if type(a:fname) == 1
- " handle uploading a single file using NetWrite
-" call Decho("handle uploading a single file via NetWrite",'~'.expand("<slnum>"))
- 1split
-" call Decho("exe e ".fnameescape(s:NetrwFile(a:fname)),'~'.expand("<slnum>"))
- exe "NetrwKeepj e ".fnameescape(s:NetrwFile(a:fname))
-" call Decho("now locally editing<".expand("%").">, has ".line("$")." lines",'~'.expand("<slnum>"))
- if a:tgt =~ '/$'
- let wfname= substitute(a:fname,'^.*/','','')
-" call Decho("exe w! ".fnameescape(wfname),'~'.expand("<slnum>"))
- exe "w! ".fnameescape(a:tgt.wfname)
- else
-" call Decho("writing local->remote: exe w ".fnameescape(a:tgt),'~'.expand("<slnum>"))
- exe "w ".fnameescape(a:tgt)
-" call Decho("done writing local->remote",'~'.expand("<slnum>"))
- endif
- q!
-
- elseif type(a:fname) == 3
- " handle uploading a list of files via scp
-" call Decho("handle uploading a list of files via scp",'~'.expand("<slnum>"))
- let curdir= getcwd()
- if a:tgt =~ '^scp:'
- if s:NetrwLcd(fromdir)
-" call Dret("s:NetrwUpload : lcd failure")
- return
- endif
- let filelist= deepcopy(s:netrwmarkfilelist_{bufnr('%')})
- let args = join(map(filelist,"s:ShellEscape(v:val, 1)"))
- if exists("g:netrw_port") && g:netrw_port != ""
- let useport= " ".g:netrw_scpport." ".g:netrw_port
- else
- let useport= ""
- endif
- let machine = substitute(a:tgt,'^scp://\([^/:]\+\).*$','\1','')
- let tgt = substitute(a:tgt,'^scp://[^/]\+/\(.*\)$','\1','')
- call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".args." ".s:ShellEscape(machine.":".tgt,1))
- if s:NetrwLcd(curdir)
-" call Dret("s:NetrwUpload : lcd failure")
- return
- endif
-
- elseif a:tgt =~ '^ftp:'
- call s:NetrwMethod(a:tgt)
-
- if b:netrw_method == 2
- " handle uploading a list of files via ftp+.netrc
- let netrw_fname = b:netrw_fname
- sil NetrwKeepj new
-" call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
-
- NetrwKeepj put =g:netrw_ftpmode
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
-
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
-
- if tgtdir == ""
- let tgtdir= '/'
- endif
- NetrwKeepj call setline(line("$")+1,'cd "'.tgtdir.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
-
- for fname in a:fname
- NetrwKeepj call setline(line("$")+1,'put "'.s:NetrwFile(fname).'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endfor
-
- if exists("g:netrw_port") && g:netrw_port != ""
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
- else
-" call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
- endif
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- sil NetrwKeepj g/Local directory now/d
- call histdel("/",-1)
- if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
- call netrw#ErrorMsg(s:ERROR,getline(1),14)
- else
- bw!|q
- endif
-
- elseif b:netrw_method == 3
- " upload with ftp + machine, id, passwd, and fname (ie. no .netrc)
- let netrw_fname= b:netrw_fname
- NetrwKeepj call s:SaveBufVars()|sil NetrwKeepj new|NetrwKeepj call s:RestoreBufVars()
- let tmpbufnr= bufnr("%")
- setl ff=unix
-
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- else
- NetrwKeepj put ='open '.g:netrw_machine
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_uid") && g:netrw_uid != ""
- if exists("g:netrw_ftp") && g:netrw_ftp == 1
- NetrwKeepj put =g:netrw_uid
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- if exists("s:netrw_passwd")
- NetrwKeepj call setline(line("$")+1,'"'.s:netrw_passwd.'"')
- endif
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- elseif exists("s:netrw_passwd")
- NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
- endif
-
- NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
-
- if exists("b:netrw_fname") && b:netrw_fname != ""
- NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endif
-
- for fname in a:fname
- NetrwKeepj call setline(line("$")+1,'put "'.fname.'"')
-" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
- endfor
-
- " perform ftp:
- " -i : turns off interactive prompting from ftp
- " -n unix : DON'T use <.netrc>, even though it exists
- " -n win32: quit being obnoxious about password
- NetrwKeepj norm! 1G"_dd
- call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
- " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
- sil NetrwKeepj g/Local directory now/d
- call histdel("/",-1)
- if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
- let debugkeep= &debug
- setl debug=msg
- call netrw#ErrorMsg(s:ERROR,getline(1),15)
- let &debug = debugkeep
- let mod = 1
- else
- bw!|q
- endif
- elseif !exists("b:netrw_method") || b:netrw_method < 0
-" call Dret("s:#NetrwUpload : unsupported method")
- return
- endif
- else
- call netrw#ErrorMsg(s:ERROR,"can't obtain files with protocol from<".a:tgt.">",63)
- endif
- endif
-
-" call Dret("s:NetrwUpload")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwPreview: supports netrw's "p" map {{{2
-fun! s:NetrwPreview(path) range
-" call Dfunc("NetrwPreview(path<".a:path.">)")
-" call Decho("g:netrw_alto =".(exists("g:netrw_alto")? g:netrw_alto : 'n/a'),'~'.expand("<slnum>"))
-" call Decho("g:netrw_preview=".(exists("g:netrw_preview")? g:netrw_preview : 'n/a'),'~'.expand("<slnum>"))
- let ykeep= @@
- NetrwKeepj call s:NetrwOptionsSave("s:")
- if a:path !~ '^\*\{1,2}/' && a:path !~ '^\a\{3,}://'
- NetrwKeepj call s:NetrwOptionsSafe(1)
- else
- NetrwKeepj call s:NetrwOptionsSafe(0)
- endif
- if has("quickfix")
-" call Decho("has quickfix",'~'.expand("<slnum>"))
- if !isdirectory(s:NetrwFile(a:path))
-" call Decho("good; not previewing a directory",'~'.expand("<slnum>"))
- if g:netrw_preview
- " vertical split
- let pvhkeep = &pvh
- let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
- let &pvh = winwidth(0) - winsz
-" call Decho("g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>"))
- else
- " horizontal split
- let pvhkeep = &pvh
- let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
- let &pvh = winheight(0) - winsz
-" call Decho("!g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>"))
- endif
- " g:netrw_preview g:netrw_alto
- " 1 : vert 1: top -- preview window is vertically split off and on the left
- " 1 : vert 0: bot -- preview window is vertically split off and on the right
- " 0 : 1: top -- preview window is horizontally split off and on the top
- " 0 : 0: bot -- preview window is horizontally split off and on the bottom
- "
- " Note that the file being previewed is already known to not be a directory, hence we can avoid doing a LocalBrowseCheck() check via
- " the BufEnter event set up in netrwPlugin.vim
-" call Decho("exe ".(g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path),'~'.expand("<slnum>"))
- let eikeep = &ei
- set ei=BufEnter
- exe (g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path)
- let &ei= eikeep
-" call Decho("winnr($)=".winnr("$"),'~'.expand("<slnum>"))
- if exists("pvhkeep")
- let &pvh= pvhkeep
- endif
- elseif !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, cannot preview a directory such as <".a:path.">",38)
- endif
- elseif !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, to preview your vim needs the quickfix feature compiled in",39)
- endif
- NetrwKeepj call s:NetrwOptionsRestore("s:")
- let @@= ykeep
-" call Dret("NetrwPreview")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRefresh: {{{2
-fun! s:NetrwRefresh(islocal,dirname)
-" call Dfunc("s:NetrwRefresh(islocal<".a:islocal.">,dirname=".a:dirname.") g:netrw_hide=".g:netrw_hide." g:netrw_sort_direction=".g:netrw_sort_direction)
- " at the current time (Mar 19, 2007) all calls to NetrwRefresh() call NetrwBrowseChgDir() first.
- setl ma noro
-" call Decho("setl ma noro",'~'.expand("<slnum>"))
-" call Decho("clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
- let ykeep = @@
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- if !exists("w:netrw_treetop")
- if exists("b:netrw_curdir")
- let w:netrw_treetop= b:netrw_curdir
- else
- let w:netrw_treetop= getcwd()
- endif
- endif
- NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop)
- endif
-
- " save the cursor position before refresh.
- let screenposn = winsaveview()
-" call Decho("saving posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>"))
-
-" call Decho("win#".winnr().": ".winheight(0)."x".winwidth(0)." curfile<".expand("%").">",'~'.expand("<slnum>"))
-" call Decho("clearing buffer prior to refresh",'~'.expand("<slnum>"))
- sil! NetrwKeepj %d _
- if a:islocal
- NetrwKeepj call netrw#LocalBrowseCheck(a:dirname)
- else
- NetrwKeepj call s:NetrwBrowse(a:islocal,a:dirname)
- endif
-
- " restore position
-" call Decho("restoring posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(screenposn)
-
- " restore file marks
- if has("syntax") && exists("g:syntax_on") && g:syntax_on
- if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != ""
-" " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/",'~'.expand("<slnum>"))
- exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/"
- else
-" " call Decho("2match none (bufnr(%)=".bufnr("%")."<".bufname("%").">)",'~'.expand("<slnum>"))
- 2match none
- endif
- endif
-
-" restore
- let @@= ykeep
-" call Dret("s:NetrwRefresh")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRefreshDir: refreshes a directory by name {{{2
-" Called by NetrwMarkFileCopy()
-" Interfaces to s:NetrwRefresh() and s:LocalBrowseRefresh()
-fun! s:NetrwRefreshDir(islocal,dirname)
- if g:netrw_fastbrowse == 0
- " slowest mode (keep buffers refreshed, local or remote)
- let tgtwin= bufwinnr(a:dirname)
-
- if tgtwin > 0
- " tgtwin is being displayed, so refresh it
- let curwin= winnr()
- exe tgtwin."wincmd w"
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- exe curwin."wincmd w"
-
- elseif bufnr(a:dirname) > 0
- let bn= bufnr(a:dirname)
- exe "sil keepj bd ".bn
- endif
-
- elseif g:netrw_fastbrowse <= 1
- NetrwKeepj call s:LocalBrowseRefresh()
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSetChgwin: set g:netrw_chgwin; a <cr> will use the specified
-" window number to do its editing in.
-" Supports [count]C where the count, if present, is used to specify
-" a window to use for editing via the <cr> mapping.
-fun! s:NetrwSetChgwin(...)
-" call Dfunc("s:NetrwSetChgwin() v:count=".v:count)
- if a:0 > 0
-" call Decho("a:1<".a:1.">",'~'.expand("<slnum>"))
- if a:1 == "" " :NetrwC win#
- let g:netrw_chgwin= winnr()
- else " :NetrwC
- let g:netrw_chgwin= a:1
- endif
- elseif v:count > 0 " [count]C
- let g:netrw_chgwin= v:count
- else " C
- let g:netrw_chgwin= winnr()
- endif
- echo "editing window now set to window#".g:netrw_chgwin
-" call Dret("s:NetrwSetChgwin : g:netrw_chgwin=".g:netrw_chgwin)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSetSort: sets up the sort based on the g:netrw_sort_sequence {{{2
-" What this function does is to compute a priority for the patterns
-" in the g:netrw_sort_sequence. It applies a substitute to any
-" "files" that satisfy each pattern, putting the priority / in
-" front. An "*" pattern handles the default priority.
-fun! s:NetrwSetSort()
-" call Dfunc("SetSort() bannercnt=".w:netrw_bannercnt)
- let ykeep= @@
- if w:netrw_liststyle == s:LONGLIST
- let seqlist = substitute(g:netrw_sort_sequence,'\$','\\%(\t\\|\$\\)','ge')
- else
- let seqlist = g:netrw_sort_sequence
- endif
- " sanity check -- insure that * appears somewhere
- if seqlist == ""
- let seqlist= '*'
- elseif seqlist !~ '\*'
- let seqlist= seqlist.',*'
- endif
- let priority = 1
- while seqlist != ""
- if seqlist =~ ','
- let seq = substitute(seqlist,',.*$','','e')
- let seqlist = substitute(seqlist,'^.\{-},\(.*\)$','\1','e')
- else
- let seq = seqlist
- let seqlist = ""
- endif
- if priority < 10
- let spriority= "00".priority.g:netrw_sepchr
- elseif priority < 100
- let spriority= "0".priority.g:netrw_sepchr
- else
- let spriority= priority.g:netrw_sepchr
- endif
-" call Decho("priority=".priority." spriority<".spriority."> seq<".seq."> seqlist<".seqlist.">",'~'.expand("<slnum>"))
-
- " sanity check
- if w:netrw_bannercnt > line("$")
- " apparently no files were left after a Hiding pattern was used
-" call Dret("SetSort : no files left after hiding")
- return
- endif
- if seq == '*'
- let starpriority= spriority
- else
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/'.seq.'/s/^/'.spriority.'/'
- call histdel("/",-1)
- " sometimes multiple sorting patterns will match the same file or directory.
- " The following substitute is intended to remove the excess matches.
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^\d\{3}'.g:netrw_sepchr.'\d\{3}\//s/^\d\{3}'.g:netrw_sepchr.'\(\d\{3}\/\).\@=/\1/e'
- NetrwKeepj call histdel("/",-1)
- endif
- let priority = priority + 1
- endwhile
- if exists("starpriority")
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v/^\d\{3}'.g:netrw_sepchr.'/s/^/'.starpriority.'/e'
- NetrwKeepj call histdel("/",-1)
- endif
-
- " Following line associated with priority -- items that satisfy a priority
- " pattern get prefixed by ###/ which permits easy sorting by priority.
- " Sometimes files can satisfy multiple priority patterns -- only the latest
- " priority pattern needs to be retained. So, at this point, these excess
- " priority prefixes need to be removed, but not directories that happen to
- " be just digits themselves.
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{3}'.g:netrw_sepchr.'\)\%(\d\{3}'.g:netrw_sepchr.'\)\+\ze./\1/e'
- NetrwKeepj call histdel("/",-1)
- let @@= ykeep
-
-" call Dret("SetSort")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSetTgt: sets the target to the specified choice index {{{2
-" Implements [count]Tb (bookhist<b>)
-" [count]Th (bookhist<h>)
-" See :help netrw-qb for how to make the choice.
-fun! s:NetrwSetTgt(islocal,bookhist,choice)
-" call Dfunc("s:NetrwSetTgt(islocal=".a:islocal." bookhist<".a:bookhist."> choice#".a:choice.")")
-
- if a:bookhist == 'b'
- " supports choosing a bookmark as a target using a qb-generated list
- let choice= a:choice - 1
- if exists("g:netrw_bookmarklist[".choice."]")
- call netrw#MakeTgt(g:netrw_bookmarklist[choice])
- else
- echomsg "Sorry, bookmark#".a:choice." doesn't exist!"
- endif
-
- elseif a:bookhist == 'h'
- " supports choosing a history stack entry as a target using a qb-generated list
- let choice= (a:choice % g:netrw_dirhistmax) + 1
- if exists("g:netrw_dirhist_".choice)
- let histentry = g:netrw_dirhist_{choice}
- call netrw#MakeTgt(histentry)
- else
- echomsg "Sorry, history#".a:choice." not available!"
- endif
- endif
-
- " refresh the display
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
- endif
- call s:NetrwRefresh(a:islocal,b:netrw_curdir)
-
-" call Dret("s:NetrwSetTgt")
-endfun
-
-" =====================================================================
-" s:NetrwSortStyle: change sorting style (name - time - size - exten) and refresh display {{{2
-fun! s:NetrwSortStyle(islocal)
- NetrwKeepj call s:NetrwSaveWordPosn()
- let svpos= winsaveview()
-
- let g:netrw_sort_by= (g:netrw_sort_by =~# '^n')? 'time' : (g:netrw_sort_by =~# '^t')? 'size' : (g:netrw_sort_by =~# '^siz')? 'exten' : 'name'
- NetrwKeepj norm! 0
- NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- NetrwKeepj call winrestview(svpos)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwSplit: mode {{{2
-" =0 : net and o
-" =1 : net and t
-" =2 : net and v
-" =3 : local and o
-" =4 : local and t
-" =5 : local and v
-fun! s:NetrwSplit(mode)
-
- let ykeep= @@
- call s:SaveWinVars()
-
- if a:mode == 0
- " remote and o
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
- if winsz == 0|let winsz= ""|endif
- exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
- let s:didsplit= 1
- NetrwKeepj call s:RestoreWinVars()
- NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
- unlet s:didsplit
-
- elseif a:mode == 1
- " remote and t
- let newdir = s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)
- tabnew
- let s:didsplit= 1
- NetrwKeepj call s:RestoreWinVars()
- NetrwKeepj call s:NetrwBrowse(0,newdir)
- unlet s:didsplit
-
- elseif a:mode == 2
- " remote and v
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
- if winsz == 0|let winsz= ""|endif
- exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v"
- let s:didsplit= 1
- NetrwKeepj call s:RestoreWinVars()
- NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
- unlet s:didsplit
-
- elseif a:mode == 3
- " local and o
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
- if winsz == 0|let winsz= ""|endif
- exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
- let s:didsplit= 1
- NetrwKeepj call s:RestoreWinVars()
- NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1))
- unlet s:didsplit
-
- elseif a:mode == 4
- " local and t
- let cursorword = s:NetrwGetWord()
- let eikeep = &ei
- let netrw_winnr = winnr()
- let netrw_line = line(".")
- let netrw_col = virtcol(".")
- NetrwKeepj norm! H0
- let netrw_hline = line(".")
- setl ei=all
- exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>"
- exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>"
- let &ei = eikeep
- let netrw_curdir = s:NetrwTreeDir(0)
- tabnew
- let b:netrw_curdir = netrw_curdir
- let s:didsplit = 1
- NetrwKeepj call s:RestoreWinVars()
- NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,cursorword,0))
- if &ft == "netrw"
- setl ei=all
- exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>"
- exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>"
- let &ei= eikeep
- endif
- unlet s:didsplit
-
- elseif a:mode == 5
- " local and v
- let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
- if winsz == 0|let winsz= ""|endif
- exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v"
- let s:didsplit= 1
- NetrwKeepj call s:RestoreWinVars()
- NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1))
- unlet s:didsplit
-
- else
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"(NetrwSplit) unsupported mode=".a:mode,45)
- endif
-
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwTgtMenu: {{{2
-fun! s:NetrwTgtMenu()
- if !exists("s:netrw_menucnt")
- return
- endif
-" call Dfunc("s:NetrwTgtMenu()")
-
- " the following test assures that gvim is running, has menus available, and has menus enabled.
- if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
- if exists("g:NetrwTopLvlMenu")
-" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>"))
- exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Targets'
- endif
- if !exists("s:netrw_initbookhist")
- call s:NetrwBookHistRead()
- endif
-
- " try to cull duplicate entries
- let tgtdict={}
-
- " target bookmarked places
- if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0
-" call Decho("installing bookmarks as easy targets",'~'.expand("<slnum>"))
- let cnt= 1
- for bmd in g:netrw_bookmarklist
- if has_key(tgtdict,bmd)
- let cnt= cnt + 1
- continue
- endif
- let tgtdict[bmd]= cnt
- let ebmd= escape(bmd,g:netrw_menu_escape)
- " show bookmarks for goto menu
-" call Decho("menu: Targets: ".bmd,'~'.expand("<slnum>"))
- exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.1.".cnt." ".g:NetrwTopLvlMenu.'Targets.'.ebmd." :call netrw#MakeTgt('".bmd."')\<cr>"
- let cnt= cnt + 1
- endfor
- endif
-
- " target directory browsing history
- if exists("g:netrw_dirhistmax") && g:netrw_dirhistmax > 0
-" call Decho("installing history as easy targets (histmax=".g:netrw_dirhistmax.")",'~'.expand("<slnum>"))
- let histcnt = 1
- while histcnt <= g:netrw_dirhistmax
- let priority = g:netrw_dirhistcnt + histcnt
- if exists("g:netrw_dirhist_{histcnt}")
- let histentry = g:netrw_dirhist_{histcnt}
- if has_key(tgtdict,histentry)
- let histcnt = histcnt + 1
- continue
- endif
- let tgtdict[histentry] = histcnt
- let ehistentry = escape(histentry,g:netrw_menu_escape)
-" call Decho("menu: Targets: ".histentry,'~'.expand("<slnum>"))
- exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.2.".priority." ".g:NetrwTopLvlMenu.'Targets.'.ehistentry." :call netrw#MakeTgt('".histentry."')\<cr>"
- endif
- let histcnt = histcnt + 1
- endwhile
- endif
- endif
-" call Dret("s:NetrwTgtMenu")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwTreeDir: determine tree directory given current cursor position {{{2
-" (full path directory with trailing slash returned)
-fun! s:NetrwTreeDir(islocal)
-
- if exists("s:treedir") && exists("s:prevwinopen")
- " s:NetrwPrevWinOpen opens a "previous" window -- and thus needs to and does call s:NetrwTreeDir early
- let treedir= s:treedir
- unlet s:treedir
- unlet s:prevwinopen
- return treedir
- endif
- if exists("s:prevwinopen")
- unlet s:prevwinopen
- endif
-
- if !exists("b:netrw_curdir") || b:netrw_curdir == ""
- let b:netrw_curdir= getcwd()
- endif
- let treedir = b:netrw_curdir
- let s:treecurpos= winsaveview()
-
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
-
- " extract tree directory if on a line specifying a subdirectory (ie. ends with "/")
- let curline= substitute(getline('.'),"\t -->.*$",'','')
- if curline =~ '/$'
- let treedir= substitute(getline('.'),'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e')
- elseif curline =~ '@$'
- let potentialdir= resolve(s:NetrwTreePath(w:netrw_treetop))
- else
- let treedir= ""
- endif
-
- " detect user attempting to close treeroot
- if curline !~ '^'.s:treedepthstring && getline('.') != '..'
- " now force a refresh
- sil! NetrwKeepj %d _
- return b:netrw_curdir
- endif
-
- " COMBAK: a symbolic link may point anywhere -- so it will be used to start a new treetop
-" if a:islocal && curline =~ '@$' && isdirectory(s:NetrwFile(potentialdir))
-" let newdir = w:netrw_treetop.'/'.potentialdir
- if a:islocal && curline =~ '@$'
- if isdirectory(s:NetrwFile(potentialdir))
- let treedir = potentialdir
- let w:netrw_treetop = treedir
- endif
- else
- let potentialdir= s:NetrwFile(substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)@$','\1',''))
- let treedir = s:NetrwTreePath(w:netrw_treetop)
- endif
- endif
-
- " sanity maintenance: keep those //s away...
- let treedir= substitute(treedir,'//$','/','')
- return treedir
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwTreeDisplay: recursive tree display {{{2
-fun! s:NetrwTreeDisplay(dir,depth)
- " ensure that there are no folds
- setl nofen
-
- " install ../ and shortdir
- if a:depth == ""
- call setline(line("$")+1,'../')
- endif
- if a:dir =~ '^\a\{3,}://'
- if a:dir == w:netrw_treetop
- let shortdir= a:dir
- else
- let shortdir= substitute(a:dir,'^.*/\([^/]\+\)/$','\1/','e')
- endif
- call setline(line("$")+1,a:depth.shortdir)
- else
- let shortdir= substitute(a:dir,'^.*/','','e')
- call setline(line("$")+1,a:depth.shortdir.'/')
- endif
- " append a / to dir if its missing one
- let dir= a:dir
-
- " display subtrees (if any)
- let depth= s:treedepthstring.a:depth
-
- " implement g:netrw_hide for tree listings (uses g:netrw_list_hide)
- if g:netrw_hide == 1
- " hide given patterns
- let listhide= split(g:netrw_list_hide,',')
- for pat in listhide
- call filter(w:netrw_treedict[dir],'v:val !~ "'.escape(pat,'\\').'"')
- endfor
-
- elseif g:netrw_hide == 2
- " show given patterns (only)
- let listhide= split(g:netrw_list_hide,',')
- let entries=[]
- for entry in w:netrw_treedict[dir]
- for pat in listhide
- if entry =~ pat
- call add(entries,entry)
- break
- endif
- endfor
- endfor
- let w:netrw_treedict[dir]= entries
- endif
- if depth != ""
- " always remove "." and ".." entries when there's depth
- call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\.$"')
- call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\./$"')
- call filter(w:netrw_treedict[dir],'v:val !~ "\\.$"')
- call filter(w:netrw_treedict[dir],'v:val !~ "\\./$"')
- endif
-
- for entry in w:netrw_treedict[dir]
- if dir =~ '/$'
- let direntry= substitute(dir.entry,'[@/]$','','e')
- else
- let direntry= substitute(dir.'/'.entry,'[@/]$','','e')
- endif
- if entry =~ '/$' && has_key(w:netrw_treedict,direntry)
- NetrwKeepj call s:NetrwTreeDisplay(direntry,depth)
- elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/')
- NetrwKeepj call s:NetrwTreeDisplay(direntry.'/',depth)
- elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@')
- NetrwKeepj call s:NetrwTreeDisplay(direntry.'@',depth)
- else
- sil! NetrwKeepj call setline(line("$")+1,depth.entry)
- endif
- endfor
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRefreshTreeDict: updates the contents information for a tree (w:netrw_treedict) {{{2
-fun! s:NetrwRefreshTreeDict(dir)
- if !exists("w:netrw_treedict")
- return
- endif
-
- for entry in w:netrw_treedict[a:dir]
- let direntry= substitute(a:dir.'/'.entry,'[@/]$','','e')
-
- if entry =~ '/$' && has_key(w:netrw_treedict,direntry)
- NetrwKeepj call s:NetrwRefreshTreeDict(direntry)
- let filelist = s:NetrwLocalListingList(direntry,0)
- let w:netrw_treedict[direntry] = sort(filelist)
-
- elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/')
- NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/')
- let filelist = s:NetrwLocalListingList(direntry.'/',0)
- let w:netrw_treedict[direntry] = sort(filelist)
-
- elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@')
- NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/')
- let liststar = s:NetrwGlob(direntry.'/','*',1)
- let listdotstar= s:NetrwGlob(direntry.'/','.*',1)
-
- else
- endif
- endfor
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwTreeListing: displays tree listing from treetop on down, using NetrwTreeDisplay() {{{2
-" Called by s:PerformListing()
-fun! s:NetrwTreeListing(dirname)
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
-
- " update the treetop
- if !exists("w:netrw_treetop")
- let w:netrw_treetop= a:dirname
- let s:netrw_treetop= w:netrw_treetop
- " use \V in case the directory contains specials chars like '$' or '~'
- elseif (w:netrw_treetop =~ ('^'.'\V'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop))
- \ || a:dirname !~ ('^'.'\V'.w:netrw_treetop)
- let w:netrw_treetop= a:dirname
- let s:netrw_treetop= w:netrw_treetop
- endif
- if exists("w:netrw_treetop")
- let s:netrw_treetop= w:netrw_treetop
- else
- let w:netrw_treetop= getcwd()
- let s:netrw_treetop= w:netrw_treetop
- endif
-
- if !exists("w:netrw_treedict")
- " insure that we have a treedict, albeit empty
- let w:netrw_treedict= {}
- endif
-
- " update the dictionary for the current directory
- exe "sil! NetrwKeepj keepp ".w:netrw_bannercnt.',$g@^\.\.\=/$@d _'
- let w:netrw_treedict[a:dirname]= getline(w:netrw_bannercnt,line("$"))
- exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _"
-
- " if past banner, record word
- if exists("w:netrw_bannercnt") && line(".") > w:netrw_bannercnt
- let fname= expand("<cword>")
- else
- let fname= ""
- endif
-
- " display from treetop on down
- NetrwKeepj call s:NetrwTreeDisplay(w:netrw_treetop,"")
-
- " remove any blank line remaining as line#1 (happens in treelisting mode with banner suppressed)
- while getline(1) =~ '^\s*$' && byte2line(1) > 0
- 1d
- endwhile
-
- exe "setl ".g:netrw_bufsettings
-
- return
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwTreePath: returns path to current file/directory in tree listing {{{2
-" Normally, treetop is w:netrw_treetop, but a
-" user of the function ( netrw#SetTreetop() )
-" wipes that out prior to calling this function
-fun! s:NetrwTreePath(treetop)
-" call Dfunc("s:NetrwTreePath(treetop<".a:treetop.">) line#".line(".")."<".getline(".").">")
- if line(".") < w:netrw_bannercnt + 2
- let treedir= a:treetop
- if treedir !~ '/$'
- let treedir= treedir.'/'
- endif
-" call Dret("s:NetrwTreePath ".treedir." : line#".line(".")." ≤ ".(w:netrw_bannercnt+2))
- return treedir
- endif
-
- let svpos = winsaveview()
-" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e')
-" call Decho("depth<".depth."> 1st subst",'~'.expand("<slnum>"))
- let depth = substitute(depth,'^'.s:treedepthstring,'','')
-" call Decho("depth<".depth."> 2nd subst (first depth removed)",'~'.expand("<slnum>"))
- let curline= getline('.')
-" call Decho("curline<".curline.'>','~'.expand("<slnum>"))
- if curline =~ '/$'
-" call Decho("extract tree directory from current line",'~'.expand("<slnum>"))
- let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e')
-" call Decho("treedir<".treedir.">",'~'.expand("<slnum>"))
- elseif curline =~ '@\s\+-->'
-" call Decho("extract tree directory using symbolic link",'~'.expand("<slnum>"))
- let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e')
- let treedir= substitute(treedir,'@\s\+-->.*$','','e')
-" call Decho("treedir<".treedir.">",'~'.expand("<slnum>"))
- else
-" call Decho("do not extract tree directory from current line and set treedir to empty",'~'.expand("<slnum>"))
- let treedir= ""
- endif
- " construct treedir by searching backwards at correct depth
-" call Decho("construct treedir by searching backwards for correct depth",'~'.expand("<slnum>"))
-" call Decho("initial treedir<".treedir."> depth<".depth.">",'~'.expand("<slnum>"))
- while depth != "" && search('^'.depth.'[^'.s:treedepthstring.'].\{-}/$','bW')
- let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e')
- let treedir= dirname.treedir
- let depth = substitute(depth,'^'.s:treedepthstring,'','')
-" call Decho("constructing treedir<".treedir.">: dirname<".dirname."> while depth<".depth.">",'~'.expand("<slnum>"))
- endwhile
-" call Decho("treedir#1<".treedir.">",'~'.expand("<slnum>"))
- if a:treetop =~ '/$'
- let treedir= a:treetop.treedir
- else
- let treedir= a:treetop.'/'.treedir
- endif
-" call Decho("treedir#2<".treedir.">",'~'.expand("<slnum>"))
- let treedir= substitute(treedir,'//$','/','')
-" call Decho("treedir#3<".treedir.">",'~'.expand("<slnum>"))
-" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))"
- call winrestview(svpos)
-" call Dret("s:NetrwTreePath <".treedir.">")
- return treedir
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwWideListing: {{{2
-fun! s:NetrwWideListing()
-
- if w:netrw_liststyle == s:WIDELIST
-" call Dfunc("NetrwWideListing() w:netrw_liststyle=".w:netrw_liststyle.' fo='.&fo.' l:fo='.&l:fo)
- " look for longest filename (cpf=characters per filename)
- " cpf: characters per filename
- " fpl: filenames per line
- " fpc: filenames per column
- setl ma noro
- let dict={}
- " save the unnamed register and register 0-9 and a
- let dict.a=[getreg('a'), getregtype('a')]
- for i in range(0, 9)
- let dict[i] = [getreg(i), getregtype(i)]
- endfor
- let dict.unnamed = [getreg(''), getregtype('')]
-" call Decho("setl ma noro",'~'.expand("<slnum>"))
- let b:netrw_cpf= 0
- if line("$") >= w:netrw_bannercnt
- " determine the maximum filename size; use that to set cpf
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif'
- NetrwKeepj call histdel("/",-1)
- else
- " restore stored registers
- call s:RestoreRegister(dict)
-" call Dret("NetrwWideListing")
- return
- endif
- " allow for two spaces to separate columns
- let b:netrw_cpf= b:netrw_cpf + 2
-" call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf,'~'.expand("<slnum>"))
-
- " determine qty files per line (fpl)
- let w:netrw_fpl= winwidth(0)/b:netrw_cpf
- if w:netrw_fpl <= 0
- let w:netrw_fpl= 1
- endif
-" call Decho("fpl= [winwidth=".winwidth(0)."]/[b:netrw_cpf=".b:netrw_cpf.']='.w:netrw_fpl,'~'.expand("<slnum>"))
-
- " make wide display
- " fpc: files per column of wide listing
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^.*$/\=escape(printf("%-'.b:netrw_cpf.'S",submatch(0)),"\\")/'
- NetrwKeepj call histdel("/",-1)
- let fpc = (line("$") - w:netrw_bannercnt + w:netrw_fpl)/w:netrw_fpl
- let newcolstart = w:netrw_bannercnt + fpc
- let newcolend = newcolstart + fpc - 1
-" call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>"))
- if !has('nvim') && has("clipboard") && g:netrw_clipboard
-" call Decho("(s:NetrwWideListing) save @* and @+",'~'.expand("<slnum>"))
- sil! let keepregstar = @*
- sil! let keepregplus = @+
- endif
- while line("$") >= newcolstart
- if newcolend > line("$") | let newcolend= line("$") | endif
- let newcolqty= newcolend - newcolstart
- exe newcolstart
- " COMBAK: both of the visual-mode using lines below are problematic vis-a-vis @*
- if newcolqty == 0
- exe "sil! NetrwKeepj norm! 0\<c-v>$h\"ax".w:netrw_bannercnt."G$\"ap"
- else
- exe "sil! NetrwKeepj norm! 0\<c-v>".newcolqty.'j$h"ax'.w:netrw_bannercnt.'G$"ap'
- endif
- exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _'
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt
- endwhile
- if !has('nvim') && has("clipboard")
-" call Decho("(s:NetrwWideListing) restore @* and @+",'~'.expand("<slnum>"))
- if @* != keepregstar | sil! let @* = keepregstar | endif
- if @+ != keepregplus | sil! let @+ = keepregplus | endif
- endif
- exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e'
- NetrwKeepj call histdel("/",-1)
- exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>"
- exe 'nno <buffer> <silent> b :call search(''^.\\|\s\s\zs\S'',''bW'')'."\<cr>"
-" call Decho("NetrwWideListing) setl noma nomod ro",'~'.expand("<slnum>"))
- exe "setl ".g:netrw_bufsettings
- call s:RestoreRegister(dict)
-" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
-" call Dret("NetrwWideListing")
- return
- else
- if hasmapto("w","n")
- sil! nunmap <buffer> w
- endif
- if hasmapto("b","n")
- sil! nunmap <buffer> b
- endif
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:PerformListing: {{{2
-fun! s:PerformListing(islocal)
-" call Dfunc("s:PerformListing(islocal=".a:islocal.")")
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
-" call Decho("settings: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (enter)"." ei<".&ei.">",'~'.expand("<slnum>"))
- sil! NetrwKeepj %d _
-" call DechoBuf(bufnr("%"))
-
- " set up syntax highlighting {{{3
-" call Decho("--set up syntax highlighting (ie. setl ft=netrw)",'~'.expand("<slnum>"))
- sil! setl ft=netrw
-
- NetrwKeepj call s:NetrwOptionsSafe(a:islocal)
- setl noro ma
-" call Decho("setl noro ma bh=".&bh,'~'.expand("<slnum>"))
-
-" if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 " Decho
-" call Decho("Processing your browsing request...",'~'.expand("<slnum>"))
-" endif " Decho
-
-" call Decho('w:netrw_liststyle='.(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
- " force a refresh for tree listings
-" call Decho("force refresh for treelisting: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
- sil! NetrwKeepj %d _
- endif
-
- " save current directory on directory history list
- NetrwKeepj call s:NetrwBookHistHandler(3,b:netrw_curdir)
-
- " Set up the banner {{{3
- if g:netrw_banner
-" call Decho("--set up banner",'~'.expand("<slnum>"))
- NetrwKeepj call setline(1,'" ============================================================================')
- if exists("g:netrw_pchk")
- " this undocumented option allows pchk to run with different versions of netrw without causing spurious
- " failure detections.
- NetrwKeepj call setline(2,'" Netrw Directory Listing')
- else
- NetrwKeepj call setline(2,'" Netrw Directory Listing (netrw '.g:loaded_netrw.')')
- endif
- if exists("g:netrw_pchk")
- let curdir= substitute(b:netrw_curdir,expand("$HOME"),'~','')
- else
- let curdir= b:netrw_curdir
- endif
- if exists("g:netrw_bannerbackslash") && g:netrw_bannerbackslash
- NetrwKeepj call setline(3,'" '.substitute(curdir,'/','\\','g'))
- else
- NetrwKeepj call setline(3,'" '.curdir)
- endif
- let w:netrw_bannercnt= 3
- NetrwKeepj exe "sil! NetrwKeepj ".w:netrw_bannercnt
- else
-" call Decho("--no banner",'~'.expand("<slnum>"))
- NetrwKeepj 1
- let w:netrw_bannercnt= 1
- endif
-" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." win#".winnr(),'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
-
- " construct sortby string: [name|time|size|exten] [reversed]
- let sortby= g:netrw_sort_by
- if g:netrw_sort_direction =~# "^r"
- let sortby= sortby." reversed"
- endif
-
- " Sorted by... {{{3
- if g:netrw_banner
-" call Decho("--handle specified sorting: g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>"))
- if g:netrw_sort_by =~# "^n"
-" call Decho("directories will be sorted by name",'~'.expand("<slnum>"))
- " sorted by name (also includes the sorting sequence in the banner)
- NetrwKeepj put ='\" Sorted by '.sortby
- NetrwKeepj put ='\" Sort sequence: '.g:netrw_sort_sequence
- let w:netrw_bannercnt= w:netrw_bannercnt + 2
- else
-" call Decho("directories will be sorted by size or time",'~'.expand("<slnum>"))
- " sorted by time, size, exten
- NetrwKeepj put ='\" Sorted by '.sortby
- let w:netrw_bannercnt= w:netrw_bannercnt + 1
- endif
- exe "sil! NetrwKeepj ".w:netrw_bannercnt
-" else " Decho
-" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
- endif
-
- " show copy/move target, if any {{{3
- if g:netrw_banner
- if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal")
-" call Decho("--show copy/move target<".s:netrwmftgt.">",'~'.expand("<slnum>"))
- NetrwKeepj put =''
- if s:netrwmftgt_islocal
- sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (local)')
- else
- sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (remote)')
- endif
- let w:netrw_bannercnt= w:netrw_bannercnt + 1
- else
-" call Decho("s:netrwmftgt does not exist, don't make Copy/Move Tgt",'~'.expand("<slnum>"))
- endif
- exe "sil! NetrwKeepj ".w:netrw_bannercnt
- endif
-
- " Hiding... -or- Showing... {{{3
- if g:netrw_banner
-" call Decho("--handle hiding/showing in banner (g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">)",'~'.expand("<slnum>"))
- if g:netrw_list_hide != "" && g:netrw_hide
- if g:netrw_hide == 1
- NetrwKeepj put ='\" Hiding: '.g:netrw_list_hide
- else
- NetrwKeepj put ='\" Showing: '.g:netrw_list_hide
- endif
- let w:netrw_bannercnt= w:netrw_bannercnt + 1
- endif
- exe "NetrwKeepj ".w:netrw_bannercnt
-
-" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- let quickhelp = g:netrw_quickhelp%len(s:QuickHelp)
-" call Decho("quickhelp =".quickhelp,'~'.expand("<slnum>"))
- NetrwKeepj put ='\" Quick Help: <F1>:help '.s:QuickHelp[quickhelp]
-" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- NetrwKeepj put ='\" =============================================================================='
- let w:netrw_bannercnt= w:netrw_bannercnt + 2
-" else " Decho
-" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
- endif
-
- " bannercnt should index the line just after the banner
- if g:netrw_banner
- let w:netrw_bannercnt= w:netrw_bannercnt + 1
- exe "sil! NetrwKeepj ".w:netrw_bannercnt
-" call Decho("--w:netrw_bannercnt=".w:netrw_bannercnt." (should index line just after banner) line($)=".line("$"),'~'.expand("<slnum>"))
-" else " Decho
-" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
- endif
-
- " get list of files
-" call Decho("--Get list of files - islocal=".a:islocal,'~'.expand("<slnum>"))
- if a:islocal
- NetrwKeepj call s:LocalListing()
- else " remote
- NetrwKeepj let badresult= s:NetrwRemoteListing()
- if badresult
-" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
-" call Dret("s:PerformListing : error detected by NetrwRemoteListing")
- return
- endif
- endif
-
- " manipulate the directory listing (hide, sort) {{{3
- if !exists("w:netrw_bannercnt")
- let w:netrw_bannercnt= 0
- endif
-" call Decho("--manipulate directory listing (hide, sort)",'~'.expand("<slnum>"))
-" call Decho("g:netrw_banner=".g:netrw_banner." w:netrw_bannercnt=".w:netrw_bannercnt." (banner complete)",'~'.expand("<slnum>"))
-" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
-
- if !g:netrw_banner || line("$") >= w:netrw_bannercnt
-" call Decho("manipulate directory listing (support hide)",'~'.expand("<slnum>"))
-" call Decho("g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>"))
- if g:netrw_hide && g:netrw_list_hide != ""
- NetrwKeepj call s:NetrwListHide()
- endif
- if !g:netrw_banner || line("$") >= w:netrw_bannercnt
-" call Decho("manipulate directory listing (sort) : g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>"))
-
- if g:netrw_sort_by =~# "^n"
- " sort by name
-" call Decho("sort by name",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwSetSort()
-
- if !g:netrw_banner || w:netrw_bannercnt < line("$")
-" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
- if g:netrw_sort_direction =~# 'n'
- " name: sort by name of file
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
- else
- " reverse direction sorting
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options
- endif
- endif
-
- " remove priority pattern prefix
-" call Decho("remove priority pattern prefix",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e'
- NetrwKeepj call histdel("/",-1)
-
- elseif g:netrw_sort_by =~# "^ext"
- " exten: sort by extension
- " The histdel(...,-1) calls remove the last search from the search history
-" call Decho("sort by extension",'~'.expand("<slnum>"))
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/'
- NetrwKeepj call histdel("/",-1)
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+[./]+s/^/002'.g:netrw_sepchr.'/'
- NetrwKeepj call histdel("/",-1)
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+['.g:netrw_sepchr.'/]+s/^\(.*\.\)\(.\{-\}\)$/\2'.g:netrw_sepchr.'&/e'
- NetrwKeepj call histdel("/",-1)
- if !g:netrw_banner || w:netrw_bannercnt < line("$")
-" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
- if g:netrw_sort_direction =~# 'n'
- " normal direction sorting
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
- else
- " reverse direction sorting
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options
- endif
- endif
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^.\{-}'.g:netrw_sepchr.'//e'
- NetrwKeepj call histdel("/",-1)
-
- elseif a:islocal
- if !g:netrw_banner || w:netrw_bannercnt < line("$")
-" call Decho("g:netrw_sort_direction=".g:netrw_sort_direction,'~'.expand("<slnum>"))
- if g:netrw_sort_direction =~# 'n'
-" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort','~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
- else
-" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!','~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options
- endif
-" call Decho("remove leading digits/ (sorting) information from listing",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e'
- NetrwKeepj call histdel("/",-1)
- endif
- endif
-
- elseif g:netrw_sort_direction =~# 'r'
-" call Decho('(s:PerformListing) reverse the sorted listing','~'.expand("<slnum>"))
- if !g:netrw_banner || w:netrw_bannercnt < line('$')
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g/^/m '.w:netrw_bannercnt
- call histdel("/",-1)
- endif
- endif
- endif
-" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
-
- " convert to wide/tree listing {{{3
-" call Decho("--modify display if wide/tree listing style",'~'.expand("<slnum>"))
-" 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. " (internal#1)",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwWideListing()
-" 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. " (internal#2)",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwTreeListing(b:netrw_curdir)
-" 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. " (internal#3)",'~'.expand("<slnum>"))
-
- " resolve symbolic links if local and (thin or tree)
- if a:islocal && (w:netrw_liststyle == s:THINLIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST))
-" call Decho("--resolve symbolic links if local and thin|tree",'~'.expand("<slnum>"))
- sil! keepp g/@$/call s:ShowLink()
- endif
-
- if exists("w:netrw_bannercnt") && (line("$") >= w:netrw_bannercnt || !g:netrw_banner)
- " place cursor on the top-left corner of the file listing
-" call Decho("--place cursor on top-left corner of file listing",'~'.expand("<slnum>"))
- exe 'sil! '.w:netrw_bannercnt
- sil! NetrwKeepj norm! 0
-" call Decho(" tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
- else
-" call Decho("--did NOT place cursor on top-left corner",'~'.expand("<slnum>"))
-" call Decho(" w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a'),'~'.expand("<slnum>"))
-" call Decho(" line($)=".line("$"),'~'.expand("<slnum>"))
-" call Decho(" g:netrw_banner=".(exists("g:netrw_banner")? g:netrw_banner : 'n/a'),'~'.expand("<slnum>"))
- endif
-
- " record previous current directory
- let w:netrw_prvdir= b:netrw_curdir
-" call Decho("--record netrw_prvdir<".w:netrw_prvdir.">",'~'.expand("<slnum>"))
-
- " save certain window-oriented variables into buffer-oriented variables {{{3
-" call Decho("--save some window-oriented variables into buffer oriented variables",'~'.expand("<slnum>"))
-" 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. " (internal#4)",'~'.expand("<slnum>"))
- NetrwKeepj call s:SetBufWinVars()
-" 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. " (internal#5)",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwOptionsRestore("w:")
-" 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. " (internal#6)",'~'.expand("<slnum>"))
-
- " set display to netrw display settings
-" call Decho("--set display to netrw display settings (".g:netrw_bufsettings.")",'~'.expand("<slnum>"))
- exe "setl ".g:netrw_bufsettings
-" 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. " (internal#7)",'~'.expand("<slnum>"))
- if g:netrw_liststyle == s:LONGLIST
-" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>"))
- exe "setl ts=".(g:netrw_maxfilenamelen+1)
- endif
-" call Decho("PerformListing buffer:",'~'.expand("<slnum>"))
-" call DechoBuf(bufnr("%"))
-
- if exists("s:treecurpos")
-" call Decho("s:treecurpos exists; restore posn",'~'.expand("<slnum>"))
-" 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. " (internal#8)",'~'.expand("<slnum>"))
-" call Decho("restoring posn to s:treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(s:treecurpos)
- unlet s:treecurpos
- 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. " (return)",'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
-" call Dret("s:PerformListing : curpos<".string(getpos(".")).">")
-endfun
-
-" ---------------------------------------------------------------------
-" s:SetupNetrwStatusLine: {{{2
-fun! s:SetupNetrwStatusLine(statline)
-" call Dfunc("SetupNetrwStatusLine(statline<".a:statline.">)")
-
- if !exists("s:netrw_setup_statline")
- let s:netrw_setup_statline= 1
-" call Decho("do first-time status line setup",'~'.expand("<slnum>"))
-
- if !exists("s:netrw_users_stl")
- let s:netrw_users_stl= &stl
- endif
- if !exists("s:netrw_users_ls")
- let s:netrw_users_ls= &laststatus
- endif
-
- " set up User9 highlighting as needed
- let dict={}
- let dict.a=[getreg('a'), getregtype('a')]
- redir @a
- try
- hi User9
- catch /^Vim\%((\a\{3,})\)\=:E411/
- if &bg == "dark"
- hi User9 ctermfg=yellow ctermbg=blue guifg=yellow guibg=blue
- else
- hi User9 ctermbg=yellow ctermfg=blue guibg=yellow guifg=blue
- endif
- endtry
- redir END
- call s:RestoreRegister(dict)
- endif
-
- " set up status line (may use User9 highlighting)
- " insure that windows have a statusline
- " make sure statusline is displayed
- let &l:stl=a:statline
- setl laststatus=2
-" call Decho("stl=".&stl,'~'.expand("<slnum>"))
- redraw
-
-" call Dret("SetupNetrwStatusLine : stl=".&stl)
-endfun
-
-" =========================================
-" Remote Directory Browsing Support: {{{1
-" =========================================
-
-" ---------------------------------------------------------------------
-" s:NetrwRemoteFtpCmd: unfortunately, not all ftp servers honor options for ls {{{2
-" This function assumes that a long listing will be received. Size, time,
-" and reverse sorts will be requested of the server but not otherwise
-" enforced here.
-fun! s:NetrwRemoteFtpCmd(path,listcmd)
-" call Dfunc("NetrwRemoteFtpCmd(path<".a:path."> listcmd<".a:listcmd.">) w:netrw_method=".(exists("w:netrw_method")? w:netrw_method : (exists("b:netrw_method")? b:netrw_method : "???")))
-" call Decho("line($)=".line("$")." win#".winnr()." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>"))
- " sanity check: {{{3
- if !exists("w:netrw_method")
- if exists("b:netrw_method")
- let w:netrw_method= b:netrw_method
- else
- call netrw#ErrorMsg(2,"(s:NetrwRemoteFtpCmd) internal netrw error",93)
-" call Dret("NetrwRemoteFtpCmd")
- return
- endif
- endif
-
- " WinXX ftp uses unix style input, so set ff to unix " {{{3
- let ffkeep= &ff
- setl ma ff=unix noro
-" call Decho("setl ma ff=unix noro",'~'.expand("<slnum>"))
-
- " clear off any older non-banner lines " {{{3
- " note that w:netrw_bannercnt indexes the line after the banner
-" call Decho('exe sil! NetrwKeepj '.w:netrw_bannercnt.",$d _ (clear off old non-banner lines)",'~'.expand("<slnum>"))
- exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _"
-
- ".........................................
- if w:netrw_method == 2 || w:netrw_method == 5 " {{{3
- " ftp + <.netrc>: Method #2
- if a:path != ""
- NetrwKeepj put ='cd \"'.a:path.'\"'
- endif
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- NetrwKeepj call setline(line("$")+1,a:listcmd)
-" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))'
- if exists("g:netrw_port") && g:netrw_port != ""
-" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1),'~'.expand("<slnum>"))
- exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)
- else
-" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1),'~'.expand("<slnum>"))
- exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)
- endif
-
- ".........................................
- elseif w:netrw_method == 3 " {{{3
- " ftp + machine,id,passwd,filename: Method #3
- setl ff=unix
- if exists("g:netrw_port") && g:netrw_port != ""
- NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
- else
- NetrwKeepj put ='open '.g:netrw_machine
- endif
-
- " handle userid and password
- let host= substitute(g:netrw_machine,'\..*$','','')
-" call Decho("host<".host.">",'~'.expand("<slnum>"))
- if exists("s:netrw_hup") && exists("s:netrw_hup[host]")
- call NetUserPass("ftp:".host)
- endif
- if exists("g:netrw_uid") && g:netrw_uid != ""
- if exists("g:netrw_ftp") && g:netrw_ftp == 1
- NetrwKeepj put =g:netrw_uid
- if exists("s:netrw_passwd") && s:netrw_passwd != ""
- NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
- endif
- elseif exists("s:netrw_passwd")
- NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
- endif
- endif
-
- if a:path != ""
- NetrwKeepj put ='cd \"'.a:path.'\"'
- endif
- if exists("g:netrw_ftpextracmd")
- NetrwKeepj put =g:netrw_ftpextracmd
-" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
- endif
- NetrwKeepj call setline(line("$")+1,a:listcmd)
-
- " perform ftp:
- " -i : turns off interactive prompting from ftp
- " -n unix : DON'T use <.netrc>, even though it exists
- " -n win32: quit being obnoxious about password
- if exists("w:netrw_bannercnt")
-" exe w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))'
- call s:NetrwExe(s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
-" else " Decho
-" call Decho("WARNING: w:netrw_bannercnt doesn't exist!",'~'.expand("<slnum>"))
-" g/^./call Decho("SKIPPING ftp#".line(".").": ".getline("."),'~'.expand("<slnum>"))
- endif
-
- ".........................................
- elseif w:netrw_method == 9 " {{{3
- " sftp username@machine: Method #9
- " s:netrw_sftp_cmd
- setl ff=unix
-
- " restore settings
- let &l:ff= ffkeep
-" call Dret("NetrwRemoteFtpCmd")
- return
-
- ".........................................
- else " {{{3
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . bufname("%") . ">",23)
- endif
-
- " cleanup for Windows " {{{3
- if has("win32")
- sil! NetrwKeepj %s/\r$//e
- NetrwKeepj call histdel("/",-1)
- endif
- if a:listcmd == "dir"
- " infer directory/link based on the file permission string
- sil! NetrwKeepj g/d\%([-r][-w][-x]\)\{3}/NetrwKeepj s@$@/@e
- sil! NetrwKeepj g/l\%([-r][-w][-x]\)\{3}/NetrwKeepj s/$/@/e
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST)
- exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/^\%(\S\+\s\+\)\{8}//e'
- NetrwKeepj call histdel("/",-1)
- endif
- endif
-
- " ftp's listing doesn't seem to include ./ or ../ " {{{3
- if !search('^\.\/$\|\s\.\/$','wn')
- exe 'NetrwKeepj '.w:netrw_bannercnt
- NetrwKeepj put ='./'
- endif
- if !search('^\.\.\/$\|\s\.\.\/$','wn')
- exe 'NetrwKeepj '.w:netrw_bannercnt
- NetrwKeepj put ='../'
- endif
-
- " restore settings " {{{3
- let &l:ff= ffkeep
-" call Dret("NetrwRemoteFtpCmd")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRemoteListing: {{{2
-fun! s:NetrwRemoteListing()
-" call Dfunc("s:NetrwRemoteListing() b:netrw_curdir<".b:netrw_curdir.">) win#".winnr())
-
- if !exists("w:netrw_bannercnt") && exists("s:bannercnt")
- let w:netrw_bannercnt= s:bannercnt
- endif
- if !exists("w:netrw_bannercnt") && exists("b:bannercnt")
- let w:netrw_bannercnt= b:bannercnt
- endif
-
- call s:RemotePathAnalysis(b:netrw_curdir)
-
- " sanity check:
- if exists("b:netrw_method") && b:netrw_method =~ '[235]'
-" call Decho("b:netrw_method=".b:netrw_method,'~'.expand("<slnum>"))
- if !executable("ftp")
-" call Decho("ftp is not executable",'~'.expand("<slnum>"))
- if !exists("g:netrw_quiet")
- call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ftp",18)
- endif
- call s:NetrwOptionsRestore("w:")
-" call Dret("s:NetrwRemoteListing -1")
- return -1
- endif
-
- elseif !exists("g:netrw_list_cmd") || g:netrw_list_cmd == ''
-" call Decho("g:netrw_list_cmd<",(exists("g:netrw_list_cmd")? 'n/a' : "-empty-").">",'~'.expand("<slnum>"))
- if !exists("g:netrw_quiet")
- if g:netrw_list_cmd == ""
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your g:netrw_list_cmd is empty; perhaps ".g:netrw_ssh_cmd." is not executable on your system",47)
- else
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ".g:netrw_list_cmd,19)
- endif
- endif
-
- NetrwKeepj call s:NetrwOptionsRestore("w:")
-" call Dret("s:NetrwRemoteListing -1")
- return -1
- endif " (remote handling sanity check)
-" call Decho("passed remote listing sanity checks",'~'.expand("<slnum>"))
-
- if exists("b:netrw_method")
-" call Decho("setting w:netrw_method to b:netrw_method<".b:netrw_method.">",'~'.expand("<slnum>"))
- let w:netrw_method= b:netrw_method
- endif
-
- if s:method == "ftp"
- " use ftp to get remote file listing {{{3
-" call Decho("use ftp to get remote file listing",'~'.expand("<slnum>"))
- let s:method = "ftp"
- let listcmd = g:netrw_ftp_list_cmd
- if g:netrw_sort_by =~# '^t'
- let listcmd= g:netrw_ftp_timelist_cmd
- elseif g:netrw_sort_by =~# '^s'
- let listcmd= g:netrw_ftp_sizelist_cmd
- endif
-" call Decho("listcmd<".listcmd."> (using g:netrw_ftp_list_cmd)",'~'.expand("<slnum>"))
- call s:NetrwRemoteFtpCmd(s:path,listcmd)
-" exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("raw listing: ".getline("."),''~''.expand("<slnum>"))'
-
- " report on missing file or directory messages
- if search('[Nn]o such file or directory\|Failed to change directory')
- let mesg= getline(".")
- if exists("w:netrw_bannercnt")
- setl ma
- exe w:netrw_bannercnt.",$d _"
- setl noma
- endif
- NetrwKeepj call s:NetrwOptionsRestore("w:")
- call netrw#ErrorMsg(s:WARNING,mesg,96)
-" call Dret("s:NetrwRemoteListing : -1")
- return -1
- endif
-
- if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST)
- " shorten the listing
-" call Decho("generate short listing",'~'.expand("<slnum>"))
- exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt
-
- " cleanup
- if g:netrw_ftp_browse_reject != ""
- exe "sil! keepalt NetrwKeepj g/".g:netrw_ftp_browse_reject."/NetrwKeepj d"
- NetrwKeepj call histdel("/",-1)
- endif
- sil! NetrwKeepj %s/\r$//e
- NetrwKeepj call histdel("/",-1)
-
- " if there's no ../ listed, then put ../ in
- let line1= line(".")
- exe "sil! NetrwKeepj ".w:netrw_bannercnt
- let line2= search('\.\.\/\%(\s\|$\)','cnW')
-" call Decho("search(".'\.\.\/\%(\s\|$\)'."','cnW')=".line2." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>"))
- if line2 == 0
-" call Decho("netrw is putting ../ into listing",'~'.expand("<slnum>"))
- sil! NetrwKeepj put='../'
- endif
- exe "sil! NetrwKeepj ".line1
- sil! NetrwKeepj norm! 0
-
-" call Decho("line1=".line1." line2=".line2." line(.)=".line("."),'~'.expand("<slnum>"))
- if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup
-" call Decho("M$ ftp cleanup",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+//'
- NetrwKeepj call histdel("/",-1)
- else " normal ftp cleanup
-" call Decho("normal ftp cleanup",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2/e'
- exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*/$#/#e'
- exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*$#/#e'
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- endif
- endif
-
- else
- " use ssh to get remote file listing {{{3
-" call Decho("use ssh to get remote file listing: s:path<".s:path.">",'~'.expand("<slnum>"))
- let listcmd= s:MakeSshCmd(g:netrw_list_cmd)
-" call Decho("listcmd<".listcmd."> (using g:netrw_list_cmd)",'~'.expand("<slnum>"))
- if g:netrw_scp_cmd =~ '^pscp'
-" call Decho("1: exe r! ".s:ShellEscape(listcmd.s:path, 1),'~'.expand("<slnum>"))
- exe "NetrwKeepj r! ".listcmd.s:ShellEscape(s:path, 1)
- " remove rubbish and adjust listing format of 'pscp' to 'ssh ls -FLa' like
- sil! NetrwKeepj g/^Listing directory/NetrwKeepj d
- sil! NetrwKeepj g/^d[-rwx][-rwx][-rwx]/NetrwKeepj s+$+/+e
- sil! NetrwKeepj g/^l[-rwx][-rwx][-rwx]/NetrwKeepj s+$+@+e
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- if g:netrw_liststyle != s:LONGLIST
- sil! NetrwKeepj g/^[dlsp-][-rwx][-rwx][-rwx]/NetrwKeepj s/^.*\s\(\S\+\)$/\1/e
- NetrwKeepj call histdel("/",-1)
- endif
- else
- if s:path == ""
-" call Decho("2: exe r! ".listcmd,'~'.expand("<slnum>"))
- exe "NetrwKeepj keepalt r! ".listcmd
- else
-" call Decho("3: exe r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1),'~'.expand("<slnum>"))
- exe "NetrwKeepj keepalt r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1)
-" call Decho("listcmd<".listcmd."> path<".s:path.">",'~'.expand("<slnum>"))
- endif
- endif
-
- " cleanup
- if g:netrw_ssh_browse_reject != ""
-" call Decho("cleanup: exe sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d",'~'.expand("<slnum>"))
- exe "sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d"
- NetrwKeepj call histdel("/",-1)
- endif
- endif
-
- if w:netrw_liststyle == s:LONGLIST
- " do a long listing; these substitutions need to be done prior to sorting {{{3
-" call Decho("fix long listing:",'~'.expand("<slnum>"))
-
- if s:method == "ftp"
- " cleanup
- exe "sil! NetrwKeepj ".w:netrw_bannercnt
- while getline('.') =~# g:netrw_ftp_browse_reject
- sil! NetrwKeepj d
- endwhile
- " if there's no ../ listed, then put ../ in
- let line1= line(".")
- sil! NetrwKeepj 1
- sil! NetrwKeepj call search('^\.\.\/\%(\s\|$\)','W')
- let line2= line(".")
- if line2 == 0
- if b:netrw_curdir != '/'
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt."put='../'"
- endif
- endif
- exe "sil! NetrwKeepj ".line1
- sil! NetrwKeepj norm! 0
- endif
-
- if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup
-" call Decho("M$ ftp site listing cleanup",'~'.expand("<slnum>"))
- exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+\)\(\w.*\)$/\2\t\1/'
- elseif exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$")
-" call Decho("normal ftp site listing cleanup: bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("<slnum>"))
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/ -> .*$//e'
- exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2 \t\1/e'
- exe 'sil NetrwKeepj '.w:netrw_bannercnt
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- NetrwKeepj call histdel("/",-1)
- endif
- endif
-
-" if exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") " Decho
-" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("listing: ".getline("."),''~''.expand("<slnum>"))'
-" endif " Decho
-
-" call Dret("s:NetrwRemoteListing 0")
- return 0
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRemoteRm: remove/delete a remote file or directory {{{2
-fun! s:NetrwRemoteRm(usrhost,path) range
- let svpos= winsaveview()
-
- let all= 0
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
- " remove all marked files
- for fname in s:netrwmarkfilelist_{bufnr("%")}
- let ok= s:NetrwRemoteRmFile(a:path,fname,all)
- if ok =~# 'q\%[uit]'
- break
- elseif ok =~# 'a\%[ll]'
- let all= 1
- endif
- endfor
- call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir)
-
- else
- " remove files specified by range
-
- " preparation for removing multiple files/directories
- let keepsol = &l:sol
- setl nosol
- let ctr = a:firstline
-
- " remove multiple files and directories
- while ctr <= a:lastline
- exe "NetrwKeepj ".ctr
- let ok= s:NetrwRemoteRmFile(a:path,s:NetrwGetWord(),all)
- if ok =~# 'q\%[uit]'
- break
- elseif ok =~# 'a\%[ll]'
- let all= 1
- endif
- let ctr= ctr + 1
- endwhile
- let &l:sol = keepsol
- endif
-
- " refresh the (remote) directory listing
- NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
- NetrwKeepj call winrestview(svpos)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRemoteRmFile: {{{2
-fun! s:NetrwRemoteRmFile(path,rmfile,all)
-" call Dfunc("s:NetrwRemoteRmFile(path<".a:path."> rmfile<".a:rmfile.">) all=".a:all)
-
- let all= a:all
- let ok = ""
-
- if a:rmfile !~ '^"' && (a:rmfile =~ '@$' || a:rmfile !~ '[\/]$')
- " attempt to remove file
-" call Decho("attempt to remove file (all=".all.")",'~'.expand("<slnum>"))
- if !all
- echohl Statement
-" call Decho("case all=0:",'~'.expand("<slnum>"))
- call inputsave()
- let ok= input("Confirm deletion of file<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
- call inputrestore()
- echohl NONE
- if ok == ""
- let ok="no"
- endif
- let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
- if ok =~# 'a\%[ll]'
- let all= 1
- endif
- endif
-
- if all || ok =~# 'y\%[es]' || ok == ""
-" call Decho("case all=".all." or ok<".ok.">".(exists("w:netrw_method")? ': netrw_method='.w:netrw_method : ""),'~'.expand("<slnum>"))
- if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
-" call Decho("case ftp:",'~'.expand("<slnum>"))
- let path= a:path
- if path =~ '^\a\{3,}://'
- let path= substitute(path,'^\a\{3,}://[^/]\+/','','')
- endif
- sil! NetrwKeepj .,$d _
- call s:NetrwRemoteFtpCmd(path,"delete ".'"'.a:rmfile.'"')
- else
-" call Decho("case ssh: g:netrw_rm_cmd<".g:netrw_rm_cmd.">",'~'.expand("<slnum>"))
- let netrw_rm_cmd= s:MakeSshCmd(g:netrw_rm_cmd)
-" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>"))
- if !exists("b:netrw_curdir")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53)
- let ok="q"
- else
- let remotedir= substitute(b:netrw_curdir,'^.\{-}//[^/]\+/\(.*\)$','\1','')
-" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>"))
-" call Decho("remotedir<".remotedir.">",'~'.expand("<slnum>"))
-" call Decho("rmfile<".a:rmfile.">",'~'.expand("<slnum>"))
- if remotedir != ""
- let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(remotedir.a:rmfile))
- else
- let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(a:rmfile))
- endif
-" call Decho("call system(".netrw_rm_cmd.")",'~'.expand("<slnum>"))
- let ret= system(netrw_rm_cmd)
- if v:shell_error != 0
- if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir
- call netrw#ErrorMsg(s:ERROR,"remove failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",102)
- else
- call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60)
- endif
- elseif ret != 0
- call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60)
- endif
-" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>"))
- endif
- endif
- elseif ok =~# 'q\%[uit]'
-" call Decho("ok==".ok,'~'.expand("<slnum>"))
- endif
-
- else
- " attempt to remove directory
-" call Decho("attempt to remove directory",'~'.expand("<slnum>"))
- if !all
- call inputsave()
- let ok= input("Confirm deletion of directory<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
- call inputrestore()
- if ok == ""
- let ok="no"
- endif
- let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
- if ok =~# 'a\%[ll]'
- let all= 1
- endif
- endif
-
- if all || ok =~# 'y\%[es]' || ok == ""
- if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
- NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rmdir ".a:rmfile)
- else
- let rmfile = substitute(a:path.a:rmfile,'/$','','')
- let netrw_rmdir_cmd = s:MakeSshCmd(netrw#WinPath(g:netrw_rmdir_cmd)).' '.s:ShellEscape(netrw#WinPath(rmfile))
-" call Decho("attempt to remove dir: system(".netrw_rmdir_cmd.")",'~'.expand("<slnum>"))
- let ret= system(netrw_rmdir_cmd)
-" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>"))
-
- if v:shell_error != 0
-" call Decho("v:shell_error not 0",'~'.expand("<slnum>"))
- let netrw_rmf_cmd= s:MakeSshCmd(netrw#WinPath(g:netrw_rmf_cmd)).' '.s:ShellEscape(netrw#WinPath(substitute(rmfile,'[\/]$','','e')))
-" call Decho("2nd attempt to remove dir: system(".netrw_rmf_cmd.")",'~'.expand("<slnum>"))
- let ret= system(netrw_rmf_cmd)
-" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>"))
-
- if v:shell_error != 0 && !exists("g:netrw_quiet")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",22)
- endif
- endif
- endif
-
- elseif ok =~# 'q\%[uit]'
-" call Decho("ok==".ok,'~'.expand("<slnum>"))
- endif
- endif
-
-" call Dret("s:NetrwRemoteRmFile ".ok)
- return ok
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRemoteRename: rename a remote file or directory {{{2
-fun! s:NetrwRemoteRename(usrhost,path) range
-
- " preparation for removing multiple files/directories
- let svpos = winsaveview()
-" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- let ctr = a:firstline
- let rename_cmd = s:MakeSshCmd(g:netrw_rename_cmd)
-
- " rename files given by the markfilelist
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
- for oldname in s:netrwmarkfilelist_{bufnr("%")}
- if exists("subfrom")
- let newname= substitute(oldname,subfrom,subto,'')
- else
- call inputsave()
- let newname= input("Moving ".oldname." to : ",oldname)
- call inputrestore()
- if newname =~ '^s/'
- let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','')
- let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','')
- let newname = substitute(oldname,subfrom,subto,'')
- endif
- endif
-
- if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
- NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname)
- else
- let oldname= s:ShellEscape(a:path.oldname)
- let newname= s:ShellEscape(a:path.newname)
- let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname)
- endif
-
- endfor
- call s:NetrwUnMarkFile(1)
-
- else
-
- " attempt to rename files/directories
- let keepsol= &l:sol
- setl nosol
- while ctr <= a:lastline
- exe "NetrwKeepj ".ctr
-
- let oldname= s:NetrwGetWord()
-
- call inputsave()
- let newname= input("Moving ".oldname." to : ",oldname)
- call inputrestore()
-
- if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
- call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname)
- else
- let oldname= s:ShellEscape(a:path.oldname)
- let newname= s:ShellEscape(a:path.newname)
- let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname)
- endif
-
- let ctr= ctr + 1
- endwhile
- let &l:sol= keepsol
- endif
-
- " refresh the directory
- NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
- NetrwKeepj call winrestview(svpos)
-endfun
-
-" ==========================================
-" Local Directory Browsing Support: {{{1
-" ==========================================
-
-" ---------------------------------------------------------------------
-" netrw#FileUrlEdit: handles editing file://* files {{{2
-" Should accept: file://localhost/etc/fstab
-" file:///etc/fstab
-" file:///c:/WINDOWS/clock.avi
-" file:///c|/WINDOWS/clock.avi
-" file://localhost/c:/WINDOWS/clock.avi
-" file://localhost/c|/WINDOWS/clock.avi
-" file://c:/foo.txt
-" file:///c:/foo.txt
-" and %XX (where X is [0-9a-fA-F] is converted into a character with the given hexadecimal value
-fun! netrw#FileUrlEdit(fname)
-" call Dfunc("netrw#FileUrlEdit(fname<".a:fname.">)")
- let fname = a:fname
- if fname =~ '^file://localhost/'
-" call Decho('converting file://localhost/ -to- file:///','~'.expand("<slnum>"))
- let fname= substitute(fname,'^file://localhost/','file:///','')
-" call Decho("fname<".fname.">",'~'.expand("<slnum>"))
- endif
- if has("win32")
- if fname =~ '^file:///\=\a[|:]/'
-" call Decho('converting file:///\a|/ -to- file://\a:/','~'.expand("<slnum>"))
- let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','')
-" call Decho("fname<".fname.">",'~'.expand("<slnum>"))
- endif
- endif
- let fname2396 = netrw#RFC2396(fname)
- let fname2396e= fnameescape(fname2396)
- let plainfname= substitute(fname2396,'file://\(.*\)','\1',"")
- if has("win32")
-" call Decho("windows exception for plainfname",'~'.expand("<slnum>"))
- if plainfname =~ '^/\+\a:'
-" call Decho('removing leading "/"s','~'.expand("<slnum>"))
- let plainfname= substitute(plainfname,'^/\+\(\a:\)','\1','')
- endif
- endif
-
-" call Decho("fname2396<".fname2396.">",'~'.expand("<slnum>"))
-" call Decho("plainfname<".plainfname.">",'~'.expand("<slnum>"))
- exe "sil doau BufReadPre ".fname2396e
- exe 'NetrwKeepj keepalt edit '.plainfname
- exe 'sil! NetrwKeepj keepalt bdelete '.fnameescape(a:fname)
-
-" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
-" call Dret("netrw#FileUrlEdit")
- exe "sil doau BufReadPost ".fname2396e
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#LocalBrowseCheck: {{{2
-fun! netrw#LocalBrowseCheck(dirname)
- " This function is called by netrwPlugin.vim's s:LocalBrowseCheck(), s:NetrwRexplore(),
- " and by <cr> when atop a listed file/directory (via a buffer-local map)
- "
- " unfortunate interaction -- split window debugging can't be used here, must use
- " D-echoRemOn or D-echoTabOn as the BufEnter event triggers
- " another call to LocalBrowseCheck() when attempts to write
- " to the DBG buffer are made.
- "
- " The &ft == "netrw" test was installed because the BufEnter event
- " would hit when re-entering netrw windows, creating unexpected
- " refreshes (and would do so in the middle of NetrwSaveOptions(), too)
-" call Dfunc("netrw#LocalBrowseCheck(dirname<".a:dirname.">)")
-" call Decho("isdir<".a:dirname."> =".isdirectory(s:NetrwFile(a:dirname)).((exists("s:treeforceredraw")? " treeforceredraw" : "")).'~'.expand("<slnum>"))
-" 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,'~'.expand("<slnum>"))
- " getting E930: Cannot use :redir inside execute
-"" call Dredir("ls!","netrw#LocalBrowseCheck")
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Decho("current buffer#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>"))
-
- let ykeep= @@
- if isdirectory(s:NetrwFile(a:dirname))
-" call Decho("is-directory ft<".&ft."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : " doesn't exist")."> dirname<".a:dirname.">"." line($)=".line("$")." ft<".&ft."> g:netrw_fastbrowse=".g:netrw_fastbrowse,'~'.expand("<slnum>"))
-
- if &ft != "netrw" || (exists("b:netrw_curdir") && b:netrw_curdir != a:dirname) || g:netrw_fastbrowse <= 1
-" call Decho("case 1 : ft=".&ft,'~'.expand("<slnum>"))
-" call Decho("s:rexposn_".bufnr("%")."<".bufname("%")."> ".(exists("s:rexposn_".bufnr("%"))? "exists" : "does not exist"),'~'.expand("<slnum>"))
- sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname)
-
- elseif &ft == "netrw" && line("$") == 1
-" call Decho("case 2 (ft≡netrw && line($)≡1)",'~'.expand("<slnum>"))
- sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname)
-
- elseif exists("s:treeforceredraw")
-" call Decho("case 3 (treeforceredraw)",'~'.expand("<slnum>"))
- unlet s:treeforceredraw
- sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname)
- endif
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
-" call Dret("netrw#LocalBrowseCheck")
- return
- endif
-
- " The following code wipes out currently unused netrw buffers
- " IF g:netrw_fastbrowse is zero (ie. slow browsing selected)
- " AND IF the listing style is not a tree listing
- if exists("g:netrw_fastbrowse") && g:netrw_fastbrowse == 0 && g:netrw_liststyle != s:TREELIST
-" call Decho("wiping out currently unused netrw buffers",'~'.expand("<slnum>"))
- let ibuf = 1
- let buflast = bufnr("$")
- while ibuf <= buflast
- if bufwinnr(ibuf) == -1 && isdirectory(s:NetrwFile(bufname(ibuf)))
- exe "sil! keepj keepalt ".ibuf."bw!"
- endif
- let ibuf= ibuf + 1
- endwhile
- endif
- let @@= ykeep
-" 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,'~'.expand("<slnum>"))
-" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
- " not a directory, ignore it
-" call Dret("netrw#LocalBrowseCheck : not a directory, ignoring it; dirname<".a:dirname.">")
-endfun
-
-" ---------------------------------------------------------------------
-" s:LocalBrowseRefresh: this function is called after a user has {{{2
-" performed any shell command. The idea is to cause all local-browsing
-" buffers to be refreshed after a user has executed some shell command,
-" on the chance that s/he removed/created a file/directory with it.
-fun! s:LocalBrowseRefresh()
- " determine which buffers currently reside in a tab
- if !exists("s:netrw_browselist")
- return
- endif
- if !exists("w:netrw_bannercnt")
- return
- endif
- if !empty(getcmdwintype())
- " cannot move away from cmdline window, see :h E11
- return
- endif
- if exists("s:netrw_events") && s:netrw_events == 1
- " s:LocalFastBrowser gets called (indirectly) from a
- let s:netrw_events= 2
- return
- endif
- let itab = 1
- let buftablist = []
- let ykeep = @@
- while itab <= tabpagenr("$")
- let buftablist = buftablist + tabpagebuflist()
- let itab = itab + 1
- sil! tabn
- endwhile
- " GO through all buffers on netrw_browselist (ie. just local-netrw buffers):
- " | refresh any netrw window
- " | wipe out any non-displaying netrw buffer
- let curwinid = win_getid(winnr())
- let ibl = 0
- for ibuf in s:netrw_browselist
- if bufwinnr(ibuf) == -1 && index(buftablist,ibuf) == -1
- " wipe out any non-displaying netrw buffer
- " (ibuf not shown in a current window AND
- " ibuf not in any tab)
- exe "sil! keepj bd ".fnameescape(ibuf)
- call remove(s:netrw_browselist,ibl)
- continue
- elseif index(tabpagebuflist(),ibuf) != -1
- " refresh any netrw buffer
- exe bufwinnr(ibuf)."wincmd w"
- if getline(".") =~# 'Quick Help'
- " decrement g:netrw_quickhelp to prevent refresh from changing g:netrw_quickhelp
- " (counteracts s:NetrwBrowseChgDir()'s incrementing)
- let g:netrw_quickhelp= g:netrw_quickhelp - 1
- endif
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop)
- endif
- NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
- endif
- let ibl= ibl + 1
- endfor
- call win_gotoid(curwinid)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:LocalFastBrowser: handles setting up/taking down fast browsing for the local browser {{{2
-"
-" g:netrw_ Directory Is
-" fastbrowse Local Remote
-" slow 0 D D D=Deleting a buffer implies it will not be re-used (slow)
-" med 1 D H H=Hiding a buffer implies it may be re-used (fast)
-" fast 2 H H
-"
-" Deleting a buffer means that it will be re-loaded when examined, hence "slow".
-" Hiding a buffer means that it will be re-used when examined, hence "fast".
-" (re-using a buffer may not be as accurate)
-"
-" s:netrw_events : doesn't exist, s:LocalFastBrowser() will install autocmds with medium-speed or fast browsing
-" =1: autocmds installed, but ignore next FocusGained event to avoid initial double-refresh of listing.
-" BufEnter may be first event, then a FocusGained event. Ignore the first FocusGained event.
-" If :Explore used: it sets s:netrw_events to 2, so no FocusGained events are ignored.
-" =2: autocmds installed (doesn't ignore any FocusGained events)
-fun! s:LocalFastBrowser()
-
- " initialize browselist, a list of buffer numbers that the local browser has used
- if !exists("s:netrw_browselist")
- let s:netrw_browselist= []
- endif
-
- " append current buffer to fastbrowse list
- if empty(s:netrw_browselist) || bufnr("%") > s:netrw_browselist[-1]
- call add(s:netrw_browselist,bufnr("%"))
- endif
-
- " enable autocmd events to handle refreshing/removing local browser buffers
- " If local browse buffer is currently showing: refresh it
- " If local browse buffer is currently hidden : wipe it
- " g:netrw_fastbrowse=0 : slow speed, never re-use directory listing
- " =1 : medium speed, re-use directory listing for remote only
- " =2 : fast speed, always re-use directory listing when possible
- if g:netrw_fastbrowse <= 1 && !exists("#ShellCmdPost") && !exists("s:netrw_events")
- let s:netrw_events= 1
- augroup AuNetrwEvent
- au!
- if has("win32")
- au ShellCmdPost * call s:LocalBrowseRefresh()
- else
- au ShellCmdPost,FocusGained * call s:LocalBrowseRefresh()
- endif
- augroup END
-
- " user must have changed fastbrowse to its fast setting, so remove
- " the associated autocmd events
- elseif g:netrw_fastbrowse > 1 && exists("#ShellCmdPost") && exists("s:netrw_events")
- unlet s:netrw_events
- augroup AuNetrwEvent
- au!
- augroup END
- augroup! AuNetrwEvent
- endif
-endfun
-
-fun! s:NetrwLocalListingList(dirname,setmaxfilenamelen)
- " get the list of files contained in the current directory
- let dirname = a:dirname
- let dirnamelen = strlen(dirname)
- let filelist = s:NetrwGlob(dirname,"*",0)
- let filelist = filelist + s:NetrwGlob(dirname,".*",0)
-
- if g:netrw_cygwin == 0 && has("win32")
- elseif index(filelist,'..') == -1 && dirname !~ '/'
- " include ../ in the glob() entry if its missing
- let filelist= filelist+[s:ComposePath(dirname,"../")]
- endif
-
- if a:setmaxfilenamelen && get(g:, 'netrw_dynamic_maxfilenamelen', 0)
- let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")')
- let g:netrw_maxfilenamelen = max(map(filelistcopy,'len(v:val)')) + 1
- endif
-
- let resultfilelist = []
- for filename in filelist
-
- if getftype(filename) == "link"
- " indicate a symbolic link
- let pfile= filename."@"
-
- elseif getftype(filename) == "socket"
- " indicate a socket
- let pfile= filename."="
-
- elseif getftype(filename) == "fifo"
- " indicate a fifo
- let pfile= filename."|"
-
- elseif isdirectory(s:NetrwFile(filename))
- " indicate a directory
- let pfile= filename."/"
-
- elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(s:NetrwFile(filename))
- if has("win32")
- if filename =~ '\.[eE][xX][eE]$' || filename =~ '\.[cC][oO][mM]$' || filename =~ '\.[bB][aA][tT]$'
- " indicate an executable
- let pfile= filename."*"
- else
- " normal file
- let pfile= filename
- endif
- elseif executable(filename)
- " indicate an executable
- let pfile= filename."*"
- else
- " normal file
- let pfile= filename
- endif
-
- else
- " normal file
- let pfile= filename
- endif
-
- if pfile =~ '//$'
- let pfile= substitute(pfile,'//$','/','e')
- endif
- let pfile= strpart(pfile,dirnamelen)
- let pfile= substitute(pfile,'^[/\\]','','e')
-
- if w:netrw_liststyle == s:LONGLIST
- let longfile = printf("%-".g:netrw_maxfilenamelen."S",pfile)
- let sz = getfsize(filename)
- let szlen = 15 - (strdisplaywidth(longfile) - g:netrw_maxfilenamelen)
- let szlen = (szlen > 0) ? szlen : 0
-
- if g:netrw_sizestyle =~# "[hH]"
- let sz= s:NetrwHumanReadable(sz)
- endif
- let fsz = printf("%".szlen."S",sz)
- let pfile= longfile." ".fsz." ".strftime(g:netrw_timefmt,getftime(filename))
- endif
-
- if g:netrw_sort_by =~# "^t"
- " sort by time (handles time up to 1 quintillion seconds, US)
- " Decorate listing by prepending a timestamp/ . Sorting will then be done based on time.
- let t = getftime(filename)
- let ft = printf("%018d",t)
- let ftpfile= ft.'/'.pfile
- let resultfilelist += [ftpfile]
-
- elseif g:netrw_sort_by =~ "^s"
- " sort by size (handles file sizes up to 1 quintillion bytes, US)
- let sz = getfsize(filename)
- let fsz = printf("%018d",sz)
- let fszpfile= fsz.'/'.pfile
- let resultfilelist += [fszpfile]
-
- else
- " sort by name
- let resultfilelist += [pfile]
- endif
- endfor
-
- return resultfilelist
-endfun
-
-" ---------------------------------------------------------------------
-" s:LocalListing: does the job of "ls" for local directories {{{2
-fun! s:LocalListing()
-
- let filelist = s:NetrwLocalListingList(b:netrw_curdir, 1)
- for filename in filelist
- sil! NetrwKeepj put =filename
- endfor
-
- " cleanup any windows mess at end-of-line
- sil! NetrwKeepj g/^$/d
- sil! NetrwKeepj %s/\r$//e
- call histdel("/",-1)
- exe "setl ts=".(g:netrw_maxfilenamelen+1)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwLocalExecute: uses system() to execute command under cursor ("X" command support) {{{2
-fun! s:NetrwLocalExecute(cmd)
-" call Dfunc("s:NetrwLocalExecute(cmd<".a:cmd.">)")
- let ykeep= @@
- " sanity check
- if !executable(a:cmd)
- call netrw#ErrorMsg(s:ERROR,"the file<".a:cmd."> is not executable!",89)
- let @@= ykeep
-" call Dret("s:NetrwLocalExecute")
- return
- endif
-
- let optargs= input(":!".a:cmd,"","file")
-" call Decho("optargs<".optargs.">",'~'.expand("<slnum>"))
- let result= system(a:cmd.optargs)
-" call Decho("result,'~'.expand("<slnum>"))
-
- " strip any ansi escape sequences off
- let result = substitute(result,"\e\\[[0-9;]*m","","g")
-
- " show user the result(s)
- echomsg result
- let @@= ykeep
-
-" call Dret("s:NetrwLocalExecute")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwLocalRename: rename a local file or directory {{{2
-fun! s:NetrwLocalRename(path) range
-
- if !exists("w:netrw_bannercnt")
- let w:netrw_bannercnt= b:netrw_bannercnt
- endif
-
- " preparation for removing multiple files/directories
- let ykeep = @@
- let ctr = a:firstline
- let svpos = winsaveview()
- let all = 0
-
- " rename files given by the markfilelist
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
- for oldname in s:netrwmarkfilelist_{bufnr("%")}
- if exists("subfrom")
- let newname= substitute(oldname,subfrom,subto,'')
- else
- call inputsave()
- let newname= input("Moving ".oldname." to : ",oldname,"file")
- call inputrestore()
- if newname =~ ''
- " two ctrl-x's : ignore all of string preceding the ctrl-x's
- let newname = substitute(newname,'^.*','','')
- elseif newname =~ ''
- " one ctrl-x : ignore portion of string preceding ctrl-x but after last /
- let newname = substitute(newname,'[^/]*','','')
- endif
- if newname =~ '^s/'
- let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','')
- let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','')
- let newname = substitute(oldname,subfrom,subto,'')
- endif
- endif
- if !all && filereadable(newname)
- call inputsave()
- let response= input("File<".newname."> already exists; do you want to overwrite it? (y/all/n) ")
- call inputrestore()
- if response == "all"
- let all= 1
- elseif response != "y" && response != "yes"
- " refresh the directory
- NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
- NetrwKeepj call winrestview(svpos)
- let @@= ykeep
- return
- endif
- endif
- call rename(oldname,newname)
- endfor
- call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir)
-
- else
-
- " attempt to rename files/directories
- while ctr <= a:lastline
- exe "NetrwKeepj ".ctr
-
- " sanity checks
- if line(".") < w:netrw_bannercnt
- let ctr= ctr + 1
- continue
- endif
- let curword= s:NetrwGetWord()
- if curword == "./" || curword == "../"
- let ctr= ctr + 1
- continue
- endif
-
- NetrwKeepj norm! 0
- let oldname= s:ComposePath(a:path,curword)
-
- call inputsave()
- let newname= input("Moving ".oldname." to : ",substitute(oldname,'/*$','','e'))
- call inputrestore()
-
- call rename(oldname,newname)
- let ctr= ctr + 1
- endwhile
- endif
-
- " refresh the directory
- NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
- NetrwKeepj call winrestview(svpos)
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwLocalRm: {{{2
-fun! s:NetrwLocalRm(path) range
- if !exists("w:netrw_bannercnt")
- let w:netrw_bannercnt= b:netrw_bannercnt
- endif
-
- " preparation for removing multiple files/directories
- let ykeep = @@
- let ret = 0
- let all = 0
- let svpos = winsaveview()
-
- if exists("s:netrwmarkfilelist_{bufnr('%')}")
- " remove all marked files
- for fname in s:netrwmarkfilelist_{bufnr("%")}
- let ok= s:NetrwLocalRmFile(a:path,fname,all)
- if ok =~# 'q\%[uit]' || ok == "no"
- break
- elseif ok =~# '^a\%[ll]$'
- let all= 1
- endif
- endfor
- call s:NetrwUnMarkFile(1)
-
- else
- " remove (multiple) files and directories
-
- let keepsol= &l:sol
- setl nosol
- let ctr = a:firstline
- while ctr <= a:lastline
- exe "NetrwKeepj ".ctr
-
- " sanity checks
- if line(".") < w:netrw_bannercnt
- let ctr= ctr + 1
- continue
- endif
- let curword= s:NetrwGetWord()
- if curword == "./" || curword == "../"
- let ctr= ctr + 1
- continue
- endif
- let ok= s:NetrwLocalRmFile(a:path,curword,all)
- if ok =~# 'q\%[uit]' || ok == "no"
- break
- elseif ok =~# '^a\%[ll]$'
- let all= 1
- endif
- let ctr= ctr + 1
- endwhile
- let &l:sol= keepsol
- endif
-
- " refresh the directory
- if bufname("%") != "NetrwMessage"
- NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
- NetrwKeepj call winrestview(svpos)
- endif
- let @@= ykeep
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwLocalRmFile: remove file fname given the path {{{2
-" Give confirmation prompt unless all==1
-fun! s:NetrwLocalRmFile(path,fname,all)
-" call Dfunc("s:NetrwLocalRmFile(path<".a:path."> fname<".a:fname."> all=".a:all)
-
- let all= a:all
- let ok = ""
- NetrwKeepj norm! 0
- let rmfile= s:NetrwFile(s:ComposePath(a:path,escape(a:fname, '\\')))
-" call Decho("rmfile<".rmfile.">",'~'.expand("<slnum>"))
-
- if rmfile !~ '^"' && (rmfile =~ '@$' || rmfile !~ '[\/]$')
- " attempt to remove file
-" call Decho("attempt to remove file<".rmfile.">",'~'.expand("<slnum>"))
- if !all
- echohl Statement
- call inputsave()
- let ok= input("Confirm deletion of file <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
- call inputrestore()
- echohl NONE
- if ok == ""
- let ok="no"
- endif
-" call Decho("response: ok<".ok.">",'~'.expand("<slnum>"))
- let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
-" call Decho("response: ok<".ok."> (after sub)",'~'.expand("<slnum>"))
- if ok =~# '^a\%[ll]$'
- let all= 1
- endif
- endif
-
- if all || ok =~# '^y\%[es]$' || ok == ""
- let ret= s:NetrwDelete(rmfile)
-" call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>"))
- endif
-
- else
- " attempt to remove directory
- if !all
- echohl Statement
- call inputsave()
- let ok= input("Confirm *recursive* deletion of directory <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
- call inputrestore()
- let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
- if ok == ""
- let ok="no"
- endif
- if ok =~# '^a\%[ll]$'
- let all= 1
- endif
- endif
- let rmfile= substitute(rmfile,'[\/]$','','e')
-
- if all || ok =~# '^y\%[es]$' || ok == ""
- if delete(rmfile,"rf")
- call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103)
- endif
- endif
- endif
-
-" call Dret("s:NetrwLocalRmFile ".ok)
- return ok
-endfun
-
-" =====================================================================
-" Support Functions: {{{1
-
-" ---------------------------------------------------------------------
-" netrw#Access: intended to provide access to variable values for netrw's test suite {{{2
-" 0: marked file list of current buffer
-" 1: marked file target
-fun! netrw#Access(ilist)
- if a:ilist == 0
- if exists("s:netrwmarkfilelist_".bufnr('%'))
- return s:netrwmarkfilelist_{bufnr('%')}
- else
- return "no-list-buf#".bufnr('%')
- endif
- elseif a:ilist == 1
- return s:netrwmftgt
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Call: allows user-specified mappings to call internal netrw functions {{{2
-fun! netrw#Call(funcname,...)
- return call("s:".a:funcname,a:000)
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2
-" I expect this function to be used in
-" :PChkAssert netrw#Expose("netrwmarkfilelist")
-" for example.
-fun! netrw#Expose(varname)
-" call Dfunc("netrw#Expose(varname<".a:varname.">)")
- if exists("s:".a:varname)
- exe "let retval= s:".a:varname
-" call Decho("retval=".retval,'~'.expand("<slnum>"))
- if exists("g:netrw_pchk")
-" call Decho("type(g:netrw_pchk=".g:netrw_pchk.")=".type(retval),'~'.expand("<slnum>"))
- if type(retval) == 3
- let retval = copy(retval)
- let i = 0
- while i < len(retval)
- let retval[i]= substitute(retval[i],expand("$HOME"),'~','')
- let i = i + 1
- endwhile
- endif
-" call Dret("netrw#Expose ".string(retval)),'~'.expand("<slnum>"))
- return string(retval)
- else
-" call Decho("g:netrw_pchk doesn't exist",'~'.expand("<slnum>"))
- endif
- else
-" call Decho("s:".a:varname." doesn't exist",'~'.expand("<slnum>"))
- let retval= "n/a"
- endif
-
-" call Dret("netrw#Expose ".string(retval))
- return retval
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#Modify: allows UserMaps to set (modify) script-local variables {{{2
-fun! netrw#Modify(varname,newvalue)
-" call Dfunc("netrw#Modify(varname<".a:varname.">,newvalue<".string(a:newvalue).">)")
- exe "let s:".a:varname."= ".string(a:newvalue)
-" call Dret("netrw#Modify")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#RFC2396: converts %xx into characters {{{2
-fun! netrw#RFC2396(fname)
-" call Dfunc("netrw#RFC2396(fname<".a:fname.">)")
- let fname = escape(substitute(a:fname,'%\(\x\x\)','\=printf("%c","0x".submatch(1))','ge')," \t")
-" call Dret("netrw#RFC2396 ".fname)
- return fname
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#UserMaps: supports user-specified maps {{{2
-" see :help function()
-"
-" g:Netrw_UserMaps is a List with members such as:
-" [[keymap sequence, function reference],...]
-"
-" The referenced function may return a string,
-" refresh : refresh the display
-" -other- : this string will be executed
-" or it may return a List of strings.
-"
-" Each keymap-sequence will be set up with a nnoremap
-" to invoke netrw#UserMaps(a:islocal).
-" Related functions:
-" netrw#Expose(varname) -- see s:varname variables
-" netrw#Modify(varname,newvalue) -- modify value of s:varname variable
-" netrw#Call(funcname,...) -- call internal netrw function with optional arguments
-fun! netrw#UserMaps(islocal)
-" call Dfunc("netrw#UserMaps(islocal=".a:islocal.")")
-" call Decho("g:Netrw_UserMaps ".(exists("g:Netrw_UserMaps")? "exists" : "does NOT exist"),'~'.expand("<slnum>"))
-
- " set up usermaplist
- if exists("g:Netrw_UserMaps") && type(g:Netrw_UserMaps) == 3
-" call Decho("g:Netrw_UserMaps has type 3<List>",'~'.expand("<slnum>"))
- for umap in g:Netrw_UserMaps
-" call Decho("type(umap[0]<".string(umap[0]).">)=".type(umap[0])." (should be 1=string)",'~'.expand("<slnum>"))
-" call Decho("type(umap[1])=".type(umap[1])." (should be 1=string)",'~'.expand("<slnum>"))
- " if umap[0] is a string and umap[1] is a string holding a function name
- if type(umap[0]) == 1 && type(umap[1]) == 1
-" call Decho("nno <buffer> <silent> ".umap[0]." :call s:UserMaps(".a:islocal.",".string(umap[1]).")<cr>",'~'.expand("<slnum>"))
- exe "nno <buffer> <silent> ".umap[0]." :call <SID>UserMaps(".a:islocal.",'".umap[1]."')<cr>"
- else
- call netrw#ErrorMsg(s:WARNING,"ignoring usermap <".string(umap[0])."> -- not a [string,funcref] entry",99)
- endif
- endfor
- endif
-" call Dret("netrw#UserMaps")
-endfun
-
-" ---------------------------------------------------------------------
-" netrw#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{2
-fun! netrw#WinPath(path)
-" call Dfunc("netrw#WinPath(path<".a:path.">)")
- if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && has("win32")
- " remove cygdrive prefix, if present
- let path = substitute(a:path,g:netrw_cygdrive.'/\(.\)','\1:','')
- " remove trailing slash (Win95)
- let path = substitute(path, '\(\\\|/\)$', '', 'g')
- " remove escaped spaces
- let path = substitute(path, '\ ', ' ', 'g')
- " convert slashes to backslashes
- let path = substitute(path, '/', '\', 'g')
- else
- let path= a:path
- endif
-" call Dret("netrw#WinPath <".path.">")
- return path
-endfun
-
-" ---------------------------------------------------------------------
-" s:StripTrailingSlash: removes trailing slashes from a path {{{2
-fun! s:StripTrailingSlash(path)
- " remove trailing slash
- return substitute(a:path, '[/\\]$', '', 'g')
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBadd: adds marked files to buffer list or vice versa {{{2
-" cb : bl2mf=0 add marked files to buffer list
-" cB : bl2mf=1 use bufferlist to mark files
-" (mnemonic: cb = copy (marked files) to buffer list)
-fun! s:NetrwBadd(islocal,bl2mf)
-" " call Dfunc("s:NetrwBadd(islocal=".a:islocal." mf2bl=".mf2bl.")")
- if a:bl2mf
- " cB: add buffer list to marked files
- redir => bufl
- ls
- redir END
- let bufl = map(split(bufl,"\n"),'substitute(v:val,''^.\{-}"\(.*\)".\{-}$'',''\1'','''')')
- for fname in bufl
- call s:NetrwMarkFile(a:islocal,fname)
- endfor
- else
- " cb: add marked files to buffer list
- for fname in s:netrwmarkfilelist_{bufnr("%")}
-" " call Decho("badd ".fname,'~'.expand("<slnum>"))
- exe "badd ".fnameescape(fname)
- endfor
- let curbufnr = bufnr("%")
- let curdir = s:NetrwGetCurdir(a:islocal)
- call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
- endif
-" call Dret("s:NetrwBadd")
-endfun
-
-" ---------------------------------------------------------------------
-" s:ComposePath: Appends a new part to a path taking different systems into consideration {{{2
-fun! s:ComposePath(base,subdir)
-" call Dfunc("s:ComposePath(base<".a:base."> subdir<".a:subdir.">)")
-
- if has("amiga")
-" call Decho("amiga",'~'.expand("<slnum>"))
- let ec = a:base[s:Strlen(a:base)-1]
- if ec != '/' && ec != ':'
- let ret = a:base."/" . a:subdir
- else
- let ret = a:base.a:subdir
- endif
-
- " COMBAK: test on windows with changing to root directory: :e C:/
- elseif a:subdir =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32")
-" call Decho("windows",'~'.expand("<slnum>"))
- let ret= a:subdir
-
- elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32")
-" call Decho("windows",'~'.expand("<slnum>"))
- if a:base =~ '[/\\]$'
- let ret= a:base.a:subdir
- else
- let ret= a:base.'/'.a:subdir
- endif
-
- elseif a:base =~ '^\a\{3,}://'
-" call Decho("remote linux/macos",'~'.expand("<slnum>"))
- let urlbase = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\1','')
- let curpath = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\2','')
- if a:subdir == '../'
- if curpath =~ '[^/]/[^/]\+/$'
- let curpath= substitute(curpath,'[^/]\+/$','','')
- else
- let curpath=""
- endif
- let ret= urlbase.curpath
- else
- let ret= urlbase.curpath.a:subdir
- endif
-" call Decho("urlbase<".urlbase.">",'~'.expand("<slnum>"))
-" call Decho("curpath<".curpath.">",'~'.expand("<slnum>"))
-" call Decho("ret<".ret.">",'~'.expand("<slnum>"))
-
- else
-" call Decho("local linux/macos",'~'.expand("<slnum>"))
- let ret = substitute(a:base."/".a:subdir,"//","/","g")
- if a:base =~ '^//'
- " keeping initial '//' for the benefit of network share listing support
- let ret= '/'.ret
- endif
- let ret= simplify(ret)
- endif
-
-" call Dret("s:ComposePath ".ret)
- return ret
-endfun
-
-" ---------------------------------------------------------------------
-" s:DeleteBookmark: deletes a file/directory from Netrw's bookmark system {{{2
-" Related Functions: s:MakeBookmark() s:NetrwBookHistHandler() s:NetrwBookmark()
-fun! s:DeleteBookmark(fname)
-" call Dfunc("s:DeleteBookmark(fname<".a:fname.">)")
- call s:MergeBookmarks()
-
- if exists("g:netrw_bookmarklist")
- let indx= index(g:netrw_bookmarklist,a:fname)
- if indx == -1
- let indx= 0
- while indx < len(g:netrw_bookmarklist)
- if g:netrw_bookmarklist[indx] =~ a:fname
- call remove(g:netrw_bookmarklist,indx)
- let indx= indx - 1
- endif
- let indx= indx + 1
- endwhile
- else
- " remove exact match
- call remove(g:netrw_bookmarklist,indx)
- endif
- endif
-
-" call Dret("s:DeleteBookmark")
-endfun
-
-" ---------------------------------------------------------------------
-" s:FileReadable: o/s independent filereadable {{{2
-fun! s:FileReadable(fname)
-" call Dfunc("s:FileReadable(fname<".a:fname.">)")
-
- if g:netrw_cygwin
- let ret= filereadable(s:NetrwFile(substitute(a:fname,g:netrw_cygdrive.'/\(.\)','\1:/','')))
- else
- let ret= filereadable(s:NetrwFile(a:fname))
- endif
-
-" call Dret("s:FileReadable ".ret)
- return ret
-endfun
-
-" ---------------------------------------------------------------------
-" s:GetTempfile: gets a tempname that'll work for various o/s's {{{2
-" Places correct suffix on end of temporary filename,
-" using the suffix provided with fname
-fun! s:GetTempfile(fname)
-" call Dfunc("s:GetTempfile(fname<".a:fname.">)")
-
- if !exists("b:netrw_tmpfile")
- " get a brand new temporary filename
- let tmpfile= tempname()
-" call Decho("tmpfile<".tmpfile."> : from tempname()",'~'.expand("<slnum>"))
-
- let tmpfile= substitute(tmpfile,'\','/','ge')
-" call Decho("tmpfile<".tmpfile."> : chgd any \\ -> /",'~'.expand("<slnum>"))
-
- " sanity check -- does the temporary file's directory exist?
- if !isdirectory(s:NetrwFile(substitute(tmpfile,'[^/]\+$','','e')))
-" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your <".substitute(tmpfile,'[^/]\+$','','e')."> directory is missing!",2)
-" call Dret("s:GetTempfile getcwd<".getcwd().">")
- return ""
- endif
-
- " let netrw#NetSource() know about the tmpfile
- let s:netrw_tmpfile= tmpfile " used by netrw#NetSource() and netrw#BrowseX()
-" call Decho("tmpfile<".tmpfile."> s:netrw_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>"))
-
- " o/s dependencies
- if g:netrw_cygwin != 0
- let tmpfile = substitute(tmpfile,'^\(\a\):',g:netrw_cygdrive.'/\1','e')
- elseif has("win32")
- if !exists("+shellslash") || !&ssl
- let tmpfile = substitute(tmpfile,'/','\','g')
- endif
- else
- let tmpfile = tmpfile
- endif
- let b:netrw_tmpfile= tmpfile
-" call Decho("o/s dependent fixed tempname<".tmpfile.">",'~'.expand("<slnum>"))
- else
- " re-use temporary filename
- let tmpfile= b:netrw_tmpfile
-" call Decho("tmpfile<".tmpfile."> re-using",'~'.expand("<slnum>"))
- endif
-
- " use fname's suffix for the temporary file
- if a:fname != ""
- if a:fname =~ '\.[^./]\+$'
-" call Decho("using fname<".a:fname.">'s suffix",'~'.expand("<slnum>"))
- if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$'
- let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
- elseif a:fname =~ '.txz$'
- let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
- else
- let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
- endif
-" call Decho("suffix<".suffix.">",'~'.expand("<slnum>"))
- let tmpfile= substitute(tmpfile,'\.tmp$','','e')
-" call Decho("chgd tmpfile<".tmpfile."> (removed any .tmp suffix)",'~'.expand("<slnum>"))
- let tmpfile .= suffix
-" call Decho("chgd tmpfile<".tmpfile."> (added ".suffix." suffix) netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>"))
- let s:netrw_tmpfile= tmpfile " supports netrw#NetSource()
- endif
- endif
-
-" 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:GetTempfile <".tmpfile.">")
- return tmpfile
-endfun
-
-" ---------------------------------------------------------------------
-" s:MakeSshCmd: transforms input command using USEPORT HOSTNAME into {{{2
-" a correct command for use with a system() call
-fun! s:MakeSshCmd(sshcmd)
-" call Dfunc("s:MakeSshCmd(sshcmd<".a:sshcmd.">) user<".s:user."> machine<".s:machine.">")
- if s:user == ""
- let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:machine,'')
- else
- let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:user."@".s:machine,'')
- endif
- if exists("g:netrw_port") && g:netrw_port != ""
- let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'')
- elseif exists("s:port") && s:port != ""
- let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'')
- else
- let sshcmd= substitute(sshcmd,"USEPORT ",'','')
- endif
-" call Dret("s:MakeSshCmd <".sshcmd.">")
- return sshcmd
-endfun
-
-" ---------------------------------------------------------------------
-" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2
-fun! s:MakeBookmark(fname)
-" call Dfunc("s:MakeBookmark(fname<".a:fname.">)")
-
- if !exists("g:netrw_bookmarklist")
- let g:netrw_bookmarklist= []
- endif
-
- if index(g:netrw_bookmarklist,a:fname) == -1
- " curdir not currently in g:netrw_bookmarklist, so include it
- if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$'
- call add(g:netrw_bookmarklist,a:fname.'/')
- elseif a:fname !~ '/'
- call add(g:netrw_bookmarklist,getcwd()."/".a:fname)
- else
- call add(g:netrw_bookmarklist,a:fname)
- endif
- call sort(g:netrw_bookmarklist)
- endif
-
-" call Dret("s:MakeBookmark")
-endfun
-
-" ---------------------------------------------------------------------
-" s:MergeBookmarks: merge current bookmarks with saved bookmarks {{{2
-fun! s:MergeBookmarks()
-" call Dfunc("s:MergeBookmarks() : merge current bookmarks into .netrwbook")
- " get bookmarks from .netrwbook file
- let savefile= s:NetrwHome()."/.netrwbook"
- if filereadable(s:NetrwFile(savefile))
-" call Decho("merge bookmarks (active and file)",'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwBookHistSave()
-" call Decho("bookmark delete savefile<".savefile.">",'~'.expand("<slnum>"))
- NetrwKeepj call delete(savefile)
- endif
-" call Dret("s:MergeBookmarks")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBMShow: {{{2
-fun! s:NetrwBMShow()
-" call Dfunc("s:NetrwBMShow()")
- redir => bmshowraw
- menu
- redir END
- let bmshowlist = split(bmshowraw,'\n')
- if bmshowlist != []
- let bmshowfuncs= filter(bmshowlist,'v:val =~# "<SNR>\\d\\+_BMShow()"')
- if bmshowfuncs != []
- let bmshowfunc = substitute(bmshowfuncs[0],'^.*:\(call.*BMShow()\).*$','\1','')
- if bmshowfunc =~# '^call.*BMShow()'
- exe "sil! NetrwKeepj ".bmshowfunc
- endif
- endif
- endif
-" call Dret("s:NetrwBMShow : bmshowfunc<".(exists("bmshowfunc")? bmshowfunc : 'n/a').">")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwCursor: responsible for setting cursorline/cursorcolumn based upon g:netrw_cursor {{{2
-fun! s:NetrwCursor(editfile)
- if !exists("w:netrw_liststyle")
- let w:netrw_liststyle= g:netrw_liststyle
- endif
-" call Dfunc("s:NetrwCursor() ft<".&ft."> liststyle=".w:netrw_liststyle." g:netrw_cursor=".g:netrw_cursor." s:netrw_usercuc=".s:netrw_usercuc." s:netrw_usercul=".s:netrw_usercul)
-
-" call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
-
- if &ft != "netrw"
- " if the current window isn't a netrw directory listing window, then use user cursorline/column
- " settings. Affects when netrw is used to read/write a file using scp/ftp/etc.
-" call Decho("case ft!=netrw: use user cul,cuc",'~'.expand("<slnum>"))
-
- elseif g:netrw_cursor == 8
- if w:netrw_liststyle == s:WIDELIST
- setl cursorline
- setl cursorcolumn
- else
- setl cursorline
- endif
- elseif g:netrw_cursor == 7
- setl cursorline
- elseif g:netrw_cursor == 6
- if w:netrw_liststyle == s:WIDELIST
- setl cursorline
- endif
- elseif g:netrw_cursor == 4
- " all styles: cursorline, cursorcolumn
-" call Decho("case g:netrw_cursor==4: setl cul cuc",'~'.expand("<slnum>"))
- setl cursorline
- setl cursorcolumn
-
- elseif g:netrw_cursor == 3
- " thin-long-tree: cursorline, user's cursorcolumn
- " wide : cursorline, cursorcolumn
- if w:netrw_liststyle == s:WIDELIST
-" call Decho("case g:netrw_cursor==3 and wide: setl cul cuc",'~'.expand("<slnum>"))
- setl cursorline
- setl cursorcolumn
- else
-" call Decho("case g:netrw_cursor==3 and not wide: setl cul (use user's cuc)",'~'.expand("<slnum>"))
- setl cursorline
- endif
-
- elseif g:netrw_cursor == 2
- " thin-long-tree: cursorline, user's cursorcolumn
- " wide : cursorline, user's cursorcolumn
-" call Decho("case g:netrw_cursor==2: setl cuc (use user's cul)",'~'.expand("<slnum>"))
- setl cursorline
-
- elseif g:netrw_cursor == 1
- " thin-long-tree: user's cursorline, user's cursorcolumn
- " wide : cursorline, user's cursorcolumn
- if w:netrw_liststyle == s:WIDELIST
-" call Decho("case g:netrw_cursor==2 and wide: setl cul (use user's cuc)",'~'.expand("<slnum>"))
- setl cursorline
- else
-" call Decho("case g:netrw_cursor==2 and not wide: (use user's cul,cuc)",'~'.expand("<slnum>"))
- endif
-
- else
- " all styles: user's cursorline, user's cursorcolumn
-" call Decho("default: (use user's cul,cuc)",'~'.expand("<slnum>"))
- let &l:cursorline = s:netrw_usercul
- let &l:cursorcolumn = s:netrw_usercuc
- endif
-
-" call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
-" call Dret("s:NetrwCursor : l:cursorline=".&l:cursorline." l:cursorcolumn=".&l:cursorcolumn)
-endfun
-
-" ---------------------------------------------------------------------
-" s:RestoreCursorline: restores cursorline/cursorcolumn to original user settings {{{2
-fun! s:RestoreCursorline()
-" call Dfunc("s:RestoreCursorline() currently, cul=".&l:cursorline." cuc=".&l:cursorcolumn." win#".winnr()." buf#".bufnr("%"))
- if exists("s:netrw_usercul")
- let &l:cursorline = s:netrw_usercul
- endif
- if exists("s:netrw_usercuc")
- let &l:cursorcolumn = s:netrw_usercuc
- endif
-" call Decho("(s:RestoreCursorline) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
-" call Dret("s:RestoreCursorline : restored cul=".&l:cursorline." cuc=".&l:cursorcolumn)
-endfun
-
-" s:RestoreRegister: restores all registers given in the dict {{{2
-fun! s:RestoreRegister(dict)
- for [key, val] in items(a:dict)
- if key == 'unnamed'
- let key = ''
- endif
- call setreg(key, val[0], val[1])
- endfor
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwDelete: Deletes a file. {{{2
-" Uses Steve Hall's idea to insure that Windows paths stay
-" acceptable. No effect on Unix paths.
-" Examples of use: let result= s:NetrwDelete(path)
-fun! s:NetrwDelete(path)
-" call Dfunc("s:NetrwDelete(path<".a:path.">)")
-
- let path = netrw#WinPath(a:path)
- if !g:netrw_cygwin && has("win32")
- if exists("+shellslash")
- let sskeep= &shellslash
- setl noshellslash
- let result = delete(path)
- let &shellslash = sskeep
- else
-" call Decho("exe let result= ".a:cmd."('".path."')",'~'.expand("<slnum>"))
- let result= delete(path)
- endif
- else
-" call Decho("let result= delete(".path.")",'~'.expand("<slnum>"))
- let result= delete(path)
- endif
- if result < 0
- NetrwKeepj call netrw#ErrorMsg(s:WARNING,"delete(".path.") failed!",71)
- endif
-
-" call Dret("s:NetrwDelete ".result)
- return result
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwBufRemover: removes a buffer that: {{{2s
-" has buffer-id > 1
-" is unlisted
-" is unnamed
-" does not appear in any window
-fun! s:NetrwBufRemover(bufid)
-" call Dfunc("s:NetrwBufRemover(".a:bufid.")")
-" call Decho("buf#".a:bufid." ".((a:bufid > 1)? ">" : "≯")." must be >1 for removal","~".expand("<slnum>"))
-" call Decho("buf#".a:bufid." is ".(buflisted(a:bufid)? "listed" : "unlisted"),"~".expand("<slnum>"))
-" call Decho("buf#".a:bufid." has name <".bufname(a:bufid).">","~".expand("<slnum>"))
-" call Decho("buf#".a:bufid." has winid#".bufwinid(a:bufid),"~".expand("<slnum>"))
-
- if a:bufid > 1 && !buflisted(a:bufid) && bufloaded(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1
-" call Decho("(s:NetrwBufRemover) removing buffer#".a:bufid,"~".expand("<slnum>"))
- exe "sil! bd! ".a:bufid
- endif
-
-" call Dret("s:NetrwBufRemover")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwEnew: opens a new buffer, passes netrw buffer variables through {{{2
-fun! s:NetrwEnew(...)
-" call Dfunc("s:NetrwEnew() a:0=".a:0." win#".winnr()." winnr($)=".winnr("$")." bufnr($)=".bufnr("$")." expand(%)<".expand("%").">")
-" call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
-
- " Clean out the last buffer:
- " Check if the last buffer has # > 1, is unlisted, is unnamed, and does not appear in a window
- " If so, delete it.
- call s:NetrwBufRemover(bufnr("$"))
-
- " grab a function-local-variable copy of buffer variables
-" call Decho("make function-local copy of netrw variables",'~'.expand("<slnum>"))
- if exists("b:netrw_bannercnt") |let netrw_bannercnt = b:netrw_bannercnt |endif
- if exists("b:netrw_browser_active") |let netrw_browser_active = b:netrw_browser_active |endif
- if exists("b:netrw_cpf") |let netrw_cpf = b:netrw_cpf |endif
- if exists("b:netrw_curdir") |let netrw_curdir = b:netrw_curdir |endif
- if exists("b:netrw_explore_bufnr") |let netrw_explore_bufnr = b:netrw_explore_bufnr |endif
- if exists("b:netrw_explore_indx") |let netrw_explore_indx = b:netrw_explore_indx |endif
- if exists("b:netrw_explore_line") |let netrw_explore_line = b:netrw_explore_line |endif
- if exists("b:netrw_explore_list") |let netrw_explore_list = b:netrw_explore_list |endif
- if exists("b:netrw_explore_listlen")|let netrw_explore_listlen = b:netrw_explore_listlen|endif
- if exists("b:netrw_explore_mtchcnt")|let netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif
- if exists("b:netrw_fname") |let netrw_fname = b:netrw_fname |endif
- if exists("b:netrw_lastfile") |let netrw_lastfile = b:netrw_lastfile |endif
- if exists("b:netrw_liststyle") |let netrw_liststyle = b:netrw_liststyle |endif
- if exists("b:netrw_method") |let netrw_method = b:netrw_method |endif
- if exists("b:netrw_option") |let netrw_option = b:netrw_option |endif
- if exists("b:netrw_prvdir") |let netrw_prvdir = b:netrw_prvdir |endif
-
- NetrwKeepj call s:NetrwOptionsRestore("w:")
-" call Decho("generate a buffer with NetrwKeepj enew!",'~'.expand("<slnum>"))
- " when tree listing uses file TreeListing... a new buffer is made.
- " Want the old buffer to be unlisted.
- " COMBAK: this causes a problem, see P43
-" setl nobl
- let netrw_keepdiff= &l:diff
- call s:NetrwEditFile("enew!","","")
- let &l:diff= netrw_keepdiff
-" call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>"))
- NetrwKeepj call s:NetrwOptionsSave("w:")
-
- " copy function-local-variables to buffer variable equivalents
-" call Decho("copy function-local variables back to buffer netrw variables",'~'.expand("<slnum>"))
- if exists("netrw_bannercnt") |let b:netrw_bannercnt = netrw_bannercnt |endif
- if exists("netrw_browser_active") |let b:netrw_browser_active = netrw_browser_active |endif
- if exists("netrw_cpf") |let b:netrw_cpf = netrw_cpf |endif
- if exists("netrw_curdir") |let b:netrw_curdir = netrw_curdir |endif
- if exists("netrw_explore_bufnr") |let b:netrw_explore_bufnr = netrw_explore_bufnr |endif
- if exists("netrw_explore_indx") |let b:netrw_explore_indx = netrw_explore_indx |endif
- if exists("netrw_explore_line") |let b:netrw_explore_line = netrw_explore_line |endif
- if exists("netrw_explore_list") |let b:netrw_explore_list = netrw_explore_list |endif
- if exists("netrw_explore_listlen")|let b:netrw_explore_listlen = netrw_explore_listlen|endif
- if exists("netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt = netrw_explore_mtchcnt|endif
- if exists("netrw_fname") |let b:netrw_fname = netrw_fname |endif
- if exists("netrw_lastfile") |let b:netrw_lastfile = netrw_lastfile |endif
- if exists("netrw_liststyle") |let b:netrw_liststyle = netrw_liststyle |endif
- if exists("netrw_method") |let b:netrw_method = netrw_method |endif
- if exists("netrw_option") |let b:netrw_option = netrw_option |endif
- if exists("netrw_prvdir") |let b:netrw_prvdir = netrw_prvdir |endif
-
- if a:0 > 0
- let b:netrw_curdir= a:1
- if b:netrw_curdir =~ '/$'
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- setl nobl
- file NetrwTreeListing
- setl nobl bt=nowrite bh=hide
- nno <silent> <buffer> [ :sil call <SID>TreeListMove('[')<cr>
- nno <silent> <buffer> ] :sil call <SID>TreeListMove(']')<cr>
- else
- call s:NetrwBufRename(b:netrw_curdir)
- endif
- endif
- endif
- if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on")
- let &l:bexpr = "netrw#BalloonHelp()"
- endif
-
-" call Dret("s:NetrwEnew : buf#".bufnr("%")."<".bufname("%")."> expand(%)<".expand("%")."> expand(#)<".expand("#")."> bh=".&bh." win#".winnr()." winnr($)#".winnr("$"))
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwExe: executes a string using "!" {{{2
-fun! s:NetrwExe(cmd)
-" call Dfunc("s:NetrwExe(a:cmd<".a:cmd.">)")
- if has("win32")
-" call Decho("using win32:",expand("<slnum>"))
- let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash]
- set shell& shellcmdflag& shellxquote& shellxescape&
- set shellquote& shellpipe& shellredir& shellslash&
- try
- exe a:cmd
- finally
- let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell
- endtry
- else
-" call Decho("exe ".a:cmd,'~'.expand("<slnum>"))
- exe a:cmd
- endif
- if v:shell_error
- call netrw#ErrorMsg(s:WARNING,"shell signalled an error",106)
- endif
-" call Dret("s:NetrwExe : v:shell_error=".v:shell_error)
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2
-fun! s:NetrwInsureWinVars()
- if !exists("w:netrw_liststyle")
-" call Dfunc("s:NetrwInsureWinVars() win#".winnr())
- let curbuf = bufnr("%")
- let curwin = winnr()
- let iwin = 1
- while iwin <= winnr("$")
- exe iwin."wincmd w"
- if winnr() != curwin && bufnr("%") == curbuf && exists("w:netrw_liststyle")
- " looks like ctrl-w_s or ctrl-w_v was used to split a netrw buffer
- let winvars= w:
- break
- endif
- let iwin= iwin + 1
- endwhile
- exe "keepalt ".curwin."wincmd w"
- if exists("winvars")
-" call Decho("copying w#".iwin." window variables to w#".curwin,'~'.expand("<slnum>"))
- for k in keys(winvars)
- let w:{k}= winvars[k]
- endfor
- endif
-" call Dret("s:NetrwInsureWinVars win#".winnr())
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwLcd: handles changing the (local) directory {{{2
-" Returns: 0=success
-" -1=failed
-fun! s:NetrwLcd(newdir)
-" call Dfunc("s:NetrwLcd(newdir<".a:newdir.">)")
-" call Decho("changing local directory",'~'.expand("<slnum>"))
-
- let err472= 0
- try
- exe 'NetrwKeepj sil lcd '.fnameescape(a:newdir)
- catch /^Vim\%((\a\+)\)\=:E344/
- " Vim's lcd fails with E344 when attempting to go above the 'root' of a Windows share.
- " Therefore, detect if a Windows share is present, and if E344 occurs, just settle at
- " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or
- " forward slashes ('//Foo'), depending on whether backslashes have been converted to
- " forward slashes by earlier code; so check for both.
- if has("win32") && !g:netrw_cygwin
- if a:newdir =~ '^\\\\\w\+' || a:newdir =~ '^//\w\+'
- let dirname = '\'
- exe 'NetrwKeepj sil lcd '.fnameescape(dirname)
- endif
- endif
- catch /^Vim\%((\a\+)\)\=:E472/
- let err472= 1
- endtry
-
- if err472
- call netrw#ErrorMsg(s:ERROR,"unable to change directory to <".a:newdir."> (permissions?)",61)
- if exists("w:netrw_prvdir")
- let a:newdir= w:netrw_prvdir
- else
- call s:NetrwOptionsRestore("w:")
-" call Decho("setl noma nomod nowrap",'~'.expand("<slnum>"))
- exe "setl ".g:netrw_bufsettings
-" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
- let a:newdir= dirname
- endif
-" call Dret("s:NetrwBrowse -1 : reusing buffer#".(exists("bufnum")? bufnum : 'N/A')."<".dirname."> getcwd<".getcwd().">")
- return -1
- endif
-
-" call Decho("getcwd <".getcwd().">")
-" call Decho("b:netrw_curdir<".b:netrw_curdir.">")
-" call Dret("s:NetrwLcd 0")
- return 0
-endfun
-
-" ------------------------------------------------------------------------
-" s:NetrwSaveWordPosn: used to keep cursor on same word after refresh, {{{2
-" changed sorting, etc. Also see s:NetrwRestoreWordPosn().
-fun! s:NetrwSaveWordPosn()
-" call Dfunc("NetrwSaveWordPosn()")
- let s:netrw_saveword= '^'.fnameescape(getline('.')).'$'
-" call Dret("NetrwSaveWordPosn : saveword<".s:netrw_saveword.">")
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwHumanReadable: takes a number and makes it "human readable" {{{2
-" 1000 -> 1K, 1000000 -> 1M, 1000000000 -> 1G
-fun! s:NetrwHumanReadable(sz)
-" call Dfunc("s:NetrwHumanReadable(sz=".a:sz.") type=".type(a:sz)." style=".g:netrw_sizestyle )
-
- if g:netrw_sizestyle == 'h'
- if a:sz >= 1000000000
- let sz = printf("%.1f",a:sz/1000000000.0)."g"
- elseif a:sz >= 10000000
- let sz = printf("%d",a:sz/1000000)."m"
- elseif a:sz >= 1000000
- let sz = printf("%.1f",a:sz/1000000.0)."m"
- elseif a:sz >= 10000
- let sz = printf("%d",a:sz/1000)."k"
- elseif a:sz >= 1000
- let sz = printf("%.1f",a:sz/1000.0)."k"
- else
- let sz= a:sz
- endif
-
- elseif g:netrw_sizestyle == 'H'
- if a:sz >= 1073741824
- let sz = printf("%.1f",a:sz/1073741824.0)."G"
- elseif a:sz >= 10485760
- let sz = printf("%d",a:sz/1048576)."M"
- elseif a:sz >= 1048576
- let sz = printf("%.1f",a:sz/1048576.0)."M"
- elseif a:sz >= 10240
- let sz = printf("%d",a:sz/1024)."K"
- elseif a:sz >= 1024
- let sz = printf("%.1f",a:sz/1024.0)."K"
- else
- let sz= a:sz
- endif
-
- else
- let sz= a:sz
- endif
-
-" call Dret("s:NetrwHumanReadable ".sz)
- return sz
-endfun
-
-" ---------------------------------------------------------------------
-" s:NetrwRestoreWordPosn: used to keep cursor on same word after refresh, {{{2
-" changed sorting, etc. Also see s:NetrwSaveWordPosn().
-fun! s:NetrwRestoreWordPosn()
-" call Dfunc("NetrwRestoreWordPosn()")
- sil! call search(s:netrw_saveword,'w')
-" call Dret("NetrwRestoreWordPosn")
-endfun
-
-" ---------------------------------------------------------------------
-" s:RestoreBufVars: {{{2
-fun! s:RestoreBufVars()
-" call Dfunc("s:RestoreBufVars()")
-
- if exists("s:netrw_curdir") |let b:netrw_curdir = s:netrw_curdir |endif
- if exists("s:netrw_lastfile") |let b:netrw_lastfile = s:netrw_lastfile |endif
- if exists("s:netrw_method") |let b:netrw_method = s:netrw_method |endif
- if exists("s:netrw_fname") |let b:netrw_fname = s:netrw_fname |endif
- if exists("s:netrw_machine") |let b:netrw_machine = s:netrw_machine |endif
- if exists("s:netrw_browser_active")|let b:netrw_browser_active = s:netrw_browser_active|endif
-
-" call Dret("s:RestoreBufVars")
-endfun
-
-" ---------------------------------------------------------------------
-" s:RemotePathAnalysis: {{{2
-fun! s:RemotePathAnalysis(dirname)
-" call Dfunc("s:RemotePathAnalysis(a:dirname<".a:dirname.">)")
-
- " method :// user @ machine :port /path
- let dirpat = '^\(\w\{-}\)://\(\(\w\+\)@\)\=\([^/:#]\+\)\%([:#]\(\d\+\)\)\=/\(.*\)$'
- let s:method = substitute(a:dirname,dirpat,'\1','')
- let s:user = substitute(a:dirname,dirpat,'\3','')
- let s:machine = substitute(a:dirname,dirpat,'\4','')
- let s:port = substitute(a:dirname,dirpat,'\5','')
- let s:path = substitute(a:dirname,dirpat,'\6','')
- let s:fname = substitute(s:path,'^.*/\ze.','','')
- if s:machine =~ '@'
- let dirpat = '^\(.*\)@\(.\{-}\)$'
- let s:user = s:user.'@'.substitute(s:machine,dirpat,'\1','')
- let s:machine = substitute(s:machine,dirpat,'\2','')
- endif
-
-" call Decho("set up s:method <".s:method .">",'~'.expand("<slnum>"))
-" call Decho("set up s:user <".s:user .">",'~'.expand("<slnum>"))
-" call Decho("set up s:machine<".s:machine.">",'~'.expand("<slnum>"))
-" call Decho("set up s:port <".s:port.">",'~'.expand("<slnum>"))
-" call Decho("set up s:path <".s:path .">",'~'.expand("<slnum>"))
-" call Decho("set up s:fname <".s:fname .">",'~'.expand("<slnum>"))
-
-" call Dret("s:RemotePathAnalysis")
-endfun
-
-" ---------------------------------------------------------------------
-" s:RemoteSystem: runs a command on a remote host using ssh {{{2
-" Returns status
-" Runs system() on
-" [cd REMOTEDIRPATH;] a:cmd
-" Note that it doesn't do s:ShellEscape(a:cmd)!
-fun! s:RemoteSystem(cmd)
-" call Dfunc("s:RemoteSystem(cmd<".a:cmd.">)")
- if !executable(g:netrw_ssh_cmd)
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"g:netrw_ssh_cmd<".g:netrw_ssh_cmd."> is not executable!",52)
- elseif !exists("b:netrw_curdir")
- NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53)
- else
- let cmd = s:MakeSshCmd(g:netrw_ssh_cmd." USEPORT HOSTNAME")
- let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','')
- if remotedir != ""
- let cmd= cmd.' cd '.s:ShellEscape(remotedir).";"
- else
- let cmd= cmd.' '
- endif
- let cmd= cmd.a:cmd
-" call Decho("call system(".cmd.")",'~'.expand("<slnum>"))
- let ret= system(cmd)
- endif
-" call Dret("s:RemoteSystem ".ret)
- return ret
-endfun
-
-" ---------------------------------------------------------------------
-" s:RestoreWinVars: (used by Explore() and NetrwSplit()) {{{2
-fun! s:RestoreWinVars()
-" call Dfunc("s:RestoreWinVars()")
- if exists("s:bannercnt") |let w:netrw_bannercnt = s:bannercnt |unlet s:bannercnt |endif
- if exists("s:col") |let w:netrw_col = s:col |unlet s:col |endif
- if exists("s:curdir") |let w:netrw_curdir = s:curdir |unlet s:curdir |endif
- if exists("s:explore_bufnr") |let w:netrw_explore_bufnr = s:explore_bufnr |unlet s:explore_bufnr |endif
- if exists("s:explore_indx") |let w:netrw_explore_indx = s:explore_indx |unlet s:explore_indx |endif
- if exists("s:explore_line") |let w:netrw_explore_line = s:explore_line |unlet s:explore_line |endif
- if exists("s:explore_listlen")|let w:netrw_explore_listlen = s:explore_listlen|unlet s:explore_listlen|endif
- if exists("s:explore_list") |let w:netrw_explore_list = s:explore_list |unlet s:explore_list |endif
- if exists("s:explore_mtchcnt")|let w:netrw_explore_mtchcnt = s:explore_mtchcnt|unlet s:explore_mtchcnt|endif
- if exists("s:fpl") |let w:netrw_fpl = s:fpl |unlet s:fpl |endif
- if exists("s:hline") |let w:netrw_hline = s:hline |unlet s:hline |endif
- if exists("s:line") |let w:netrw_line = s:line |unlet s:line |endif
- if exists("s:liststyle") |let w:netrw_liststyle = s:liststyle |unlet s:liststyle |endif
- if exists("s:method") |let w:netrw_method = s:method |unlet s:method |endif
- if exists("s:prvdir") |let w:netrw_prvdir = s:prvdir |unlet s:prvdir |endif
- if exists("s:treedict") |let w:netrw_treedict = s:treedict |unlet s:treedict |endif
- if exists("s:treetop") |let w:netrw_treetop = s:treetop |unlet s:treetop |endif
- if exists("s:winnr") |let w:netrw_winnr = s:winnr |unlet s:winnr |endif
-" call Dret("s:RestoreWinVars")
-endfun
-
-" ---------------------------------------------------------------------
-" s:Rexplore: implements returning from a buffer to a netrw directory {{{2
-"
-" s:SetRexDir() sets up <2-leftmouse> maps (if g:netrw_retmap
-" is true) and a command, :Rexplore, which call this function.
-"
-" s:netrw_posn is set up by s:NetrwBrowseChgDir()
-"
-" s:rexposn_BUFNR used to save/restore cursor position
-fun! s:NetrwRexplore(islocal,dirname)
- if exists("s:netrwdrag")
- return
- endif
-" call Dfunc("s:NetrwRexplore() w:netrw_rexlocal=".w:netrw_rexlocal." w:netrw_rexdir<".w:netrw_rexdir."> win#".winnr())
-" call Decho("currently in bufname<".bufname("%").">",'~'.expand("<slnum>"))
-" call Decho("ft=".&ft." win#".winnr()." w:netrw_rexfile<".(exists("w:netrw_rexfile")? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>"))
-
- if &ft == "netrw" && exists("w:netrw_rexfile") && w:netrw_rexfile != ""
- " a :Rex while in a netrw buffer means: edit the file in w:netrw_rexfile
-" call Decho("in netrw buffer, will edit file<".w:netrw_rexfile.">",'~'.expand("<slnum>"))
- exe "NetrwKeepj e ".w:netrw_rexfile
- unlet w:netrw_rexfile
-" call Dret("s:NetrwRexplore returning from netrw to buf#".bufnr("%")."<".bufname("%")."> (ft=".&ft.")")
- return
-" else " Decho
-" call Decho("treating as not-netrw-buffer: ft=".&ft.((&ft == "netrw")? " == netrw" : "!= netrw"),'~'.expand("<slnum>"))
-" call Decho("treating as not-netrw-buffer: w:netrw_rexfile<".((exists("w:netrw_rexfile"))? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>"))
- endif
-
- " ---------------------------
- " :Rex issued while in a file
- " ---------------------------
-
- " record current file so :Rex can return to it from netrw
- let w:netrw_rexfile= expand("%")
-" call Decho("set w:netrw_rexfile<".w:netrw_rexfile."> (win#".winnr().")",'~'.expand("<slnum>"))
-
- if !exists("w:netrw_rexlocal")
-" call Dret("s:NetrwRexplore w:netrw_rexlocal doesn't exist (".&ft." win#".winnr().")")
- return
- 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,'~'.expand("<slnum>"))
- if w:netrw_rexlocal
- NetrwKeepj call netrw#LocalBrowseCheck(w:netrw_rexdir)
- else
- NetrwKeepj call s:NetrwBrowse(0,w:netrw_rexdir)
- endif
- if exists("s:initbeval")
- setl beval
- endif
- if exists("s:rexposn_".bufnr("%"))
-" call Decho("restore posn, then unlet s:rexposn_".bufnr('%')."<".bufname("%").">",'~'.expand("<slnum>"))
- " restore position in directory listing
-" call Decho("restoring posn to s:rexposn_".bufnr('%')."<".string(s:rexposn_{bufnr('%')}).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(s:rexposn_{bufnr('%')})
- if exists("s:rexposn_".bufnr('%'))
- unlet s:rexposn_{bufnr('%')}
- endif
- else
-" call Decho("s:rexposn_".bufnr('%')."<".bufname("%")."> doesn't exist",'~'.expand("<slnum>"))
- endif
-
- if has("syntax") && exists("g:syntax_on") && g:syntax_on
- if exists("s:explore_match")
- exe "2match netrwMarkFile /".s:explore_match."/"
- endif
- 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,'~'.expand("<slnum>"))
-" call Dret("s:NetrwRexplore : ft=".&ft)
-endfun
-
-" ---------------------------------------------------------------------
-" s:SaveBufVars: save selected b: variables to s: variables {{{2
-" use s:RestoreBufVars() to restore b: variables from s: variables
-fun! s:SaveBufVars()
-" call Dfunc("s:SaveBufVars() buf#".bufnr("%"))
-
- if exists("b:netrw_curdir") |let s:netrw_curdir = b:netrw_curdir |endif
- if exists("b:netrw_lastfile") |let s:netrw_lastfile = b:netrw_lastfile |endif
- if exists("b:netrw_method") |let s:netrw_method = b:netrw_method |endif
- if exists("b:netrw_fname") |let s:netrw_fname = b:netrw_fname |endif
- if exists("b:netrw_machine") |let s:netrw_machine = b:netrw_machine |endif
- if exists("b:netrw_browser_active")|let s:netrw_browser_active = b:netrw_browser_active|endif
-
-" call Dret("s:SaveBufVars")
-endfun
-
-" ---------------------------------------------------------------------
-" s:SavePosn: saves position associated with current buffer into a dictionary {{{2
-fun! s:SavePosn(posndict)
-" call Dfunc("s:SavePosn(posndict) curbuf#".bufnr("%")."<".bufname("%").">")
-
- if !exists("a:posndict[bufnr('%')]")
- let a:posndict[bufnr("%")]= []
- endif
-" call Decho("before push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
- call add(a:posndict[bufnr("%")],winsaveview())
-" call Decho("after push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
-
-" call Dret("s:SavePosn posndict")
- return a:posndict
-endfun
-
-" ---------------------------------------------------------------------
-" s:RestorePosn: restores position associated with current buffer using dictionary {{{2
-fun! s:RestorePosn(posndict)
-" call Dfunc("s:RestorePosn(posndict) curbuf#".bufnr("%")."<".bufname("%").">")
- if exists("a:posndict")
- if has_key(a:posndict,bufnr("%"))
-" call Decho("before pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
- let posnlen= len(a:posndict[bufnr("%")])
- if posnlen > 0
- let posnlen= posnlen - 1
-" call Decho("restoring posn posndict[".bufnr("%")."][".posnlen."]=".string(a:posndict[bufnr("%")][posnlen]),'~'.expand("<slnum>"))
- call winrestview(a:posndict[bufnr("%")][posnlen])
- call remove(a:posndict[bufnr("%")],posnlen)
-" call Decho("after pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
- endif
- endif
- endif
-" call Dret("s:RestorePosn")
-endfun
-
-" ---------------------------------------------------------------------
-" s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2
-fun! s:SaveWinVars()
-" call Dfunc("s:SaveWinVars() win#".winnr())
- if exists("w:netrw_bannercnt") |let s:bannercnt = w:netrw_bannercnt |endif
- if exists("w:netrw_col") |let s:col = w:netrw_col |endif
- if exists("w:netrw_curdir") |let s:curdir = w:netrw_curdir |endif
- if exists("w:netrw_explore_bufnr") |let s:explore_bufnr = w:netrw_explore_bufnr |endif
- if exists("w:netrw_explore_indx") |let s:explore_indx = w:netrw_explore_indx |endif
- if exists("w:netrw_explore_line") |let s:explore_line = w:netrw_explore_line |endif
- if exists("w:netrw_explore_listlen")|let s:explore_listlen = w:netrw_explore_listlen|endif
- if exists("w:netrw_explore_list") |let s:explore_list = w:netrw_explore_list |endif
- if exists("w:netrw_explore_mtchcnt")|let s:explore_mtchcnt = w:netrw_explore_mtchcnt|endif
- if exists("w:netrw_fpl") |let s:fpl = w:netrw_fpl |endif
- if exists("w:netrw_hline") |let s:hline = w:netrw_hline |endif
- if exists("w:netrw_line") |let s:line = w:netrw_line |endif
- if exists("w:netrw_liststyle") |let s:liststyle = w:netrw_liststyle |endif
- if exists("w:netrw_method") |let s:method = w:netrw_method |endif
- if exists("w:netrw_prvdir") |let s:prvdir = w:netrw_prvdir |endif
- if exists("w:netrw_treedict") |let s:treedict = w:netrw_treedict |endif
- if exists("w:netrw_treetop") |let s:treetop = w:netrw_treetop |endif
- if exists("w:netrw_winnr") |let s:winnr = w:netrw_winnr |endif
-" call Dret("s:SaveWinVars")
-endfun
-
-" ---------------------------------------------------------------------
-" s:SetBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck()) {{{2
-" To allow separate windows to have their own activities, such as
-" Explore **/pattern, several variables have been made window-oriented.
-" However, when the user splits a browser window (ex: ctrl-w s), these
-" variables are not inherited by the new window. SetBufWinVars() and
-" UseBufWinVars() get around that.
-fun! s:SetBufWinVars()
-" call Dfunc("s:SetBufWinVars() win#".winnr())
- if exists("w:netrw_liststyle") |let b:netrw_liststyle = w:netrw_liststyle |endif
- if exists("w:netrw_bannercnt") |let b:netrw_bannercnt = w:netrw_bannercnt |endif
- if exists("w:netrw_method") |let b:netrw_method = w:netrw_method |endif
- if exists("w:netrw_prvdir") |let b:netrw_prvdir = w:netrw_prvdir |endif
- if exists("w:netrw_explore_indx") |let b:netrw_explore_indx = w:netrw_explore_indx |endif
- if exists("w:netrw_explore_listlen")|let b:netrw_explore_listlen= w:netrw_explore_listlen|endif
- if exists("w:netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt= w:netrw_explore_mtchcnt|endif
- if exists("w:netrw_explore_bufnr") |let b:netrw_explore_bufnr = w:netrw_explore_bufnr |endif
- if exists("w:netrw_explore_line") |let b:netrw_explore_line = w:netrw_explore_line |endif
- if exists("w:netrw_explore_list") |let b:netrw_explore_list = w:netrw_explore_list |endif
-" call Dret("s:SetBufWinVars")
-endfun
-
-" ---------------------------------------------------------------------
-" s:SetRexDir: set directory for :Rexplore {{{2
-fun! s:SetRexDir(islocal,dirname)
-" call Dfunc("s:SetRexDir(islocal=".a:islocal." dirname<".a:dirname.">) win#".winnr())
- let w:netrw_rexdir = a:dirname
- let w:netrw_rexlocal = a:islocal
- let s:rexposn_{bufnr("%")} = winsaveview()
-" call Decho("setting w:netrw_rexdir =".w:netrw_rexdir,'~'.expand("<slnum>"))
-" call Decho("setting w:netrw_rexlocal=".w:netrw_rexlocal,'~'.expand("<slnum>"))
-" call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>"))
-" call Decho("setting s:rexposn_".bufnr("%")."<".bufname("%")."> to ".string(winsaveview()),'~'.expand("<slnum>"))
-" call Dret("s:SetRexDir : win#".winnr()." ".(a:islocal? "local" : "remote")." dir: ".a:dirname)
-endfun
-
-" ---------------------------------------------------------------------
-" s:ShowLink: used to modify thin and tree listings to show links {{{2
-fun! s:ShowLink()
- if exists("b:netrw_curdir")
- norm! $?\a
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop")
- let basedir = s:NetrwTreePath(w:netrw_treetop)
- else
- let basedir = b:netrw_curdir.'/'
- endif
- let fname = basedir.s:NetrwGetWord()
- let resname = resolve(fname)
- if resname =~ '^\M'.basedir
- let dirlen = strlen(basedir)
- let resname = strpart(resname,dirlen)
- endif
- let modline = getline(".")."\t --> ".resname
- setl noro ma
- call setline(".",modline)
- setl ro noma nomod
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:ShowStyle: {{{2
-fun! s:ShowStyle()
- if !exists("w:netrw_liststyle")
- let liststyle= g:netrw_liststyle
- else
- let liststyle= w:netrw_liststyle
- endif
- if liststyle == s:THINLIST
- return s:THINLIST.":thin"
- elseif liststyle == s:LONGLIST
- return s:LONGLIST.":long"
- elseif liststyle == s:WIDELIST
- return s:WIDELIST.":wide"
- elseif liststyle == s:TREELIST
- return s:TREELIST.":tree"
- else
- return 'n/a'
- endif
-endfun
-
-" ---------------------------------------------------------------------
-" s:Strlen: this function returns the length of a string, even if its using multi-byte characters. {{{2
-" Solution from Nicolai Weibull, vim docs (:help strlen()),
-" Tony Mechelynck, and my own invention.
-fun! s:Strlen(x)
-" "" call Dfunc("s:Strlen(x<".a:x."> g:Align_xstrlen=".g:Align_xstrlen.")")
-
- if v:version >= 703 && exists("*strdisplaywidth")
- let ret= strdisplaywidth(a:x)
-
- elseif type(g:Align_xstrlen) == 1
- " allow user to specify a function to compute the string length (ie. let g:Align_xstrlen="mystrlenfunc")
- exe "let ret= ".g:Align_xstrlen."('".substitute(a:x,"'","''","g")."')"
-
- elseif g:Align_xstrlen == 1
- " number of codepoints (Latin a + combining circumflex is two codepoints)
- " (comment from TM, solution from NW)
- let ret= strlen(substitute(a:x,'.','c','g'))
-
- elseif g:Align_xstrlen == 2
- " number of spacing codepoints (Latin a + combining circumflex is one spacing
- " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.)
- " (comment from TM, solution from TM)
- let ret=strlen(substitute(a:x, '.\Z', 'x', 'g'))
-
- elseif g:Align_xstrlen == 3
- " virtual length (counting, for instance, tabs as anything between 1 and
- " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately
- " preceded by lam, one otherwise, etc.)
- " (comment from TM, solution from me)
- let modkeep= &l:mod
- exe "norm! o\<esc>"
- call setline(line("."),a:x)
- let ret= virtcol("$") - 1
- d
- NetrwKeepj norm! k
- let &l:mod= modkeep
-
- else
- " at least give a decent default
- let ret= strlen(a:x)
- endif
-" "" call Dret("s:Strlen ".ret)
- return ret
-endfun
-
-" ---------------------------------------------------------------------
-" s:ShellEscape: shellescape(), or special windows handling {{{2
-fun! s:ShellEscape(s, ...)
- if has('win32') && $SHELL == '' && &shellslash
- return printf('"%s"', substitute(a:s, '"', '""', 'g'))
- endif
- let f = a:0 > 0 ? a:1 : 0
- return shellescape(a:s, f)
-endfun
-
-" ---------------------------------------------------------------------
-" s:TreeListMove: supports [[, ]], [], and ][ in tree mode {{{2
-fun! s:TreeListMove(dir)
-" call Dfunc("s:TreeListMove(dir<".a:dir.">)")
- let curline = getline('.')
- let prvline = (line(".") > 1)? getline(line(".")-1) : ''
- let nxtline = (line(".") < line("$"))? getline(line(".")+1) : ''
- let curindent = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e')
- let indentm1 = substitute(curindent,'^'.s:treedepthstring,'','')
- let treedepthchr = substitute(s:treedepthstring,' ','','g')
- let stopline = exists("w:netrw_bannercnt")? w:netrw_bannercnt : 1
-" call Decho("prvline <".prvline."> #".(line(".")-1), '~'.expand("<slnum>"))
-" call Decho("curline <".curline."> #".line(".") , '~'.expand("<slnum>"))
-" call Decho("nxtline <".nxtline."> #".(line(".")+1), '~'.expand("<slnum>"))
-" call Decho("curindent<".curindent.">" , '~'.expand("<slnum>"))
-" call Decho("indentm1 <".indentm1.">" , '~'.expand("<slnum>"))
- " COMBAK : need to handle when on a directory
- " COMBAK : need to handle ]] and ][. In general, needs work!!!
- if curline !~ '/$'
- if a:dir == '[[' && prvline != ''
- NetrwKeepj norm! 0
- let nl = search('^'.indentm1.'\%('.s:treedepthstring.'\)\@!','bWe',stopline) " search backwards
-" call Decho("regfile srch back: ".nl,'~'.expand("<slnum>"))
- elseif a:dir == '[]' && nxtline != ''
- NetrwKeepj norm! 0
-" call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>','~'.expand("<slnum>"))
- let nl = search('^\%('.curindent.'\)\@!','We') " search forwards
- if nl != 0
- NetrwKeepj norm! k
- else
- NetrwKeepj norm! G
- endif
-" call Decho("regfile srch fwd: ".nl,'~'.expand("<slnum>"))
- endif
- endif
-
-" call Dret("s:TreeListMove")
-endfun
-
-" ---------------------------------------------------------------------
-" s:UpdateBuffersMenu: does emenu Buffers.Refresh (but due to locale, the menu item may not be called that) {{{2
-" The Buffers.Refresh menu calls s:BMShow(); unfortunately, that means that that function
-" can't be called except via emenu. But due to locale, that menu line may not be called
-" Buffers.Refresh; hence, s:NetrwBMShow() utilizes a "cheat" to call that function anyway.
-fun! s:UpdateBuffersMenu()
-" call Dfunc("s:UpdateBuffersMenu()")
- if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
- try
- sil emenu Buffers.Refresh\ menu
- catch /^Vim\%((\a\+)\)\=:E/
- let v:errmsg= ""
- sil NetrwKeepj call s:NetrwBMShow()
- endtry
- endif
-" call Dret("s:UpdateBuffersMenu")
-endfun
-
-" ---------------------------------------------------------------------
-" s:UseBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck() {{{2
-" Matching function to s:SetBufWinVars()
-fun! s:UseBufWinVars()
-" call Dfunc("s:UseBufWinVars()")
- if exists("b:netrw_liststyle") && !exists("w:netrw_liststyle") |let w:netrw_liststyle = b:netrw_liststyle |endif
- if exists("b:netrw_bannercnt") && !exists("w:netrw_bannercnt") |let w:netrw_bannercnt = b:netrw_bannercnt |endif
- if exists("b:netrw_method") && !exists("w:netrw_method") |let w:netrw_method = b:netrw_method |endif
- if exists("b:netrw_prvdir") && !exists("w:netrw_prvdir") |let w:netrw_prvdir = b:netrw_prvdir |endif
- if exists("b:netrw_explore_indx") && !exists("w:netrw_explore_indx") |let w:netrw_explore_indx = b:netrw_explore_indx |endif
- if exists("b:netrw_explore_listlen") && !exists("w:netrw_explore_listlen")|let w:netrw_explore_listlen = b:netrw_explore_listlen|endif
- if exists("b:netrw_explore_mtchcnt") && !exists("w:netrw_explore_mtchcnt")|let w:netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif
- if exists("b:netrw_explore_bufnr") && !exists("w:netrw_explore_bufnr") |let w:netrw_explore_bufnr = b:netrw_explore_bufnr |endif
- if exists("b:netrw_explore_line") && !exists("w:netrw_explore_line") |let w:netrw_explore_line = b:netrw_explore_line |endif
- if exists("b:netrw_explore_list") && !exists("w:netrw_explore_list") |let w:netrw_explore_list = b:netrw_explore_list |endif
-" call Dret("s:UseBufWinVars")
-endfun
-
-" ---------------------------------------------------------------------
-" s:UserMaps: supports user-defined UserMaps {{{2
-" * calls a user-supplied funcref(islocal,curdir)
-" * interprets result
-" See netrw#UserMaps()
-fun! s:UserMaps(islocal,funcname)
- if !exists("b:netrw_curdir")
- let b:netrw_curdir= getcwd()
- endif
- let Funcref = function(a:funcname)
- let result = Funcref(a:islocal)
-
- if type(result) == 1
- " if result from user's funcref is a string...
- if result == "refresh"
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- elseif result != ""
- exe result
- endif
-
- elseif type(result) == 3
- " if result from user's funcref is a List...
- for action in result
- if action == "refresh"
- call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
- elseif action != ""
- exe action
- endif
- endfor
- endif
-endfun
-
-" ==========================
-" Settings Restoration: {{{1
-" ==========================
-let &cpo= s:keepcpo
-unlet s:keepcpo
-
-" ===============
-" Modelines: {{{1
-" ===============
-" vim:ts=8 fdm=marker
diff --git a/runtime/autoload/netrwFileHandlers.vim b/runtime/autoload/netrwFileHandlers.vim
deleted file mode 100644
index e69de29bb2..0000000000
--- a/runtime/autoload/netrwFileHandlers.vim
+++ /dev/null
diff --git a/runtime/autoload/netrwSettings.vim b/runtime/autoload/netrwSettings.vim
deleted file mode 100644
index 3452602272..0000000000
--- a/runtime/autoload/netrwSettings.vim
+++ /dev/null
@@ -1,249 +0,0 @@
-" netrwSettings.vim: makes netrw settings simpler
-" Date: Nov 15, 2021
-" Maintainer: This runtime file is looking for a new maintainer.
-" Former Maintainer: Charles E Campbell
-" Version: 18
-" Last Change:
-" 2024 May 08 by Vim Project: cleanup legacy Win9X checks
-" Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{1
-" Permission is hereby granted to use and distribute this code,
-" with or without modifications, provided that this copyright
-" notice is copied with it. Like anything else that's free,
-" netrwSettings.vim is provided *as is* and comes with no
-" warranty of any kind, either expressed or implied. By using
-" this plugin, you agree that in no event will the copyright
-" holder be liable for any damages resulting from the use
-" of this software.
-"
-" Mat 4:23 (WEB) Jesus went about in all Galilee, teaching in their {{{1
-" synagogues, preaching the gospel of the kingdom, and healing
-" every disease and every sickness among the people.
-" Load Once: {{{1
-if exists("g:loaded_netrwSettings") || &cp
- finish
-endif
-let g:loaded_netrwSettings = "v18"
-if v:version < 700
- echohl WarningMsg
- echo "***warning*** this version of netrwSettings needs vim 7.0"
- echohl Normal
- finish
-endif
-
-" ---------------------------------------------------------------------
-" NetrwSettings: {{{1
-fun! netrwSettings#NetrwSettings()
- " this call is here largely just to insure that netrw has been loaded
- call netrw#WinPath("")
- if !exists("g:loaded_netrw")
- echohl WarningMsg | echomsg "***sorry*** netrw needs to be loaded prior to using NetrwSettings" | echohl None
- return
- endif
-
- above wincmd s
- enew
- setlocal noswapfile bh=wipe
- set ft=vim
- file Netrw\ Settings
-
- " these variables have the following default effects when they don't
- " exist (ie. have not been set by the user in his/her .vimrc)
- if !exists("g:netrw_liststyle")
- let g:netrw_liststyle= 0
- let g:netrw_list_cmd= "ssh HOSTNAME ls -FLa"
- endif
- if !exists("g:netrw_silent")
- let g:netrw_silent= 0
- endif
- if !exists("g:netrw_use_nt_rcp")
- let g:netrw_use_nt_rcp= 0
- endif
- if !exists("g:netrw_ftp")
- let g:netrw_ftp= 0
- endif
- if !exists("g:netrw_ignorenetrc")
- let g:netrw_ignorenetrc= 0
- endif
-
- put ='+ ---------------------------------------------'
- put ='+ NetrwSettings: by Charles E. Campbell'
- put ='+ Press <F1> with cursor atop any line for help'
- put ='+ ---------------------------------------------'
- let s:netrw_settings_stop= line(".")
-
- put =''
- put ='+ Netrw Protocol Commands'
- put = 'let g:netrw_dav_cmd = '.g:netrw_dav_cmd
- put = 'let g:netrw_fetch_cmd = '.g:netrw_fetch_cmd
- put = 'let g:netrw_ftp_cmd = '.g:netrw_ftp_cmd
- put = 'let g:netrw_http_cmd = '.g:netrw_http_cmd
- put = 'let g:netrw_rcp_cmd = '.g:netrw_rcp_cmd
- put = 'let g:netrw_rsync_cmd = '.g:netrw_rsync_cmd
- put = 'let g:netrw_scp_cmd = '.g:netrw_scp_cmd
- put = 'let g:netrw_sftp_cmd = '.g:netrw_sftp_cmd
- put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd
- let s:netrw_protocol_stop= line(".")
- put = ''
-
- put ='+Netrw Transfer Control'
- put = 'let g:netrw_cygwin = '.g:netrw_cygwin
- put = 'let g:netrw_ftp = '.g:netrw_ftp
- put = 'let g:netrw_ftpmode = '.g:netrw_ftpmode
- put = 'let g:netrw_ignorenetrc = '.g:netrw_ignorenetrc
- put = 'let g:netrw_sshport = '.g:netrw_sshport
- put = 'let g:netrw_silent = '.g:netrw_silent
- put = 'let g:netrw_use_nt_rcp = '.g:netrw_use_nt_rcp
- let s:netrw_xfer_stop= line(".")
- put =''
- put ='+ Netrw Messages'
- put ='let g:netrw_use_errorwindow = '.g:netrw_use_errorwindow
-
- put = ''
- put ='+ Netrw Browser Control'
- if exists("g:netrw_altfile")
- put = 'let g:netrw_altfile = '.g:netrw_altfile
- else
- put = 'let g:netrw_altfile = 0'
- endif
- put = 'let g:netrw_alto = '.g:netrw_alto
- put = 'let g:netrw_altv = '.g:netrw_altv
- put = 'let g:netrw_banner = '.g:netrw_banner
- if exists("g:netrw_bannerbackslash")
- put = 'let g:netrw_bannerbackslash = '.g:netrw_bannerbackslash
- else
- put = '\" let g:netrw_bannerbackslash = (not defined)'
- endif
- put = 'let g:netrw_browse_split = '.g:netrw_browse_split
- if exists("g:netrw_browsex_viewer")
- put = 'let g:netrw_browsex_viewer = '.g:netrw_browsex_viewer
- else
- put = '\" let g:netrw_browsex_viewer = (not defined)'
- endif
- put = 'let g:netrw_compress = '.g:netrw_compress
- if exists("g:Netrw_corehandler")
- put = 'let g:Netrw_corehandler = '.g:Netrw_corehandler
- else
- put = '\" let g:Netrw_corehandler = (not defined)'
- endif
- put = 'let g:netrw_ctags = '.g:netrw_ctags
- put = 'let g:netrw_cursor = '.g:netrw_cursor
- let decompressline= line("$")
- put = 'let g:netrw_decompress = '.string(g:netrw_decompress)
- if exists("g:netrw_dynamic_maxfilenamelen")
- put = 'let g:netrw_dynamic_maxfilenamelen='.g:netrw_dynamic_maxfilenamelen
- else
- put = '\" let g:netrw_dynamic_maxfilenamelen= (not defined)'
- endif
- put = 'let g:netrw_dirhistmax = '.g:netrw_dirhistmax
- put = 'let g:netrw_errorlvl = '.g:netrw_errorlvl
- put = 'let g:netrw_fastbrowse = '.g:netrw_fastbrowse
- let fnameescline= line("$")
- put = 'let g:netrw_fname_escape = '.string(g:netrw_fname_escape)
- put = 'let g:netrw_ftp_browse_reject = '.g:netrw_ftp_browse_reject
- put = 'let g:netrw_ftp_list_cmd = '.g:netrw_ftp_list_cmd
- put = 'let g:netrw_ftp_sizelist_cmd = '.g:netrw_ftp_sizelist_cmd
- put = 'let g:netrw_ftp_timelist_cmd = '.g:netrw_ftp_timelist_cmd
- let globescline= line("$")
- put = 'let g:netrw_glob_escape = '.string(g:netrw_glob_escape)
- put = 'let g:netrw_hide = '.g:netrw_hide
- if exists("g:netrw_home")
- put = 'let g:netrw_home = '.g:netrw_home
- else
- put = '\" let g:netrw_home = (not defined)'
- endif
- put = 'let g:netrw_keepdir = '.g:netrw_keepdir
- put = 'let g:netrw_list_cmd = '.g:netrw_list_cmd
- put = 'let g:netrw_list_hide = '.g:netrw_list_hide
- put = 'let g:netrw_liststyle = '.g:netrw_liststyle
- put = 'let g:netrw_localcopycmd = '.g:netrw_localcopycmd
- put = 'let g:netrw_localcopycmdopt = '.g:netrw_localcopycmdopt
- put = 'let g:netrw_localmkdir = '.g:netrw_localmkdir
- put = 'let g:netrw_localmkdiropt = '.g:netrw_localmkdiropt
- put = 'let g:netrw_localmovecmd = '.g:netrw_localmovecmd
- put = 'let g:netrw_localmovecmdopt = '.g:netrw_localmovecmdopt
- put = 'let g:netrw_maxfilenamelen = '.g:netrw_maxfilenamelen
- put = 'let g:netrw_menu = '.g:netrw_menu
- put = 'let g:netrw_mousemaps = '.g:netrw_mousemaps
- put = 'let g:netrw_mkdir_cmd = '.g:netrw_mkdir_cmd
- if exists("g:netrw_nobeval")
- put = 'let g:netrw_nobeval = '.g:netrw_nobeval
- else
- put = '\" let g:netrw_nobeval = (not defined)'
- endif
- put = 'let g:netrw_remote_mkdir = '.g:netrw_remote_mkdir
- put = 'let g:netrw_preview = '.g:netrw_preview
- put = 'let g:netrw_rename_cmd = '.g:netrw_rename_cmd
- put = 'let g:netrw_retmap = '.g:netrw_retmap
- put = 'let g:netrw_rm_cmd = '.g:netrw_rm_cmd
- put = 'let g:netrw_rmdir_cmd = '.g:netrw_rmdir_cmd
- put = 'let g:netrw_rmf_cmd = '.g:netrw_rmf_cmd
- put = 'let g:netrw_sort_by = '.g:netrw_sort_by
- put = 'let g:netrw_sort_direction = '.g:netrw_sort_direction
- put = 'let g:netrw_sort_options = '.g:netrw_sort_options
- put = 'let g:netrw_sort_sequence = '.g:netrw_sort_sequence
- put = 'let g:netrw_servername = '.g:netrw_servername
- put = 'let g:netrw_special_syntax = '.g:netrw_special_syntax
- put = 'let g:netrw_ssh_browse_reject = '.g:netrw_ssh_browse_reject
- put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd
- put = 'let g:netrw_scpport = '.g:netrw_scpport
- put = 'let g:netrw_sepchr = '.g:netrw_sepchr
- put = 'let g:netrw_sshport = '.g:netrw_sshport
- put = 'let g:netrw_timefmt = '.g:netrw_timefmt
- let tmpfileescline= line("$")
- put ='let g:netrw_tmpfile_escape...'
- put = 'let g:netrw_use_noswf = '.g:netrw_use_noswf
- put = 'let g:netrw_xstrlen = '.g:netrw_xstrlen
- put = 'let g:netrw_winsize = '.g:netrw_winsize
-
- put =''
- put ='+ For help, place cursor on line and press <F1>'
-
- 1d
- silent %s/^+/"/e
- res 99
- silent %s/= \([^0-9].*\)$/= '\1'/e
- silent %s/= $/= ''/e
- 1
-
- call setline(decompressline,"let g:netrw_decompress = ".substitute(string(g:netrw_decompress),"^'\\(.*\\)'$",'\1',''))
- call setline(fnameescline, "let g:netrw_fname_escape = '".escape(g:netrw_fname_escape,"'")."'")
- call setline(globescline, "let g:netrw_glob_escape = '".escape(g:netrw_glob_escape,"'")."'")
- call setline(tmpfileescline,"let g:netrw_tmpfile_escape = '".escape(g:netrw_tmpfile_escape,"'")."'")
-
- set nomod
-
- nmap <buffer> <silent> <F1> :call NetrwSettingHelp()<cr>
- nnoremap <buffer> <silent> <leftmouse> <leftmouse>:call NetrwSettingHelp()<cr>
- let tmpfile= tempname()
- exe 'au BufWriteCmd Netrw\ Settings silent w! '.tmpfile.'|so '.tmpfile.'|call delete("'.tmpfile.'")|set nomod'
-endfun
-
-" ---------------------------------------------------------------------
-" NetrwSettingHelp: {{{2
-fun! NetrwSettingHelp()
-" call Dfunc("NetrwSettingHelp()")
- let curline = getline(".")
- if curline =~ '='
- let varhelp = substitute(curline,'^\s*let ','','e')
- let varhelp = substitute(varhelp,'\s*=.*$','','e')
-" call Decho("trying help ".varhelp)
- try
- exe "he ".varhelp
- catch /^Vim\%((\a\+)\)\=:E149/
- echo "***sorry*** no help available for <".varhelp.">"
- endtry
- elseif line(".") < s:netrw_settings_stop
- he netrw-settings
- elseif line(".") < s:netrw_protocol_stop
- he netrw-externapp
- elseif line(".") < s:netrw_xfer_stop
- he netrw-variables
- else
- he netrw-browse-var
- endif
-" call Dret("NetrwSettingHelp")
-endfun
-
-" ---------------------------------------------------------------------
-" Modelines: {{{1
-" vim:ts=8 fdm=marker
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 58d3d4550f..0bfd82f61d 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -158,7 +158,7 @@ function! provider#clipboard#Executable() abort
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'termux-clipboard'
- elseif !empty($TMUX) && executable('tmux')
+ elseif executable('tmux') && (!empty($TMUX) || 0 == jobwait([jobstart(['tmux', 'list-buffers'])], 2000)[0])
let tmux_v = v:lua.vim.version.parse(system(['tmux', '-V']))
if !empty(tmux_v) && !v:lua.vim.version.lt(tmux_v, [3,2,0])
let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-']
@@ -169,6 +169,14 @@ function! provider#clipboard#Executable() abort
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
return 'tmux'
+ elseif get(get(g:, 'termfeatures', {}), 'osc52') && &clipboard ==# ''
+ " Don't use OSC 52 when 'clipboard' is set. It can be slow and cause a lot
+ " of user prompts. Users can opt-in to it by setting g:clipboard manually.
+ let s:copy['+'] = v:lua.require'vim.ui.clipboard.osc52'.copy('+')
+ let s:copy['*'] = v:lua.require'vim.ui.clipboard.osc52'.copy('*')
+ let s:paste['+'] = v:lua.require'vim.ui.clipboard.osc52'.paste('+')
+ let s:paste['*'] = v:lua.require'vim.ui.clipboard.osc52'.paste('*')
+ return 'OSC 52'
endif
let s:err = 'clipboard: No clipboard tool. :help clipboard'
diff --git a/runtime/autoload/spotbugs.vim b/runtime/autoload/spotbugs.vim
new file mode 100644
index 0000000000..6fd822d68e
--- /dev/null
+++ b/runtime/autoload/spotbugs.vim
@@ -0,0 +1,354 @@
+" Default pre- and post-compiler actions and commands for SpotBugs
+" Maintainers: @konfekt and @zzzyxwvut
+" Last Change: 2024 Dec 08
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+" Look for the setting of "g:spotbugs#state" in "ftplugin/java.vim".
+let s:state = get(g:, 'spotbugs#state', {})
+let s:commands = get(s:state, 'commands', {})
+let s:compiler = get(s:state, 'compiler', '')
+let s:readable = filereadable($VIMRUNTIME . '/compiler/' . s:compiler . '.vim')
+
+if has_key(s:commands, 'DefaultPreCompilerCommand')
+ let g:SpotBugsPreCompilerCommand = s:commands.DefaultPreCompilerCommand
+else
+
+ function! s:DefaultPreCompilerCommand(arguments) abort
+ execute 'make ' . a:arguments
+ cc
+ endfunction
+
+ let g:SpotBugsPreCompilerCommand = function('s:DefaultPreCompilerCommand')
+endif
+
+if has_key(s:commands, 'DefaultPreCompilerTestCommand')
+ let g:SpotBugsPreCompilerTestCommand = s:commands.DefaultPreCompilerTestCommand
+else
+
+ function! s:DefaultPreCompilerTestCommand(arguments) abort
+ execute 'make ' . a:arguments
+ cc
+ endfunction
+
+ let g:SpotBugsPreCompilerTestCommand = function('s:DefaultPreCompilerTestCommand')
+endif
+
+if has_key(s:commands, 'DefaultPostCompilerCommand')
+ let g:SpotBugsPostCompilerCommand = s:commands.DefaultPostCompilerCommand
+else
+
+ function! s:DefaultPostCompilerCommand(arguments) abort
+ execute 'make ' . a:arguments
+ endfunction
+
+ let g:SpotBugsPostCompilerCommand = function('s:DefaultPostCompilerCommand')
+endif
+
+if v:version > 900 || has('nvim')
+
+ function! spotbugs#DeleteClassFiles() abort
+ if !exists('b:spotbugs_class_files')
+ return
+ endif
+
+ for pathname in b:spotbugs_class_files
+ let classname = pathname =~# "^'.\\+\\.class'$"
+ \ ? eval(pathname)
+ \ : pathname
+
+ if classname =~# '\.class$' && filereadable(classname)
+ " Since v9.0.0795.
+ let octad = readblob(classname, 0, 8)
+
+ " Test the magic number and the major version number (45 for v1.0).
+ " Since v9.0.2027.
+ if len(octad) == 8 && octad[0 : 3] == 0zcafe.babe &&
+ " Nvim: no << operator
+ "\ or((octad[6] << 8), octad[7]) >= 45
+ \ or((octad[6] * 256), octad[7]) >= 45
+ echomsg printf('Deleting %s: %d', classname, delete(classname))
+ endif
+ endif
+ endfor
+
+ let b:spotbugs_class_files = []
+ endfunction
+
+else
+
+ function! s:DeleteClassFilesWithNewLineCodes(classname) abort
+ " The distribution of "0a"s in class file versions 2560 and 2570:
+ "
+ " 0zca.fe.ba.be.00.00.0a.00 0zca.fe.ba.be.00.00.0a.0a
+ " 0zca.fe.ba.be.00.0a.0a.00 0zca.fe.ba.be.00.0a.0a.0a
+ " 0zca.fe.ba.be.0a.00.0a.00 0zca.fe.ba.be.0a.00.0a.0a
+ " 0zca.fe.ba.be.0a.0a.0a.00 0zca.fe.ba.be.0a.0a.0a.0a
+ let numbers = [0, 0, 0, 0, 0, 0, 0, 0]
+ let offset = 0
+ let lines = readfile(a:classname, 'b', 4)
+
+ " Track NL byte counts to handle files of less than 8 bytes.
+ let nl_cnt = len(lines)
+ " Track non-NL byte counts for "0zca.fe.ba.be.0a.0a.0a.0a".
+ let non_nl_cnt = 0
+
+ for line in lines
+ for idx in range(strlen(line))
+ " Remap NLs to Nuls.
+ let numbers[offset] = (line[idx] == "\n") ? 0 : char2nr(line[idx]) % 256
+ let non_nl_cnt += 1
+ let offset += 1
+
+ if offset > 7
+ break
+ endif
+ endfor
+
+ let nl_cnt -= 1
+
+ if offset > 7 || (nl_cnt < 1 && non_nl_cnt > 4)
+ break
+ endif
+
+ " Reclaim NLs.
+ let numbers[offset] = 10
+ let offset += 1
+
+ if offset > 7
+ break
+ endif
+ endfor
+
+ " Test the magic number and the major version number (45 for v1.0).
+ if offset > 7 && numbers[0] == 0xca && numbers[1] == 0xfe &&
+ \ numbers[2] == 0xba && numbers[3] == 0xbe &&
+ \ (numbers[6] * 256 + numbers[7]) >= 45
+ echomsg printf('Deleting %s: %d', a:classname, delete(a:classname))
+ endif
+ endfunction
+
+ function! spotbugs#DeleteClassFiles() abort
+ if !exists('b:spotbugs_class_files')
+ return
+ endif
+
+ let encoding = &encoding
+
+ try
+ set encoding=latin1
+
+ for pathname in b:spotbugs_class_files
+ let classname = pathname =~# "^'.\\+\\.class'$"
+ \ ? eval(pathname)
+ \ : pathname
+
+ if classname =~# '\.class$' && filereadable(classname)
+ let line = get(readfile(classname, 'b', 1), 0, '')
+ let length = strlen(line)
+
+ " Test the magic number and the major version number (45 for v1.0).
+ if length > 3 && line[0 : 3] == "\xca\xfe\xba\xbe"
+ if length > 7 && ((line[6] == "\n" ? 0 : char2nr(line[6]) % 256) * 256 +
+ \ (line[7] == "\n" ? 0 : char2nr(line[7]) % 256)) >= 45
+ echomsg printf('Deleting %s: %d', classname, delete(classname))
+ else
+ call s:DeleteClassFilesWithNewLineCodes(classname)
+ endif
+ endif
+ endif
+ endfor
+ finally
+ let &encoding = encoding
+ endtry
+
+ let b:spotbugs_class_files = []
+ endfunction
+
+endif
+
+function! spotbugs#DefaultPostCompilerAction() abort
+ " Since v7.4.191.
+ call call(g:SpotBugsPostCompilerCommand, ['%:S'])
+endfunction
+
+if s:readable && s:compiler ==# 'maven' && executable('mvn')
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ call call(g:SpotBugsPreCompilerCommand, ['compile'])
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ call call(g:SpotBugsPreCompilerTestCommand, ['test-compile'])
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ 'sourceDirPath': ['src/main/java'],
+ \ 'classDirPath': ['target/classes'],
+ \ 'testSourceDirPath': ['src/test/java'],
+ \ 'testClassDirPath': ['target/test-classes'],
+ \ }
+ endfunction
+
+ unlet s:readable s:compiler
+elseif s:readable && s:compiler ==# 'ant' && executable('ant')
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler ant
+ call call(g:SpotBugsPreCompilerCommand, ['compile'])
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler ant
+ call call(g:SpotBugsPreCompilerTestCommand, ['compile-test'])
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ 'sourceDirPath': ['src'],
+ \ 'classDirPath': ['build/classes'],
+ \ 'testSourceDirPath': ['test'],
+ \ 'testClassDirPath': ['build/test/classes'],
+ \ }
+ endfunction
+
+ unlet s:readable s:compiler
+elseif s:readable && s:compiler ==# 'javac' && executable('javac')
+ let s:filename = tempname()
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler javac
+
+ if get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')) =~ '\s@\S'
+ " Only read options and filenames from @options [@sources ...] and do
+ " not update these files when filelists change.
+ call call(g:SpotBugsPreCompilerCommand, [''])
+ else
+ " Collect filenames so that Javac can figure out what to compile.
+ let filelist = []
+
+ for arg_num in range(argc(-1))
+ let arg_name = argv(arg_num)
+
+ if arg_name =~# '\.java\=$'
+ call add(filelist, fnamemodify(arg_name, ':p:S'))
+ endif
+ endfor
+
+ for buf_num in range(1, bufnr('$'))
+ if !buflisted(buf_num)
+ continue
+ endif
+
+ let buf_name = bufname(buf_num)
+
+ if buf_name =~# '\.java\=$'
+ let buf_name = fnamemodify(buf_name, ':p:S')
+
+ if index(filelist, buf_name) < 0
+ call add(filelist, buf_name)
+ endif
+ endif
+ endfor
+
+ noautocmd call writefile(filelist, s:filename)
+ call call(g:SpotBugsPreCompilerCommand, [shellescape('@' . s:filename)])
+ endif
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DefaultPreCompilerAction()
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ }
+ endfunction
+
+ unlet s:readable s:compiler g:SpotBugsPreCompilerTestCommand
+ delfunction! s:DefaultPreCompilerTestCommand
+else
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ echomsg printf('Not supported: "%s"', s:compiler)
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DefaultPreCompilerAction()
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {}
+ endfunction
+
+ " XXX: Keep "s:compiler" around for "spotbugs#DefaultPreCompilerAction()",
+ " "s:DefaultPostCompilerCommand" -- "spotbugs#DefaultPostCompilerAction()".
+ unlet s:readable g:SpotBugsPreCompilerCommand g:SpotBugsPreCompilerTestCommand
+ delfunction! s:DefaultPreCompilerCommand
+ delfunction! s:DefaultPreCompilerTestCommand
+endif
+
+function! s:DefineBufferAutocmd(event, ...) abort
+ if !exists('#java_spotbugs#User')
+ return 1
+ endif
+
+ for l:event in insert(copy(a:000), a:event)
+ if l:event != 'User'
+ execute printf('silent! autocmd! java_spotbugs %s <buffer>', l:event)
+ execute printf('autocmd java_spotbugs %s <buffer> doautocmd User', l:event)
+ endif
+ endfor
+
+ return 0
+endfunction
+
+function! s:RemoveBufferAutocmd(event, ...) abort
+ if !exists('#java_spotbugs')
+ return 1
+ endif
+
+ for l:event in insert(copy(a:000), a:event)
+ if l:event != 'User'
+ execute printf('silent! autocmd! java_spotbugs %s <buffer>', l:event)
+ endif
+ endfor
+
+ return 0
+endfunction
+
+" Documented in ":help compiler-spotbugs".
+command! -bar -nargs=+ -complete=event SpotBugsDefineBufferAutocmd
+ \ call s:DefineBufferAutocmd(<f-args>)
+command! -bar -nargs=+ -complete=event SpotBugsRemoveBufferAutocmd
+ \ call s:RemoveBufferAutocmd(<f-args>)
+
+let &cpo = s:save_cpo
+unlet s:commands s:state s:save_cpo
+
+" vim: set foldmethod=syntax shiftwidth=2 expandtab:
diff --git a/runtime/autoload/typst.vim b/runtime/autoload/typst.vim
index 6d097dd922..362da3f45e 100644
--- a/runtime/autoload/typst.vim
+++ b/runtime/autoload/typst.vim
@@ -1,6 +1,7 @@
" Language: Typst
-" Maintainer: Gregory Anders
-" Last Change: 2024 Nov 02
+" Previous Maintainer: Gregory Anders
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Last Change: 2024 Dec 09
" Based on: https://github.com/kaarmu/typst.vim
function! typst#indentexpr() abort
diff --git a/runtime/colors/blue.vim b/runtime/colors/blue.vim
index 9ee878e103..7a17fc38b7 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -82,6 +82,7 @@ hi Type guifg=#ffa500 guibg=NONE gui=bold cterm=NONE
hi Underlined guifg=NONE guibg=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline
hi Label guifg=#ffd700 guibg=NONE gui=NONE cterm=NONE
hi! link Terminal Normal
+hi! link PopupSelected PmenuSel
hi! link Debug Special
hi! link Added String
hi! link Removed WarningMsg
@@ -194,6 +195,7 @@ if s:t_Co >= 256
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Label ctermfg=220 ctermbg=NONE cterm=NONE
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link Debug Special
hi! link Added String
hi! link Removed WarningMsg
@@ -309,6 +311,7 @@ if s:t_Co >= 16
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Label ctermfg=yellow ctermbg=NONE cterm=NONE
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link Debug Special
hi! link Added String
hi! link Removed WarningMsg
@@ -423,6 +426,7 @@ if s:t_Co >= 8
hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline
hi Label ctermfg=yellow ctermbg=NONE cterm=NONE
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link Debug Special
hi! link Added String
hi! link Removed WarningMsg
diff --git a/runtime/colors/darkblue.vim b/runtime/colors/darkblue.vim
index 9451e397e5..90dc304791 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -24,6 +24,7 @@ if (has('termguicolors') && &termguicolors) || has('gui_running')
endfor
endif
hi! link Terminal Normal
+hi! link PopupSelected PmenuSel
hi! link CursorColumn CursorLine
hi! link CursorIM Cursor
hi! link EndOfBuffer NonText
@@ -134,6 +135,7 @@ hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
if s:t_Co >= 256
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link CursorColumn CursorLine
hi! link CursorIM Cursor
hi! link EndOfBuffer NonText
diff --git a/runtime/colors/delek.vim b/runtime/colors/delek.vim
index 35d4934f5c..d29980d685 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -24,6 +24,7 @@ if (has('termguicolors') && &termguicolors) || has('gui_running')
endfor
endif
hi! link Terminal Normal
+hi! link PopupSelected PmenuSel
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link CurSearch Search
@@ -100,6 +101,7 @@ hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
if s:t_Co >= 256
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link CurSearch Search
diff --git a/runtime/colors/desert.vim b/runtime/colors/desert.vim
index 07ef937ab6..b5f404a14e 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -32,6 +32,7 @@ hi! link CursorLineSign CursorLine
hi! link EndOfBuffer NonText
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -108,6 +109,7 @@ if s:t_Co >= 256
hi! link EndOfBuffer NonText
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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/evening.vim b/runtime/colors/evening.vim
index d5e081337a..2f6859ad10 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -24,6 +24,7 @@ if (has('termguicolors') && &termguicolors) || has('gui_running')
endfor
endif
hi! link VertSplit StatusLineNC
+hi! link PopupSelected PmenuSel
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link TabLineFill TabLine
@@ -134,6 +135,7 @@ hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE
if s:t_Co >= 256
hi! link VertSplit StatusLineNC
+ hi! link PopupSelected PmenuSel
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link TabLineFill TabLine
@@ -247,6 +249,7 @@ endif
if s:t_Co >= 16
hi! link VertSplit StatusLineNC
+ hi! link PopupSelected PmenuSel
hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link TabLineFill TabLine
diff --git a/runtime/colors/habamax.vim b/runtime/colors/habamax.vim
index 4b4c95e050..e4113d80bb 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -27,6 +27,7 @@ hi! link Terminal Normal
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
hi! link MessageWindow Pmenu
+hi! link PopupSelected PmenuSel
hi! link javaScriptFunction Statement
hi! link javaScriptIdentifier Statement
hi! link sqlKeyword Statement
@@ -122,6 +123,7 @@ if s:t_Co >= 256
hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
hi! link MessageWindow Pmenu
+ hi! link PopupSelected PmenuSel
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 6e66a4a791..edb86188a8 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -86,6 +86,7 @@ hi Conceal guifg=#6c6c6c guibg=NONE gui=NONE cterm=NONE
hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold
hi! link Terminal Normal
+hi! link PopupSelected PmenuSel
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link CurSearch Search
@@ -162,6 +163,7 @@ if s:t_Co >= 256
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi Title ctermfg=201 ctermbg=NONE cterm=bold
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link CurSearch Search
@@ -241,6 +243,7 @@ if s:t_Co >= 16
hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
hi Title ctermfg=magenta ctermbg=NONE cterm=bold
hi! link Terminal Normal
+ hi! link PopupSelected PmenuSel
hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link CurSearch Search
diff --git a/runtime/colors/lunaperche.vim b/runtime/colors/lunaperche.vim
index 75d25e2a23..464b7af00e 100644
--- a/runtime/colors/lunaperche.vim
+++ b/runtime/colors/lunaperche.vim
@@ -4,7 +4,7 @@
" Maintainer: Maxim Kim <habamax@gmail.com>
" Website: https://www.github.com/vim/colorschemes
" License: Vim License (see `:help license`)
-" Last Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -93,6 +93,7 @@ hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link MessageWindow PMenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
if &background ==# 'dark'
if (has('termguicolors') && &termguicolors) || has('gui_running')
let g:terminal_ansi_colors = ['#000000', '#af5f5f', '#5faf5f', '#af875f', '#5f87af', '#d787d7', '#5fafaf', '#c6c6c6', '#767676', '#ff5f5f', '#5fd75f', '#ffd787', '#5fafff', '#ff87ff', '#5fd7d7', '#ffffff']
@@ -369,6 +370,7 @@ if s:t_Co >= 256
hi! link LineNrBelow LineNr
hi! link MessageWindow PMenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
if &background ==# 'dark'
hi Normal ctermfg=251 ctermbg=16 cterm=NONE
hi Statusline ctermfg=251 ctermbg=16 cterm=bold,reverse
diff --git a/runtime/colors/morning.vim b/runtime/colors/morning.vim
index 82a3d6d97d..463d009e88 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -33,6 +33,7 @@ hi! link StatuslineTerm Statusline
hi! link StatuslineTermNC StatuslineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -107,6 +108,7 @@ if s:t_Co >= 256
hi! link StatuslineTermNC StatuslineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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 f38c8259dd..f00bdbd608 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -33,6 +33,7 @@ hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
hi! link Added Constant
hi Normal guifg=#87ff87 guibg=#000000 gui=NONE cterm=NONE
hi EndOfBuffer guifg=#0000ff guibg=#000000 gui=NONE cterm=NONE
@@ -108,6 +109,7 @@ if s:t_Co >= 256
hi! link StatusLineTermNC StatusLineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
hi! link Added Constant
hi Normal ctermfg=120 ctermbg=16 cterm=NONE
hi EndOfBuffer ctermfg=21 ctermbg=16 cterm=NONE
diff --git a/runtime/colors/pablo.vim b/runtime/colors/pablo.vim
index de585adfe2..202f505136 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -30,6 +30,7 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -105,6 +106,7 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
hi Normal ctermfg=231 ctermbg=16 cterm=NONE
hi Comment ctermfg=244 ctermbg=NONE cterm=NONE
hi Constant ctermfg=51 ctermbg=NONE cterm=NONE
diff --git a/runtime/colors/peachpuff.vim b/runtime/colors/peachpuff.vim
index 91c98119b3..508346a7ce 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -31,6 +31,7 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -105,6 +106,7 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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 bcf2eced16..38349d76e8 100644
--- a/runtime/colors/quiet.vim
+++ b/runtime/colors/quiet.vim
@@ -4,7 +4,7 @@
" Maintainer: Maxence Weynans <neutaaaaan@gmail.com>
" Website: https://github.com/vim/colorschemes
" License: Vim License (see `:help license`)`
-" Last Change: 2024 Aug 05
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -22,6 +22,7 @@ hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
diff --git a/runtime/colors/retrobox.vim b/runtime/colors/retrobox.vim
index a89bf0557e..f34fc99dc5 100644
--- a/runtime/colors/retrobox.vim
+++ b/runtime/colors/retrobox.vim
@@ -4,7 +4,7 @@
" Maintainer: Maxim Kim <habamax@gmail.com>, ported from gruvbox8 of Lifepillar <lifepillar@lifepillar.me>
" Website: https://www.github.com/vim/colorschemes
" License: Vim License (see `:help license`)
-" Last Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -22,6 +22,7 @@ hi! link Tag Special
hi! link lCursor Cursor
hi! link MessageWindow PMenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
hi! link CurSearch IncSearch
hi! link Terminal Normal
diff --git a/runtime/colors/shine.vim b/runtime/colors/shine.vim
index f3697c9ad6..39e6dae956 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -35,6 +35,7 @@ hi! link Tag Special
hi! link Operator Statement
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -115,6 +116,7 @@ if s:t_Co >= 256
hi! link Operator Statement
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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 c9ce78946b..319ab9e0aa 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -31,6 +31,7 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -110,6 +111,7 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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/sorbet.vim b/runtime/colors/sorbet.vim
index bd4fb7baf7..25c27bf635 100644
--- a/runtime/colors/sorbet.vim
+++ b/runtime/colors/sorbet.vim
@@ -4,7 +4,7 @@
" Maintainer: Maxence Weynans <neutaaaaan@gmail.com>
" Website: https://github.com/vim/colorschemes
" License: Vim License (see `:help license`)`
-" Last Change: 2024 Aug 05
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -21,6 +21,7 @@ hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
diff --git a/runtime/colors/torte.vim b/runtime/colors/torte.vim
index 7271188f0d..0709263f7c 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -33,6 +33,7 @@ hi! link StatusLineTerm StatusLine
hi! link StatusLineTermNC StatusLineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -108,6 +109,7 @@ if s:t_Co >= 256
hi! link StatusLineTermNC StatusLineNC
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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/unokai.vim b/runtime/colors/unokai.vim
new file mode 100644
index 0000000000..168eda1483
--- /dev/null
+++ b/runtime/colors/unokai.vim
@@ -0,0 +1,522 @@
+" Name: unokai
+" Description: Color scheme similar to Monokai originally created by Wimer Hazenberg for TextMate
+" Author: k-37 <60838818+k-37@users.noreply.github.com>
+" Maintainer: k-37 <60838818+k-37@users.noreply.github.com>
+" Website: https://github.com/vim/colorschemes
+" License: Vim License (see `:help license`)
+" Last Change: 2024 Dec 15
+
+" Generated by Colortemplate v2.2.3
+
+set background=dark
+
+" hi clear
+source $VIMRUNTIME/colors/vim.lua " Nvim: revert to Vim default color scheme
+let g:colors_name = 'unokai'
+
+let s:t_Co = &t_Co
+
+if (has('termguicolors') && &termguicolors) || has('gui_running')
+ let g:terminal_ansi_colors = ['#282923', '#c61e5c', '#81af24', '#fd971f', '#51aebe', '#ae81ff', '#80beb5', '#bababa', '#74705d', '#f92672', '#a6e22e', '#e6db74', '#66d9ef', '#fd5ff0', '#a1efe4', '#f8f8f2']
+ " 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 CursorLineFold FoldColumn
+hi! link CursorLineSign SignColumn
+hi! link MessageWindow Pmenu
+hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
+hi! link StatusLineTerm StatusLine
+hi! link StatusLineTermNC StatusLineNC
+hi! link Terminal Normal
+hi! link Delimiter PreProc
+hi! link Operator PreProc
+hi! link StorageClass PreProc
+hi! link Structure PreProc
+hi! link Define Identifier
+hi! link Label String
+hi! link markdownCode Comment
+hi! link markdownCodeBlock markdownCode
+hi! link markdownCodeDelimiter markdownCode
+hi Normal guifg=#f8f8f2 guibg=#282923 gui=NONE cterm=NONE
+hi StatusLine guifg=#282923 guibg=#bababa gui=NONE cterm=NONE
+hi StatusLineNC guifg=#282923 guibg=#74705d gui=NONE cterm=NONE
+hi VertSplit guifg=#74705d guibg=#74705d gui=NONE cterm=NONE
+hi TabLine guifg=#282923 guibg=#74705d gui=NONE cterm=NONE
+hi TabLineFill guifg=#282923 guibg=#74705d gui=NONE cterm=NONE
+hi TabLineSel guifg=#282923 guibg=#bababa gui=bold cterm=bold
+hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi ToolbarButton guifg=#74705d guibg=#f8f8f2 gui=bold,reverse cterm=bold,reverse
+hi QuickFixLine guifg=#282923 guibg=#51aebe gui=NONE cterm=NONE
+hi CursorLineNr guifg=#dadada guibg=NONE gui=bold cterm=bold
+hi LineNr guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi LineNrAbove guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi LineNrBelow guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi NonText guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi EndOfBuffer guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi SpecialKey guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi FoldColumn guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi Visual guifg=#a1efe4 guibg=#282923 gui=reverse cterm=reverse
+hi VisualNOS guifg=#282923 guibg=#80beb5 gui=NONE cterm=NONE
+hi Pmenu guifg=NONE guibg=#585858 gui=NONE cterm=NONE
+hi PmenuThumb guifg=NONE guibg=#74705d gui=NONE cterm=NONE
+hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi PmenuSel guifg=NONE guibg=#8a8a8a gui=NONE cterm=NONE
+hi PmenuKind guifg=#80beb5 guibg=#585858 gui=NONE cterm=NONE
+hi PmenuKindSel guifg=#80beb5 guibg=#8a8a8a gui=NONE cterm=NONE
+hi PmenuExtra guifg=#bababa guibg=#585858 gui=NONE cterm=NONE
+hi PmenuExtraSel guifg=#bababa guibg=#8a8a8a gui=NONE cterm=NONE
+hi PmenuMatch guifg=#ffaf5f guibg=#585858 gui=NONE cterm=NONE
+hi PmenuMatchSel guifg=#ffaf5f guibg=#8a8a8a gui=NONE cterm=NONE
+hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Error guifg=#f92672 guibg=#000000 gui=reverse cterm=reverse
+hi ErrorMsg guifg=#f92672 guibg=#000000 gui=reverse cterm=reverse
+hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
+hi MoreMsg guifg=#81af24 guibg=NONE gui=NONE cterm=NONE
+hi Question guifg=#e6db74 guibg=NONE gui=NONE cterm=NONE
+hi WarningMsg guifg=#f92672 guibg=NONE gui=NONE cterm=NONE
+hi Todo guifg=#dadada guibg=NONE gui=bold cterm=bold
+hi MatchParen guifg=#fd971f guibg=NONE gui=bold cterm=bold
+hi Search guifg=#66d9ef guibg=#282923 gui=reverse cterm=reverse
+hi IncSearch guifg=#ffaf5f guibg=#282923 gui=reverse cterm=reverse
+hi CurSearch guifg=#ffaf5f guibg=#282923 gui=reverse cterm=reverse
+hi WildMenu guifg=#282923 guibg=#e6db74 gui=bold cterm=bold
+hi debugPC guifg=#282923 guibg=#51aebe gui=NONE cterm=NONE
+hi debugBreakpoint guifg=#282923 guibg=#f92672 gui=NONE cterm=NONE
+hi Cursor guifg=#000000 guibg=#dadada gui=NONE cterm=NONE
+hi lCursor guifg=#282923 guibg=#5fff00 gui=NONE cterm=NONE
+hi CursorLine guifg=NONE guibg=#3a392f gui=NONE cterm=NONE
+hi CursorColumn guifg=NONE guibg=#3a392f gui=NONE cterm=NONE
+hi Folded guifg=#bababa guibg=#414141 gui=NONE cterm=NONE
+hi ColorColumn guifg=NONE guibg=#585858 gui=NONE cterm=NONE
+hi SpellBad guifg=NONE guibg=NONE guisp=#d75f5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline
+hi SpellCap guifg=NONE guibg=NONE guisp=#ffaf5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline
+hi SpellLocal guifg=NONE guibg=NONE guisp=#5fd75f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline
+hi SpellRare guifg=NONE guibg=NONE guisp=#fd5ff0 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline
+hi Constant guifg=#ae81ff guibg=NONE gui=NONE cterm=NONE
+hi Type guifg=#fd971f guibg=NONE gui=bold cterm=bold
+hi Character guifg=#a6e22e guibg=NONE gui=NONE cterm=NONE
+hi Comment guifg=#74705d guibg=NONE gui=NONE cterm=NONE
+hi String guifg=#e6db74 guibg=NONE gui=NONE cterm=NONE
+hi Function guifg=#a6e22e guibg=NONE gui=NONE cterm=NONE
+hi Identifier guifg=#66d9ef guibg=NONE gui=NONE cterm=NONE
+hi PreProc guifg=#f92672 guibg=NONE gui=NONE cterm=NONE
+hi Special guifg=#80beb5 guibg=NONE gui=NONE cterm=NONE
+hi Statement guifg=#f92672 guibg=NONE gui=bold cterm=bold
+hi Underlined guifg=#66d9ef guibg=NONE gui=underline cterm=underline
+hi Title guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold
+hi Debug guifg=#80beb5 guibg=NONE gui=NONE cterm=NONE
+hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE
+hi Directory guifg=#a1efe4 guibg=NONE gui=bold cterm=bold
+hi Conceal guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE
+hi DiffAdd guifg=#5faf5f guibg=NONE gui=reverse cterm=reverse
+hi DiffChange guifg=#5f87af guibg=NONE gui=reverse cterm=reverse
+hi DiffText guifg=#af87af guibg=NONE gui=reverse cterm=reverse
+hi DiffDelete guifg=#af5f5f guibg=NONE gui=reverse cterm=reverse
+hi Added guifg=#5fd75f guibg=NONE gui=NONE cterm=NONE
+hi Changed guifg=#ffaf5f guibg=NONE gui=NONE cterm=NONE
+hi Removed guifg=#d75f5f guibg=NONE gui=NONE cterm=NONE
+hi htmlBold guifg=#f8f8f2 guibg=NONE gui=bold cterm=bold
+hi htmlItalic guifg=#f8f8f2 guibg=NONE gui=italic cterm=italic
+hi markdownHeadingDelimiter guifg=#f8f8f2 guibg=NONE gui=NONE cterm=NONE
+hi markdownH1Delimiter guifg=#f92672 guibg=NONE gui=NONE cterm=NONE
+hi markdownH2Delimiter guifg=#e6db74 guibg=NONE gui=NONE cterm=NONE
+hi markdownH4Delimiter guifg=#66d9ef guibg=NONE gui=NONE cterm=NONE
+hi markdownH6Delimiter guifg=#a6e22e guibg=NONE gui=NONE cterm=NONE
+hi markdownH3Delimiter guifg=#fd971f guibg=NONE gui=NONE cterm=NONE
+hi markdownH5Delimiter guifg=#51aebe guibg=NONE gui=NONE cterm=NONE
+
+if s:t_Co >= 256
+ hi! link CursorLineFold FoldColumn
+ hi! link CursorLineSign SignColumn
+ hi! link MessageWindow Pmenu
+ hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
+ hi! link StatusLineTerm StatusLine
+ hi! link StatusLineTermNC StatusLineNC
+ hi! link Terminal Normal
+ hi! link Delimiter PreProc
+ hi! link Operator PreProc
+ hi! link StorageClass PreProc
+ hi! link Structure PreProc
+ hi! link Define Identifier
+ hi! link Label String
+ hi! link markdownCode Comment
+ hi! link markdownCodeBlock markdownCode
+ hi! link markdownCodeDelimiter markdownCode
+ hi Normal ctermfg=255 ctermbg=235 cterm=NONE
+ hi StatusLine ctermfg=235 ctermbg=250 cterm=NONE
+ hi StatusLineNC ctermfg=235 ctermbg=244 cterm=NONE
+ hi VertSplit ctermfg=244 ctermbg=244 cterm=NONE
+ hi TabLine ctermfg=235 ctermbg=244 cterm=NONE
+ hi TabLineFill ctermfg=235 ctermbg=244 cterm=NONE
+ hi TabLineSel ctermfg=235 ctermbg=250 cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=244 ctermbg=255 cterm=bold,reverse
+ hi QuickFixLine ctermfg=235 ctermbg=141 cterm=NONE
+ hi CursorLineNr ctermfg=253 ctermbg=NONE cterm=bold
+ hi LineNr ctermfg=245 ctermbg=NONE cterm=NONE
+ hi LineNrAbove ctermfg=245 ctermbg=NONE cterm=NONE
+ hi LineNrBelow ctermfg=245 ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=245 ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=245 ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=245 ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=245 ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=116 ctermbg=235 cterm=reverse
+ hi VisualNOS ctermfg=235 ctermbg=73 cterm=NONE
+ hi Pmenu ctermfg=NONE ctermbg=240 cterm=NONE
+ hi PmenuThumb ctermfg=NONE ctermbg=244 cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi PmenuSel ctermfg=NONE ctermbg=245 cterm=NONE
+ hi PmenuKind ctermfg=73 ctermbg=240 cterm=NONE
+ hi PmenuKindSel ctermfg=73 ctermbg=245 cterm=NONE
+ hi PmenuExtra ctermfg=250 ctermbg=240 cterm=NONE
+ hi PmenuExtraSel ctermfg=250 ctermbg=245 cterm=NONE
+ hi PmenuMatch ctermfg=215 ctermbg=240 cterm=NONE
+ hi PmenuMatchSel ctermfg=215 ctermbg=245 cterm=NONE
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Error ctermfg=197 ctermbg=16 cterm=reverse
+ hi ErrorMsg ctermfg=197 ctermbg=16 cterm=reverse
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=106 ctermbg=NONE cterm=NONE
+ hi Question ctermfg=185 ctermbg=NONE cterm=NONE
+ hi WarningMsg ctermfg=197 ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=253 ctermbg=NONE cterm=bold
+ hi MatchParen ctermfg=208 ctermbg=NONE cterm=bold
+ hi Search ctermfg=81 ctermbg=235 cterm=reverse
+ hi IncSearch ctermfg=215 ctermbg=235 cterm=reverse
+ hi CurSearch ctermfg=215 ctermbg=235 cterm=reverse
+ hi WildMenu ctermfg=235 ctermbg=185 cterm=bold
+ hi debugPC ctermfg=235 ctermbg=73 cterm=NONE
+ hi debugBreakpoint ctermfg=235 ctermbg=197 cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=237 cterm=NONE
+ hi CursorColumn ctermfg=NONE ctermbg=237 cterm=NONE
+ hi Folded ctermfg=250 ctermbg=238 cterm=NONE
+ hi ColorColumn ctermfg=NONE ctermbg=240 cterm=NONE
+ hi SpellBad ctermfg=167 ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=215 ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=77 ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=207 ctermbg=NONE cterm=underline
+ hi Constant ctermfg=141 ctermbg=NONE cterm=NONE
+ hi Type ctermfg=208 ctermbg=NONE cterm=bold
+ hi Character ctermfg=112 ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=244 ctermbg=NONE cterm=NONE
+ hi String ctermfg=185 ctermbg=NONE cterm=NONE
+ hi Function ctermfg=112 ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=81 ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=197 ctermbg=NONE cterm=NONE
+ hi Special ctermfg=73 ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=197 ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=81 ctermbg=NONE cterm=underline
+ hi Title ctermfg=NONE ctermbg=NONE cterm=bold
+ hi Debug ctermfg=73 ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=116 ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=245 ctermbg=NONE cterm=NONE
+ hi DiffAdd ctermfg=71 ctermbg=NONE cterm=reverse
+ hi DiffChange ctermfg=67 ctermbg=NONE cterm=reverse
+ hi DiffText ctermfg=139 ctermbg=NONE cterm=reverse
+ hi DiffDelete ctermfg=131 ctermbg=NONE cterm=reverse
+ hi Added ctermfg=77 ctermbg=NONE cterm=NONE
+ hi Changed ctermfg=215 ctermbg=NONE cterm=NONE
+ hi Removed ctermfg=167 ctermbg=NONE cterm=NONE
+ hi htmlBold ctermfg=255 ctermbg=NONE cterm=bold
+ hi htmlItalic ctermfg=255 ctermbg=NONE cterm=underline
+ hi markdownHeadingDelimiter ctermfg=255 ctermbg=NONE cterm=NONE
+ hi markdownH1Delimiter ctermfg=197 ctermbg=NONE cterm=NONE
+ hi markdownH2Delimiter ctermfg=185 ctermbg=NONE cterm=NONE
+ hi markdownH4Delimiter ctermfg=81 ctermbg=NONE cterm=NONE
+ hi markdownH6Delimiter ctermfg=112 ctermbg=NONE cterm=NONE
+ hi markdownH3Delimiter ctermfg=208 ctermbg=NONE cterm=NONE
+ hi markdownH5Delimiter ctermfg=73 ctermbg=NONE cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 16
+ hi Normal ctermfg=white ctermbg=black cterm=NONE
+ hi StatusLine ctermfg=black ctermbg=gray cterm=NONE
+ hi StatusLineNC ctermfg=black ctermbg=darkgray cterm=NONE
+ hi VertSplit ctermfg=darkgray ctermbg=darkgray cterm=NONE
+ hi TabLine ctermfg=black ctermbg=darkgray cterm=NONE
+ hi TabLineFill ctermfg=black ctermbg=darkgray cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=gray cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=darkgray ctermbg=white cterm=bold,reverse
+ hi QuickFixLine ctermfg=black ctermbg=darkmagenta cterm=NONE
+ hi CursorLineNr ctermfg=white ctermbg=NONE cterm=bold
+ hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi LineNrAbove ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi LineNrBelow ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi NonText ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi FoldColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi Visual ctermfg=cyan ctermbg=black cterm=reverse
+ hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=gray cterm=NONE
+ hi PmenuThumb ctermfg=gray ctermbg=black cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=gray cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuKind ctermfg=darkred ctermbg=gray cterm=NONE
+ hi PmenuKindSel ctermfg=darkred ctermbg=darkyellow cterm=NONE
+ hi PmenuExtra ctermfg=darkgray ctermbg=gray cterm=NONE
+ hi PmenuExtraSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuMatch ctermfg=black ctermbg=gray cterm=bold
+ hi PmenuMatchSel ctermfg=black ctermbg=darkyellow cterm=bold
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Error ctermfg=red ctermbg=black cterm=reverse
+ hi ErrorMsg ctermfg=red ctermbg=black cterm=reverse
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Question ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=white ctermbg=NONE cterm=bold
+ hi MatchParen ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Search ctermfg=blue ctermbg=black cterm=reverse
+ hi IncSearch ctermfg=red ctermbg=black cterm=reverse
+ hi CurSearch ctermfg=red ctermbg=black cterm=reverse
+ hi WildMenu ctermfg=black ctermbg=yellow cterm=bold
+ hi debugPC ctermfg=black ctermbg=darkblue cterm=NONE
+ hi debugBreakpoint ctermfg=black ctermbg=red cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Folded ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=NONE cterm=underline
+ hi SpellCap ctermfg=darkyellow ctermbg=NONE cterm=underline
+ hi SpellLocal ctermfg=darkgreen ctermbg=NONE cterm=underline
+ hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Character ctermfg=green ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=darkgray ctermbg=NONE cterm=NONE
+ hi String ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi Function ctermfg=green ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=blue ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=red ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Statement ctermfg=red ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=blue ctermbg=NONE cterm=underline
+ hi Title ctermfg=NONE ctermbg=NONE cterm=bold
+ hi Debug ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=cyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=darkgrey ctermbg=NONE cterm=NONE
+ hi DiffAdd ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi DiffChange ctermfg=darkblue ctermbg=NONE cterm=reverse
+ hi DiffText ctermfg=darkmagenta ctermbg=NONE cterm=reverse
+ hi DiffDelete ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi Added ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Changed ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Removed ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi htmlBold ctermfg=white ctermbg=NONE cterm=bold
+ hi htmlItalic ctermfg=white ctermbg=NONE cterm=underline
+ hi markdownHeadingDelimiter ctermfg=white ctermbg=NONE cterm=NONE
+ hi markdownH1Delimiter ctermfg=red ctermbg=NONE cterm=NONE
+ hi markdownH2Delimiter ctermfg=yellow ctermbg=NONE cterm=NONE
+ hi markdownH4Delimiter ctermfg=blue ctermbg=NONE cterm=NONE
+ hi markdownH6Delimiter ctermfg=green ctermbg=NONE cterm=NONE
+ hi markdownH3Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi markdownH5Delimiter ctermfg=darkblue ctermbg=NONE cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 8
+ hi Normal ctermfg=gray ctermbg=black cterm=NONE
+ hi StatusLine ctermfg=gray ctermbg=black cterm=bold,reverse
+ hi StatusLineNC ctermfg=gray ctermbg=black cterm=reverse
+ hi VertSplit ctermfg=gray ctermbg=gray cterm=NONE
+ hi TabLine ctermfg=black ctermbg=gray cterm=NONE
+ hi TabLineFill ctermfg=gray ctermbg=gray cterm=NONE
+ hi TabLineSel ctermfg=black ctermbg=gray cterm=bold
+ hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi ToolbarButton ctermfg=gray ctermbg=black cterm=reverse
+ hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi LineNr ctermfg=gray ctermbg=NONE cterm=bold
+ hi LineNrAbove ctermfg=gray ctermbg=NONE cterm=bold
+ hi LineNrBelow ctermfg=gray ctermbg=NONE cterm=bold
+ hi NonText ctermfg=gray ctermbg=NONE cterm=bold
+ hi EndOfBuffer ctermfg=gray ctermbg=NONE cterm=bold
+ hi SpecialKey ctermfg=gray ctermbg=NONE cterm=bold
+ hi FoldColumn ctermfg=gray ctermbg=NONE cterm=bold
+ hi Visual ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi Pmenu ctermfg=black ctermbg=gray cterm=NONE
+ hi PmenuThumb ctermfg=gray ctermbg=black cterm=NONE
+ hi PmenuSbar ctermfg=NONE ctermbg=gray cterm=NONE
+ hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuKind ctermfg=darkred ctermbg=gray cterm=NONE
+ hi PmenuKindSel ctermfg=darkred ctermbg=darkyellow cterm=NONE
+ hi PmenuExtra ctermfg=black ctermbg=gray cterm=NONE
+ hi PmenuExtraSel ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi PmenuMatch ctermfg=black ctermbg=gray cterm=bold
+ hi PmenuMatchSel ctermfg=black ctermbg=darkyellow cterm=bold
+ hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Error ctermfg=darkred ctermbg=gray cterm=bold,reverse
+ hi ErrorMsg ctermfg=darkred ctermbg=gray cterm=bold,reverse
+ hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold
+ hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Question ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Todo ctermfg=gray ctermbg=NONE cterm=bold
+ hi MatchParen ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Search ctermfg=black ctermbg=darkblue cterm=NONE
+ hi IncSearch ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi CurSearch ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi debugPC ctermfg=black ctermbg=darkblue cterm=NONE
+ hi debugBreakpoint ctermfg=black ctermbg=darkcyan cterm=NONE
+ hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline
+ hi CursorColumn ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi Folded ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE
+ hi SpellBad ctermfg=darkred ctermbg=gray cterm=reverse
+ hi SpellCap ctermfg=darkblue ctermbg=gray cterm=reverse
+ hi SpellLocal ctermfg=darkgreen ctermbg=black cterm=reverse
+ hi SpellRare ctermfg=darkmagenta ctermbg=gray cterm=reverse
+ hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE
+ hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Character ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Comment ctermfg=gray ctermbg=NONE cterm=bold
+ hi String ctermfg=darkyellow ctermbg=NONE cterm=bold
+ hi Function ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Identifier ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi PreProc ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi Special ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Statement ctermfg=darkred ctermbg=NONE cterm=bold
+ hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline
+ hi Title ctermfg=NONE ctermbg=NONE cterm=bold
+ hi Debug ctermfg=darkcyan ctermbg=NONE cterm=NONE
+ hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE
+ hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold
+ hi Conceal ctermfg=gray ctermbg=NONE cterm=NONE
+ hi DiffAdd ctermfg=darkgreen ctermbg=NONE cterm=reverse
+ hi DiffChange ctermfg=darkblue ctermbg=NONE cterm=reverse
+ hi DiffText ctermfg=darkmagenta ctermbg=NONE cterm=reverse
+ hi DiffDelete ctermfg=darkred ctermbg=NONE cterm=reverse
+ hi Added ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi Changed ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi Removed ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi htmlBold ctermfg=gray ctermbg=NONE cterm=bold
+ hi htmlItalic ctermfg=gray ctermbg=NONE cterm=underline
+ hi markdownHeadingDelimiter ctermfg=gray ctermbg=NONE cterm=NONE
+ hi markdownH1Delimiter ctermfg=darkred ctermbg=NONE cterm=NONE
+ hi markdownH2Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi markdownH4Delimiter ctermfg=darkblue ctermbg=NONE cterm=NONE
+ hi markdownH6Delimiter ctermfg=darkgreen ctermbg=NONE cterm=NONE
+ hi markdownH3Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE
+ hi markdownH5Delimiter ctermfg=darkblue ctermbg=NONE cterm=NONE
+ unlet s:t_Co
+ finish
+endif
+
+if s:t_Co >= 0
+ hi Normal term=NONE
+ hi ColorColumn term=reverse
+ hi Conceal term=NONE
+ hi Cursor term=reverse
+ hi CursorColumn term=NONE
+ hi CursorLine term=underline
+ hi CursorLineNr term=bold
+ hi DiffAdd term=reverse
+ hi DiffChange term=NONE
+ hi DiffDelete term=reverse
+ hi DiffText term=reverse
+ hi Directory term=NONE
+ hi EndOfBuffer term=NONE
+ hi ErrorMsg term=bold,reverse
+ hi FoldColumn term=NONE
+ hi Folded term=NONE
+ hi IncSearch term=bold,reverse,underline
+ hi LineNr term=NONE
+ hi MatchParen term=bold,underline
+ hi ModeMsg term=bold
+ hi MoreMsg term=NONE
+ hi NonText term=NONE
+ hi Pmenu term=reverse
+ hi PmenuSbar term=reverse
+ hi PmenuSel term=bold
+ hi PmenuThumb term=NONE
+ hi Question term=standout
+ hi Search term=reverse
+ hi SignColumn term=reverse
+ hi SpecialKey term=bold
+ hi SpellBad term=underline
+ hi SpellCap term=underline
+ hi SpellLocal term=underline
+ hi SpellRare term=underline
+ hi StatusLine term=bold,reverse
+ hi StatusLineNC term=bold,underline
+ hi TabLine term=bold,underline
+ hi TabLineFill term=NONE
+ hi Terminal term=NONE
+ hi TabLineSel term=bold,reverse
+ hi Title term=NONE
+ hi VertSplit term=NONE
+ hi Visual term=reverse
+ hi VisualNOS term=NONE
+ hi WarningMsg term=standout
+ hi WildMenu term=bold
+ hi CursorIM term=NONE
+ hi ToolbarLine term=reverse
+ hi ToolbarButton term=bold,reverse
+ hi CurSearch term=reverse
+ hi CursorLineFold term=underline
+ hi CursorLineSign term=underline
+ hi Comment term=bold
+ hi Constant term=NONE
+ hi Error term=bold,reverse
+ hi Identifier term=NONE
+ hi Ignore term=NONE
+ hi PreProc term=NONE
+ hi Special term=NONE
+ hi Statement term=NONE
+ hi Todo term=bold,reverse
+ hi Type term=NONE
+ hi Underlined term=underline
+ unlet s:t_Co
+ finish
+endif
+
+" Background: dark
+" Color: color00 #282923 235 black
+" Color: color08 #74705d 244 darkgray
+" Color: color01 #c61e5c 125 darkred
+" Color: color09 #f92672 197 red
+" Color: color02 #81af24 106 darkgreen
+" Color: color10 #a6e22e 112 green
+" Color: color03 #fd971f 208 darkyellow
+" Color: color11 #e6db74 185 yellow
+" Color: color04 #51aebe 73 darkblue
+" Color: color12 #66d9ef 81 blue
+" Color: color05 #ae81ff 141 darkmagenta
+" Color: color13 #fd5ff0 207 magenta
+" Color: color06 #80beb5 73 darkcyan
+" Color: color14 #a1efe4 116 cyan
+" Color: color07 #bababa 250 gray
+" Color: color15 #f8f8f2 255 white
+" Color: colorLine #3a392f 237 darkgrey
+" Color: colorB #585858 240 darkgrey
+" Color: colorF #414141 238 darkgrey
+" Color: colorNonT #8a8a8a 245 darkgrey
+" Color: colorC #ffaf5f 215 red
+" Color: colorlC #5fff00 82 green
+" Color: colorV #1f3f5f 109 cyan
+" Color: colorMP #fd971f 208 darkyellow
+" Color: diffAdd #5faf5f 71 darkgreen
+" Color: diffDelete #af5f5f 131 darkred
+" Color: diffChange #5f87af 67 darkblue
+" Color: diffText #af87af 139 darkmagenta
+" Color: black #000000 16 black
+" Color: white #dadada 253 white
+" Color: Added #5fd75f 77 darkgreen
+" Color: Changed #ffaf5f 215 darkyellow
+" Color: Removed #d75f5f 167 darkred
+" Term colors: color00 color01 color02 color03 color04 color05 color06 color07
+" Term colors: color08 color09 color10 color11 color12 color13 color14 color15
+" vim: et ts=8 sw=2 sts=2
diff --git a/runtime/colors/vim.lua b/runtime/colors/vim.lua
index 5b9309ab38..dd59e5075d 100644
--- a/runtime/colors/vim.lua
+++ b/runtime/colors/vim.lua
@@ -60,6 +60,7 @@ hi('PmenuMatch', { link = 'Pmenu' })
hi('PmenuMatchSel', { link = 'PmenuSel' })
hi('PmenuExtra', { link = 'Pmenu' })
hi('PmenuExtraSel', { link = 'PmenuSel' })
+hi('ComplMatchIns', {})
hi('Substitute', { link = 'Search' })
hi('Whitespace', { link = 'NonText' })
hi('MsgSeparator', { link = 'StatusLine' })
diff --git a/runtime/colors/wildcharm.vim b/runtime/colors/wildcharm.vim
index 47ca5a1408..00ebc16529 100644
--- a/runtime/colors/wildcharm.vim
+++ b/runtime/colors/wildcharm.vim
@@ -4,7 +4,7 @@
" Maintainer: Maxim Kim <habamax@gmail.com>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -21,6 +21,7 @@ hi! link LineNrAbove LineNr
hi! link LineNrBelow LineNr
hi! link MessageWindow PMenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
hi! link CurSearch IncSearch
if &background ==# 'dark'
if (has('termguicolors') && &termguicolors) || has('gui_running')
@@ -194,6 +195,7 @@ if s:t_Co >= 256
hi! link LineNrBelow LineNr
hi! link MessageWindow PMenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
hi! link CurSearch IncSearch
if &background ==# 'dark'
hi Normal ctermfg=252 ctermbg=16 cterm=NONE
diff --git a/runtime/colors/zaibatsu.vim b/runtime/colors/zaibatsu.vim
index 90c6104afe..49047617c4 100644
--- a/runtime/colors/zaibatsu.vim
+++ b/runtime/colors/zaibatsu.vim
@@ -4,7 +4,7 @@
" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
" Website: https://github.com/vim/colorschemes
" License: Same as Vim
-" Last Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -96,6 +96,7 @@ hi! link TabLineFill StatusLineNC
hi! link TabLineSel StatusLine
hi! link Terminal Normal
hi! link lCursor Cursor
+hi! link PopupSelected PmenuSel
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
@@ -201,6 +202,7 @@ if s:t_Co >= 256
hi! link TabLineSel StatusLine
hi! link Terminal Normal
hi! link lCursor Cursor
+ hi! link PopupSelected PmenuSel
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
@@ -309,6 +311,7 @@ if s:t_Co >= 16
hi! link TabLineSel StatusLine
hi! link Terminal Normal
hi! link lCursor Cursor
+ hi! link PopupSelected PmenuSel
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
@@ -417,6 +420,7 @@ if s:t_Co >= 8
hi! link TabLineSel StatusLine
hi! link Terminal Normal
hi! link lCursor Cursor
+ hi! link PopupSelected PmenuSel
hi! link Boolean Constant
hi! link Character Constant
hi! link Conditional Statement
diff --git a/runtime/colors/zellner.vim b/runtime/colors/zellner.vim
index 7781ca9ab8..298ec0f700 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 Change: 2024 Aug 15
+" Last Change: 2025 Jan 07
" Generated by Colortemplate v2.2.3
@@ -31,6 +31,7 @@ hi! link CursorLineFold CursorLine
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+hi! link PopupSelected PmenuSel
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
@@ -106,6 +107,7 @@ if s:t_Co >= 256
hi! link CursorLineSign CursorLine
hi! link MessageWindow Pmenu
hi! link PopupNotification Todo
+ hi! link PopupSelected PmenuSel
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/bash.vim b/runtime/compiler/bash.vim
new file mode 100644
index 0000000000..cbd76ae410
--- /dev/null
+++ b/runtime/compiler/bash.vim
@@ -0,0 +1,12 @@
+" Vim compiler file
+" Compiler: Bash Syntax Checker
+" Maintainer: @konfekt
+" Last Change: 2024 Dec 27
+
+if exists("current_compiler")
+ finish
+endif
+let current_compiler = "bash"
+
+CompilerSet makeprg=bash\ -n
+CompilerSet errorformat=%f:\ line\ %l:\ %m
diff --git a/runtime/compiler/cppcheck.vim b/runtime/compiler/cppcheck.vim
index 4df12d1714..033613c091 100644
--- a/runtime/compiler/cppcheck.vim
+++ b/runtime/compiler/cppcheck.vim
@@ -1,7 +1,7 @@
" vim compiler file
" Compiler: cppcheck (C++ static checker)
" Maintainer: Vincent B. (twinside@free.fr)
-" Last Change: 2024 Nov 08 by @Konfekt
+" Last Change: 2024 Nov 19 by @Konfekt
if exists("current_compiler") | finish | endif
let current_compiler = "cppcheck"
@@ -25,7 +25,7 @@ let &l:makeprg = 'cppcheck --quiet'
\ (filereadable('compile_commands.json') ? '--project=compile_commands.json' :
\ (!empty(glob('*'..s:slash..'compile_commands.json', 1, 1)) ? '--project='..glob('*'..s:slash..'compile_commands.json', 1, 1)[0] :
\ (empty(&path) ? '' : '-I')..join(map(filter(split(&path, ','), 'isdirectory(v:val)'),'shellescape(v:val)'), ' -I')))))
-exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "')
+exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"')
CompilerSet errorformat=
\%f:%l:%c:\ %tarning:\ %m,
diff --git a/runtime/compiler/eslint.vim b/runtime/compiler/eslint.vim
index db7a665991..0414817900 100644
--- a/runtime/compiler/eslint.vim
+++ b/runtime/compiler/eslint.vim
@@ -1,13 +1,12 @@
" Vim compiler file
" Compiler: ESLint for JavaScript
" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
-" Last Change: 2020 August 20
-" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition)
+" Last Change: 2024 Nov 30
if exists("current_compiler")
finish
endif
let current_compiler = "eslint"
-CompilerSet makeprg=npx\ eslint\ --format\ compact
-CompilerSet errorformat=%f:\ line\ %l\\,\ col\ %c\\,\ %m,%-G%.%#
+CompilerSet makeprg=npx\ eslint\ --format\ stylish
+CompilerSet errorformat=%-P%f,\%\\s%#%l:%c\ %#\ %trror\ \ %m,\%\\s%#%l:%c\ %#\ %tarning\ \ %m,\%-Q,\%-G%.%#,
diff --git a/runtime/compiler/groff.vim b/runtime/compiler/groff.vim
index 640146d6a1..3e9ae0488f 100644
--- a/runtime/compiler/groff.vim
+++ b/runtime/compiler/groff.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: Groff
" Maintainer: Konfekt
-" Last Change: 2024 Sep 8
+" Last Change: 2024 Nov 19
"
" Expects output file extension, say `:make html` or `:make pdf`.
" Supported devices as of Sept 2024 are: (x)html, pdf, ps, dvi, lj4, lbp ...
@@ -30,7 +30,7 @@ execute 'CompilerSet makeprg=groff'..escape(
\ ' '..s:groff_compiler_lang()..
\ ' -K'..get(b:, 'groff_compiler_encoding', get(g:, 'groff_compiler_encoding', 'utf8'))..
\ ' '..get(b:, 'groff_compiler_args', get(g:, 'groff_compiler_args', ''))..
- \ ' -mom -T$* -- %:S > %:r:S.$*', ' ')
+ \ ' -mom -T$* -- %:S > %:r:S.$*', ' \|"')
" From Gavin Freeborn's https://github.com/Gavinok/vim-troff under Vim License
" https://github.com/Gavinok/vim-troff/blob/91017b1423caa80aba541c997909a4f810edd275/compiler/troff.vim#L39
CompilerSet errorformat=%o:<standard\ input>\ (%f):%l:%m,
diff --git a/runtime/compiler/javac.vim b/runtime/compiler/javac.vim
index 9bd4cdf270..53cd772ed8 100644
--- a/runtime/compiler/javac.vim
+++ b/runtime/compiler/javac.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: Java Development Kit Compiler
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2024 Jun 14
+" Last Change: 2024 Nov 19 (enable local javac_makeprg_params)
if exists("current_compiler")
finish
@@ -11,11 +11,7 @@ let current_compiler = "javac"
let s:cpo_save = &cpo
set cpo&vim
-if exists("g:javac_makeprg_params")
- execute $'CompilerSet makeprg=javac\ {escape(g:javac_makeprg_params, ' \|"')}'
-else
- CompilerSet makeprg=javac
-endif
+execute $'CompilerSet makeprg=javac\ {escape(get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')), ' \|"')}'
CompilerSet errorformat=%E%f:%l:\ error:\ %m,
\%W%f:%l:\ warning:\ %m,
diff --git a/runtime/compiler/maven.vim b/runtime/compiler/maven.vim
index ef8d8a6fb2..72e74e301d 100644
--- a/runtime/compiler/maven.vim
+++ b/runtime/compiler/maven.vim
@@ -14,7 +14,7 @@ if exists("current_compiler")
endif
let current_compiler = "maven"
-CompilerSet makeprg=mvn\ --batch-mode
+execute $'CompilerSet makeprg=mvn\ --batch-mode\ {escape(get(b:, 'maven_makeprg_params', get(g:, 'maven_makeprg_params', '')), ' \|"')}'
" Error message for POM
CompilerSet errorformat=[FATAL]\ Non-parseable\ POM\ %f:\ %m%\\s%\\+@%.%#line\ %l\\,\ column\ %c%.%#,
diff --git a/runtime/compiler/mypy.vim b/runtime/compiler/mypy.vim
index 891488626a..907b98b777 100644
--- a/runtime/compiler/mypy.vim
+++ b/runtime/compiler/mypy.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: Mypy (Python static checker)
" Maintainer: @Konfekt
-" Last Change: 2024 Nov 07
+" Last Change: 2024 Nov 19
if exists("current_compiler") | finish | endif
let current_compiler = "mypy"
@@ -12,7 +12,7 @@ set cpo&vim
" CompilerSet makeprg=mypy
let &l:makeprg = 'mypy --show-column-numbers '
\ ..get(b:, 'mypy_makeprg_params', get(g:, 'mypy_makeprg_params', '--strict --ignore-missing-imports'))
-exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "')
+exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"')
CompilerSet errorformat=%f:%l:%c:\ %t%*[^:]:\ %m
let &cpo = s:cpo_save
diff --git a/runtime/compiler/pandoc.vim b/runtime/compiler/pandoc.vim
index 6c15e104c3..5d90a518c9 100644
--- a/runtime/compiler/pandoc.vim
+++ b/runtime/compiler/pandoc.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: Pandoc
" Maintainer: Konfekt
-" Last Change: 2024 Sep 8
+" Last Change: 2024 Nov 19
"
" Expects output file extension, say `:make html` or `:make pdf`.
" Passes additional arguments to pandoc, say `:make html --self-contained`.
@@ -56,7 +56,7 @@ execute 'CompilerSet makeprg=pandoc'..escape(
\ ' '..s:PandocLang()..
\ ' --from='..s:PandocFiletype(&filetype)..
\ ' '..get(b:, 'pandoc_compiler_args', get(g:, 'pandoc_compiler_args', ''))..
- \ ' --output %:r:S.$* -- %:S', ' ')
+ \ ' --output %:r:S.$* -- %:S', ' \|"')
CompilerSet errorformat=\"%f\",\ line\ %l:\ %m
let &cpo = s:keepcpo
diff --git a/runtime/compiler/powershell.vim b/runtime/compiler/powershell.vim
index 821fea4085..3d37d7c847 100644
--- a/runtime/compiler/powershell.vim
+++ b/runtime/compiler/powershell.vim
@@ -3,8 +3,9 @@
" URL: https://github.com/PProvost/vim-ps1
" Contributors: Enno Nagel
" Last Change: 2024 Mar 29
-" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition)
-" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg)
+" 2024 Apr 03 by the Vim Project (removed :CompilerSet definition)
+" 2024 Apr 05 by the Vim Project (avoid leaving behind g:makeprg)
+" 2024 Nov 19 by the Vim Project (properly escape makeprg setting)
if exists("current_compiler")
finish
@@ -49,7 +50,7 @@ let s:makeprg = g:ps1_makeprg_cmd .. ' %:p:S'
" + CategoryInfo : ObjectNotFound: (Write-Ouput:String) [], CommandNotFoundException
" + FullyQualifiedErrorId : CommandNotFoundException
-execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ')
+execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' \|"')
" Showing error in context with underlining.
CompilerSet errorformat=%+G+%m
diff --git a/runtime/compiler/pylint.vim b/runtime/compiler/pylint.vim
index 4c9c23e125..96abf315ab 100644
--- a/runtime/compiler/pylint.vim
+++ b/runtime/compiler/pylint.vim
@@ -2,6 +2,7 @@
" Compiler: Pylint for Python
" Maintainer: Daniel Moch <daniel@danielmoch.com>
" Last Change: 2024 Nov 07 by The Vim Project (added params variable)
+" 2024 Nov 19 by the Vim Project (properly escape makeprg setting)
if exists("current_compiler") | finish | endif
let current_compiler = "pylint"
@@ -13,7 +14,7 @@ set cpo&vim
let &l:makeprg = 'pylint ' .
\ '--output-format=text --msg-template="{path}:{line}:{column}:{C}: [{symbol}] {msg}" --reports=no ' .
\ get(b:, "pylint_makeprg_params", get(g:, "pylint_makeprg_params", '--jobs=0'))
-exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "')
+exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"')
CompilerSet errorformat=%A%f:%l:%c:%t:\ %m,%A%f:%l:\ %m,%A%f:(%l):\ %m,%-Z%p^%.%#,%-G%.%#
let &cpo = s:cpo_save
diff --git a/runtime/compiler/pytest.vim b/runtime/compiler/pytest.vim
new file mode 100644
index 0000000000..7fc189932c
--- /dev/null
+++ b/runtime/compiler/pytest.vim
@@ -0,0 +1,103 @@
+" Vim compiler file
+" Compiler: Pytest (Python testing framework)
+" Maintainer: @Konfekt and @mgedmin
+" Last Change: 2024 Nov 28
+
+if exists("current_compiler") | finish | endif
+let current_compiler = "pytest"
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" CompilerSet makeprg=pytest
+if has('unix')
+ execute $'CompilerSet makeprg=/usr/bin/env\ PYTHONWARNINGS=ignore\ pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}'
+elseif has('win32')
+ execute $'CompilerSet makeprg=set\ PYTHONWARNINGS=ignore\ &&\ pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}'
+else
+ CompilerSet makeprg=pytest\ --tb=short\ --quiet
+ execute $'CompilerSet makeprg=pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}'
+endif
+
+" Pytest syntax errors {{{2
+
+" Reset error format so that sourcing .vimrc again and again doesn't grow it
+" without bounds
+setlocal errorformat&
+
+" For the record, the default errorformat is this:
+"
+" %*[^"]"%f"%*\D%l: %m
+" "%f"%*\D%l: %m
+" %-G%f:%l: (Each undeclared identifier is reported only once
+" %-G%f:%l: for each function it appears in.)
+" %-GIn file included from %f:%l:%c:
+" %-GIn file included from %f:%l:%c\,
+" %-GIn file included from %f:%l:%c
+" %-GIn file included from %f:%l
+" %-G%*[ ]from %f:%l:%c
+" %-G%*[ ]from %f:%l:
+" %-G%*[ ]from %f:%l\,
+" %-G%*[ ]from %f:%l
+" %f:%l:%c:%m
+" %f(%l):%m
+" %f:%l:%m
+" "%f"\, line %l%*\D%c%*[^ ] %m
+" %D%*\a[%*\d]: Entering directory %*[`']%f'
+" %X%*\a[%*\d]: Leaving directory %*[`']%f'
+" %D%*\a: Entering directory %*[`']%f'
+" %X%*\a: Leaving directory %*[`']%f'
+" %DMaking %*\a in %f
+" %f|%l| %m
+"
+" and sometimes it misfires, so let's fix it up a bit
+" (TBH I don't even know what compiler produces filename(lineno) so why even
+" have it?)
+setlocal errorformat-=%f(%l):%m
+
+" Sometimes Vim gets confused about ISO-8601 timestamps and thinks they're
+" filenames; this is a big hammer that ignores anything filename-like on lines
+" that start with at least two spaces, possibly preceded by a number and
+" optional punctuation
+setlocal errorformat^=%+G%\\d%#%.%\\=\ \ %.%#
+
+" Similar, but when the entire line starts with a date
+setlocal errorformat^=%+G\\d\\d\\d\\d-\\d\\d-\\d\\d\ \\d\\d:\\d\\d%.%#
+
+" make: *** [Makefile:14: target] Error 1
+setlocal errorformat^=%+Gmake:\ ***\ %.%#
+
+" FAILED tests.py::test_with_params[YYYY-MM-DD:HH:MM:SS] - Exception: bla bla
+setlocal errorformat^=%+GFAILED\ %.%#
+
+" AssertionError: assert ...YYYY-MM-DD:HH:MM:SS...
+setlocal errorformat^=%+GAssertionError:\ %.%#
+
+" --- /path/to/file:before YYYY-MM-DD HH:MM:SS.ssssss
+setlocal errorformat^=---%f:%m
+
+" +++ /path/to/file:before YYYY-MM-DD HH:MM:SS.ssssss
+setlocal errorformat^=+++%f:%m
+
+" Sometimes pytest prepends an 'E' marker at the beginning of a traceback line
+setlocal errorformat+=E\ %#File\ \"%f\"\\,\ line\ %l%.%#
+
+" Python tracebacks (unittest + doctest output) {{{2
+
+" This collapses the entire traceback into just the last file+lineno,
+" which is convenient when you want to jump to the line that failed (and not
+" the top-level entry point), but it makes it impossible to see the full
+" traceback, which sucks.
+""setlocal errorformat+=
+"" \File\ \"%f\"\\,\ line\ %l%.%#,
+"" \%C\ %.%#,
+"" \%-A\ \ File\ \"unittest%.py\"\\,\ line\ %.%#,
+"" \%-A\ \ File\ \"%f\"\\,\ line\ 0%.%#,
+"" \%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,
+"" \%Z%[%^\ ]%\\@=%m
+setlocal errorformat+=File\ \"%f\"\\,\ line\ %l\\,%#%m
+
+exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"')
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/compiler/ruff.vim b/runtime/compiler/ruff.vim
index 11a69740d8..318f4fe5cb 100644
--- a/runtime/compiler/ruff.vim
+++ b/runtime/compiler/ruff.vim
@@ -2,6 +2,7 @@
" Compiler: Ruff (Python linter)
" Maintainer: @pbnj-dragon
" Last Change: 2024 Nov 07
+" 2024 Nov 19 by the Vim Project (properly escape makeprg setting)
if exists("current_compiler") | finish | endif
let current_compiler = "ruff"
@@ -12,7 +13,7 @@ set cpo&vim
" CompilerSet makeprg=ruff
let &l:makeprg= 'ruff check --output-format=concise '
\ ..get(b:, 'ruff_makeprg_params', get(g:, 'ruff_makeprg_params', '--preview'))
-exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "')
+exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"')
CompilerSet errorformat=%f:%l:%c:\ %m,%f:%l:\ %m,%f:%l:%c\ -\ %m,%f:
let &cpo = s:cpo_save
diff --git a/runtime/compiler/spotbugs.vim b/runtime/compiler/spotbugs.vim
new file mode 100644
index 0000000000..8ed45f8ee0
--- /dev/null
+++ b/runtime/compiler/spotbugs.vim
@@ -0,0 +1,254 @@
+" Vim compiler file
+" Compiler: Spotbugs (Java static checker; needs javac compiled classes)
+" Maintainers: @konfekt and @zzzyxwvut
+" Last Change: 2024 Dec 20
+
+if exists('g:current_compiler') || bufname() !~# '\.java\=$' || wordcount().chars < 9
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Unfortunately Spotbugs does not output absolute paths, so you need to
+" pass the directory of the files being checked as `-sourcepath` parameter.
+" The regex, auxpath and glob try to include all dependent classes of the
+" current buffer. See https://github.com/spotbugs/spotbugs/issues/856
+
+" FIXME: When "search()" is used with the "e" flag, it makes no _further_
+" progress after claiming an EOL match (i.e. "\_" or "\n", but not "$").
+" XXX: Omit anonymous class declarations
+let s:keywords = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\|package\)\%(\s\|$\)'
+let s:type_names = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\)\s*\(\K\k*\)\>'
+" Capture ";" for counting a class file directory (see s:package_dir_heads below)
+let s:package_names = '\C\<package\s*\(\K\%(\k*\.\=\)\+;\)'
+let s:package = ''
+
+if has('syntax') && exists('g:syntax_on') &&
+ \ exists('b:current_syntax') && b:current_syntax == 'java' &&
+ \ hlexists('javaClassDecl') && hlexists('javaExternal')
+
+ function! s:GetDeclaredTypeNames() abort
+ if bufname() =~# '\<\%(module\|package\)-info\.java\=$'
+ return [expand('%:t:r')]
+ endif
+ defer execute('silent! normal! g``')
+ call cursor(1, 1)
+ let type_names = []
+ let lnum = search(s:keywords, 'eW')
+ while lnum > 0
+ let name_attr = synIDattr(synID(lnum, (col('.') - 1), 0), 'name')
+ if name_attr ==# 'javaClassDecl'
+ let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:type_names)
+ if !empty(tokens) | call add(type_names, tokens[1]) | endif
+ elseif name_attr ==# 'javaExternal'
+ let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:package_names)
+ if !empty(tokens) | let s:package = tokens[1] | endif
+ endif
+ let lnum = search(s:keywords, 'eW')
+ endwhile
+ return type_names
+ endfunction
+
+else
+ function! s:GetDeclaredTypeNames() abort
+ if bufname() =~# '\<\%(module\|package\)-info\.java\=$'
+ return [expand('%:t:r')]
+ endif
+ " Undo the unsetting of &hls, see below
+ if &hls
+ defer execute('set hls')
+ endif
+ " Possibly restore the current values for registers '"' and "y", see below
+ defer call('setreg', ['"', getreg('"'), getregtype('"')])
+ defer call('setreg', ['y', getreg('y'), getregtype('y')])
+ defer execute('silent bwipeout')
+ " Copy buffer contents for modification
+ silent %y y
+ new
+ " Apply ":help scratch-buffer" effects and match "$" in Java (generated)
+ " type names (see s:type_names)
+ setlocal iskeyword+=$ buftype=nofile bufhidden=hide noswapfile nohls
+ 0put y
+ " Discard text blocks and strings
+ silent keeppatterns %s/\\\@<!"""\_.\{-}\\\@<!"""\|\\"//ge
+ silent keeppatterns %s/".*"//ge
+ " Discard comments
+ silent keeppatterns %s/\/\/.\+$//ge
+ silent keeppatterns %s/\/\*\_.\{-}\*\///ge
+ call cursor(1, 1)
+ let type_names = []
+ let lnum = search(s:keywords, 'eW')
+ while lnum > 0
+ let line = getline(lnum)
+ if line =~# '\<package\>'
+ let tokens = matchlist(line..getline(lnum + 1), s:package_names)
+ if !empty(tokens) | let s:package = tokens[1] | endif
+ else
+ let tokens = matchlist(line..getline(lnum + 1), s:type_names)
+ if !empty(tokens) | call add(type_names, tokens[1]) | endif
+ endif
+ let lnum = search(s:keywords, 'eW')
+ endwhile
+ return type_names
+ endfunction
+endif
+
+if has('win32')
+
+ function! s:GlobClassFiles(src_type_name) abort
+ return glob(a:src_type_name..'$*.class', 1, 1)
+ endfunction
+
+else
+ function! s:GlobClassFiles(src_type_name) abort
+ return glob(a:src_type_name..'\$*.class', 1, 1)
+ endfunction
+endif
+
+if exists('b:spotbugs_properties')
+ " Let "ftplugin/java.vim" merge global entries, if any, in buffer-local
+ " entries
+
+ function! s:GetProperty(name, default) abort
+ return get(b:spotbugs_properties, a:name, a:default)
+ endfunction
+
+elseif exists('g:spotbugs_properties')
+
+ function! s:GetProperty(name, default) abort
+ return get(g:spotbugs_properties, a:name, a:default)
+ endfunction
+
+else
+ function! s:GetProperty(dummy, default) abort
+ return a:default
+ endfunction
+endif
+
+if (exists('g:spotbugs_properties') || exists('b:spotbugs_properties')) &&
+ \ ((!empty(s:GetProperty('sourceDirPath', [])) &&
+ \ !empty(s:GetProperty('classDirPath', []))) ||
+ \ (!empty(s:GetProperty('testSourceDirPath', [])) &&
+ \ !empty(s:GetProperty('testClassDirPath', []))))
+
+ function! s:CommonIdxsAndDirs() abort
+ let src_dir_path = s:GetProperty('sourceDirPath', [])
+ let bin_dir_path = s:GetProperty('classDirPath', [])
+ let test_src_dir_path = s:GetProperty('testSourceDirPath', [])
+ let test_bin_dir_path = s:GetProperty('testClassDirPath', [])
+ let dir_cnt = min([len(src_dir_path), len(bin_dir_path)])
+ let test_dir_cnt = min([len(test_src_dir_path), len(test_bin_dir_path)])
+ " Do not break up path pairs with filtering!
+ return [[range(dir_cnt),
+ \ src_dir_path[0 : dir_cnt - 1],
+ \ bin_dir_path[0 : dir_cnt - 1]],
+ \ [range(test_dir_cnt),
+ \ test_src_dir_path[0 : test_dir_cnt - 1],
+ \ test_bin_dir_path[0 : test_dir_cnt - 1]]]
+ endfunction
+
+ let s:common_idxs_and_dirs = s:CommonIdxsAndDirs()
+ delfunction s:CommonIdxsAndDirs
+
+ function! s:FindClassFiles(src_type_name) abort
+ let class_files = []
+ " Match pairwise the components of source and class pathnames
+ for [idxs, src_dirs, bin_dirs] in s:common_idxs_and_dirs
+ " Do not use "fnamemodify(a:src_type_name, ':p:s?src?bin?')" because
+ " only the rightmost "src" is looked for
+ for idx in idxs
+ let tail_idx = strridx(a:src_type_name, src_dirs[idx])
+ " No such directory or no such inner type (i.e. without "$")
+ if tail_idx < 0 | continue | endif
+ " Substitute "bin_dirs[idx]" for the rightmost "src_dirs[idx]"
+ let candidate_type_name = strpart(a:src_type_name, 0, tail_idx)..
+ \ bin_dirs[idx]..
+ \ strpart(a:src_type_name, (tail_idx + strlen(src_dirs[idx])))
+ for candidate in insert(s:GlobClassFiles(candidate_type_name),
+ \ candidate_type_name..'.class')
+ if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif
+ endfor
+ if !empty(class_files) | break | endif
+ endfor
+ if !empty(class_files) | break | endif
+ endfor
+ return class_files
+ endfunction
+
+else
+ function! s:FindClassFiles(src_type_name) abort
+ let class_files = []
+ for candidate in insert(s:GlobClassFiles(a:src_type_name),
+ \ a:src_type_name..'.class')
+ if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif
+ endfor
+ return class_files
+ endfunction
+endif
+
+if exists('g:spotbugs_alternative_path') &&
+ \ !empty(get(g:spotbugs_alternative_path, 'fromPath', '')) &&
+ \ !empty(get(g:spotbugs_alternative_path, 'toPath', ''))
+
+ " See https://github.com/spotbugs/spotbugs/issues/909
+ function! s:ResolveAbsolutePathname() abort
+ let pathname = expand('%:p')
+ let head_idx = stridx(pathname, g:spotbugs_alternative_path.toPath)
+ " No such file: a mismatched path request for a project
+ if head_idx < 0 | return pathname | endif
+ " Settle for failure with file readability tests _in s:FindClassFiles()_
+ return strpart(pathname, 0, head_idx)..
+ \ g:spotbugs_alternative_path.fromPath..
+ \ strpart(pathname, (head_idx + strlen(g:spotbugs_alternative_path.toPath)))
+ endfunction
+
+else
+ function! s:ResolveAbsolutePathname() abort
+ return expand('%:p')
+ endfunction
+endif
+
+function! s:CollectClassFiles() abort
+ " Possibly obtain a symlinked path for an unsupported directory name
+ let pathname = s:ResolveAbsolutePathname()
+ " Get a platform-independent pathname prefix, cf. "expand('%:p:h')..'/'"
+ let tail_idx = strridx(pathname, expand('%:t'))
+ let src_pathname = strpart(pathname, 0, tail_idx)
+ let all_class_files = []
+ " Get all type names in the current buffer and let the filename globbing
+ " discover inner type names from arbitrary type names
+ for type_name in s:GetDeclaredTypeNames()
+ call extend(all_class_files, s:FindClassFiles(src_pathname..type_name))
+ endfor
+ return all_class_files
+endfunction
+
+" Expose class files for removal etc.
+let b:spotbugs_class_files = s:CollectClassFiles()
+let s:package_dir_heads = repeat(':h', (1 + strlen(substitute(s:package, '[^.;]', '', 'g'))))
+let s:package_root_dir = fnamemodify(s:ResolveAbsolutePathname(), s:package_dir_heads..':S')
+let g:current_compiler = 'spotbugs'
+" CompilerSet makeprg=spotbugs
+let &l:makeprg = 'spotbugs'..(has('win32') ? '.bat' : '')..' '..
+ \ get(b:, 'spotbugs_makeprg_params', get(g:, 'spotbugs_makeprg_params', '-workHard -experimental'))..
+ \ ' -textui -emacs -auxclasspath '..s:package_root_dir..' -sourcepath '..s:package_root_dir..' '..
+ \ join(b:spotbugs_class_files, ' ')
+" Emacs expects doubled line numbers
+setlocal errorformat=%f:%l:%*[0-9]\ %m,%f:-%*[0-9]:-%*[0-9]\ %m
+
+" " This compiler is meant to be used for a single buffer only
+" exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"')
+" exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"')
+
+delfunction s:CollectClassFiles
+delfunction s:ResolveAbsolutePathname
+delfunction s:FindClassFiles
+delfunction s:GetProperty
+delfunction s:GlobClassFiles
+delfunction s:GetDeclaredTypeNames
+let &cpo = s:cpo_save
+unlet! s:package_root_dir s:package_dir_heads s:common_idxs_and_dirs s:package
+unlet! s:package_names s:type_names s:keywords s:cpo_save
+
+" vim: set foldmethod=syntax shiftwidth=2 expandtab:
diff --git a/runtime/compiler/tex.vim b/runtime/compiler/tex.vim
index 282b3a0588..bc1623729a 100644
--- a/runtime/compiler/tex.vim
+++ b/runtime/compiler/tex.vim
@@ -3,8 +3,9 @@
" Maintainer: Artem Chuprina <ran@ran.pp.ru>
" Contributors: Enno Nagel
" Last Change: 2024 Mar 29
-" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition)
-" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg)
+" 2024 Apr 03 by the Vim Project (removed :CompilerSet definition)
+" 2024 Apr 05 by the Vim Project (avoid leaving behind g:makeprg)
+" 2024 Nov 19 by the Vim Project (properly escape makeprg setting)
if exists("current_compiler")
finish
@@ -27,7 +28,7 @@ if exists('b:tex_ignore_makefile') || exists('g:tex_ignore_makefile') ||
let current_compiler = "latex"
endif
let s:makeprg=current_compiler .. ' -interaction=nonstopmode'
- execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ')
+ execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' \|"')
else
let current_compiler = 'make'
endif
diff --git a/runtime/compiler/typst.vim b/runtime/compiler/typst.vim
index 33e55818e9..13699f4675 100644
--- a/runtime/compiler/typst.vim
+++ b/runtime/compiler/typst.vim
@@ -1,7 +1,8 @@
" Vim compiler file
" Language: Typst
-" Maintainer: Gregory Anders
-" Last Change: 2024-07-14
+" Previous Maintainer: Gregory Anders
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Last Change: 2024 Dec 09
" Based on: https://github.com/kaarmu/typst.vim
if exists('current_compiler')
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index c5dabeb551..92f5a261ee 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -406,18 +406,9 @@ Another use case are plugins that show output in an append-only buffer, and
want to add highlights to the outputs. Highlight data cannot be preserved
on writing and loading a buffer to file, nor in undo/redo cycles.
-Highlights are registered using the |nvim_buf_add_highlight()| function. If an
-external highlighter plugin wants to add many highlights in a batch,
-performance can be improved by calling |nvim_buf_add_highlight()| as an
-asynchronous notification, after first (synchronously) requesting a source id.
-
-|nvim_buf_add_highlight()| adds highlights as |extmarks|. If highlights need to
-be tracked or manipulated after adding them, it is better to use
-|nvim_buf_set_extmark()| directly, as this function returns the placed |extmark|
-id. Thus, instead of >lua
- vim.api.nvim_buf_add_highlight(buf, ns_id, hl_group, line, col_start, col_end)
-<
-use >lua
+Highlights are registered using the |nvim_buf_set_extmark()| function, which
+adds highlights as |extmarks|. If highlights need to be tracked or manipulated
+after adding them, the returned |extmark| id can be used. >lua
-- create the highlight through an extmark
extid = vim.api.nvim_buf_set_extmark(buf, ns_id, line, col_start, {end_col = col_end, hl_group = hl_group})
@@ -428,32 +419,10 @@ use >lua
vim.api.nvim_buf_set_extmark(buf, ns_id, NEW_LINE, col_start, {end_col = col_end, hl_group = NEW_HL_GROUP, id = extid})
<
-Example using the Python API client (|pynvim|):
->python
- src = vim.new_highlight_source()
- buf = vim.current.buffer
- for i in range(5):
- buf.add_highlight("String",i,0,-1,src_id=src)
- # some time later ...
- buf.clear_namespace(src)
-<
-If the highlights don't need to be deleted or updated, just pass -1 as
-src_id (this is the default in python). Use |nvim_buf_clear_namespace()| to
-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: >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)
- call nvim_buf_add_highlight(0, src, "Identifier", 0, 5, -1)
- " some time later ...
- call nvim_buf_clear_namespace(0, src, 0, -1)
-
+See also |vim.hl.range()|.
==============================================================================
-Floating windows *api-floatwin*
+Floating windows *api-floatwin* *floating-windows*
Floating windows ("floats") are displayed on top of normal windows. This is
useful to implement simple widgets, such as tooltips displayed next to the
@@ -654,35 +623,22 @@ nvim_del_var({name}) *nvim_del_var()*
• {name} Variable name
nvim_echo({chunks}, {history}, {opts}) *nvim_echo()*
- Echo a message.
+ Prints a message given by a list of `[text, hl_group]` "chunks".
+
+ Example: >lua
+ vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {})
+<
Parameters: ~
- • {chunks} A list of `[text, hl_group]` arrays, each representing a
- text chunk with specified highlight group name or ID.
- `hl_group` element can be omitted for no highlight.
+ • {chunks} List of `[text, hl_group]` pairs, where each is a `text`
+ string highlighted by the (optional) name or ID `hl_group`.
• {history} if true, add to |message-history|.
• {opts} Optional parameters.
- • verbose: Message is 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
-
-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
-
- See also: ~
- • nvim_err_write()
+ • err: Treat the message like `:echoerr`. Sets `hl_group`
+ to |hl-ErrorMsg| by default.
+ • verbose: Message is controlled by the 'verbose' option.
+ Nvim invoked with `-V3log` will write the message to the
+ "log" file instead of standard output.
nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()*
Evaluates statusline string.
@@ -716,7 +672,10 @@ nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()*
true. Each element of the array is a |Dict| with these keys:
• start: (number) Byte index (0-based) of first character that uses
the highlight.
- • group: (string) Name of highlight group.
+ • group: (string) Name of highlight group. May be removed in the
+ future, use `groups` instead.
+ • groups: (array) Names of stacked highlight groups (highest
+ priority last).
nvim_exec_lua({code}, {args}) *nvim_exec_lua()*
Execute Lua code. Parameters (if any) are available as `...` inside the
@@ -775,6 +734,8 @@ nvim_get_api_info() *nvim_get_api_info()*
nvim_get_chan_info({chan}) *nvim_get_chan_info()*
Gets information about a channel.
+ See |nvim_list_uis()| for an example of how to get channel info.
+
Parameters: ~
• {chan} channel_id, or 0 for current channel
@@ -796,7 +757,7 @@ nvim_get_chan_info({chan}) *nvim_get_chan_info()*
present if a pty is used (e.g. for conpty on Windows).
• "buffer" (optional) Buffer connected to |terminal| instance.
• "client" (optional) Info about the peer (client on the other end of
- the RPC channel), which it provided via |nvim_set_client_info()|.
+ the channel), as set by |nvim_set_client_info()|.
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
@@ -1079,6 +1040,12 @@ nvim_list_tabpages() *nvim_list_tabpages()*
nvim_list_uis() *nvim_list_uis()*
Gets a list of dictionaries representing attached UIs.
+ Example: The Nvim builtin |TUI| sets its channel info as described in
+ |startup-tui|. In particular, it sets `client.name` to "nvim-tui". So you
+ can check if the TUI is running by inspecting the client name of each UI: >lua
+ vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name)
+<
+
Return: ~
Array of UI dictionaries, each with these keys:
• "height" Requested height of the UI
@@ -1099,22 +1066,11 @@ nvim_load_context({dict}) *nvim_load_context()*
Parameters: ~
• {dict} |Context| map.
-nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()*
- Notify the user with a message
-
- Relays the call to vim.notify . By default forwards your message in the
- 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.
-
nvim_open_term({buffer}, {opts}) *nvim_open_term()*
Open a terminal instance in a buffer
By default (and currently the only option) the terminal will not be
- connected to an external process. Instead, input send on the channel will
+ connected to an external process. Instead, input sent on the channel will
be echoed directly by the terminal. This is useful to display ANSI
terminal sequences returned as part of a rpc message, or similar.
@@ -1125,6 +1081,17 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()*
|nvim_chan_send()| can be called immediately to process sequences in a
virtual terminal having the intended size.
+ Example: this `TermHl` command can be used to display and highlight raw
+ ANSI termcodes, so you can use Nvim as a "scrollback pager" (for terminals
+ like kitty): *ansi-colorize* *terminal-scrollback-pager* >lua
+ vim.api.nvim_create_user_command('TermHl', function()
+ local b = vim.api.nvim_create_buf(false, true)
+ local chan = vim.api.nvim_open_term(b, {})
+ vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
+ vim.api.nvim_win_set_buf(0, b)
+ end, { desc = 'Highlights ANSI termcodes in curbuf' })
+<
+
Attributes: ~
not allowed when |textlock| is active
@@ -1143,13 +1110,6 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()*
Return: ~
Channel id, or 0 on error
-nvim_out_write({str}) *nvim_out_write()*
- Writes a message to the Vim output buffer. Does not append "\n", the
- message is buffered (won't display) until a linefeed is written.
-
- Parameters: ~
- • {str} Message
-
nvim_paste({data}, {crlf}, {phase}) *nvim_paste()*
Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat
the input. UIs call this to implement "paste", but it's also intended for
@@ -1248,25 +1208,23 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
*nvim_set_client_info()*
nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes})
- Self-identifies the client.
+ Self-identifies the client. Sets the `client` object returned by
+ |nvim_get_chan_info()|.
- The client/plugin/application should call this after connecting, to
- provide hints about its identity and purpose, for debugging and
- orchestration.
+ Clients should call this just after connecting, to provide hints for
+ debugging and orchestration. (Note: Something is better than nothing!
+ Fields are optional, but at least set `name`.)
Can be called more than once; the caller should merge old info if
appropriate. Example: library first identifies the channel, then a plugin
using that library later identifies itself.
- Note: ~
- • "Something is better than nothing". You don't need to include all the
- fields.
-
Attributes: ~
|RPC| only
Parameters: ~
- • {name} Short name for the connected client
+ • {name} Client short-name. Sets the `client.name` field of
+ |nvim_get_chan_info()|.
• {version} Dict describing the version, with these (optional) keys:
• "major" major version (defaults to 0 if not set, for
no release yet)
@@ -1636,11 +1594,9 @@ nvim_command({command}) *nvim_command()*
On execution error: fails with Vimscript error, updates v:errmsg.
- Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate
- multiple lines of Vim script or an Ex command directly, use
- |nvim_exec2()|. To construct an Ex command using a structured format and
- then execute it, use |nvim_cmd()|. To modify an Ex command before
- evaluating it, use |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
+ Prefer |nvim_cmd()| or |nvim_exec2()| instead. To modify an Ex command in
+ a structured way before executing it, modify the result of
+ |nvim_parse_cmd()| then pass it to |nvim_cmd()|.
Parameters: ~
• {command} Ex command string
@@ -2161,12 +2117,12 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()*
This temporarily switches current buffer to "buffer". If the current
window already shows "buffer", the window is not switched. If a window
inside the current tabpage (including a float) already shows the buffer,
- then one of these windows will be set as current window temporarily.
+ then one of those windows will be set as current window temporarily.
Otherwise a temporary scratch window (called the "autocmd window" for
historical reasons) will be used.
This is useful e.g. to call Vimscript functions that only work with the
- current buffer/window currently, like |termopen()|.
+ current buffer/window currently, like `jobstart(…, {'term': v:true})`.
Attributes: ~
Lua |vim.api| only
@@ -2507,42 +2463,6 @@ nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
==============================================================================
Extmark Functions *api-extmark*
- *nvim_buf_add_highlight()*
-nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start},
- {col_end})
- Adds a highlight to buffer.
-
- Useful for plugins that dynamically generate highlights to a buffer (like
- a semantic highlighter or linter). The function adds a single highlight to
- a buffer. Unlike |matchaddpos()| highlights follow changes to line
- numbering (as lines are inserted/removed above the highlighted line), like
- signs and marks do.
-
- Namespaces are used for batch deletion/updating of a set of highlights. To
- create a namespace, use |nvim_create_namespace()| which returns a
- namespace id. Pass it in to this function as `ns_id` to add highlights to
- the namespace. All highlights in the same namespace can then be cleared
- with single call to |nvim_buf_clear_namespace()|. If the highlight never
- will be deleted by an API call, pass `ns_id = -1`.
-
- As a shorthand, `ns_id = 0` can be used to create a new namespace for the
- highlight, the allocated id is then returned. If `hl_group` is the empty
- string no highlight is added, but a new `ns_id` is still returned. This is
- supported for backwards compatibility, new code should use
- |nvim_create_namespace()| to create a new empty namespace.
-
- Parameters: ~
- • {buffer} Buffer handle, or 0 for current buffer
- • {ns_id} namespace to use or -1 for ungrouped highlight
- • {hl_group} Name of the highlight group to use
- • {line} Line to highlight (zero-indexed)
- • {col_start} Start of (byte-indexed) column range to highlight
- • {col_end} End of (byte-indexed) column range to highlight, or -1 to
- highlight to end of line
-
- Return: ~
- The ns_id that was used
-
*nvim_buf_clear_namespace()*
nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end})
Clears |namespace|d objects (highlights, |extmarks|, virtual text) from a
@@ -2676,6 +2596,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts})
and below highlight groups can be supplied either as a
string or as an integer, the latter of which can be
obtained using |nvim_get_hl_id_by_name()|.
+ Multiple highlight groups can be stacked by passing an
+ array (highest priority last).
• hl_eol : when true, for a multiline highlight covering the
EOL of a line, continue the highlight for the rest of the
screen line (just like for diff and cursorline highlight).
@@ -2687,6 +2609,13 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts})
last).
• virt_text_pos : position of virtual text. Possible values:
• "eol": right after eol character (default).
+ • "eol_right_align": display right aligned in the window
+ unless the virtual text is longer than the space
+ available. If the virtual text is too long, it is
+ truncated to fit in the window after the EOL character.
+ If the line is wrapped, the virtual text is shown after
+ the end of the line rather than the previous screen
+ line.
• "overlay": display over the specified column, without
shifting the underlying text.
• "right_align": display right aligned in the window.
@@ -2780,7 +2709,7 @@ nvim_create_namespace({name}) *nvim_create_namespace()*
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()|.
+ |nvim_buf_set_extmark()|.
Namespaces can be named or anonymous. If `name` matches an existing
namespace, the associated id is returned. If `name` is an empty string a
@@ -2838,8 +2767,8 @@ nvim_set_decoration_provider({ns_id}, {opts})
• 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 (once per
+ edit, before window callbacks) >
["buf", bufnr, tick]
<
• on_win: called when starting to redraw a specific window. >
@@ -3166,11 +3095,13 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
• {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
+ • "cursor" Cursor position in current window.
+ • "editor" The global editor grid.
+ • "laststatus" 'laststatus' if present, or last row.
+ • "mouse" Mouse position.
+ • "tabline" Tabline if present, or first row.
• "win" Window given by the `win` field, or current
window.
- • "cursor" Cursor position in current window.
- • "mouse" Mouse position
• win: |window-ID| window to split, or relative window when
creating a float (relative="win").
• anchor: Decides which corner of the float to place at
@@ -3483,9 +3414,9 @@ nvim_create_autocmd({event}, {opts}) *nvim_create_autocmd()*
• 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>
+ • file: (string) <afile> (not expanded to a full path)
+ • match: (string) <amatch> (expanded to a full path)
+ • buf: (number) <abuf>
• data: (any) arbitrary data passed from
|nvim_exec_autocmds()| *event-data*
• command (string) optional: Vim command to execute on event.
@@ -3580,33 +3511,35 @@ nvim_get_autocmds({opts}) *nvim_get_autocmds()*
Parameters: ~
• {opts} Dict 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
+ • buffer: (integer) Buffer number or list of buffer numbers
+ for buffer local autocommands |autocmd-buflocal|. Cannot be
+ used with {pattern}
+ • event: (string|table) event or events to match against
|autocmd-events|.
- • pattern (string|array): pattern or patterns to match against
+ • id: (integer) Autocommand ID to match.
+ • group: (string|table) the autocommand group name or id to
+ match against.
+ • pattern: (string|table) 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).
- • group (integer): the autocommand group id.
- • group_name (string): the autocommand group name.
- • desc (string): the autocommand description.
- • event (string): the autocommand event.
- • command (string): the autocommand command. Note: this will be empty
+ • buffer: (integer) the buffer number.
+ • buflocal: (boolean) true if the autocommand is buffer local.
+ • command: (string) the autocommand command. Note: this will be empty
if a callback is set.
- • callback (function|string|nil): Lua function or name of a Vim script
- function which is executed when this autocommand is triggered.
- • once (boolean): whether the autocommand is only run once.
- • pattern (string): the autocommand pattern. If the autocommand is
+ • callback: (function|string|nil): Lua function or name of a Vim
+ script function which is executed when this autocommand is
+ triggered.
+ • desc: (string) the autocommand description.
+ • event: (string) the autocommand event.
+ • id: (integer) the autocommand id (only when defined with the API).
+ • group: (integer) the autocommand group id.
+ • group_name: (string) the autocommand group name.
+ • once: (boolean) whether the autocommand is only run once.
+ • pattern: (string) the autocommand pattern. If the autocommand is
buffer local |autocmd-buffer-local|:
- • buflocal (boolean): true if the autocommand is buffer local.
- • buffer (number): the buffer number.
==============================================================================
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 998ef5e9f3..c094281154 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -463,6 +463,10 @@ CompleteDone After Insert mode completion is done. Either
|v:completed_item| gives the completed item.
Sets these |v:event| keys:
+ complete_word The word that was
+ selected, empty if
+ abandoned complete.
+ complete_type |complete_info_mode|
reason Reason for completion being
done. Can be one of:
- "accept": completion was
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index ada3b7103c..96574e2899 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1174,16 +1174,22 @@ complete_info([{what}]) *complete_info()*
See |complete_info_mode| for the values.
pum_visible |TRUE| if popup menu is visible.
See |pumvisible()|.
- items List of completion matches. Each item is a
- dictionary containing the entries "word",
+ items List of all completion candidates. Each item
+ is a dictionary containing the entries "word",
"abbr", "menu", "kind", "info" and "user_data".
See |complete-items|.
+ matches Same as "items", but only returns items that
+ are matching current query. If both "matches"
+ and "items" are in "what", the returned list
+ will still be named "items", but each item
+ will have an additional "match" field.
selected Selected item index. First index is zero.
Index is -1 if no item is selected (showing
typed text only, or the last completion after
no item is selected when using the <Up> or
<Down> keys)
- inserted Inserted string. [NOT IMPLEMENTED YET]
+ completed Return a dictionary containing the entries of
+ the currently selected index item.
preview_winid Info floating preview window id.
preview_bufnr Info floating preview buffer id.
@@ -1305,10 +1311,10 @@ copy({expr}) *copy()*
Also see |deepcopy()|.
Parameters: ~
- • {expr} (`any`)
+ • {expr} (`T`)
Return: ~
- (`any`)
+ (`T`)
cos({expr}) *cos()*
Return the cosine of {expr}, measured in radians, as a |Float|.
@@ -1407,7 +1413,7 @@ ctxset({context} [, {index}]) *ctxset()*
• {index} (`integer?`)
Return: ~
- (`any`)
+ (`integer`)
ctxsize() *ctxsize()*
Returns the size of the |context-stack|.
@@ -1490,11 +1496,11 @@ deepcopy({expr} [, {noref}]) *deepcopy()* *E69
Also see |copy()|.
Parameters: ~
- • {expr} (`any`)
+ • {expr} (`T`)
• {noref} (`boolean?`)
Return: ~
- (`any`)
+ (`T`)
delete({fname} [, {flags}]) *delete()*
Without {flags} or with {flags} empty: Deletes the file by the
@@ -1618,7 +1624,7 @@ did_filetype() *did_filetype()*
file.
Return: ~
- (`any`)
+ (`integer`)
diff_filler({lnum}) *diff_filler()*
Returns the number of filler lines above line {lnum}.
@@ -1633,7 +1639,7 @@ diff_filler({lnum}) *diff_filler()*
• {lnum} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
diff_hlID({lnum}, {col}) *diff_hlID()*
Returns the highlight ID for diff mode at line {lnum} column
@@ -1674,7 +1680,7 @@ digraph_get({chars}) *digraph_get()* *E121
• {chars} (`string`)
Return: ~
- (`any`)
+ (`string`)
digraph_getlist([{listall}]) *digraph_getlist()*
Return a list of digraphs. If the {listall} argument is given
@@ -1695,7 +1701,7 @@ digraph_getlist([{listall}]) *digraph_getlist()*
• {listall} (`boolean?`)
Return: ~
- (`any`)
+ (`string[][]`)
digraph_set({chars}, {digraph}) *digraph_set()*
Add digraph {chars} to the list. {chars} must be a string
@@ -1756,7 +1762,7 @@ empty({expr}) *empty()*
• {expr} (`any`)
Return: ~
- (`any`)
+ (`integer`)
environ() *environ()*
Return all of environment variables as dictionary. You can
@@ -1783,7 +1789,7 @@ escape({string}, {chars}) *escape()*
• {chars} (`string`)
Return: ~
- (`any`)
+ (`string`)
eval({string}) *eval()*
Evaluate {string} and return the result. Especially useful to
@@ -2666,7 +2672,7 @@ foreach({expr1}, {expr2}) *foreach()*
• {expr2} (`string|function`)
Return: ~
- (`any`)
+ (`string|table`)
fullcommand({name}) *fullcommand()*
Get the full command name from a short abbreviated command
@@ -2997,10 +3003,10 @@ getbufline({buf}, {lnum} [, {end}]) *getbufline()*
Parameters: ~
• {buf} (`integer|string`)
• {lnum} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
- (`any`)
+ (`string[]`)
getbufoneline({buf}, {lnum}) *getbufoneline()*
Just like `getbufline()` but only get one line and return it
@@ -3073,14 +3079,16 @@ getchangelist([{buf}]) *getchangelist()*
Return: ~
(`table[]`)
-getchar([{expr}]) *getchar()*
+getchar([{expr} [, {opts}]]) *getchar()*
Get a single character from the user or input stream.
- If {expr} is omitted, wait until a character is available.
+ If {expr} is omitted or is -1, wait until a character is
+ available.
If {expr} is 0, only get a character when one is available.
Return zero otherwise.
If {expr} is 1, only check if a character is available, it is
not consumed. Return zero if no character available.
- If you prefer always getting a string use |getcharstr()|.
+ If you prefer always getting a string use |getcharstr()|, or
+ specify |FALSE| as "number" in {opts}.
Without {expr} and when {expr} is 0 a whole character or
special key is returned. If it is a single character, the
@@ -3090,7 +3098,8 @@ getchar([{expr}]) *getchar()*
starting with 0x80 (decimal: 128). This is the same value as
the String "\<Key>", e.g., "\<Left>". The returned value is
also a String when a modifier (shift, control, alt) was used
- that is not included in the character.
+ that is not included in the character. |keytrans()| can also
+ be used to convert a returned String into a readable form.
When {expr} is 0 and Esc is typed, there will be a short delay
while Vim waits to see if this is the start of an escape
@@ -3102,6 +3111,32 @@ getchar([{expr}]) *getchar()*
Use getcharmod() to obtain any additional modifiers.
+ The optional argument {opts} is a Dict and supports the
+ following items:
+
+ cursor A String specifying cursor behavior
+ when waiting for a character.
+ "hide": hide the cursor.
+ "keep": keep current cursor unchanged.
+ "msg": move cursor to message area.
+ (default: automagically decide
+ between "keep" and "msg")
+
+ number If |TRUE|, return a Number when getting
+ a single character.
+ If |FALSE|, the return value is always
+ converted to a String, and an empty
+ String (instead of 0) is returned when
+ no character is available.
+ (default: |TRUE|)
+
+ simplify If |TRUE|, include modifiers in the
+ character if possible. E.g., return
+ the same value for CTRL-I and <Tab>.
+ If |FALSE|, don't include modifiers in
+ the character.
+ (default: |TRUE|)
+
When the user clicks a mouse button, the mouse event will be
returned. The position can then be found in |v:mouse_col|,
|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -3139,10 +3174,11 @@ getchar([{expr}]) *getchar()*
<
Parameters: ~
- • {expr} (`0|1?`)
+ • {expr} (`-1|0|1?`)
+ • {opts} (`table?`)
Return: ~
- (`integer`)
+ (`integer|string`)
getcharmod() *getcharmod()*
The result is a Number which is the state of the modifiers for
@@ -3207,19 +3243,12 @@ getcharsearch() *getcharsearch()*
(`table`)
getcharstr([{expr}]) *getcharstr()*
- Get a single character from the user or input stream as a
- string.
- If {expr} is omitted, wait until a character is available.
- If {expr} is 0 or false, only get a character when one is
- available. Return an empty string otherwise.
- If {expr} is 1 or true, only check if a character is
- available, it is not consumed. Return an empty string
- if no character is available.
- Otherwise this works like |getchar()|, except that a number
- result is converted to a string.
+ The same as |getchar()|, except that this always returns a
+ String, and "number" isn't allowed in {opts}.
Parameters: ~
- • {expr} (`0|1?`)
+ • {expr} (`-1|0|1?`)
+ • {opts} (`table?`)
Return: ~
(`string`)
@@ -3295,7 +3324,7 @@ getcmdscreenpos() *getcmdscreenpos()*
|setcmdline()|.
Return: ~
- (`any`)
+ (`integer`)
getcmdtype() *getcmdtype()*
Return the current command-line type. Possible return values
@@ -3635,7 +3664,7 @@ getline({lnum} [, {end}]) *getline()*
Parameters: ~
• {lnum} (`integer|string`)
- • {end_} (`nil|false?`)
+ • {end} (`nil|false?`)
Return: ~
(`string`)
@@ -4177,6 +4206,21 @@ getscriptinfo([{opts}]) *getscriptinfo()*
Return: ~
(`vim.fn.getscriptinfo.ret[]`)
+getstacktrace() *getstacktrace()*
+ Returns the current stack trace of Vim scripts.
+ Stack trace is a |List|, of which each item is a |Dictionary|
+ with the following items:
+ funcref The funcref if the stack is at a function,
+ otherwise this item is omitted.
+ event The string of the event description if the
+ stack is at an autocmd event, otherwise this
+ item is omitted.
+ lnum The line number in the script on the stack.
+ filepath The file path of the script on the stack.
+
+ Return: ~
+ (`table[]`)
+
gettabinfo([{tabnr}]) *gettabinfo()*
If {tabnr} is not specified, then information about all the
tab pages is returned as a |List|. Each List item is a
@@ -4299,7 +4343,7 @@ gettext({text}) *gettext()*
• {text} (`string`)
Return: ~
- (`any`)
+ (`string`)
getwininfo([{winid}]) *getwininfo()*
Returns information about windows as a |List| with Dictionaries.
@@ -4315,6 +4359,8 @@ getwininfo([{winid}]) *getwininfo()*
botline last complete displayed buffer line
bufnr number of buffer in the window
height window height (excluding winbar)
+ leftcol first column displayed; only used when
+ 'wrap' is off
loclist 1 if showing a location list
quickfix 1 if quickfix or location list window
terminal 1 if a terminal window
@@ -4465,7 +4511,7 @@ glob2regpat({string}) *glob2regpat()*
• {string} (`string`)
Return: ~
- (`any`)
+ (`string`)
globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]]) *globpath()*
Perform glob() for String {expr} on all directories in {path}
@@ -4822,7 +4868,7 @@ iconv({string}, {from}, {to}) *iconv()*
• {to} (`string`)
Return: ~
- (`any`)
+ (`string`)
id({expr}) *id()*
Returns a |String| which is a unique identifier of the
@@ -4845,7 +4891,7 @@ id({expr}) *id()*
• {expr} (`any`)
Return: ~
- (`any`)
+ (`string`)
indent({lnum}) *indent()*
The result is a Number, which is indent of line {lnum} in the
@@ -4895,7 +4941,7 @@ index({object}, {expr} [, {start} [, {ic}]]) *index()*
• {ic} (`boolean?`)
Return: ~
- (`any`)
+ (`integer`)
indexof({object}, {expr} [, {opts}]) *indexof()*
Returns the index of an item in {object} where {expr} is
@@ -4942,7 +4988,7 @@ indexof({object}, {expr} [, {opts}]) *indexof()*
• {opts} (`table?`)
Return: ~
- (`any`)
+ (`integer`)
input({prompt} [, {text} [, {completion}]]) *input()*
@@ -4952,7 +4998,7 @@ input({prompt} [, {text} [, {completion}]]) *input()*
• {completion} (`string?`)
Return: ~
- (`any`)
+ (`string`)
input({opts})
The result is a String, which is whatever the user typed on
@@ -5069,7 +5115,7 @@ input({opts})
• {opts} (`table`)
Return: ~
- (`any`)
+ (`string`)
inputlist({textlist}) *inputlist()*
{textlist} must be a |List| of strings. This |List| is
@@ -5101,7 +5147,7 @@ inputrestore() *inputrestore()*
Returns TRUE when there is nothing to restore, FALSE otherwise.
Return: ~
- (`any`)
+ (`integer`)
inputsave() *inputsave()*
Preserve typeahead (also from mappings) and clear it, so that
@@ -5112,7 +5158,7 @@ inputsave() *inputsave()*
Returns TRUE when out of memory, FALSE otherwise.
Return: ~
- (`any`)
+ (`integer`)
inputsecret({prompt} [, {text}]) *inputsecret()*
This function acts much like the |input()| function with but
@@ -5130,7 +5176,7 @@ inputsecret({prompt} [, {text}]) *inputsecret()*
• {text} (`string?`)
Return: ~
- (`any`)
+ (`string`)
insert({object}, {item} [, {idx}]) *insert()*
When {object} is a |List| or a |Blob| insert {item} at the start
@@ -5181,10 +5227,10 @@ invert({expr}) *invert()*
<
Parameters: ~
- • {expr} (`number`)
+ • {expr} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
isabsolutepath({path}) *isabsolutepath()*
The result is a Number, which is |TRUE| when {path} is an
@@ -5279,7 +5325,7 @@ items({dict}) *items()*
the index.
Parameters: ~
- • {dict} (`any`)
+ • {dict} (`table`)
Return: ~
(`any`)
@@ -5307,7 +5353,7 @@ jobresize({job}, {width}, {height}) *jobresize()*
(`any`)
jobstart({cmd} [, {opts}]) *jobstart()*
- Note: Prefer |vim.system()| in Lua (unless using the `pty` option).
+ Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`).
Spawns {cmd} as a job.
If {cmd} is a List it runs directly (no 'shell').
@@ -5315,8 +5361,11 @@ jobstart({cmd} [, {opts}]) *jobstart()*
call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
< (See |shell-unquoting| for details.)
- Example: >vim
- call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
+ Example: start a job and handle its output: >vim
+ call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}})
+<
+ Example: start a job in a |terminal| connected to the current buffer: >vim
+ call jobstart(['nvim', '-h'], {'term':v:true})
<
Returns |job-id| on success, 0 on invalid arguments (or job
table is full), -1 if {cmd}[0] or 'shell' is not executable.
@@ -5381,6 +5430,10 @@ jobstart({cmd} [, {opts}]) *jobstart()*
stdin: (string) Either "pipe" (default) to connect the
job's stdin to a channel or "null" to disconnect
stdin.
+ term: (boolean) Spawns {cmd} in a new pseudo-terminal session
+ connected to the current (unmodified) buffer. Implies "pty".
+ Default "height" and "width" are set to the current window
+ dimensions. |jobstart()|. Defaults $TERM to "xterm-256color".
width: (number) Width of the `pty` terminal.
{opts} is passed as |self| dictionary to the callback; the
@@ -5397,7 +5450,7 @@ jobstart({cmd} [, {opts}]) *jobstart()*
• {opts} (`table?`)
Return: ~
- (`any`)
+ (`integer`)
jobstop({id}) *jobstop()*
Stop |job-id| {id} by sending SIGTERM to the job process. If
@@ -5413,7 +5466,7 @@ jobstop({id}) *jobstop()*
• {id} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
jobwait({jobs} [, {timeout}]) *jobwait()*
Waits for jobs and their |on_exit| handlers to complete.
@@ -5441,7 +5494,7 @@ jobwait({jobs} [, {timeout}]) *jobwait()*
• {timeout} (`integer?`)
Return: ~
- (`any`)
+ (`integer[]`)
join({list} [, {sep}]) *join()*
Join the items in {list} together into one String.
@@ -5459,7 +5512,7 @@ join({list} [, {sep}]) *join()*
• {sep} (`string?`)
Return: ~
- (`any`)
+ (`string`)
json_decode({expr}) *json_decode()*
Convert {expr} from JSON object. Accepts |readfile()|-style
@@ -5498,7 +5551,7 @@ json_encode({expr}) *json_encode()*
• {expr} (`any`)
Return: ~
- (`any`)
+ (`string`)
keys({dict}) *keys()*
Return a |List| with all the keys of {dict}. The |List| is in
@@ -5508,7 +5561,7 @@ keys({dict}) *keys()*
• {dict} (`table`)
Return: ~
- (`any`)
+ (`string[]`)
keytrans({string}) *keytrans()*
Turn the internal byte representation of keys into a form that
@@ -5521,7 +5574,7 @@ keytrans({string}) *keytrans()*
• {string} (`string`)
Return: ~
- (`any`)
+ (`string`)
len({expr}) *len()* *E701*
The result is a Number, which is the length of the argument.
@@ -5535,10 +5588,10 @@ len({expr}) *len()* *E70
Otherwise an error is given and returns zero.
Parameters: ~
- • {expr} (`any`)
+ • {expr} (`any[]`)
Return: ~
- (`any`)
+ (`integer`)
libcall({libname}, {funcname}, {argument}) *libcall()* *E364* *E368*
Call function {funcname} in the run-time library {libname}
@@ -5664,7 +5717,7 @@ lispindent({lnum}) *lispindent()*
• {lnum} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
list2blob({list}) *list2blob()*
Return a Blob concatenating all the number values in {list}.
@@ -5680,7 +5733,7 @@ list2blob({list}) *list2blob()*
• {list} (`any[]`)
Return: ~
- (`any`)
+ (`string`)
list2str({list} [, {utf8}]) *list2str()*
Convert each number in {list} to a character string can
@@ -5703,14 +5756,14 @@ list2str({list} [, {utf8}]) *list2str()*
• {utf8} (`boolean?`)
Return: ~
- (`any`)
+ (`string`)
localtime() *localtime()*
Return the current time, measured as seconds since 1st Jan
1970. See also |strftime()|, |strptime()| and |getftime()|.
Return: ~
- (`any`)
+ (`integer`)
log({expr}) *log()*
Return the natural logarithm (base e) of {expr} as a |Float|.
@@ -5727,7 +5780,7 @@ log({expr}) *log()*
• {expr} (`number`)
Return: ~
- (`any`)
+ (`number`)
log10({expr}) *log10()*
Return the logarithm of Float {expr} to base 10 as a |Float|.
@@ -5743,7 +5796,7 @@ log10({expr}) *log10()*
• {expr} (`number`)
Return: ~
- (`any`)
+ (`number`)
luaeval({expr} [, {expr}]) *luaeval()*
Evaluate Lua expression {expr} and return its result converted
@@ -6290,7 +6343,7 @@ matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}]) *matchbufline()*
• {buf} (`string|integer`)
• {pat} (`string`)
• {lnum} (`string|integer`)
- • {end_} (`string|integer`)
+ • {end} (`string|integer`)
• {dict} (`table?`)
Return: ~
@@ -6565,7 +6618,7 @@ max({expr}) *max()*
• {expr} (`any`)
Return: ~
- (`any`)
+ (`number`)
menu_get({path} [, {modes}]) *menu_get()*
Returns a |List| of |Dictionaries| describing |menus| (defined
@@ -6712,7 +6765,7 @@ min({expr}) *min()*
• {expr} (`any`)
Return: ~
- (`any`)
+ (`number`)
mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E739*
Create directory {name}.
@@ -6760,7 +6813,7 @@ mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E73
• {prot} (`string?`)
Return: ~
- (`any`)
+ (`integer`)
mode([{expr}]) *mode()*
Return a string that indicates the current mode.
@@ -6935,7 +6988,7 @@ nextnonblank({lnum}) *nextnonblank()*
• {lnum} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
nr2char({expr} [, {utf8}]) *nr2char()*
Return a string with a single character, which has the number
@@ -6957,7 +7010,7 @@ nr2char({expr} [, {utf8}]) *nr2char()*
• {utf8} (`boolean?`)
Return: ~
- (`any`)
+ (`string`)
nvim_...({...}) *nvim_...()* *E5555* *eval-api*
Call nvim |api| functions. The type checking of arguments will
@@ -7014,7 +7067,7 @@ pathshorten({path} [, {len}]) *pathshorten()*
• {len} (`integer?`)
Return: ~
- (`any`)
+ (`string`)
perleval({expr}) *perleval()*
Evaluate |perl| expression {expr} and return its result
@@ -7054,7 +7107,7 @@ pow({x}, {y}) *pow()*
• {y} (`number`)
Return: ~
- (`any`)
+ (`number`)
prevnonblank({lnum}) *prevnonblank()*
Return the line number of the first line at or above {lnum}
@@ -7069,7 +7122,7 @@ prevnonblank({lnum}) *prevnonblank()*
• {lnum} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
printf({fmt}, {expr1} ...) *printf()*
Return a String with {fmt}, where "%" items are replaced by
@@ -7731,11 +7784,11 @@ reduce({object}, {func} [, {initial}]) *reduce()* *E99
Parameters: ~
• {object} (`any`)
- • {func} (`function`)
+ • {func} (`fun(accumulator: T, current: any): any`)
• {initial} (`any?`)
Return: ~
- (`any`)
+ (`T`)
reg_executing() *reg_executing()*
Returns the single letter name of the register being executed.
@@ -7784,7 +7837,7 @@ reltime({start}, {end})
Parameters: ~
• {start} (`any?`)
- • {end_} (`any?`)
+ • {end} (`any?`)
Return: ~
(`any`)
@@ -7845,7 +7898,7 @@ remove({list}, {idx}, {end})
Parameters: ~
• {list} (`any[]`)
• {idx} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
@@ -7867,7 +7920,7 @@ remove({blob}, {idx}, {end})
Parameters: ~
• {blob} (`any`)
• {idx} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
@@ -7899,7 +7952,7 @@ rename({from}, {to}) *rename()*
• {to} (`string`)
Return: ~
- (`any`)
+ (`integer`)
repeat({expr}, {count}) *repeat()*
Repeat {expr} {count} times and return the concatenated
@@ -7935,7 +7988,7 @@ resolve({filename}) *resolve()* *E65
• {filename} (`string`)
Return: ~
- (`any`)
+ (`string`)
reverse({object}) *reverse()*
Reverse the order of items in {object}. {object} can be a
@@ -7949,10 +8002,10 @@ reverse({object}) *reverse()*
<
Parameters: ~
- • {object} (`any`)
+ • {object} (`T[]`)
Return: ~
- (`any`)
+ (`T[]`)
round({expr}) *round()*
Round off {expr} to the nearest integral value and return it
@@ -7972,7 +8025,7 @@ round({expr}) *round()*
• {expr} (`number`)
Return: ~
- (`any`)
+ (`number`)
rpcnotify({channel}, {event} [, {args}...]) *rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately.
@@ -7984,10 +8037,10 @@ rpcnotify({channel}, {event} [, {args}...]) *rpcnotify()*
Parameters: ~
• {channel} (`integer`)
• {event} (`string`)
- • {args} (`any?`)
+ • {...} (`any`)
Return: ~
- (`any`)
+ (`integer`)
rpcrequest({channel}, {method} [, {args}...]) *rpcrequest()*
Sends a request to {channel} to invoke {method} via
@@ -7999,7 +8052,7 @@ rpcrequest({channel}, {method} [, {args}...]) *rpcrequest()*
Parameters: ~
• {channel} (`integer`)
• {method} (`string`)
- • {args} (`any?`)
+ • {...} (`any`)
Return: ~
(`any`)
@@ -8031,7 +8084,7 @@ screenattr({row}, {col}) *screenattr()*
• {col} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
screenchar({row}, {col}) *screenchar()*
The result is a Number, which is the character at position
@@ -8048,7 +8101,7 @@ screenchar({row}, {col}) *screenchar()*
• {col} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
screenchars({row}, {col}) *screenchars()*
The result is a |List| of Numbers. The first number is the same
@@ -8062,7 +8115,7 @@ screenchars({row}, {col}) *screenchars()*
• {col} (`integer`)
Return: ~
- (`any`)
+ (`integer[]`)
screencol() *screencol()*
The result is a Number, which is the current screen column of
@@ -8080,7 +8133,7 @@ screencol() *screencol()*
<
Return: ~
- (`any`)
+ (`integer[]`)
screenpos({winid}, {lnum}, {col}) *screenpos()*
The result is a Dict with the screen position of the text
@@ -8123,7 +8176,7 @@ screenrow() *screenrow()*
Note: Same restrictions as with |screencol()|.
Return: ~
- (`any`)
+ (`integer`)
screenstring({row}, {col}) *screenstring()*
The result is a String that contains the base character and
@@ -8138,7 +8191,7 @@ screenstring({row}, {col}) *screenstring()*
• {col} (`integer`)
Return: ~
- (`any`)
+ (`string`)
search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) *search()*
Search for regexp pattern {pattern}. The search starts at the
@@ -8253,7 +8306,7 @@ search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) *search()*
• {skip} (`string|function?`)
Return: ~
- (`any`)
+ (`integer`)
searchcount([{options}]) *searchcount()*
Get or update the last search count, like what is displayed
@@ -8498,7 +8551,7 @@ searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeo
Parameters: ~
• {start} (`string`)
• {middle} (`string`)
- • {end_} (`string`)
+ • {end} (`string`)
• {flags} (`string?`)
• {skip} (`string|function?`)
• {stopline} (`integer?`)
@@ -8522,7 +8575,7 @@ searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {ti
Parameters: ~
• {start} (`string`)
• {middle} (`string`)
- • {end_} (`string`)
+ • {end} (`string`)
• {flags} (`string?`)
• {skip} (`string|function?`)
• {stopline} (`integer?`)
@@ -8565,7 +8618,7 @@ serverlist() *serverlist()*
<
Return: ~
- (`any`)
+ (`string[]`)
serverstart([{address}]) *serverstart()*
Opens a socket or named pipe at {address} and listens for
@@ -8605,7 +8658,7 @@ serverstart([{address}]) *serverstart()*
• {address} (`string?`)
Return: ~
- (`any`)
+ (`string`)
serverstop({address}) *serverstop()*
Closes the pipe or socket at {address}.
@@ -8617,7 +8670,7 @@ serverstop({address}) *serverstop()*
• {address} (`string`)
Return: ~
- (`any`)
+ (`integer`)
setbufline({buf}, {lnum}, {text}) *setbufline()*
Set line {lnum} to {text} in buffer {buf}. This works like
@@ -8650,7 +8703,7 @@ setbufline({buf}, {lnum}, {text}) *setbufline()*
• {text} (`string|string[]`)
Return: ~
- (`any`)
+ (`integer`)
setbufvar({buf}, {varname}, {val}) *setbufvar()*
Set option or local variable {varname} in buffer {buf} to
@@ -8770,7 +8823,7 @@ setcmdline({str} [, {pos}]) *setcmdline()*
• {pos} (`integer?`)
Return: ~
- (`any`)
+ (`integer`)
setcmdpos({pos}) *setcmdpos()*
Set the cursor position in the command line to byte position
@@ -9102,7 +9155,7 @@ setqflist({list} [, {action} [, {what}]]) *setqflist()*
• {what} (`vim.fn.setqflist.what?`)
Return: ~
- (`any`)
+ (`integer`)
setreg({regname}, {value} [, {options}]) *setreg()*
Set the register {regname} to {value}.
@@ -9273,7 +9326,7 @@ sha256({string}) *sha256()*
• {string} (`string`)
Return: ~
- (`any`)
+ (`string`)
shellescape({string} [, {special}]) *shellescape()*
Escape {string} for use as a shell command argument.
@@ -9312,7 +9365,7 @@ shellescape({string} [, {special}]) *shellescape()*
• {special} (`boolean?`)
Return: ~
- (`any`)
+ (`string`)
shiftwidth([{col}]) *shiftwidth()*
Returns the effective value of 'shiftwidth'. This is the
@@ -9790,7 +9843,7 @@ simplify({filename}) *simplify()*
• {filename} (`string`)
Return: ~
- (`any`)
+ (`string`)
sin({expr}) *sin()*
Return the sine of {expr}, measured in radians, as a |Float|.
@@ -9806,7 +9859,7 @@ sin({expr}) *sin()*
• {expr} (`number`)
Return: ~
- (`any`)
+ (`number`)
sinh({expr}) *sinh()*
Return the hyperbolic sine of {expr} as a |Float| in the range
@@ -9838,7 +9891,7 @@ slice({expr}, {start} [, {end}]) *slice()*
Parameters: ~
• {expr} (`any`)
• {start} (`integer`)
- • {end_} (`integer?`)
+ • {end} (`integer?`)
Return: ~
(`any`)
@@ -9950,12 +10003,12 @@ sort({list} [, {how} [, {dict}]]) *sort()* *E70
<
Parameters: ~
- • {list} (`any`)
+ • {list} (`T[]`)
• {how} (`string|function?`)
• {dict} (`any?`)
Return: ~
- (`any`)
+ (`T[]`)
soundfold({word}) *soundfold()*
Return the sound-folded equivalent of {word}. Uses the first
@@ -9969,7 +10022,7 @@ soundfold({word}) *soundfold()*
• {word} (`string`)
Return: ~
- (`any`)
+ (`string`)
spellbadword([{sentence}]) *spellbadword()*
Without argument: The result is the badly spelled word under
@@ -10028,7 +10081,7 @@ spellsuggest({word} [, {max} [, {capital}]]) *spellsuggest()*
• {capital} (`boolean?`)
Return: ~
- (`any`)
+ (`string[]`)
split({string} [, {pattern} [, {keepempty}]]) *split()*
Make a |List| out of {string}. When {pattern} is omitted or
@@ -10061,7 +10114,7 @@ split({string} [, {pattern} [, {keepempty}]]) *split()*
• {keepempty} (`boolean?`)
Return: ~
- (`any`)
+ (`string[]`)
sqrt({expr}) *sqrt()*
Return the non-negative square root of Float {expr} as a
@@ -10232,6 +10285,7 @@ str2list({string} [, {utf8}]) *str2list()*
and exists only for backwards-compatibility.
With UTF-8 composing characters are handled properly: >vim
echo str2list("á") " returns [97, 769]
+<
Parameters: ~
• {string} (`string`)
@@ -11165,28 +11219,6 @@ tempname() *tempname()*
Return: ~
(`string`)
-termopen({cmd} [, {opts}]) *termopen()*
- Spawns {cmd} in a new pseudo-terminal session connected
- to the current (unmodified) buffer. Parameters and behavior
- are the same as |jobstart()| except "pty", "width", "height",
- and "TERM" are ignored: "height" and "width" are taken from
- the current window. Note that termopen() implies a "pty" arg
- to jobstart(), and thus has the implications documented at
- |jobstart()|.
-
- Returns the same values as jobstart().
-
- Terminal environment is initialized as in |jobstart-env|,
- except $TERM is set to "xterm-256color". Full behavior is
- described in |terminal|.
-
- Parameters: ~
- • {cmd} (`string|string[]`)
- • {opts} (`table?`)
-
- Return: ~
- (`any`)
-
test_garbagecollect_now() *test_garbagecollect_now()*
Like |garbagecollect()|, but executed right away. This must
only be called directly to avoid any structure to exist
@@ -11646,7 +11678,7 @@ virtcol2col({winid}, {lnum}, {col}) *virtcol2col()*
• {col} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
visualmode([{expr}]) *visualmode()*
The result is a String, which describes the last Visual mode
@@ -11670,7 +11702,7 @@ visualmode([{expr}]) *visualmode()*
• {expr} (`boolean?`)
Return: ~
- (`any`)
+ (`string`)
wait({timeout}, {condition} [, {interval}]) *wait()*
Waits until {condition} evaluates to |TRUE|, where {condition}
@@ -11812,7 +11844,7 @@ win_id2win({expr}) *win_id2win()*
• {expr} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
win_move_separator({nr}, {offset}) *win_move_separator()*
Move window {nr}'s vertical separator (i.e., the right border)
@@ -11989,7 +12021,7 @@ winlayout([{tabnr}]) *winlayout()*
• {tabnr} (`integer?`)
Return: ~
- (`any`)
+ (`any[]`)
winline() *winline()*
The result is a Number, which is the screen line of the cursor
@@ -12037,7 +12069,7 @@ winnr([{arg}]) *winnr()*
• {arg} (`string|integer?`)
Return: ~
- (`any`)
+ (`integer`)
winrestcmd() *winrestcmd()*
Returns a sequence of |:resize| commands that should restore
@@ -12051,7 +12083,7 @@ winrestcmd() *winrestcmd()*
<
Return: ~
- (`any`)
+ (`string`)
winrestview({dict}) *winrestview()*
Uses the |Dictionary| returned by |winsaveview()| to restore
@@ -12123,7 +12155,7 @@ winwidth({nr}) *winwidth()*
• {nr} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
wordcount() *wordcount()*
The result is a dictionary of byte/chars/word statistics for
@@ -12213,11 +12245,11 @@ xor({expr}, {expr}) *xor()*
<
Parameters: ~
- • {expr} (`number`)
- • {expr1} (`number`)
+ • {expr} (`integer`)
+ • {expr1} (`integer`)
Return: ~
- (`any`)
+ (`integer`)
==============================================================================
2. Matching a pattern in a String *string-match*
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 768a449581..9e44f54e6b 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -1443,18 +1443,17 @@ since formatting is highly dependent on the type of file. It makes
sense to use an |autoload| script, so the corresponding script is only loaded
when actually needed and the script should be called <filetype>format.vim.
-For example, the XML filetype plugin distributed with Vim in the $VIMRUNTIME
-directory, sets the 'formatexpr' option to: >
+For example, the XML filetype plugin distributed with Vim in the
+$VIMRUNTIME/ftplugin directory, sets the 'formatexpr' option to: >
setlocal formatexpr=xmlformat#Format()
That means, you will find the corresponding script, defining the
-xmlformat#Format() function, in the directory:
-`$VIMRUNTIME/autoload/xmlformat.vim`
+xmlformat#Format() function, in the file `$VIMRUNTIME/autoload/xmlformat.vim`
Here is an example script that removes trailing whitespace from the selected
-text. Put it in your autoload directory, e.g. ~/.vim/autoload/format.vim: >
-
+text. Put it in your autoload directory, e.g. ~/.vim/autoload/format.vim:
+>vim
func! format#Format()
" only reformat on explicit gq command
if mode() != 'n'
@@ -1487,7 +1486,7 @@ debugging it helps to set the 'debug' option.
*right-justify*
There is no command in Vim to right justify text. You can do it with
-an external command, like "par" (e.g.: "!}par" to format until the end of the
+an external command, like "par" (e.g.: `:.,}!par` to format until the end of the
paragraph) or set 'formatprg' to "par".
*format-comments*
@@ -1553,7 +1552,7 @@ type of comment string. A part consists of:
some indent for the start or end part that can be removed.
When a string has none of the 'f', 's', 'm' or 'e' flags, Vim assumes the
-comment string repeats at the start of each line. The flags field may be
+comment string repeats at the start of each line. The {flags} field may be
empty.
Any blank space in the text before and after the {string} is part of the
diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt
index 7184151cda..c2d220041c 100644
--- a/runtime/doc/channel.txt
+++ b/runtime/doc/channel.txt
@@ -11,21 +11,20 @@ Nvim asynchronous IO *channel*
==============================================================================
1. Introduction *channel-intro*
-Channels are nvim's way of communicating with external processes.
+Channels are Nvim's way of communicating with external processes.
There are several ways to open a channel:
- 1. Through stdin/stdout when `nvim` is started with `--headless`, and a startup
- script or --cmd command opens the stdio channel using |stdioopen()|.
+ 1. Through stdin/stdout when `nvim` is started with `--headless` and a startup
+ script or `--cmd` command opens the stdio channel using |stdioopen()|.
2. Through stdin, stdout and stderr of a process spawned by |jobstart()|.
- 3. Through the PTY master end of a PTY opened with
- `jobstart(..., {'pty': v:true})` or |termopen()|.
+ 3. Through the PTY master end opened with `jobstart(…, {'pty': v:true})`.
4. By connecting to a TCP/IP socket or named pipe with |sockconnect()|.
- 5. By another process connecting to a socket listened to by nvim. This only
+ 5. By another process connecting to a socket listened to by Nvim. This only
supports RPC channels, see |rpc-connecting|.
Channels support multiple modes or protocols. In the most basic
@@ -146,7 +145,7 @@ 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')
==============================================================================
-3. Communicating using msgpack-rpc *channel-rpc*
+3. Communicating with msgpack RPC *channel-rpc*
When channels are opened with the `rpc` option set to true, the channel can be
used for remote method calls in both directions, see |msgpack-rpc|. Note that
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 7967e2ce1a..3fc17af185 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -41,15 +41,19 @@ thus you cannot edit beyond that.
The command-lines that you enter are remembered in a history table. You can
recall them with the up and down cursor keys. There are actually five
history tables:
+
- one for ':' commands
- one for search strings
- one for expressions
- one for input lines, typed for the |input()| function.
- one for debug mode commands
+
These are completely separate. Each history can only be accessed when
entering the same type of line.
Use the 'history' option to set the number of lines that are remembered.
+
Notes:
+
- When you enter a command-line that is exactly the same as an older one, the
old one is removed (to avoid repeated commands moving older commands out of
the history).
@@ -1218,10 +1222,10 @@ Thus you can resize the command-line window, but not others.
The |getcmdwintype()| function returns the type of the command-line being
edited as described in |cmdwin-char|.
-Nvim defines this default CmdWinEnter autocmd in the "nvim_cmdwin" group: >
+Nvim defines this default CmdWinEnter autocmd in the "nvim.cmdwin" group: >
autocmd CmdWinEnter [:>] syntax sync minlines=1 maxlines=1
<
-You can disable this in your config with "autocmd! nvim_cmdwin". |default-autocmds|
+You can disable this in your config with "autocmd! nvim.cmdwin". |default-autocmds|
AUTOCOMMANDS
diff --git a/runtime/doc/backers.txt b/runtime/doc/credits.txt
index d0cbd94978..5fe79dfef8 100644
--- a/runtime/doc/backers.txt
+++ b/runtime/doc/credits.txt
@@ -1,11 +1,118 @@
-*backers.txt* Nvim
+*credits.txt* Nvim
NVIM REFERENCE MANUAL
==============================================================================
-Fundraiser Backers
+Credits *credits*
+
+Most of Vim was written by Bram Moolenaar <Bram@vim.org> |Bram-Moolenaar|.
+
+Parts of the documentation come from several Vi manuals, written by:
+ W.N. Joy
+ Alan P.W. Hewett
+ Mark Horton
+
+The Vim editor is based on Stevie and includes (ideas from) other software,
+worked on by the people mentioned here. Other people helped by sending me
+patches, suggestions and giving feedback about what is good and bad in Vim.
+
+Vim would never have become what it is now, without the help of these people!
+
+ Ron Aaron Win32 GUI changes
+ Mohsin Ahmed encryption
+ Zoltan Arpadffy work on VMS port
+ Tony Andrews Stevie
+ Gert van Antwerpen changes for DJGPP on MS-DOS
+ Berkeley DB(3) ideas for swap file implementation
+ Keith Bostic Nvi
+ Walter Briscoe Makefile updates, various patches
+ Ralf Brown SPAWNO library for MS-DOS
+ Robert Colon many useful remarks
+ Marcin Dalecki GTK+ GUI port, toolbar icons, gettext()
+ Kayhan Demirel sent me news in Uganda
+ Chris & John Downey xvi (ideas for multi-windows version)
+ Henk Elbers first VMS port
+ Daniel Elstner GTK+ 2 port
+ Eric Fischer Mac port, 'cindent', and other improvements
+ Benji Fisher Answering lots of user questions
+ Bill Foster Athena GUI port (later removed)
+ Google Let Bram work on Vim one day a week
+ Loic Grenie xvim (ideas for multi windows version)
+ Sven Guckes Vim promoter and previous WWW page maintainer
+ Darren Hiebert Exuberant ctags
+ Jason Hildebrand GTK+ 2 port
+ Bruce Hunsaker improvements for VMS port
+ Andy Kahn Cscope support, GTK+ GUI port
+ Oezguer Kesim Maintainer of Vim Mailing Lists
+ Axel Kielhorn work on the Macintosh port
+ Steve Kirkendall Elvis
+ Roger Knobbe original port to Windows NT
+ Sergey Laskavy Vim's help from Moscow
+ Felix von Leitner Previous maintainer of Vim Mailing Lists
+ David Leonard Port of Python extensions to Unix
+ Avner Lottem Edit in right-to-left windows
+ Flemming Madsen X11 client-server, various features and patches
+ Tony Mechelynck answers many user questions
+ Paul Moore Python interface extensions, many patches
+ Katsuhito Nagano Work on multibyte versions
+ Sung-Hyun Nam Work on multibyte versions
+ Vince Negri Win32 GUI and generic console enhancements
+ Steve Oualline Author of the first Vim book |frombook|
+ Dominique Pelle Valgrind reports and many fixes
+ A.Politz Many bug reports and some fixes
+ George V. Reilly Win32 port, Win32 GUI start-off
+ Stephen Riehm bug collector
+ Stefan Roemer various patches and help to users
+ Ralf Schandl IBM OS/390 port
+ Olaf Seibert DICE and BeBox version, regexp improvements
+ Mortaza Shiran Farsi patches
+ Peter da Silva termlib
+ Paul Slootman OS/2 port
+ Henry Spencer regular expressions
+ Dany St-Amant Macintosh port
+ Tim Thompson Stevie
+ G. R. (Fred) Walter Stevie
+ Sven Verdoolaege Perl interface
+ Robert Webb Command-line completion, GUI versions, and
+ lots of patches
+ Ingo Wilken Tcl interface
+ Mike Williams PostScript printing
+ Juergen Weigert Lattice version, AUX improvements, Unix and
+ MS-DOS ports, autoconf
+ Stefan 'Sec' Zehl Maintainer of vim.org
+ Yasuhiro Matsumoto many MS-Windows improvements
+ Ken Takata fixes and features
+ Kazunobu Kuriyama GTK 3
+ Christian Brabandt many fixes, features, user support, etc.
+ Yegappan Lakshmanan many quickfix features
+
+I wish to thank all the people that sent me bug reports and suggestions. The
+list is too long to mention them all here. Vim would not be the same without
+the ideas from all these people: They keep Vim alive!
+*love* *peace* *friendship* *gross-national-happiness*
+
+
+Documentation may refer to other versions of Vi:
+ *Vi* *vi*
+Vi "the original". Without further remarks this is the version
+ of Vi that appeared in Sun OS 4.x. ":version" returns
+ "Version 3.7, 6/7/85". Source code only available with a license.
+ *Nvi*
+Nvi The "New" Vi. The version of Vi that comes with BSD 4.4 and FreeBSD.
+ Very good compatibility with the original Vi, with a few extensions.
+ The version used is 1.79. ":version" returns "Version 1.79
+ (10/23/96)". Source code is freely available.
+ *Elvis*
+Elvis Another Vi clone, made by Steve Kirkendall. Very compact but isn't
+ as flexible as Vim. Source code is freely available.
+
+Vim Nvim is based on Vim. https://www.vim.org/
+
+
+==============================================================================
+Neovim fundraiser backers *backers.txt*
Thank you to everyone who backed the original Neovim Fundraiser.
diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt
index 5e809ad26c..68258fedb4 100644
--- a/runtime/doc/deprecated.txt
+++ b/runtime/doc/deprecated.txt
@@ -16,46 +16,57 @@ Deprecated features
DEPRECATED IN 0.11 *deprecated-0.11*
API
-- nvim_subscribe() Plugins must maintain their own "multicast" channels list.
-- nvim_unsubscribe() Plugins must maintain their own "multicast" channels list.
-
-LUA
-- vim.region() Use |getregionpos()| instead.
-- *vim.highlight* Renamed to |vim.hl|.
-- vim.validate(opts: table) Use form 1. See |vim.validate()|.
+• nvim_notify() Use |nvim_echo()| or `nvim_exec_lua("vim.notify(...)", ...)` instead.
+• nvim_subscribe() Plugins must maintain their own "multicast" channels list.
+• nvim_unsubscribe() Plugins must maintain their own "multicast" channels list.
+• nvim_out_write() Use |nvim_echo()|.
+• nvim_err_write() Use |nvim_echo()| with `{err=true}`.
+• nvim_err_writeln() Use |nvim_echo()| with `{err=true}`.
+• nvim_buf_add_highlight() Use |vim.hl.range()| or |nvim_buf_set_extmark()|
DIAGNOSTICS
-- *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead.
-- *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count=-1, float=true}` instead.
-- *vim.diagnostic.get_next_pos()*
- Use the "lnum" and "col" fields from the return value of
- |vim.diagnostic.get_next()| instead.
-- *vim.diagnostic.get_prev_pos()*
- Use the "lnum" and "col" fields from the return value of
- |vim.diagnostic.get_prev()| instead.
-- The "win_id" parameter used by various functions is deprecated in favor of
+• *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead.
+• *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count=-1, float=true}` instead.
+• *vim.diagnostic.get_next_pos()* Use the "lnum" and "col" fields from the
+ return value of |vim.diagnostic.get_next()| instead.
+• *vim.diagnostic.get_prev_pos()* Use the "lnum" and "col" fields from the
+ return value of |vim.diagnostic.get_prev()| instead.
+• The "win_id" parameter used by various functions is deprecated in favor of
"winid" |winid|
-- The "cursor_position" parameter of |vim.diagnostic.JumpOpts| is renamed to
- "pos"
+• |vim.diagnostic.JumpOpts| renamed its "cursor_position" field to "pos".
-TREESITTER
-• *TSNode:child_containing_descendant()* Use
- |TSNode:child_with_descendant()| instead; it is identical except that it can
- return the descendant itself.
+HIGHLIGHTS
+• *TermCursorNC* Unfocused |terminal| windows do not have a cursor.
LSP
• *vim.lsp.util.jump_to_location* Use |vim.lsp.util.show_document()| with
- `{focus=true}` instead.
+ `{focus=true}` instead.
• *vim.lsp.buf.execute_command* Use |Client:exec_cmd()| instead.
• *vim.lsp.buf.completion* Use |vim.lsp.completion.trigger()| instead.
• vim.lsp.buf_request_all The `error` key has been renamed to `err` inside
the result parameter of the handler.
• *vim.lsp.with()* Pass configuration to equivalent
- functions in `vim.lsp.buf.*'.
-• |vim.lsp.handlers|
- No longer support client to server response handlers. Only server to
- client requests/notification handlers are supported.
+ functions in `vim.lsp.buf.*`.
+• |vim.lsp.handlers| Does not support client-to-server response handlers. Only
+ supports server-to-client requests/notification handlers.
• *vim.lsp.handlers.signature_help()* Use |vim.lsp.buf.signature_help()| instead.
+• `client.request()` Use |Client:request()| instead.
+• `client.request_sync()` Use |Client:request_sync()| instead.
+• `client.notify()` Use |Client:notify()| instead.
+• `client.cancel_request()` Use |Client:cancel_request()| instead.
+• `client.stop()` Use |Client:stop()| instead.
+• `client.is_stopped()` Use |Client:is_stopped()| instead.
+• `client.supports_method()` Use |Client:supports_method()| instead.
+• `client.on_attach()` Use |Client:on_attach()| instead.
+• `vim.lsp.start_client()` Use |vim.lsp.start()| instead.
+
+LUA
+• vim.region() Use |getregionpos()| instead.
+• *vim.highlight* Renamed to |vim.hl|.
+• vim.validate(opts: table) Use form 1. See |vim.validate()|.
+
+VIMSCRIPT
+• *termopen()* Use |jobstart() with `{term: v:true}`.
------------------------------------------------------------------------------
DEPRECATED IN 0.10 *deprecated-0.10*
@@ -117,198 +128,198 @@ TREESITTER
DEPRECATED IN 0.9 *deprecated-0.9*
API
-- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead.
-- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead.
+• *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead.
+• *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead.
TREESITTER
-- *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead.
-- *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead.
-- *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()|
+• *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead.
+• *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead.
+• *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()|
and |TSNode:type()| instead.
• The following top level Treesitter functions have been moved:
- - *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()|
- - *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()|
- - *vim.treesitter.set_query()* -> |vim.treesitter.query.set()|
- - *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()|
- - *vim.treesitter.get_query()* -> |vim.treesitter.query.get()|
- - *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()|
- - *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()|
- - *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()|
- - *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()|
- - *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()|
- - *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()|
- - *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()|
- - *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()|
- - *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()|
+ • *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()|
+ • *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()|
+ • *vim.treesitter.set_query()* -> |vim.treesitter.query.set()|
+ • *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()|
+ • *vim.treesitter.get_query()* -> |vim.treesitter.query.get()|
+ • *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()|
+ • *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()|
+ • *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()|
+ • *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()|
+ • *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()|
+ • *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()|
+ • *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()|
+ • *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()|
+ • *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()|
LUA
- - *nvim_exec()* Use |nvim_exec2()| instead.
- - *vim.pretty_print()* Use |vim.print()| instead.
+ • *nvim_exec()* Use |nvim_exec2()| instead.
+ • *vim.pretty_print()* Use |vim.print()| instead.
------------------------------------------------------------------------------
DEPRECATED IN 0.8 OR EARLIER
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_exec2()| instead.
-- *nvim_execute_lua()* Use |nvim_exec_lua()| instead.
-- *nvim_get_option_info()* Use |nvim_get_option_info2()| instead.
+• *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_exec2()| instead.
+• *nvim_execute_lua()* Use |nvim_exec_lua()| instead.
+• *nvim_get_option_info()* Use |nvim_get_option_info2()| instead.
COMMANDS
-- *:rv* *:rviminfo* Deprecated alias to |:rshada| command.
-- *:wv* *:wviminfo* Deprecated alias to |:wshada| command.
+• *: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"
+• *$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.
+• *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("$").
-- *rpcstart()* Use |jobstart()| with `{'rpc': v:true}` instead.
-- *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.
+• *<MouseDown>* Use <ScrollWheelUp> instead.
+• *<MouseUp>* Use <ScrollWheelDown> instead.
HIGHLIGHTS
-- *hl-VertSplit* Use |hl-WinSeparator| instead.
+• *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()* Use |vim.diagnostic.enable()| instead.
-- *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.count()| 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
+• *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead.
+• *vim.lsp.diagnostic.disable()* Use |vim.diagnostic.enable()| instead.
+• *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.count()| 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.
+• *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()*
+• *vim.lsp.diagnostic.set_signs()*
+• *vim.lsp.diagnostic.set_underline()*
+• *vim.lsp.diagnostic.set_virtual_text()*
LSP FUNCTIONS
-- *vim.lsp.buf.server_ready()*
+• *vim.lsp.buf.server_ready()*
Use |LspAttach| instead, depending on your use-case. "Server ready" is not
part of the LSP spec, so the Nvim LSP client cannot meaningfully implement
it. "Ready" is ambiguous because:
- - Language servers may finish analyzing the workspace, but edits can always
+ • Language servers may finish analyzing the workspace, but edits can always
re-trigger analysis/builds.
- - Language servers can serve some requests even while processing changes.
-- *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with
+ • Language servers can serve some requests even while processing changes.
+• *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_clients()| with
+• *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_clients()| with
{buffer=bufnr} instead.
-- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with
+• *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with
{async=true} instead.
-- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with
+• *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with
{async=false} instead.
-- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()|
+• *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.
+• vim.register_keystroke_callback() Use |vim.on_key()| instead.
NORMAL COMMANDS
-- *]f* *[f* Same as "gf".
+• *]f* *[f* Same as "gf".
OPTIONS
-- *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special*
+• *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special*
`<>` notation is always enabled.
-- *'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.
-- *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete:
+• *'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.
+• *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete:
|paste| is handled automatically when you paste text
using your terminal's or GUI's paste feature
(CTRL-SHIFT-v, CMD-v (macOS), middle-click, …).
Enables "paste mode":
- - Disables mappings in Insert, Cmdline mode.
- - Disables abbreviations.
- - Resets 'autoindent' 'expandtab' 'revins' 'ruler'
+ • Disables mappings in Insert, Cmdline mode.
+ • Disables abbreviations.
+ • Resets 'autoindent' 'expandtab' 'revins' 'ruler'
'showmatch' 'smartindent' 'smarttab' 'softtabstop'
'textwidth' 'wrapmargin'.
- - Treats 'formatoptions' as empty.
- - Disables the effect of these options:
- - 'cindent'
- - 'indentexpr'
- - 'lisp'
+ • Treats 'formatoptions' as empty.
+ • Disables the effect of these options:
+ • 'cindent'
+ • 'indentexpr'
+ • 'lisp'
UI EXTENSIONS
-- *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled
+• *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"]`
-- *term_background* Unused. The terminal background color is now detected
+ • `["wildmenu_show", items]`
+ • `["wildmenu_select", selected]`
+ • `["wildmenu_hide"]`
+• *term_background* Unused. The terminal background color is now detected
by the Nvim core directly instead of the TUI.
VARIABLES
-- *b:terminal_job_pid* Use `jobpid(&channel)` instead.
-- *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer:
- - Lua: `vim.bo[bufnr].channel`
- - Vimscript: `getbufvar(bufnr, '&channel')`
+• *b:terminal_job_pid* Use `jobpid(&channel)` instead.
+• *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer:
+ • Lua: `vim.bo[bufnr].channel`
+ • Vimscript: `getbufvar(bufnr, '&channel')`
+
+VIMSCRIPT
+• *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("$").
+• *rpcstart()* Use |jobstart()| with `{'rpc': v:true}` instead.
+• *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.
vim:noet:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt
index 1cb3b9ad67..2be6221117 100644
--- a/runtime/doc/dev_arch.txt
+++ b/runtime/doc/dev_arch.txt
@@ -46,11 +46,11 @@ Remember to bump NVIM_API_LEVEL if it wasn't already during this development
cycle.
Other references:
-* |msgpack-rpc|
-* |ui|
-* https://github.com/neovim/neovim/pull/3246
-* https://github.com/neovim/neovim/pull/18375
-* https://github.com/neovim/neovim/pull/21605
+- |msgpack-rpc|
+- |ui|
+- https://github.com/neovim/neovim/pull/3246
+- https://github.com/neovim/neovim/pull/18375
+- https://github.com/neovim/neovim/pull/21605
diff --git a/runtime/doc/dev_vimpatch.txt b/runtime/doc/dev_vimpatch.txt
index 5119613b55..76be24878a 100644
--- a/runtime/doc/dev_vimpatch.txt
+++ b/runtime/doc/dev_vimpatch.txt
@@ -188,6 +188,7 @@ information.
vim_strcat strncat xstrlcat
VIM_ISWHITE ascii_iswhite
IS_WHITE_OR_NUL ascii_iswhite_or_nul
+ IS_WHITE_NL_OR_NUL ascii_iswhite_nl_or_nul
vim_isalpha mb_isalpha
vim_isNormalIDc ascii_isident
vim_islower vim_isupper mb_islower mb_isupper
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index a61c569a67..d3170f114f 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -300,7 +300,7 @@ vim.paste in runtime/lua/vim/_editor.lua like this: >
--- @returns false if client should cancel the paste.
-LUA STDLIB DESIGN GUIDELINES *dev-lua*
+STDLIB DESIGN GUIDELINES *dev-lua*
See also |dev-naming|.
@@ -337,7 +337,7 @@ preference):
way. Advantage is that propagation happens for free and it's harder to
accidentally swallow errors. (E.g. using
`uv_handle/pipe:write()` without checking return values is common.)
-4. `on_error` parameter
+4. `on_error` callback
- For async and "visitors" traversing a graph, where many errors may be
collected while work continues.
5. `vim.notify` (sometimes with optional `opts.silent` (async, visitors ^))
@@ -376,6 +376,10 @@ Where possible, these patterns apply to _both_ Lua and the API:
- See |vim.lsp.inlay_hint.enable()| and |vim.lsp.inlay_hint.is_enabled()|
for a reference implementation of these "best practices".
- NOTE: open questions: https://github.com/neovim/neovim/issues/28603
+- Transformation functions should also have a filter functionality when
+ appropriate. That is, when the function returns a nil value it "filters" its
+ input, otherwise the transformed value is used.
+ - Example: |vim.diagnostic.config.format()|
API DESIGN GUIDELINES *dev-api*
@@ -434,7 +438,9 @@ Use existing common {verb} names (actions) if possible:
- eval: Evaluates an expression
- exec: Executes code, may return a result
- fmt: Formats
- - get: Gets things (often by a query)
+ - get: Gets things. Two variants (overloads):
+ 1. `get<T>(id: int): T` returns one item.
+ 2. `get<T>(filter: dict): T[]` returns a list.
- inspect: Presents a high-level, often interactive, view
- is_enabled: Checks if functionality is enabled.
- open: Opens something (a buffer, window, …)
@@ -506,6 +512,15 @@ be a parameter (typically manifest as mutually-exclusive buf/win/… flags like
- Example: `nvim_buf_del_mark` acts on a `Buffer` object (the first parameter)
and uses the "del" {verb}.
+ *dev-namespace-name*
+Use namespace names like `nvim.foo.bar`: >
+ vim.api.nvim_create_namespace('nvim.lsp.codelens')
+<
+
+ *dev-augroup-name*
+Use autocommand group names like `nvim.foo.bar`: >
+ vim.api.nvim_create_augroup('nvim.treesitter.dev')
+<
INTERFACE PATTERNS *dev-api-patterns*
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index 9ccc3102b6..5e1e04ce56 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -93,8 +93,12 @@ The {opts} table passed to a handler is the full set of configuration options
values in the table are already resolved (i.e. if a user specifies a
function for a config option, the function has already been evaluated).
-Nvim provides these handlers by default: "virtual_text", "signs", and
-"underline".
+If a diagnostic handler is configured with a "severity" key then the list of
+diagnostics passed to that handler will be filtered using the value of that
+key (see example below).
+
+Nvim provides these handlers by default: "virtual_text", "virtual_lines",
+"signs", and "underline".
*diagnostic-handlers-example*
The example below creates a new handler that notifies the user of diagnostics
@@ -119,6 +123,9 @@ with |vim.notify()|: >lua
vim.diagnostic.config({
["my/notify"] = {
log_level = vim.log.levels.INFO
+
+ -- This handler will only receive "error" diagnostics.
+ severity = vim.diagnostic.severity.ERROR,
}
})
<
@@ -163,6 +170,43 @@ show a sign for the highest severity diagnostic on a given line: >lua
}
<
+ *diagnostic-toggle-virtual-lines-example*
+Diagnostic handlers can also be toggled. For example, you might want to toggle
+the `virtual_lines` handler with the following keymap: >lua
+
+ vim.keymap.set('n', 'gK', function()
+ local new_config = not vim.diagnostic.config().virtual_lines
+ vim.diagnostic.config({ virtual_lines = new_config })
+ end, { desc = 'Toggle diagnostic virtual_lines' })
+<
+
+ *diagnostic-loclist-example*
+Whenever the |location-list| is opened, the following `show` handler will show
+the most recent diagnostics: >lua
+
+ vim.diagnostic.handlers.loclist = {
+ show = function(_, _, _, opts)
+ -- Generally don't want it to open on every update
+ opts.loclist.open = opts.loclist.open or false
+ local winid = vim.api.nvim_get_current_win()
+ vim.diagnostic.setloclist(opts.loclist)
+ vim.api.nvim_set_current_win(winid)
+ end
+ }
+<
+
+The handler accepts the same options as |vim.diagnostic.setloclist()| and can be
+configured using |vim.diagnostic.config()|: >lua
+
+ -- Open the location list on every diagnostic change (warnings/errors only).
+ vim.diagnostic.config({
+ loclist = {
+ open = true,
+ severity = { min = vim.diagnostic.severity.WARN },
+ }
+ })
+<
+
==============================================================================
HIGHLIGHTS *diagnostic-highlights*
@@ -430,11 +474,13 @@ Lua module: vim.diagnostic *diagnostic-api*
Fields: ~
• {underline}? (`boolean|vim.diagnostic.Opts.Underline|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Underline`, default: `true`)
Use underline for diagnostics.
- • {virtual_text}? (`boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText`, default: `true`)
+ • {virtual_text}? (`boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText`, default: `false`)
Use virtual text for diagnostics. If multiple
diagnostics are set for a namespace, one prefix
per diagnostic + the last diagnostic message are
shown.
+ • {virtual_lines}? (`boolean|vim.diagnostic.Opts.VirtualLines|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualLines`, default: `false`)
+ Use virtual lines for diagnostics.
• {signs}? (`boolean|vim.diagnostic.Opts.Signs|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Signs`, default: `true`)
Use signs for diagnostics |diagnostic-signs|.
• {float}? (`boolean|vim.diagnostic.Opts.Float|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Float`)
@@ -486,11 +532,13 @@ Lua module: vim.diagnostic *diagnostic-api*
the buffer. Otherwise, any truthy value means to
always show the diagnostic source. Overrides the
setting from |vim.diagnostic.config()|.
- • {format}? (`fun(diagnostic:vim.Diagnostic): string`) A
+ • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) A
function that takes a diagnostic as input and
- returns a string. The return value is the text used
- to display the diagnostic. Overrides the setting
- from |vim.diagnostic.config()|.
+ returns a string or nil. If the return value is nil,
+ the diagnostic is not displayed by the handler. Else
+ the output text is used to display the diagnostic.
+ Overrides the setting from
+ |vim.diagnostic.config()|.
• {prefix}? (`string|table|(fun(diagnostic:vim.Diagnostic,i:integer,total:integer): string, string)`)
Prefix each diagnostic in the floating window:
• If a `function`, {i} is the index of the
@@ -556,12 +604,25 @@ Lua module: vim.diagnostic *diagnostic-api*
diagnostics matching the given severity
|diagnostic-severity|.
+*vim.diagnostic.Opts.VirtualLines*
+
+ Fields: ~
+ • {current_line}? (`boolean`, default: `false`) Only show diagnostics
+ for the current line.
+ • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) A
+ function that takes a diagnostic as input and returns
+ a string or nil. If the return value is nil, the
+ diagnostic is not displayed by the handler. Else the
+ output text is used to display the diagnostic.
+
*vim.diagnostic.Opts.VirtualText*
Fields: ~
• {severity}? (`vim.diagnostic.SeverityFilter`) Only show
virtual text for diagnostics matching the given
severity |diagnostic-severity|
+ • {current_line}? (`boolean`) Only show diagnostics for the
+ current line. (default `false`)
• {source}? (`boolean|"if_many"`) Include the diagnostic
source in virtual text. Use `'if_many'` to only
show sources if there is more than one
@@ -579,9 +640,9 @@ Lua module: vim.diagnostic *diagnostic-api*
• {suffix}? (`string|(fun(diagnostic:vim.Diagnostic): string)`)
Append diagnostic message with suffix. This can
be used to render an LSP diagnostic error code.
- • {format}? (`fun(diagnostic:vim.Diagnostic): string`) The
- return value is the text used to display the
- diagnostic. Example: >lua
+ • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) If
+ not nil, the return value is 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)
@@ -589,11 +650,14 @@ Lua module: vim.diagnostic *diagnostic-api*
return diagnostic.message
end
<
+
+ If the return value is nil, the diagnostic is
+ not displayed by the handler.
• {hl_mode}? (`'replace'|'combine'|'blend'`) See
|nvim_buf_set_extmark()|.
• {virt_text}? (`[string,any][]`) See |nvim_buf_set_extmark()|.
- • {virt_text_pos}? (`'eol'|'overlay'|'right_align'|'inline'`) See
- |nvim_buf_set_extmark()|.
+ • {virt_text_pos}? (`'eol'|'eol_right_align'|'inline'|'overlay'|'right_align'`)
+ See |nvim_buf_set_extmark()|.
• {virt_text_win_col}? (`integer`) See |nvim_buf_set_extmark()|.
• {virt_text_hide}? (`boolean`) See |nvim_buf_set_extmark()|.
@@ -847,7 +911,8 @@ setqflist({opts}) *vim.diagnostic.setqflist()*
• {open}? (`boolean`, default: `true`) Open quickfix list
after setting.
• {title}? (`string`) Title of quickfix list. Defaults to
- "Diagnostics".
+ "Diagnostics". If there's already a quickfix list with this
+ title, it's updated. If not, a new quickfix list is created.
• {severity}? (`vim.diagnostic.SeverityFilter`) See
|diagnostic-severity|.
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index 1e91e5e4b8..13df8ad4ac 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -119,8 +119,8 @@ see them.
On most systems Vim uses the same digraphs. They work for the Unicode and
ISO-8859-1 character sets. These default digraphs are taken from the RFC1345
-mnemonics. To make it easy to remember the mnemonic, the second character has
-a standard meaning:
+mnemonics (with some additions). To make it easy to remember the mnemonic,
+the second character has a standard meaning:
char name char meaning ~
Exclamation mark ! Grave
@@ -1064,6 +1064,7 @@ the 1', 2' and 3' digraphs.
≅ ?= 2245 8773 APPROXIMATELY EQUAL TO
≈ ?2 2248 8776 ALMOST EQUAL TO
≌ =? 224C 8780 ALL EQUAL TO
+ ≐ .= 2250 8784 APPROACHES THE LIMIT
≓ HI 2253 8787 IMAGE OF OR APPROXIMATELY EQUAL TO
≠ != 2260 8800 NOT EQUAL TO
≡ =3 2261 8801 IDENTICAL TO
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index e0c45503cc..60238bc90d 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2848,7 +2848,8 @@ in the variable |v:exception|: >
: echo "Number thrown. Value is" v:exception
You may also be interested where an exception was thrown. This is stored in
-|v:throwpoint|. Note that "v:exception" and "v:throwpoint" are valid for the
+|v:throwpoint|. And you can obtain the stack trace from |v:stacktrace|.
+Note that "v:exception", "v:stacktrace" and "v:throwpoint" are valid for the
exception most recently caught as long it is not finished.
Example: >
diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt
index 19c018bc11..cc520484b3 100644
--- a/runtime/doc/filetype.txt
+++ b/runtime/doc/filetype.txt
@@ -597,7 +597,7 @@ To disable this behavior, set the following variable in your vimrc: >
let g:gdscript_recommended_style = 0
-GIT COMMIT *ft-gitcommit-plugin*
+GIT COMMIT *ft-gitcommit-plugin*
One command, :DiffGitCached, is provided to show a diff of the current commit
in the preview window. It is equivalent to calling "git diff --cached" plus
@@ -778,7 +778,7 @@ An alternative to using `MANPAGER` in shell can be redefined `man`, for example:
nvim "+hide Man $1"
}
-MARKDOWN *ft-markdown-plugin*
+MARKDOWN *ft-markdown-plugin*
To enable folding use this: >
let g:markdown_folding = 1
@@ -870,7 +870,7 @@ To enable this behavior, set the following variable in your vimrc: >
let g:rst_style = 1
-RNOWEB *ft-rnoweb-plugin*
+RNOWEB *ft-rnoweb-plugin*
The 'formatexpr' option is set dynamically with different values for R code
and for LaTeX code. If you prefer that 'formatexpr' is not set, add to your
diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt
index b844e0ed85..9e61e40814 100644
--- a/runtime/doc/fold.txt
+++ b/runtime/doc/fold.txt
@@ -82,9 +82,11 @@ The most efficient is to call a function without arguments: >
The function must use v:lnum. See |expr-option-function|.
These are the conditions with which the expression is evaluated:
+
- The current buffer and window are set for the line.
- The variable "v:lnum" is set to the line number.
-- The result is used for the fold level in this way:
+
+The result of foldexpr then determines the fold level as follows:
value meaning ~
0 the line is not in a fold
1, 2, .. the line is in a fold with this level
@@ -99,6 +101,9 @@ These are the conditions with which the expression is evaluated:
"<1", "<2", .. a fold with this level ends at this line
">1", ">2", .. a fold with this level starts at this line
+The result values "=", "s" and "a" are more expensive, please see
+|fold-expr-slow|.
+
It is not required to mark the start (end) of a fold with ">1" ("<1"), a fold
will also start (end) when the fold level is higher (lower) than the fold
level of the previous line.
@@ -112,14 +117,8 @@ recognized, there is no error message and the fold level will be zero.
For debugging the 'debug' option can be set to "msg", the error messages will
be visible then.
-Note: Since the expression has to be evaluated for every line, this fold
-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: >
+with the script ID (|local-function|). Examples: >
set foldexpr=s:MyFoldExpr()
set foldexpr=<SID>SomeFoldExpr()
<
@@ -143,6 +142,37 @@ end in that line.
It may happen that folds are not updated properly. You can use |zx| or |zX|
to force updating folds.
+MINIMIZING COMPUTATIONAL COST *fold-expr-slow*
+
+Due to its computational cost, this fold method can make Vim unresponsive,
+especially when the fold level of all lines have to be initially computed.
+Afterwards, after each change, Vim restricts the computation of foldlevels
+to those lines whose fold level was affected by it (and reuses the known
+foldlevels of all the others).
+
+The fold expression should therefore strive to minimize the number of
+dependent lines needed for the computation of a given line: For example, try
+to avoid the "=", "a" and "s" return values, because these will require the
+evaluation of the fold levels on previous lines until an independent fold
+level is found.
+
+If this proves difficult, the next best thing could be to cache all fold
+levels in a buffer-local variable (b:foldlevels) that is only updated on
+|b:changedtick|:
+>vim
+ func MyFoldFunc()
+ if b:lasttick == b:changedtick
+ return b:foldlevels[v:lnum - 1]
+ endif
+ let b:lasttick = b:changedtick
+ let b:foldlevels = []
+ " compute foldlevels ...
+ return b:foldlevels[v:lnum - 1]
+ enddef
+ set foldexpr=s:MyFoldFunc()
+<
+In above example further speedup was gained by using a function without
+arguments (that must still use v:lnum). See |expr-option-function|.
SYNTAX *fold-syntax*
@@ -379,8 +409,8 @@ zX Undo manually opened and closed folds: re-apply 'foldlevel'.
Also forces recomputing folds, like |zx|.
*zm*
-zm Fold more: Subtract |v:count1| from 'foldlevel'. If 'foldlevel' was
- already zero nothing happens.
+zm Fold more: Subtract |v:count1| from 'foldlevel'. If
+ 'foldlevel' was already zero nothing happens.
'foldenable' will be set.
*zM*
@@ -444,7 +474,7 @@ zk Move upwards to the end of the previous fold. A closed fold
EXECUTING COMMANDS ON FOLDS ~
-:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen*
+:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen*
Execute {cmd} on all lines that are not in a closed fold.
When [range] is given, only these lines are used.
Each time {cmd} is executed the cursor is positioned on the
@@ -532,7 +562,7 @@ 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: >
+with the script ID (|local-function|). Examples: >
set foldtext=s:MyFoldText()
set foldtext=<SID>SomeFoldText()
<
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index 21f1ba8241..ecb4de09bb 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -1,10 +1,10 @@
*gui.txt* Nvim
- VIM REFERENCE MANUAL by Bram Moolenaar
+ VIM REFERENCE MANUAL by Bram Moolenaar
-Nvim Graphical User Interface *gui* *GUI*
+Nvim Graphical User Interface *gui* *GUI*
Any client that supports the Nvim |ui-protocol| can be used as a UI for Nvim.
And multiple UIs can connect to the same Nvim instance! The terms "UI" and
@@ -17,11 +17,16 @@ TUI and GUI (assuming the UI supports the given feature). See |TUI| for notes
specific to the terminal UI. Help tags with the "gui-" prefix refer to UI
features, whereas help tags with the "ui-" prefix refer to the |ui-protocol|.
-Nvim provides a default, builtin UI (the |TUI|), but there are many other
-(third-party) GUIs that you can use instead:
+==============================================================================
+Third-party GUIs *third-party-guis* *vscode*
+
+Nvim provides a builtin "terminal UI" (|TUI|), but also works with many
+(third-party) GUIs which may provide a fresh look or extra features on top of
+Nvim. For example, "vscode-neovim" essentially allows you to use VSCode as
+a Nvim GUI.
-- Firenvim (Nvim in your web browser!) https://github.com/glacambre/firenvim
- vscode-neovim (Nvim in VSCode!) https://github.com/vscode-neovim/vscode-neovim
+- Firenvim (Nvim in your web browser!) https://github.com/glacambre/firenvim
- Neovide https://neovide.dev/
- Goneovim https://github.com/akiyosi/goneovim
- Nvy https://github.com/RMichelsen/Nvy
@@ -32,71 +37,257 @@ Nvim provides a default, builtin UI (the |TUI|), but there are many other
Type |gO| to see the table of contents.
==============================================================================
-Starting the GUI *gui-config* *gui-start*
+Starting the GUI *gui-config* *gui-start*
- *ginit.vim* *gui-init* *gvimrc* *$MYGVIMRC*
+ *ginit.vim* *gui-init* *gvimrc* *$MYGVIMRC*
For GUI-specific configuration Nvim provides the |UIEnter| event. This
happens after other |initialization|s, or whenever a UI attaches (multiple UIs
can connect to any Nvim instance).
Example: this sets "g:gui" to the value of the UI's "rgb" field: >
- :autocmd UIEnter * let g:gui = filter(nvim_list_uis(),{k,v-> v.chan==v:event.chan})[0].rgb
+ :autocmd UIEnter * let g:gui = filter(nvim_list_uis(),{k,v-> v.chan==v:event.chan})[0].rgb
<
- *:winp* *:winpos* *E188*
+ *:winp* *:winpos* *E188*
:winp[os]
- Display current position of the top left corner of the GUI vim
- window in pixels. Does not work in all versions.
- Also see |getwinpos()|, |getwinposx()| and |getwinposy()|.
-
-:winp[os] {X} {Y} *E466*
- Put the GUI vim window at the given {X} and {Y} coordinates.
- The coordinates should specify the position in pixels of the
- top left corner of the window.
- When the GUI window has not been opened yet, the values are
- remembered until the window is opened. The position is
- adjusted to make the window fit on the screen (if possible).
-
- *:wi* *:win* *:winsize* *E465*
+ Display current position of the top left corner of the GUI vim
+ window in pixels. Does not work in all versions.
+ Also see |getwinpos()|, |getwinposx()| and |getwinposy()|.
+
+:winp[os] {X} {Y} *E466*
+ Put the GUI vim window at the given {X} and {Y} coordinates.
+ The coordinates should specify the position in pixels of the
+ top left corner of the window.
+ When the GUI window has not been opened yet, the values are
+ remembered until the window is opened. The position is
+ adjusted to make the window fit on the screen (if possible).
+
+ *:wi* *:win* *:winsize* *E465*
:win[size] {width} {height}
- Set the window height to {width} by {height} characters.
- Obsolete, use ":set lines=11 columns=22".
+ Set the window height to {width} by {height} characters.
+ Obsolete, use ":set lines=11 columns=22".
==============================================================================
-Scrollbars *gui-scrollbars*
+Using the mouse *mouse-using*
+
+ *mouse-mode-table* *mouse-overview*
+Overview of what the mouse buttons do, when 'mousemodel' is "extend":
+
+ *<S-LeftMouse>* *<A-RightMouse>* *<S-RightMouse>* *<RightDrag>*
+ *<RightRelease>* *<LeftDrag>*
+Normal Mode: >
+ event position selection change action
+ cursor window
+ ---------------------------------------------------------------------------
+ <LeftMouse> yes end yes
+ <C-LeftMouse> yes end yes "CTRL-]" (2)
+ <S-LeftMouse> yes no change yes "*" (2)
+ <LeftDrag> yes start or extend (1) no
+ <LeftRelease> yes start or extend (1) no
+ <MiddleMouse> yes if not active no put
+ <MiddleMouse> yes if active no yank and put
+ <RightMouse> yes start or extend yes
+ <A-RightMouse> yes start or extend blockw. yes
+ <S-RightMouse> yes no change yes "#" (2)
+ <C-RightMouse> no no change no "CTRL-T"
+ <RightDrag> yes extend no
+ <RightRelease> yes extend no
+
+Insert or Replace Mode: >
+ event position selection change action
+ cursor window
+ ---------------------------------------------------------------------------
+ <LeftMouse> yes (cannot be active) yes
+ <C-LeftMouse> yes (cannot be active) yes "CTRL-O^]" (2)
+ <S-LeftMouse> yes (cannot be active) yes "CTRL-O*" (2)
+ <LeftDrag> yes start or extend (1) no like CTRL-O (1)
+ <LeftRelease> yes start or extend (1) no like CTRL-O (1)
+ <MiddleMouse> no (cannot be active) no put register
+ <RightMouse> yes start or extend yes like CTRL-O
+ <A-RightMouse> yes start or extend blockw. yes
+ <S-RightMouse> yes (cannot be active) yes "CTRL-O#" (2)
+ <C-RightMouse> no (cannot be active) no "CTRL-O CTRL-T"
+
+In a help window: >
+ event position selection change action
+ cursor window
+ ---------------------------------------------------------------------------
+ <2-LeftMouse> yes (cannot be active) no "^]" (jump to help tag)
+
+When 'mousemodel' is "popup", these are different:
+
+ *<A-LeftMouse>*
+Normal Mode: >
+ event position selection change action
+ cursor window
+ ---------------------------------------------------------------------------
+ <S-LeftMouse> yes start or extend (1) no
+ <A-LeftMouse> yes start/extend blockw no
+ <RightMouse> no popup menu no
+
+Insert or Replace Mode: >
+ event position selection change action
+ cursor window
+ ---------------------------------------------------------------------------
+ <S-LeftMouse> yes start or extend (1) no like CTRL-O (1)
+ <A-LeftMouse> yes start/extend blockw no
+ <RightMouse> no popup menu no
+
+(1) only if mouse pointer moved since press
+(2) only if click is in same buffer
+
+Clicking the left mouse button causes the cursor to be positioned. If the
+click is in another window that window is made the active window. When
+editing the command-line the cursor can only be positioned on the
+command-line. When in Insert mode Vim remains in Insert mode. If 'scrolloff'
+is set, and the cursor is positioned within 'scrolloff' lines from the window
+border, the text is scrolled.
+
+A selection can be started by pressing the left mouse button on the first
+character, moving the mouse to the last character, then releasing the mouse
+button. You will not always see the selection until you release the button,
+only in some versions (GUI, Win32) will the dragging be shown immediately.
+Note that you can make the text scroll by moving the mouse at least one
+character in the first/last line in the window when 'scrolloff' is non-zero.
+
+In Normal, Visual and Select mode clicking the right mouse button causes the
+Visual area to be extended. When 'mousemodel' is "popup", the left button has
+to be used while keeping the shift key pressed. When clicking in a window
+which is editing another buffer, the Visual or Select mode is stopped.
+
+In Normal, Visual and Select mode clicking the right mouse button with the alt
+key pressed causes the Visual area to become blockwise. When 'mousemodel' is
+"popup" the left button has to be used with the alt key. Note that this won't
+work on systems where the window manager consumes the mouse events when the
+alt key is pressed (it may move the window).
+
+ *double-click* *<2-LeftMouse>* *<3-LeftMouse>* *<4-LeftMouse>*
+Double, triple and quadruple clicks are supported. For selecting text, extra
+clicks extend the selection: >
+
+ click select
+ ---------------------------------
+ double word or % match
+ triple line
+ quadruple rectangular block
+
+Exception: In a :help window, double-click jumps to help for the word that is
+clicked on.
+
+Double-click on a word selects that word. 'iskeyword' is used to specify
+which characters are included in a word. Double-click on a character that has
+a match selects until that match (like using "v%"). If the match is an
+#if/#else/#endif block, the selection becomes linewise. The time for
+double-clicking can be set with the 'mousetime' option.
+
+Example: configure 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
+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
+'selectmode' option contains "mouse".
+
+ *X1Mouse* *X1Drag* *X1Release*
+ *X2Mouse* *X2Drag* *X2Release*
+ *<MiddleRelease>* *<MiddleDrag>*
+Mouse clicks can be mapped using these |keycodes|: >
+ code mouse button normal action
+ ---------------------------------------------------------------------------
+ <LeftMouse> left pressed set cursor position
+ <LeftDrag> left moved while pressed extend selection
+ <LeftRelease> left released set selection end
+ <MiddleMouse> middle pressed paste text at cursor position
+ <MiddleDrag> middle moved while pressed -
+ <MiddleRelease> middle released -
+ <RightMouse> right pressed extend selection
+ <RightDrag> right moved while pressed extend selection
+ <RightRelease> right released set selection end
+ <X1Mouse> X1 button pressed -
+ <X1Drag> X1 moved while pressed -
+ <X1Release> X1 button release -
+ <X2Mouse> X2 button pressed -
+ <X2Drag> X2 moved while pressed -
+ <X2Release> X2 button release -
+
+The X1 and X2 buttons refer to the extra buttons found on some mice (e.g. the
+right thumb).
+
+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). >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: >vim
+ :noremap <LeftMouse> <RightMouse>
+ :noremap <LeftDrag> <RightDrag>
+ :noremap <LeftRelease> <RightRelease>
+ :noremap <RightMouse> <LeftMouse>
+ :noremap <RightDrag> <LeftDrag>
+ :noremap <RightRelease> <LeftRelease>
+ :noremap g<LeftMouse> <C-RightMouse>
+ :noremap g<RightMouse> <C-LeftMouse>
+ :noremap! <LeftMouse> <RightMouse>
+ :noremap! <LeftDrag> <RightDrag>
+ :noremap! <LeftRelease> <RightRelease>
+ :noremap! <RightMouse> <LeftMouse>
+ :noremap! <RightDrag> <LeftDrag>
+ :noremap! <RightRelease> <LeftRelease>
+<
+
+==============================================================================
+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`):
>
- +------------------------------+ `
- | File Edit Help | <- Menu bar (m) `
- +-+--------------------------+-+ `
- |^| |^| `
- |#| Text area. |#| `
- | | | | `
- |v|__________________________|v| `
- Normal status line -> |-+ File.c 5,2 +-| `
+ +------------------------------+ `
+ | File Edit Help | <- Menu bar (m) `
+ +-+--------------------------+-+ `
+ |^| |^| `
+ |#| Text area. |#| `
+ | | | | `
+ |v|__________________________|v| `
+ Normal status line -> |-+ File.c 5,2 +-| `
between Vim windows |^|""""""""""""""""""""""""""|^| `
- | | | | `
- | | Another file buffer. | | `
- | | | | `
- |#| |#| `
- Left scrollbar (l) -> |#| |#| <- Right `
- |#| |#| scrollbar (r) `
- | | | | `
- |v| |v| `
- +-+--------------------------+-+ `
- | |< #### >| | <- Bottom `
- +-+--------------------------+-+ scrollbar (b) `
+ | | | | `
+ | | Another file buffer. | | `
+ | | | | `
+ |#| |#| `
+ Left scrollbar (l) -> |#| |#| <- Right `
+ |#| |#| scrollbar (r) `
+ | | | | `
+ |v| |v| `
+ +-+--------------------------+-+ `
+ | |< #### >| | <- 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.
-VERTICAL SCROLLBARS *gui-vert-scroll*
+VERTICAL SCROLLBARS *gui-vert-scroll*
Each Vim window has a scrollbar next to it which may be scrolled up and down
to move through the text in that buffer. The size of the scrollbar-thumb
@@ -115,7 +306,7 @@ is on the left half, the right scrollbar column will contain scrollbars for
the rightmost windows. The same happens on the other side.
-HORIZONTAL SCROLLBARS *gui-horiz-scroll*
+HORIZONTAL SCROLLBARS *gui-horiz-scroll*
The horizontal scrollbar (at the bottom of the Vim GUI) may be used to
scroll text sideways when the 'wrap' option is turned off. The
@@ -131,7 +322,7 @@ include the 'h' flag in 'guioptions'. Then the scrolling is limited by the
text of the current cursor line.
==============================================================================
-Drag and drop *drag-n-drop*
+Drag and drop *drag-n-drop*
You can drag and drop one or more files into the Vim window, where they will
be opened as if a |:drop| command was used.
@@ -150,12 +341,12 @@ names with any Ex command. Special characters (space, tab, double quote and
"|"; backslash on non-MS-Windows systems) will be escaped.
==============================================================================
-Menus *menus*
+Menus *menus*
For an introduction see |usr_42.txt| in the user manual.
-Using Menus *using-menus*
+Using Menus *using-menus*
Basically, menus can be used just like mappings. You can define your own
menus, as many as you like.
@@ -165,45 +356,45 @@ what the key sequence was.
For creating menus in a different language, see |:menutrans|.
- *menu.vim*
+ *menu.vim*
The default menus are read from the file "$VIMRUNTIME/menu.vim". See
|$VIMRUNTIME| for where the path comes from. You can set up your own menus.
Starting off with the default set is a good idea. You can add more items, or,
if you don't like the defaults at all, start with removing all menus
|:unmenu-all|. You can also avoid the default menus being loaded by adding
this line to your vimrc file (NOT your gvimrc file!): >
- :let did_install_default_menus = 1
+ :let did_install_default_menus = 1
If you also want to avoid the Syntax menu: >
- :let did_install_syntax_menu = 1
+ :let did_install_syntax_menu = 1
The first item in the Syntax menu can be used to show all available filetypes
in the menu (which can take a bit of time to load). If you want to have all
filetypes already present at startup, add: >
- :let do_syntax_sel_menu = 1
+ :let do_syntax_sel_menu = 1
Note that the menu.vim is sourced when `:syntax on` or `:filetype on` is
executed or after your .vimrc file is sourced. This means that the 'encoding'
option and the language of messages (`:language messages`) must be set before
that (if you want to change them).
- *console-menus*
+ *console-menus*
Although this documentation is in the GUI section, you can actually use menus
in console mode too. You will have to load |menu.vim| explicitly then, it is
not done by default. You can use the |:emenu| command and command-line
completion with 'wildmenu' to access the menu entries almost like a real menu
system. To do this, put these commands in your vimrc file: >
- :source $VIMRUNTIME/menu.vim
- :set wildmenu
- :set cpo-=<
- :set wcm=<C-Z>
- :map <F4> :emenu <C-Z>
+ :source $VIMRUNTIME/menu.vim
+ :set wildmenu
+ :set cpo-=<
+ :set wcm=<C-Z>
+ :map <F4> :emenu <C-Z>
Pressing <F4> will start the menu. You can now use the cursor keys to select
a menu entry. Hit <Enter> to execute it. Hit <Esc> if you want to cancel.
-Creating New Menus *creating-menus*
+Creating New Menus *creating-menus*
- *:me* *:menu* *:noreme* *:noremenu*
- *E330* *E327* *E331* *E336* *E333*
- *E328* *E329* *E337* *E792*
+ *:me* *:menu* *:noreme* *:noremenu*
+ *E330* *E327* *E331* *E336* *E333*
+ *E328* *E329* *E337* *E792*
To create a new menu item, use the ":menu" commands. They are mostly like
the ":map" set of commands (see |map-modes|), but the first argument is a menu
item name, given as a path of menus and submenus with a '.' between them,
@@ -224,15 +415,16 @@ tooltips for menus. See |terminal-input|.
Special characters in a menu name:
- *menu-shortcut*
- & The next character is the shortcut key. Make sure each
- shortcut key is only used once in a (sub)menu. If you want to
- insert a literal "&" in the menu name use "&&".
- *menu-text*
- <Tab> Separates the menu name from right-aligned text. This can be
- used to show the equivalent typed command. The text "<Tab>"
- can be used here for convenience. If you are using a real
- tab, don't forget to put a backslash before it!
+ *menu-shortcut*
+- & The next character is the shortcut key. Make sure each shortcut key is
+ only used once in a (sub)menu. If you want to insert a literal "&" in the
+ menu name use "&&".
+ *menu-text*
+- <Tab> Separates the menu name from right-aligned text. This can be used to
+ show the equivalent typed command. The text "<Tab>" can be used here for
+ convenience. If you are using a real tab, don't forget to put a backslash
+ before it!
+
Example: >
:amenu &File.&Open<Tab>:e :browse e<CR>
@@ -242,99 +434,99 @@ With the shortcut "F" (while keeping the <Alt> key pressed), and then "O",
this menu can be used. The second part is shown as "Open :e". The ":e"
is right aligned, and the "O" is underlined, to indicate it is the shortcut.
- *:am* *:amenu* *:an* *:anoremenu*
+ *:am* *:amenu* *:an* *:anoremenu*
The ":amenu" command can be used to define menu entries for all modes at once,
except for Terminal mode. To make the command work correctly, a character is
-automatically inserted for some modes:
- mode inserted appended ~
- Normal nothing nothing
- Visual <C-C> <C-\><C-G>
- Insert <C-\><C-O>
- Cmdline <C-C> <C-\><C-G>
- Op-pending <C-C> <C-\><C-G>
-
+automatically inserted for some modes: >
+ mode inserted appended
+ Normal nothing nothing
+ Visual <C-C> <C-\><C-G>
+ Insert <C-\><C-O>
+ Cmdline <C-C> <C-\><C-G>
+ Op-pending <C-C> <C-\><C-G>
+<
Example: >
- :amenu File.Next :next^M
+ :amenu File.Next :next^M
is equal to: >
- :nmenu File.Next :next^M
- :vmenu File.Next ^C:next^M^\^G
- :imenu File.Next ^\^O:next^M
- :cmenu File.Next ^C:next^M^\^G
- :omenu File.Next ^C:next^M^\^G
+ :nmenu File.Next :next^M
+ :vmenu File.Next ^C:next^M^\^G
+ :imenu File.Next ^\^O:next^M
+ :cmenu File.Next ^C:next^M^\^G
+ :omenu File.Next ^C:next^M^\^G
Careful: In Insert mode this only works for a SINGLE Normal mode command,
because of the CTRL-O. If you have two or more commands, you will need to use
the ":imenu" command. For inserting text in any mode, you can use the
expression register: >
- :amenu Insert.foobar "='foobar'<CR>P
+ :amenu Insert.foobar "='foobar'<CR>P
The special text <Cmd> begins a "command menu", it executes the command
directly without changing modes. Where you might use ":...<CR>" you can
instead use "<Cmd>...<CR>". See |<Cmd>| for more info. Example: >
- anoremenu File.Next <Cmd>next<CR>
+ anoremenu File.Next <Cmd>next<CR>
Note that <Esc> in Cmdline mode executes the command, like in a mapping. This
is Vi compatible. Use CTRL-C to quit Cmdline mode.
- *:nme* *:nmenu* *:nnoreme* *:nnoremenu* *:nunme* *:nunmenu*
+ *:nme* *:nmenu* *:nnoreme* *:nnoremenu* *:nunme* *:nunmenu*
Menu commands starting with "n" work in Normal mode. |mapmode-n|
- *:ome* *:omenu* *:onoreme* *:onoremenu* *:ounme* *:ounmenu*
+ *:ome* *:omenu* *:onoreme* *:onoremenu* *:ounme* *:ounmenu*
Menu commands starting with "o" work in Operator-pending mode. |mapmode-o|
- *:vme* *:vmenu* *:vnoreme* *:vnoremenu* *:vunme* *:vunmenu*
+ *:vme* *:vmenu* *:vnoreme* *:vnoremenu* *:vunme* *:vunmenu*
Menu commands starting with "v" work in Visual mode. |mapmode-v|
- *:xme* *:xmenu* *:xnoreme* *:xnoremenu* *:xunme* *:xunmenu*
+ *:xme* *:xmenu* *:xnoreme* *:xnoremenu* *:xunme* *:xunmenu*
Menu commands starting with "x" work in Visual and Select mode. |mapmode-x|
- *:sme* *:smenu* *:snoreme* *:snoremenu* *:sunme* *:sunmenu*
+ *:sme* *:smenu* *:snoreme* *:snoremenu* *:sunme* *:sunmenu*
Menu commands starting with "s" work in Select mode. |mapmode-s|
- *:ime* *:imenu* *:inoreme* *:inoremenu* *:iunme* *:iunmenu*
+ *:ime* *:imenu* *:inoreme* *:inoremenu* *:iunme* *:iunmenu*
Menu commands starting with "i" work in Insert mode. |mapmode-i|
- *:cme* *:cmenu* *:cnoreme* *:cnoremenu* *:cunme* *:cunmenu*
+ *:cme* *:cmenu* *:cnoreme* *:cnoremenu* *:cunme* *:cunmenu*
Menu commands starting with "c" work in Cmdline mode. |mapmode-c|
- *:tlm* *:tlmenu* *:tln* *:tlnoremenu* *:tlu* *:tlunmenu*
+ *:tlm* *:tlmenu* *:tln* *:tlnoremenu* *:tlu* *:tlunmenu*
Menu commands starting with "tl" work in Terminal mode. |mapmode-t|
- *:menu-<silent>* *:menu-silent*
+ *:menu-<silent>* *:menu-silent*
To define a menu which will not be echoed on the command line, add
"<silent>" as the first argument. Example: >
- :menu <silent> Settings.Ignore\ case :set ic<CR>
+ :menu <silent> Settings.Ignore\ case :set ic<CR>
The ":set ic" will not be echoed when using this menu. Messages from the
executed command are still given though. To shut them up too, add a ":silent"
in the executed command: >
- :menu <silent> Search.Header :exe ":silent normal /Header\r"<CR>
+ :menu <silent> Search.Header :exe ":silent normal /Header\r"<CR>
"<silent>" may also appear just after "<script>".
- *:menu-<script>* *:menu-script*
+ *:menu-<script>* *:menu-script*
The "to" part of the menu will be inspected for mappings. If you don't want
this, use the ":noremenu" command (or the similar one for a specific mode).
If you do want to use script-local mappings, add "<script>" as the very first
argument to the ":menu" command or just after "<silent>".
- *menu-priority*
+ *menu-priority*
You can give a priority to a menu. Menus with a higher priority go more to
the right. The priority is given as a number before the ":menu" command.
Example: >
- :80menu Buffer.next :bn<CR>
-
-The default menus have these priorities:
- File 10
- Edit 20
- Tools 40
- Syntax 50
- Buffers 60
- Window 70
- Help 9999
-
+ :80menu Buffer.next :bn<CR>
+
+The default menus have these priorities: >
+ File 10
+ Edit 20
+ Tools 40
+ Syntax 50
+ Buffers 60
+ Window 70
+ Help 9999
+<
When no or zero priority is given, 500 is used.
The priority for the PopUp menu is not used.
@@ -342,18 +534,18 @@ You can use a priority higher than 9999, to make it go after the Help menu,
but that is non-standard and is discouraged. The highest possible priority is
about 32000. The lowest is 1.
- *sub-menu-priority*
+ *sub-menu-priority*
The same mechanism can be used to position a sub-menu. The priority is then
given as a dot-separated list of priorities, before the menu name: >
- :menu 80.500 Buffer.next :bn<CR>
+ :menu 80.500 Buffer.next :bn<CR>
Giving the sub-menu priority is only needed when the item is not to be put
in a normal position. For example, to put a sub-menu before the other items: >
- :menu 80.100 Buffer.first :brew<CR>
+ :menu 80.100 Buffer.first :brew<CR>
Or to put a sub-menu after the other items, and further items with default
priority will be put before it: >
- :menu 80.900 Buffer.last :blast<CR>
+ :menu 80.900 Buffer.last :blast<CR>
When a number is missing, the default value 500 will be used: >
- :menu .900 myMenu.test :echo "text"<CR>
+ :menu .900 myMenu.test :echo "text"<CR>
The menu priority is only used when creating a new menu. When it already
existed, e.g., in another mode, the priority will not change. Thus, the
priority only needs to be given the first time a menu is used.
@@ -363,49 +555,49 @@ menus can be different. This is different from menu-bar menus, which have
the same order for all modes.
NOTE: sub-menu priorities currently don't work for all versions of the GUI.
- *menu-separator* *E332*
+ *menu-separator* *E332*
Menu items can be separated by a special item that inserts some space between
items. Depending on the system this is displayed as a line or a dotted line.
These items must start with a '-' and end in a '-'. The part in between is
used to give it a unique name. Priorities can be used as with normal items.
Example: >
- :menu Example.item1 :do something
- :menu Example.-Sep- :
- :menu Example.item2 :do something different
+ :menu Example.item1 :do something
+ :menu Example.-Sep- :
+ :menu Example.item2 :do something different
Note that the separator also requires a rhs. It doesn't matter what it is,
because the item will never be selected. Use a single colon to keep it
simple.
- *gui-toolbar*
+ *gui-toolbar*
The default toolbar is setup in menu.vim. The display of the toolbar is
controlled by the 'guioptions' letter 'T'. You can thus have menu & toolbar
together, or either on its own, or neither. The appearance is controlled by
the 'toolbar' option. You can choose between an image, text or both.
- *toolbar-icon*
+ *toolbar-icon*
The toolbar is defined as a special menu called ToolBar, which only has one
level. Vim interprets the items in this menu as follows:
-1) If an "icon=" argument was specified, the file with this name is used.
+- 1 If an "icon=" argument was specified, the file with this name is used.
The file can either be specified with the full path or with the base name.
In the last case it is searched for in the "bitmaps" directory in
'runtimepath', like in point 3. Examples: >
- :amenu icon=/usr/local/pixmaps/foo_icon.xpm ToolBar.Foo :echo "Foo"<CR>
- :amenu icon=FooIcon ToolBar.Foo :echo "Foo"<CR>
+ :amenu icon=/usr/local/pixmaps/foo_icon.xpm ToolBar.Foo :echo "Foo"<CR>
+ :amenu icon=FooIcon ToolBar.Foo :echo "Foo"<CR>
< Note that in the first case the extension is included, while in the second
case it is omitted.
If the file cannot be opened the next points are tried.
A space in the file name must be escaped with a backslash.
A menu priority must come _after_ the icon argument: >
- :amenu icon=foo 1.42 ToolBar.Foo :echo "42!"<CR>
-2) An item called 'BuiltIn##', where ## is a number, is taken as number ## of
+ :amenu icon=foo 1.42 ToolBar.Foo :echo "42!"<CR>
+- 2 An item called 'BuiltIn##', where ## is a number, is taken as number ## of
the built-in bitmaps available in Vim. Currently there are 31 numbered
from 0 to 30 which cover most common editing operations |builtin-tools|. >
- :amenu ToolBar.BuiltIn22 :call SearchNext("back")<CR>
-3) An item with another name is first searched for in the directory
+ :amenu ToolBar.BuiltIn22 :call SearchNext("back")<CR>
+- 3 An item with another name is first searched for in the directory
"bitmaps" in 'runtimepath'. If found, the bitmap file is used as the
toolbar button image. Note that the exact filename is OS-specific: For
example, under Win32 the command >
- :amenu ToolBar.Hello :echo "hello"<CR>
+ :amenu ToolBar.Hello :echo "hello"<CR>
< would find the file 'hello.bmp'. Under X11 it is 'Hello.xpm'.
For MS-Windows and the bitmap is scaled to fit the button. For
MS-Windows a size of 18 by 18 pixels works best.
@@ -413,55 +605,56 @@ level. Vim interprets the items in this menu as follows:
The light grey pixels will be changed to the Window frame color and the
dark grey pixels to the window shadow color. More colors might also work,
depending on your system.
-4) If the bitmap is still not found, Vim checks for a match against its list
+- 4 If the bitmap is still not found, Vim checks for a match against its list
of built-in names. Each built-in button image has a name.
So the command >
- :amenu ToolBar.Open :e
+ :amenu ToolBar.Open :e
< will show the built-in "open a file" button image if no open.bmp exists.
All the built-in names can be seen used in menu.vim.
-5) If all else fails, a blank, but functioning, button is displayed.
-
- *builtin-tools*
-nr Name Normal action ~
-00 New open new window
-01 Open browse for file to open in current window
-02 Save write buffer to file
-03 Undo undo last change
-04 Redo redo last undone change
-05 Cut delete selected text to clipboard
-06 Copy copy selected text to clipboard
-07 Paste paste text from clipboard
-08 Print print current buffer
-09 Help open a buffer on Vim's builtin help
-10 Find start a search command
-11 SaveAll write all modified buffers to file
-12 SaveSesn write session file for current situation
-13 NewSesn write new session file
-14 LoadSesn load session file
-15 RunScript browse for file to run as a Vim script
-16 Replace prompt for substitute command
-17 WinClose close current window
-18 WinMax make current window use many lines
-19 WinMin make current window use few lines
-20 WinSplit split current window
-21 Shell start a shell
-22 FindPrev search again, backward
-23 FindNext search again, forward
-24 FindHelp prompt for word to search help for
-25 Make run make and jump to first error
-26 TagJump jump to tag under the cursor
-27 RunCtags build tags for files in current directory
-28 WinVSplit split current window vertically
-29 WinMaxWidth make current window use many columns
-30 WinMinWidth make current window use few columns
-
- *hidden-menus* *win32-hidden-menus*
+- 5 If all else fails, a blank, but functioning, button is displayed.
+
+ *builtin-tools*
+>
+ nr Name Normal action
+ 00 New open new window
+ 01 Open browse for file to open in current window
+ 02 Save write buffer to file
+ 03 Undo undo last change
+ 04 Redo redo last undone change
+ 05 Cut delete selected text to clipboard
+ 06 Copy copy selected text to clipboard
+ 07 Paste paste text from clipboard
+ 08 Print print current buffer
+ 09 Help open a buffer on Vim's builtin help
+ 10 Find start a search command
+ 11 SaveAll write all modified buffers to file
+ 12 SaveSesn write session file for current situation
+ 13 NewSesn write new session file
+ 14 LoadSesn load session file
+ 15 RunScript browse for file to run as a Vim script
+ 16 Replace prompt for substitute command
+ 17 WinClose close current window
+ 18 WinMax make current window use many lines
+ 19 WinMin make current window use few lines
+ 20 WinSplit split current window
+ 21 Shell start a shell
+ 22 FindPrev search again, backward
+ 23 FindNext search again, forward
+ 24 FindHelp prompt for word to search help for
+ 25 Make run make and jump to first error
+ 26 TagJump jump to tag under the cursor
+ 27 RunCtags build tags for files in current directory
+ 28 WinVSplit split current window vertically
+ 29 WinMaxWidth make current window use many columns
+ 30 WinMinWidth make current window use few columns
+<
+ *hidden-menus* *win32-hidden-menus*
In the Win32 GUI, starting a menu name with ']' excludes that menu from the
main menu bar. You must then use the |:popup| command to display it.
When splitting the window the window toolbar is not copied to the new window.
- *popup-menu*
+ *popup-menu*
You can define the special menu "PopUp". This is the menu that is displayed
when the right mouse button is pressed, if 'mousemodel' is set to popup or
popup_setpos.
@@ -483,7 +676,7 @@ The default "PopUp" menu is: >vim
anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR>
<
-Showing What Menus Are Mapped To *showing-menus*
+Showing What Menus Are Mapped To *showing-menus*
To see what an existing menu is mapped to, use just one argument after the
menu commands (just like you would with the ":map" commands). If the menu
@@ -502,25 +695,25 @@ Note that hitting <Tab> while entering a menu name after a menu command may
be used to complete the name of the menu item.
-Executing Menus *execute-menus*
+Executing Menus *execute-menus*
- *:em* *:emenu* *E334* *E335*
-:[range]em[enu] {menu} Execute {menu} from the command line.
- The default is to execute the Normal mode
- menu. If a range is specified, it executes
- the Visual mode menu.
- If used from <c-o>, it executes the
- insert-mode menu Eg: >
- :emenu File.Exit
+ *:em* *:emenu* *E334* *E335*
+:[range]em[enu] {menu} Execute {menu} from the command line.
+ The default is to execute the Normal mode
+ menu. If a range is specified, it executes
+ the Visual mode menu.
+ If used from <c-o>, it executes the
+ insert-mode menu Eg: >
+ :emenu File.Exit
-:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}:
- 'n': |:nmenu| Normal mode
- 'v': |:vmenu| Visual mode
- 's': |:smenu| Select mode
- 'o': |:omenu| Operator-pending mode
- 't': |:tlmenu| Terminal mode
- 'i': |:imenu| Insert mode
- 'c': |:cmenu| Cmdline mode
+:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}:
+ - 'n': |:nmenu| Normal mode
+ - 'v': |:vmenu| Visual mode
+ - 's': |:smenu| Select mode
+ - 'o': |:omenu| Operator-pending mode
+ - 't': |:tlmenu| Terminal mode
+ - 'i': |:imenu| Insert mode
+ - 'c': |:cmenu| Cmdline mode
You can use :emenu to access useful menu items you may have got used to from
@@ -531,10 +724,10 @@ When using a range, if the lines match with '<,'>, then the menu is executed
using the last visual selection.
-Deleting Menus *delete-menus*
+Deleting Menus *delete-menus*
- *:unme* *:unmenu*
- *:aun* *:aunmenu*
+ *:unme* *:unmenu*
+ *:aun* *:aunmenu*
To delete a menu item or a whole submenu, use the unmenu commands, which are
analogous to the unmap commands. Eg: >
:unmenu! Edit.Paste
@@ -545,26 +738,26 @@ Command-line modes.
Note that hitting <Tab> while entering a menu name after an umenu command
may be used to complete the name of the menu item for the appropriate mode.
-To remove all menus use: *:unmenu-all* >
- :unmenu * " remove all menus in Normal and visual mode
- :unmenu! * " remove all menus in Insert and Command-line mode
- :aunmenu * " remove all menus in all modes, except for Terminal
- " mode
- :tlunmenu * " remove all menus in Terminal mode
+To remove all menus use: *:unmenu-all* >
+ :unmenu * " remove all menus in Normal and visual mode
+ :unmenu! * " remove all menus in Insert and Command-line mode
+ :aunmenu * " remove all menus in all modes, except for Terminal
+ " mode
+ :tlunmenu * " remove all menus in Terminal mode
If you want to get rid of the menu bar: >
- :set guioptions-=m
+ :set guioptions-=m
-Disabling Menus *disable-menus*
+Disabling Menus *disable-menus*
- *:menu-disable* *:menu-enable*
+ *:menu-disable* *:menu-enable*
If you do not want to remove a menu, but disable it for a moment, this can be
done by adding the "enable" or "disable" keyword to a ":menu" command.
Examples: >
- :menu disable &File.&Open\.\.\.
- :amenu enable *
- :amenu disable &Tools.*
+ :menu disable &File.&Open\.\.\.
+ :amenu enable *
+ :amenu disable &Tools.*
The command applies to the modes as used with all menu commands. Note that
characters like "&" need to be included for translated names to be found.
@@ -572,36 +765,36 @@ When the argument is "*", all menus are affected. Otherwise the given menu
name and all existing submenus below it are affected.
-Examples for Menus *menu-examples*
+Examples for Menus *menu-examples*
Here is an example on how to add menu items with menus! You can add a menu
item for the keyword under the cursor. The register "z" is used. >
- :nmenu Words.Add\ Var wb"zye:menu! Words.<C-R>z <C-R>z<CR>
- :nmenu Words.Remove\ Var wb"zye:unmenu! Words.<C-R>z<CR>
- :vmenu Words.Add\ Var "zy:menu! Words.<C-R>z <C-R>z <CR>
- :vmenu Words.Remove\ Var "zy:unmenu! Words.<C-R>z<CR>
- :imenu Words.Add\ Var <Esc>wb"zye:menu! Words.<C-R>z <C-R>z<CR>a
- :imenu Words.Remove\ Var <Esc>wb"zye:unmenu! Words.<C-R>z<CR>a
+ :nmenu Words.Add\ Var wb"zye:menu! Words.<C-R>z <C-R>z<CR>
+ :nmenu Words.Remove\ Var wb"zye:unmenu! Words.<C-R>z<CR>
+ :vmenu Words.Add\ Var "zy:menu! Words.<C-R>z <C-R>z <CR>
+ :vmenu Words.Remove\ Var "zy:unmenu! Words.<C-R>z<CR>
+ :imenu Words.Add\ Var <Esc>wb"zye:menu! Words.<C-R>z <C-R>z<CR>a
+ :imenu Words.Remove\ Var <Esc>wb"zye:unmenu! Words.<C-R>z<CR>a
(the rhs is in <> notation, you can copy/paste this text to try out the
mappings, or put these lines in your gvimrc; "<C-R>" is CTRL-R, "<CR>" is
the <CR> key. |<>|)
- *tooltips* *menu-tips*
+ *tooltips* *menu-tips*
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)
+ *:tmenu*
+: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)
+ *:tunmenu*
+:tu[nmenu] {menupath} Remove a tip for a menu or tool.
+ (only in X11 and Win32 GUI)
Note: To create menus for terminal mode, use |:tlmenu| instead.
@@ -615,11 +808,11 @@ highlight group to change its colors.
A "tip" can be defined for each menu item. For example, when defining a menu
item like this: >
- :amenu MyMenu.Hello :echo "Hello"<CR>
+ :amenu MyMenu.Hello :echo "Hello"<CR>
The tip is defined like this: >
- :tmenu MyMenu.Hello Displays a greeting.
+ :tmenu MyMenu.Hello Displays a greeting.
And delete it with: >
- :tunmenu MyMenu.Hello
+ :tunmenu MyMenu.Hello
Tooltips are currently only supported for the X11 and Win32 GUI. However, they
should appear for the other gui platforms in the not too distant future.
@@ -638,24 +831,24 @@ a menu item - you don't need to do a :tunmenu as well.
You can cause a menu to popup at the cursor. This behaves similarly to the
PopUp menus except that any menu tree can be popped up.
- *:popup* *:popu*
-:popu[p] {name} Popup the menu {name}. The menu named must
- have at least one subentry, but need not
- appear on the menu-bar (see |hidden-menus|).
+ *:popup* *:popu*
+:popu[p] {name} Popup the menu {name}. The menu named must
+ have at least one subentry, but need not
+ appear on the menu-bar (see |hidden-menus|).
-:popu[p]! {name} Like above, but use the position of the mouse
- pointer instead of the cursor.
+:popu[p]! {name} Like above, but use the position of the mouse
+ pointer instead of the cursor.
Example: >
- :popup File
+ :popup File
will make the "File" menu (if there is one) appear at the text cursor (mouse
pointer if ! was used). >
- :amenu ]Toolbar.Make :make<CR>
- :popup ]Toolbar
+ :amenu ]Toolbar.Make :make<CR>
+ :popup ]Toolbar
This creates a popup menu that doesn't exist on the main menu-bar.
Note that a menu that starts with ']' will not be displayed.
- vim:tw=78:sw=4:ts=8:noet:ft=help:norl:
+ vim:tw=78:sw=4:ts=8:et:ft=help:norl:
diff --git a/runtime/doc/health.txt b/runtime/doc/health.txt
index cb70961b55..3d37b88321 100644
--- a/runtime/doc/health.txt
+++ b/runtime/doc/health.txt
@@ -21,7 +21,7 @@ To run all healthchecks, use: >vim
<
Plugin authors are encouraged to write new healthchecks. |health-dev|
-Commands *health-commands*
+COMMANDS *health-commands*
*:che* *:checkhealth*
:che[ckhealth] Run all healthchecks.
@@ -49,6 +49,23 @@ Commands *health-commands*
:checkhealth vim*
<
+USAGE *health-usage*
+
+Local mappings in the healthcheck buffer:
+
+q Closes the window.
+
+Global configuration:
+
+ *g:health*
+g:health Dictionary with the following optional keys:
+ - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
+ a floating window instead of the default behavior.
+
+ Example: >lua
+ vim.g.health = { style = 'float' }
+
+--------------------------------------------------------------------------------
Create a healthcheck *health-dev*
Healthchecks are functions that check the user environment, configuration, or
diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt
index fd8bfd644f..914dc64c0a 100644
--- a/runtime/doc/help.txt
+++ b/runtime/doc/help.txt
@@ -150,6 +150,7 @@ LANGUAGE SUPPORT
|arabic.txt| Arabic language support and editing
|hebrew.txt| Hebrew language support and editing
|russian.txt| Russian language support and editing
+|vietnamese.txt| Vietnamese language support and editing
------------------------------------------------------------------------------
INTEROP
diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt
index 46b3ab507d..72d37f6088 100644
--- a/runtime/doc/helphelp.txt
+++ b/runtime/doc/helphelp.txt
@@ -193,6 +193,7 @@ Jump to specific subjects by using tags. This can be done in two ways:
Use CTRL-T or CTRL-O to jump back.
Use ":q" to close the help window.
+Use `g==` to execute the current Lua/Vimscript code block.
If there are several matches for an item you are looking for, this is how you
can jump to each one of them:
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 9ee75ea950..0256707420 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1190,7 +1190,7 @@ tag command action ~
|:breakdel| :breakd[el] delete a debugger breakpoint
|:breaklist| :breakl[ist] list debugger breakpoints
|:browse| :bro[wse] use file selection dialog
-|:bufdo| :bufdo execute command in each listed buffer
+|:bufdo| :bufd[o] execute command in each listed buffer
|:buffers| :buffers list all files in the buffer list
|:bunload| :bun[load] unload a specific buffer
|:bwipeout| :bw[ipeout] really delete a buffer
@@ -1206,7 +1206,7 @@ tag command action ~
|:cafter| :caf[ter] go to error after current cursor
|:call| :cal[l] call a function
|:catch| :cat[ch] part of a :try command
-|:cbefore| :cbef[ore] go to error before current cursor
+|:cbefore| :cbe[fore] go to error before current cursor
|:cbelow| :cbel[ow] go to error below current line
|:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window
|:cbuffer| :cb[uffer] parse error messages and jump to first error
@@ -1262,6 +1262,7 @@ tag command action ~
|:delete| :d[elete] delete lines
|:debug| :deb[ug] run a command in debugging mode
|:debuggreedy| :debugg[reedy] read debug mode commands from normal input
+|:defer| :defe[r] call function when current function is done
|:delcommand| :delc[ommand] delete user-defined command
|:delfunction| :delf[unction] delete a user function
|:delmarks| :delm[arks] delete marks
@@ -1271,7 +1272,7 @@ tag command action ~
|:diffpatch| :diffp[atch] apply a patch and show differences
|:diffput| :diffpu[t] remove differences in other buffer
|:diffsplit| :diffs[plit] show differences with another file
-|:diffthis| :diffthis make current window a diff window
+|:diffthis| :difft[his] make current window a diff window
|:digraphs| :dig[raphs] show or enter digraphs
|:display| :di[splay] display registers
|:djump| :dj[ump] jump to #define
@@ -1372,7 +1373,7 @@ tag command action ~
|:last| :la[st] go to the last file in the argument list
|:language| :lan[guage] set the language (locale)
|:later| :lat[er] go to newer change, redo
-|:lbefore| :lbef[ore] go to location before current cursor
+|:lbefore| :lbe[fore] go to location before current cursor
|:lbelow| :lbel[ow] go to location below current line
|:lbottom| :lbo[ttom] scroll to the bottom of the location window
|:lbuffer| :lb[uffer] parse locations and jump to first location
@@ -1410,7 +1411,7 @@ tag command action ~
|:lockmarks| :loc[kmarks] following command keeps marks where they are
|:lockvar| :lockv[ar] lock variables
|:lolder| :lol[der] go to older location list
-|:lopen| :lope[n] open location window
+|:lopen| :lop[en] open location window
|:lprevious| :lp[revious] go to previous location
|:lpfile| :lpf[ile] go to last location in previous file
|:lrewind| :lr[ewind] go to the specified location, default first one
@@ -1472,6 +1473,7 @@ tag command action ~
|:ownsyntax| :ow[nsyntax] set new local syntax highlight for this window
|:packadd| :pa[ckadd] add a plugin from 'packpath'
|:packloadall| :packl[oadall] load all packages under 'packpath'
+|:pbuffer| :pb[uffer] edit buffer in the preview window
|:pclose| :pc[lose] close preview window
|:pedit| :ped[it] edit file in the preview window
|:perl| :pe[rl] execute perl command
@@ -1569,6 +1571,8 @@ tag command action ~
|:sign| :sig[n] manipulate signs
|:silent| :sil[ent] run a command silently
|:sleep| :sl[eep] do nothing for a few seconds
+|:sleep!| :sl[eep]! do nothing for a few seconds, without the
+ cursor visible
|:slast| :sla[st] split window and go to last file in the
argument list
|:smagic| :sm[agic] :substitute with 'magic'
@@ -1615,7 +1619,7 @@ tag command action ~
|:tNext| :tN[ext] jump to previous matching tag
|:tabNext| :tabN[ext] go to previous tab page
|:tabclose| :tabc[lose] close current tab page
-|:tabdo| :tabdo execute command in each tab page
+|:tabdo| :tabd[o] execute command in each tab page
|:tabedit| :tabe[dit] edit a file in a new tab page
|:tabfind| :tabf[ind] find file in 'path', edit it in a new tab page
|:tabfirst| :tabfir[st] go to first tab page
@@ -1684,7 +1688,7 @@ tag command action ~
|:vsplit| :vs[plit] split current window vertically
|:vunmap| :vu[nmap] like ":unmap" but for Visual+Select mode
|:vunmenu| :vunme[nu] remove menu for Visual+Select mode
-|:windo| :windo execute command in each window
+|:windo| :wind[o] execute command in each window
|:write| :w[rite] write to a file
|:wNext| :wN[ext] write to a file and go to previous file in
argument list
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 48fd442b7e..bc0e9ba314 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -1189,6 +1189,7 @@ items:
|hl-PmenuKind| highlight group, allowing for the
customization of ctermfg and guifg properties for the
completion kind
+ match See "matches" in |complete_info()|.
All of these except "icase", "equal", "dup" and "empty" must be a string. If
an item does not meet these requirements then an error message is given and
@@ -2026,7 +2027,7 @@ the cursor is, or below the specified line. To insert text above the first
line use the command ":0r {name}".
After the ":read" command, the cursor is left on the first non-blank in the
-first new line. Unless in Ex mode, then the cursor is left on the last new
+first new line. If in Ex mode, then the cursor is left on the last new
line (sorry, this is Vi compatible).
If a file name is given with ":r", it becomes the alternate file. This can be
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index d099c29bdb..0c654b8b30 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -1,15 +1,15 @@
*intro.txt* Nvim
- NVIM REFERENCE MANUAL
+ NVIM REFERENCE MANUAL
-Nvim *ref* *reference*
+Nvim *ref* *reference*
Type |gO| to see the table of contents.
==============================================================================
-Introduction *intro*
+Introduction *intro*
Vim is a text editor which includes most commands from the Unix program "Vi"
and many new ones.
@@ -21,52 +21,75 @@ It can be accessed from within Vim with the <Help> or <F1> key and with the
is not located in the default place. You can jump to subjects like with tags:
Use CTRL-] to jump to a subject under the cursor, use CTRL-T to jump back.
- *pronounce*
+ *pronounce*
Vim is pronounced as one word, like Jim. So Nvim is "En-Vim", two syllables.
This manual is a reference for all Nvim editor and API features. It is not an
-introduction; instead for beginners, there is a hands-on |tutor| and a user
-manual |usr_toc.txt|.
-
- *book*
-There are many books on Vi and Vim. We recommend:
-
- "Practical Vim" by Drew Neil
- "Modern Vim" by Drew Neil
- https://vimcasts.org/publications/
-
-"Practical Vim" is acclaimed for its focus on quickly learning common editing
-tasks with Vim. "Modern Vim" explores new features in Nvim and Vim 8.
-
- "Vim - Vi Improved" by Steve Oualline
-
-This was the first book dedicated to Vim. Parts of it were included in the
-user manual. |frombook| ISBN: 0735710015
-For more information try one of these:
- https://iccf-holland.org/click5.html
- https://www.vim.org/iccf/click5.html
-
-==============================================================================
-Nvim on the interwebs *internet*
-
- *www* *distribution* *download*
-
- Nvim home page: https://neovim.io/
- Downloads: https://github.com/neovim/neovim/releases
- Vim FAQ: https://vimhelp.org/vim_faq.txt.html
-
-
- *bugs* *bug-report*
-Report bugs and request features here:
-https://github.com/neovim/neovim/issues
-
+introduction; instead for beginners, there is a hands-on |tutor|, |lua-guide|,
+and |user-manual|.
+
+------------------------------------------------------------------------------
+Resources *resources*
+
+ *internet* *www* *distribution*
+Nvim home page:
+
+ https://neovim.io/
+
+ *book*
+There are many resources to learn Vi, Vim, and Nvim. We recommend:
+
+- "Practical Vim" by Drew Neil. Acclaimed for its focus on quickly learning
+ common editing tasks with Vim.
+- "Modern Vim" by Drew Neil. Explores new features in Nvim and Vim 8.
+- https://vimcasts.org/publications/
+- "Vim - Vi Improved" by Steve Oualline. This was the first book dedicated to
+ Vim. Parts of it were included in the Vim user manual. |frombook| ISBN:
+ 0735710015
+- For more information try one of these:
+ - https://iccf-holland.org/click5.html
+ - https://www.vim.org/iccf/click5.html
+- Vim FAQ: https://vimhelp.org/vim_faq.txt.html
+
+ *bugs* *bug-report* *feature-request*
+Report bugs and request features here: https://github.com/neovim/neovim/issues
Be brief, yet complete. Always give a reproducible example and try to find
-out which settings or other things trigger the bug.
+out which settings or other things trigger the bug. If Nvim crashed, try to
+get a backtrace (see |dev-tools-backtrace|).
-If Nvim crashes, try to get a backtrace. See |debug.txt|.
+==============================================================================
+Installing Nvim *install*
+
+ *download* *upgrade* *ubuntu*
+To install or upgrade Nvim, you can...
+- Download a pre-built archive:
+ https://github.com/neovim/neovim/releases
+- Use your system package manager:
+ https://github.com/neovim/neovim/blob/master/INSTALL.md#install-from-package
+- Build from source:
+ https://github.com/neovim/neovim/blob/master/INSTALL.md#install-from-source
+
+------------------------------------------------------------------------------
+Un-installing Nvim *uninstall*
+
+To uninstall Nvim:
+- If you downloaded a pre-built archive or built Nvim from source (e.g.
+ `make install`), just delete its files, typically located in: >
+ /usr/local/bin/nvim
+ /usr/local/share/nvim
+<
+ - To find where Nvim is installed, run these commands: >
+ :echo v:progpath
+ :echo $VIMRUNTIME
+<
+- If you installed via package manager, read your package manager's
+ documentation. Common examples:
+ - APT (Debian, Ubuntu, …): `apt-get remove neovim`
+ - Homebrew (macOS): `brew uninstall neovim`
+ - Scoop (Windows): `scoop uninstall neovim`
==============================================================================
-Sponsor Vim/Nvim development *sponsor* *register*
+Sponsor Vim/Nvim development *sponsor* *register*
Fixing bugs and adding new features takes a lot of time and effort. To show
your appreciation for the work and motivate developers to continue working on
@@ -78,121 +101,15 @@ motivation to keep working on Vim!
For the most recent information about sponsoring look on the Vim web site:
- https://www.vim.org/sponsor/
+ https://www.vim.org/sponsor/
Nvim development is funded separately from Vim:
- https://neovim.io/#sponsor
-
-==============================================================================
-Credits *credits*
-
-Most of Vim was written by Bram Moolenaar <Bram@vim.org> |Bram-Moolenaar|.
-
-Parts of the documentation come from several Vi manuals, written by:
- W.N. Joy
- Alan P.W. Hewett
- Mark Horton
-
-The Vim editor is based on Stevie and includes (ideas from) other software,
-worked on by the people mentioned here. Other people helped by sending me
-patches, suggestions and giving feedback about what is good and bad in Vim.
-
-Vim would never have become what it is now, without the help of these people!
-
- Ron Aaron Win32 GUI changes
- Mohsin Ahmed encryption
- Zoltan Arpadffy work on VMS port
- Tony Andrews Stevie
- Gert van Antwerpen changes for DJGPP on MS-DOS
- Berkeley DB(3) ideas for swap file implementation
- Keith Bostic Nvi
- Walter Briscoe Makefile updates, various patches
- Ralf Brown SPAWNO library for MS-DOS
- Robert Colon many useful remarks
- Marcin Dalecki GTK+ GUI port, toolbar icons, gettext()
- Kayhan Demirel sent me news in Uganda
- Chris & John Downey xvi (ideas for multi-windows version)
- Henk Elbers first VMS port
- Daniel Elstner GTK+ 2 port
- Eric Fischer Mac port, 'cindent', and other improvements
- Benji Fisher Answering lots of user questions
- Bill Foster Athena GUI port (later removed)
- Google Let Bram work on Vim one day a week
- Loic Grenie xvim (ideas for multi windows version)
- Sven Guckes Vim promoter and previous WWW page maintainer
- Darren Hiebert Exuberant ctags
- Jason Hildebrand GTK+ 2 port
- Bruce Hunsaker improvements for VMS port
- Andy Kahn Cscope support, GTK+ GUI port
- Oezguer Kesim Maintainer of Vim Mailing Lists
- Axel Kielhorn work on the Macintosh port
- Steve Kirkendall Elvis
- Roger Knobbe original port to Windows NT
- Sergey Laskavy Vim's help from Moscow
- Felix von Leitner Previous maintainer of Vim Mailing Lists
- David Leonard Port of Python extensions to Unix
- Avner Lottem Edit in right-to-left windows
- Flemming Madsen X11 client-server, various features and patches
- Tony Mechelynck answers many user questions
- Paul Moore Python interface extensions, many patches
- Katsuhito Nagano Work on multibyte versions
- Sung-Hyun Nam Work on multibyte versions
- Vince Negri Win32 GUI and generic console enhancements
- Steve Oualline Author of the first Vim book |frombook|
- Dominique Pelle Valgrind reports and many fixes
- A.Politz Many bug reports and some fixes
- George V. Reilly Win32 port, Win32 GUI start-off
- Stephen Riehm bug collector
- Stefan Roemer various patches and help to users
- Ralf Schandl IBM OS/390 port
- Olaf Seibert DICE and BeBox version, regexp improvements
- Mortaza Shiran Farsi patches
- Peter da Silva termlib
- Paul Slootman OS/2 port
- Henry Spencer regular expressions
- Dany St-Amant Macintosh port
- Tim Thompson Stevie
- G. R. (Fred) Walter Stevie
- Sven Verdoolaege Perl interface
- Robert Webb Command-line completion, GUI versions, and
- lots of patches
- Ingo Wilken Tcl interface
- Mike Williams PostScript printing
- Juergen Weigert Lattice version, AUX improvements, Unix and
- MS-DOS ports, autoconf
- Stefan 'Sec' Zehl Maintainer of vim.org
- Yasuhiro Matsumoto many MS-Windows improvements
- Ken Takata fixes and features
- Kazunobu Kuriyama GTK 3
- Christian Brabandt many fixes, features, user support, etc.
- Yegappan Lakshmanan many quickfix features
-
-I wish to thank all the people that sent me bug reports and suggestions. The
-list is too long to mention them all here. Vim would not be the same without
-the ideas from all these people: They keep Vim alive!
-*love* *peace* *friendship* *gross-national-happiness*
-
-
-Documentation may refer to other versions of Vi:
- *Vi* *vi*
-Vi "the original". Without further remarks this is the version
- of Vi that appeared in Sun OS 4.x. ":version" returns
- "Version 3.7, 6/7/85". Source code only available with a license.
- *Nvi*
-Nvi The "New" Vi. The version of Vi that comes with BSD 4.4 and FreeBSD.
- Very good compatibility with the original Vi, with a few extensions.
- The version used is 1.79. ":version" returns "Version 1.79
- (10/23/96)". Source code is freely available.
- *Elvis*
-Elvis Another Vi clone, made by Steve Kirkendall. Very compact but isn't
- as flexible as Vim. Source code is freely available.
-
-Vim Nvim is based on Vim. https://www.vim.org/
+ https://neovim.io/#sponsor
==============================================================================
-Bram Moolenaar *Bram* *Moolenaar* *Bram-Moolenaar* *brammool*
+Bram Moolenaar *Bram* *Moolenaar* *Bram-Moolenaar* *brammool*
Nvim is a fork of the Vim ("Vi IMproved") text editor, which was originally
developed by Bram Moolenaar. Searching his name within the source code of
@@ -201,11 +118,11 @@ On August 3, 2023, he passed away at the age of 62. If Vim or Nvim have been
of use to you in your life, please read |Uganda| and consider honoring his
memory however you may see fit.
-Obituary Articles: https://github.com/vim/vim/discussions/12742
-Say Farewell: https://github.com/vim/vim/discussions/12737
+- Obituary Articles: https://github.com/vim/vim/discussions/12742
+- Say Farewell: https://github.com/vim/vim/discussions/12737
==============================================================================
-Notation *notation*
+Notation *notation*
When syntax highlighting is used to read this, text that is not typed
literally is often highlighted with the Special group. These are items in [],
@@ -215,177 +132,177 @@ Note that Vim uses all possible characters in commands. Sometimes the [], {}
and <> are part of what you type, the context should make this clear.
-[] Characters in square brackets are optional.
-
- *count* *[count]*
-[count] An optional number that may precede the command to multiply
- or iterate the command. If no number is given, a count of one
- is used, unless otherwise noted. Note that in this manual the
- [count] is not mentioned in the description of the command,
- but only in the explanation. This was done to make the
- commands easier to look up. If the 'showcmd' option is on,
- the (partially) entered count is shown at the bottom of the
- window. You can use <Del> to erase the last digit (|N<Del>|).
-
- *[quotex]*
-["x] An optional register designation where text can be stored.
- See |registers|. The x is a single character between 'a' and
- 'z' or 'A' and 'Z' or '"', and in some cases (with the put
- command) between '0' and '9', '%', '#', or others. The
- uppercase and lowercase letter designate the same register,
- but the lowercase letter is used to overwrite the previous
- register contents, while the uppercase letter is used to
- append to the previous register contents. Without the ""x" or
- with """" the stored text is put into the unnamed register.
-
- *{}*
-{} Curly braces denote parts of the command which must appear,
- but which can take a number of different values. The
- differences between Vim and Vi are also given in curly braces
- (this will be clear from the context).
-
- *{char1-char2}*
-{char1-char2} A single character from the range char1 to char2. For
- example: {a-z} is a lowercase letter. Multiple ranges may be
- concatenated. For example, {a-zA-Z0-9} is any alphanumeric
- character.
-
- *{motion}* *movement*
-{motion} A command that moves the cursor. These are explained in
- |motion.txt|. Examples:
- w to start of next word
- b to begin of current word
- 4j four lines down
- /The<CR> to next occurrence of "The"
- This is used after an |operator| command to move over the text
- that is to be operated upon.
- - If the motion includes a count and the operator also has a
- count, the two counts are multiplied. For example: "2d3w"
- deletes six words.
- - The motion can be backwards, e.g. "db" to delete to the
- start of the word.
- - The motion can also be a mouse click. The mouse is not
- supported in every terminal though.
- - The ":omap" command can be used to map characters while an
- operator is pending.
- - Ex commands can be used to move the cursor. This can be
- used to call a function that does some complicated motion.
- The motion is always charwise exclusive, no matter
- what ":" command is used. This means it's impossible to
- include the last character of a line without the line break
- (unless 'virtualedit' is set).
- If the Ex command changes the text before where the operator
- starts or jumps to another buffer the result is
- unpredictable. It is possible to change the text further
- down. Jumping to another buffer is possible if the current
- buffer is not unloaded.
-
- *{Visual}*
-{Visual} A selected text area. It is started with the "v", "V", or
- CTRL-V command, then any cursor movement command can be used
- to change the end of the selected text.
- This is used before an |operator| command to highlight the
- text that is to be operated upon.
- See |Visual-mode|.
-
- *<character>*
-<character> A special character from the table below, optionally with
- modifiers, or a single ASCII character with modifiers.
-
- *'character'*
-'c' A single ASCII character.
-
- *CTRL-{char}*
-CTRL-{char} {char} typed as a control character; that is, typing {char}
- while holding the CTRL key down. The case of {char} is
- ignored; thus CTRL-A and CTRL-a are equivalent. But in
- some terminals and environments, using the SHIFT key will
- produce a distinct code (e.g. CTRL-SHIFT-a); in these
- environments using the SHIFT key will not trigger commands
- such as CTRL-A.
-
- *'option'*
-'option' An option, or parameter, that can be set to a value, is
- enclosed in single quotes. See |options|.
-
- *quotecommandquote*
-"command" A reference to a command that you can type is enclosed in
- double quotes.
-`command` New style command, this distinguishes it from other quoted
- text and strings.
-
- *key-notation* *key-codes* *keycodes*
+- [] Characters in square brackets are optional.
+
+ *count* *[count]*
+- [count] An optional number that may precede the command to multiply
+ or iterate the command. If no number is given, a count of one
+ is used, unless otherwise noted. Note that in this manual the
+ [count] is not mentioned in the description of the command,
+ but only in the explanation. This was done to make the
+ commands easier to look up. If the 'showcmd' option is on,
+ the (partially) entered count is shown at the bottom of the
+ window. You can use <Del> to erase the last digit (|N<Del>|).
+
+ *[quotex]*
+- ["x] An optional register designation where text can be stored.
+ See |registers|. The x is a single character between 'a' and
+ 'z' or 'A' and 'Z' or '"', and in some cases (with the put
+ command) between '0' and '9', '%', '#', or others. The
+ uppercase and lowercase letter designate the same register,
+ but the lowercase letter is used to overwrite the previous
+ register contents, while the uppercase letter is used to
+ append to the previous register contents. Without the ""x" or
+ with """" the stored text is put into the unnamed register.
+
+ *{}*
+- {} Curly braces denote parts of the command which must appear,
+ but which can take a number of different values. The
+ differences between Vim and Vi are also given in curly braces
+ (this will be clear from the context).
+
+ *{char1-char2}*
+- {char1-char2} A single character from the range char1 to char2. For
+ example: {a-z} is a lowercase letter. Multiple ranges may be
+ concatenated. For example, {a-zA-Z0-9} is any alphanumeric
+ character.
+
+ *{motion}* *movement*
+- {motion} A command that moves the cursor. These are explained in
+ |motion.txt|.
+ - Examples:
+ - `w` to start of next word
+ - `b` to begin of current word
+ - `4j` four lines down
+ - `/The<CR>` to next occurrence of "The"
+ - This is used after an |operator| command to move over the
+ text that is to be operated upon.
+ - If the motion includes a count and the operator also has
+ a count, the two counts are multiplied. For example:
+ "2d3w" deletes six words.
+ - The motion can be backwards, e.g. "db" to delete to the
+ start of the word.
+ - The motion can also be a mouse click. The mouse is not
+ supported in every terminal though.
+ - The ":omap" command can be used to map characters while an
+ operator is pending.
+ - Ex commands can be used to move the cursor. This can be
+ used to call a function that does some complicated motion.
+ The motion is always charwise exclusive, no matter what
+ ":" command is used. This means it's impossible to
+ include the last character of a line without the line
+ break (unless 'virtualedit' is set). If the Ex command
+ changes the text before where the operator starts or jumps
+ to another buffer the result is unpredictable. It is
+ possible to change the text further down. Jumping to
+ another buffer is possible if the current buffer is not
+ unloaded.
+
+ *{Visual}*
+- {Visual} A selected text area. It is started with the "v", "V", or
+ CTRL-V command, then any cursor movement command can be used
+ to change the end of the selected text.
+ This is used before an |operator| command to highlight the
+ text that is to be operated upon.
+ See |Visual-mode|.
+
+ *<character>*
+- <character> A special character from the table below, optionally with
+ modifiers, or a single ASCII character with modifiers.
+
+ *'character'*
+- 'c' A single ASCII character.
+
+ *CTRL-{char}*
+- CTRL-{char} {char} typed as a control character; that is, typing {char}
+ while holding the CTRL key down. The case of {char} is
+ ignored; thus CTRL-A and CTRL-a are equivalent. But in
+ some terminals and environments, using the SHIFT key will
+ produce a distinct code (e.g. CTRL-SHIFT-a); in these
+ environments using the SHIFT key will not trigger commands
+ such as CTRL-A.
+
+ *'option'*
+- 'option' An option, or parameter, that can be set to a value, is
+ enclosed in single quotes. See |options|.
+
+ *quotecommandquote*
+- "command" A reference to a command that you can type is enclosed in
+ double quotes.
+- `command` New style command, this distinguishes it from other quoted
+ text and strings.
+
+ *key-notation* *key-codes* *keycodes*
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*
- *linefeed*
-<NL> linefeed CTRL-J 10 (used for <Nul>)
-<CR> carriage return CTRL-M 13 *carriage-return*
-<Return> same as <CR> *<Return>*
-<Enter> same as <CR> *<Enter>*
-<Esc> escape CTRL-[ 27 *escape* *<Esc>*
-<Space> space 32 *space*
-<lt> less-than < 60 *<lt>*
-<Bslash> backslash \ 92 *backslash* *<Bslash>*
-<Bar> vertical bar | 124 *<Bar>*
-<Del> delete 127
-<CSI> command sequence intro ALT-Esc 155 *<CSI>*
-
-<EOL> end-of-line (can be <CR>, <NL> or <CR><NL>,
- depends on system and 'fileformat') *<EOL>*
-<Ignore> cancel wait-for-character *<Ignore>*
-<NOP> no-op: do nothing (useful in mappings) *<Nop>*
-
-<Up> cursor-up *cursor-up* *cursor_up*
-<Down> cursor-down *cursor-down* *cursor_down*
-<Left> cursor-left *cursor-left* *cursor_left*
-<Right> cursor-right *cursor-right* *cursor_right*
-<S-Up> shift-cursor-up
-<S-Down> shift-cursor-down
-<S-Left> shift-cursor-left
-<S-Right> shift-cursor-right
-<C-Left> control-cursor-left
-<C-Right> control-cursor-right
-<F1> - <F12> function keys 1 to 12 *function_key* *function-key*
-<S-F1> - <S-F12> shift-function keys 1 to 12 *<S-F1>*
-<Help> help key
-<Undo> undo key
-<Insert> insert key
-<Home> home *home*
-<End> end *end*
-<PageUp> page-up *page_up* *page-up*
-<PageDown> page-down *page_down* *page-down*
-<kUp> keypad cursor-up *keypad-cursor-up*
-<kDown> keypad cursor-down *keypad-cursor-down*
-<kLeft> keypad cursor-left *keypad-cursor-left*
-<kRight> keypad cursor-right *keypad-cursor-right*
-<kHome> keypad home (upper left) *keypad-home*
-<kEnd> keypad end (lower left) *keypad-end*
-<kOrigin> keypad origin (middle) *keypad-origin*
-<kPageUp> keypad page-up (upper right) *keypad-page-up*
-<kPageDown> keypad page-down (lower right) *keypad-page-down*
-<kDel> keypad delete *keypad-delete*
-<kPlus> keypad + *keypad-plus*
-<kMinus> keypad - *keypad-minus*
-<kMultiply> keypad * *keypad-multiply*
-<kDivide> keypad / *keypad-divide*
-<kPoint> keypad . *keypad-point*
-<kComma> keypad , *keypad-comma*
-<kEqual> keypad = *keypad-equal*
-<kEnter> keypad Enter *keypad-enter*
-<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9*
-<S-…> shift-key *shift* *<S-*
-<C-…> control-key *control* *ctrl* *<C-*
-<M-…> alt-key or meta-key *META* *ALT* *<M-*
-<A-…> same as <M-…> *<A-*
-<T-…> meta-key when it's not alt *<T-*
-<D-…> command-key or "super" key *<D-*
------------------------------------------------------------------------ ~
+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*
+ *linefeed*
+<NL> linefeed CTRL-J 10 (used for <Nul>)
+<CR> carriage return CTRL-M 13 *carriage-return*
+<Return> same as <CR> *<Return>*
+<Enter> same as <CR> *<Enter>*
+<Esc> escape CTRL-[ 27 *escape* *<Esc>*
+<Space> space 32 *space*
+<lt> less-than < 60 *<lt>*
+<Bslash> backslash \ 92 *backslash* *<Bslash>*
+<Bar> vertical bar | 124 *<Bar>*
+<Del> delete 127
+<CSI> command sequence intro ALT-Esc 155 *<CSI>*
+
+<EOL> end-of-line (can be <CR>, <NL> or <CR><NL>,
+ depends on system and 'fileformat') *<EOL>*
+<Ignore> cancel wait-for-character *<Ignore>*
+<NOP> no-op: do nothing (useful in mappings) *<Nop>*
+
+<Up> cursor-up *cursor-up* *cursor_up*
+<Down> cursor-down *cursor-down* *cursor_down*
+<Left> cursor-left *cursor-left* *cursor_left*
+<Right> cursor-right *cursor-right* *cursor_right*
+<S-Up> shift-cursor-up
+<S-Down> shift-cursor-down
+<S-Left> shift-cursor-left
+<S-Right> shift-cursor-right
+<C-Left> control-cursor-left
+<C-Right> control-cursor-right
+<F1> - <F12> function keys 1 to 12 *function_key* *function-key*
+<S-F1> - <S-F12> shift-function keys 1 to 12 *<S-F1>*
+<Help> help key
+<Undo> undo key
+<Insert> insert key
+<Home> home *home*
+<End> end *end*
+<PageUp> page-up *page_up* *page-up*
+<PageDown> page-down *page_down* *page-down*
+<kUp> keypad cursor-up *keypad-cursor-up*
+<kDown> keypad cursor-down *keypad-cursor-down*
+<kLeft> keypad cursor-left *keypad-cursor-left*
+<kRight> keypad cursor-right *keypad-cursor-right*
+<kHome> keypad home (upper left) *keypad-home*
+<kEnd> keypad end (lower left) *keypad-end*
+<kOrigin> keypad origin (middle) *keypad-origin*
+<kPageUp> keypad page-up (upper right) *keypad-page-up*
+<kPageDown> keypad page-down (lower right) *keypad-page-down*
+<kDel> keypad delete *keypad-delete*
+<kPlus> keypad + *keypad-plus*
+<kMinus> keypad - *keypad-minus*
+<kMultiply> keypad * *keypad-multiply*
+<kDivide> keypad / *keypad-divide*
+<kPoint> keypad . *keypad-point*
+<kComma> keypad , *keypad-comma*
+<kEqual> keypad = *keypad-equal*
+<kEnter> keypad Enter *keypad-enter*
+<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9*
+<S-…> shift-key *shift* *<S-*
+<C-…> control-key *control* *ctrl* *<C-*
+<M-…> alt-key or meta-key *META* *ALT* *<M-*
+<A-…> same as <M-…> *<A-*
+<T-…> meta-key when it's not alt *<T-*
+<D-…> command-key or "super" key *<D-*
+
Note:
@@ -400,125 +317,125 @@ Note:
- It is possible to notate combined modifiers (e.g. <M-C-T> for CTRL-ALT-T),
but your terminal must encode the input for that to work. |tui-input|
- *<>*
+ *<>*
Examples are often given in the <> notation. Sometimes this is just to make
clear what you need to type, but often it can be typed literally, e.g., with
the ":map" command. The rules are:
- 1. Printable characters are typed directly, except backslash and "<"
- 2. Backslash is represented with "\\", double backslash, or "<Bslash>".
- 3. Literal "<" is represented with "\<" or "<lt>". When there is no
- confusion possible, "<" can be used directly.
- 4. "<key>" means the special key typed (see the table above). Examples:
- <Esc> Escape key
- <C-G> CTRL-G
- <Up> cursor up key
- <C-LeftMouse> Control- left mouse click
- <S-F11> Shifted function key 11
- <M-a> Meta- a ('a' with bit 8 set)
- <M-A> Meta- A ('A' with bit 8 set)
+1. Printable characters are typed directly, except backslash and "<"
+2. Backslash is represented with "\\", double backslash, or "<Bslash>".
+3. Literal "<" is represented with "\<" or "<lt>". When there is no
+ confusion possible, "<" can be used directly.
+4. "<key>" means the special key typed (see the table above). Examples:
+ - <Esc> Escape key
+ - <C-G> CTRL-G
+ - <Up> cursor up key
+ - <C-LeftMouse> Control- left mouse click
+ - <S-F11> Shifted function key 11
+ - <M-a> Meta- a ('a' with bit 8 set)
+ - <M-A> Meta- A ('A' with bit 8 set)
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>": >vim
- :imap <C-H> \<Home>
- :imap <C-H> <lt>Home>
+ :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: >vim
- :map <C-L> <lt>lt>
+ :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*
+Modes, introduction *vim-modes-intro* *vim-modes*
Vim has seven BASIC modes:
- *Normal* *Normal-mode* *command-mode*
-Normal mode In Normal mode you can enter all the normal editor
- commands. If you start the editor you are in this
- mode. This is also known as command mode.
-
-Visual mode This is like Normal mode, but the movement commands
- extend a highlighted area. When a non-movement
- command is used, it is executed for the highlighted
- area. See |Visual-mode|.
- If the 'showmode' option is on "-- VISUAL --" is shown
- at the bottom of the window.
-
-Select mode This looks most like the MS-Windows selection mode.
- Typing a printable character deletes the selection
- and starts Insert mode. See |Select-mode|.
- If the 'showmode' option is on "-- SELECT --" is shown
- at the bottom of the window.
-
-Insert mode In Insert mode the text you type is inserted into the
- buffer. See |Insert-mode|.
- If the 'showmode' option is on "-- INSERT --" is shown
- at the bottom of the window.
-
-Command-line mode In Command-line mode (also called Cmdline mode) you
-Cmdline mode can enter one line of text at the bottom of the
- window. This is for the Ex commands, ":", the pattern
- search commands, "?" and "/", and the filter command,
- "!". |Cmdline-mode|
-
-Ex mode Like Command-line mode, but after entering a command
- you remain in Ex mode. Very limited editing of the
- command line. |Ex-mode|
-
- *Terminal-mode*
-Terminal mode In Terminal mode all input (except CTRL-\) is sent to
- the process running in the current |terminal| buffer.
- If CTRL-\ is pressed, the next key is sent unless it
- is CTRL-N (|CTRL-\_CTRL-N|) or CTRL-O (|t_CTRL-\_CTRL-O|).
- If the 'showmode' option is on "-- TERMINAL --" is shown
- at the bottom of the window.
+ *Normal* *Normal-mode* *command-mode*
+- Normal mode: In Normal mode you can enter all the normal editor
+ commands. If you start the editor you are in this
+ mode. This is also known as command mode.
+
+- Visual mode: This is like Normal mode, but the movement commands
+ extend a highlighted area. When a non-movement
+ command is used, it is executed for the highlighted
+ area. See |Visual-mode|.
+ If the 'showmode' option is on "-- VISUAL --" is shown
+ at the bottom of the window.
+
+- Select mode: This looks most like the MS-Windows selection mode.
+ Typing a printable character deletes the selection
+ and starts Insert mode. See |Select-mode|.
+ If the 'showmode' option is on "-- SELECT --" is shown
+ at the bottom of the window.
+
+- Insert mode: In Insert mode the text you type is inserted into the
+ buffer. See |Insert-mode|.
+ If the 'showmode' option is on "-- INSERT --" is shown
+ at the bottom of the window.
+
+- Cmdline mode: In Command-line mode (also called Cmdline mode) you
+ can enter one line of text at the bottom of the
+ window. This is for the Ex commands, ":", the pattern
+ search commands, "?" and "/", and the filter command,
+ "!". |Cmdline-mode|
+
+- Ex mode: Like Command-line mode, but after entering a command
+ you remain in Ex mode. Very limited editing of the
+ command line. |Ex-mode|
+
+ *Terminal-mode*
+- Terminal mode: In Terminal mode all input (except CTRL-\) is sent to
+ the process running in the current |terminal| buffer.
+ If CTRL-\ is pressed, the next key is sent unless it
+ is CTRL-N (|CTRL-\_CTRL-N|) or CTRL-O (|t_CTRL-\_CTRL-O|).
+ If the 'showmode' option is on "-- TERMINAL --" is shown
+ at the bottom of the window.
There are six ADDITIONAL modes. These are variants of the BASIC modes:
- *Operator-pending* *Operator-pending-mode*
-Operator-pending mode This is like Normal mode, but after an operator
- command has started, and Vim is waiting for a {motion}
- to specify the text that the operator will work on.
-
-Replace mode Replace mode is a special case of Insert mode. You
- can do the same things as in Insert mode, but for
- each character you enter, one character of the existing
- text is deleted. See |Replace-mode|.
- If the 'showmode' option is on "-- REPLACE --" is
- shown at the bottom of the window.
-
-Virtual Replace mode Virtual Replace mode is similar to Replace mode, but
- instead of file characters you are replacing screen
- real estate. See |Virtual-Replace-mode|.
- If the 'showmode' option is on "-- VREPLACE --" is
- shown at the bottom of the window.
-
-Insert Normal mode Entered when CTRL-O is typed in Insert mode (see
- |i_CTRL-O|). This is like Normal mode, but after
- executing one command Vim returns to Insert mode.
- If the 'showmode' option is on "-- (insert) --" is
- shown at the bottom of the window.
-
-Insert Visual mode Entered when starting a Visual selection from Insert
- mode, e.g., by using CTRL-O and then "v", "V" or
- CTRL-V. When the Visual selection ends, Vim returns
- to Insert mode.
- If the 'showmode' option is on "-- (insert) VISUAL --"
- is shown at the bottom of the window.
-
-Insert Select mode Entered when starting Select mode from Insert mode.
- E.g., by dragging the mouse or <S-Right>.
- When the Select mode ends, Vim returns to Insert mode.
- If the 'showmode' option is on "-- (insert) SELECT --"
- is shown at the bottom of the window.
+ *Operator-pending* *Operator-pending-mode*
+- Operator-pending mode: This is like Normal mode, but after an operator
+ command has started, and Vim is waiting for a {motion}
+ to specify the text that the operator will work on.
+
+- Replace mode: Replace mode is a special case of Insert mode. You
+ can do the same things as in Insert mode, but for
+ each character you enter, one character of the existing
+ text is deleted. See |Replace-mode|.
+ If the 'showmode' option is on "-- REPLACE --" is
+ shown at the bottom of the window.
+
+- Virtual Replace mode: Virtual Replace mode is similar to Replace mode, but
+ instead of file characters you are replacing screen
+ real estate. See |Virtual-Replace-mode|.
+ If the 'showmode' option is on "-- VREPLACE --" is
+ shown at the bottom of the window.
+
+- Insert Normal mode: Entered when CTRL-O is typed in Insert mode (see
+ |i_CTRL-O|). This is like Normal mode, but after
+ executing one command Vim returns to Insert mode.
+ If the 'showmode' option is on "-- (insert) --" is
+ shown at the bottom of the window.
+
+- Insert Visual mode: Entered when starting a Visual selection from Insert
+ mode, e.g., by using CTRL-O and then "v", "V" or
+ CTRL-V. When the Visual selection ends, Vim returns
+ to Insert mode.
+ If the 'showmode' option is on "-- (insert) VISUAL --"
+ is shown at the bottom of the window.
+
+- Insert Select mode: Entered when starting Select mode from Insert mode.
+ E.g., by dragging the mouse or <S-Right>.
+ When the Select mode ends, Vim returns to Insert mode.
+ If the 'showmode' option is on "-- (insert) SELECT --"
+ is shown at the bottom of the window.
==============================================================================
-Switching from mode to mode *mode-switching*
+Switching from mode to mode *mode-switching*
If for any reason you do not know which mode you are in, you can always get
back to Normal mode by typing <Esc> twice. This doesn't work for Ex mode
@@ -528,25 +445,27 @@ hear the bell after you type <Esc>. However, when pressing <Esc> after using
CTRL-O in Insert mode you get a beep but you are still in Insert mode, type
<Esc> again.
- *i_esc*
- FROM mode TO mode ~
- Normal Visual Select Insert Replace Cmd-line Ex >
- 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 -- --
- Ex :vi -- -- -- -- --
-
--- not possible
+ *i_esc*
+ >
+ FROM mode TO mode
+ Normal Visual Select Insert Replace Cmd-line Ex >
+ 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 -- --
+ Ex :vi -- -- -- -- --
+
+ -- not possible
+<
-* 1 Go from Normal mode to Insert mode by giving the command "i", "I", "a",
+- 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
+- 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:
+- 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
@@ -554,37 +473,37 @@ CTRL-O in Insert mode you get a beep but you are still in Insert mode, type
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:
+- 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
+- 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
+- 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*
+ *CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N*
+ *v_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N*
Additionally the command CTRL-\ CTRL-N or <C-\><C-N> can be used to go to
Normal mode from any other mode. This can be used to make sure Vim is in
Normal mode, without causing a beep like <Esc> would. However, this does not
work in Ex mode. When used after a command that takes an argument, such as
|f| or |m|, the timeout set with 'ttimeoutlen' applies.
- *CTRL-\_CTRL-G* *i_CTRL-\_CTRL-G* *c_CTRL-\_CTRL-G* *v_CTRL-\_CTRL-G*
+ *CTRL-\_CTRL-G* *i_CTRL-\_CTRL-G* *c_CTRL-\_CTRL-G* *v_CTRL-\_CTRL-G*
CTRL-\ CTRL-G works the same as |CTRL-\_CTRL-N| for backward compatibility.
- *gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501*
-gQ Switch to Ex mode. This is like typing ":" commands
- one after another, except:
- - You don't have to keep pressing ":".
- - The screen doesn't get updated after each command.
- Use the `:vi` command (|:visual|) to exit this mode.
+ *gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501*
+gQ Switch to Ex mode. This is like typing ":" commands
+ one after another, except:
+ - You don't have to keep pressing ":".
+ - The screen doesn't get updated after each command.
+ Use the `:vi` command (|:visual|) to exit this mode.
==============================================================================
-Window contents *window-contents*
+Window contents *window-contents*
In Normal mode and Insert/Replace mode the screen window will show the current
contents of the buffer: What You See Is What You Get. There are two
@@ -601,24 +520,24 @@ 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 |
- |~ |
- |~ |
- +-----------------------+
+ +-----------------------+
+ |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 |
- |@ |
- |@ |
- +-----------------------+
+ +-----------------------+
+ |first line |
+ |second line |
+ |@ |
+ |@ |
+ +-----------------------+
<
Thus the '@' lines indicate that there is a line that doesn't fit in the
window.
@@ -628,12 +547,12 @@ When the "lastline" flag is present in the 'display' option, you will not see
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@@@|
- +-----------------------+
+ +-----------------------+
+ |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
@@ -646,7 +565,7 @@ from real characters in the buffer.
The 'showbreak' option contains the string to put in front of wrapped lines.
- *wrap-off*
+ *wrap-off*
If the 'wrap' option is off, long lines will not wrap. Only the part that
fits on the screen is shown. If the cursor is moved to a part of the line
that is not shown, the screen is scrolled horizontally. The advantage of
@@ -665,8 +584,8 @@ position on the screen. The cursor can only be positioned on the first one.
If you set the 'number' option, all lines will be preceded with their
number. Tip: If you don't like wrapping lines to mix with the line numbers,
-set the 'showbreak' option to eight spaces:
- ":set showbreak=\ \ \ \ \ \ \ \ "
+set the 'showbreak' option to eight spaces: >
+ ":set showbreak=\ \ \ \ \ \ \ \ "
If you set the 'list' option, <Tab> characters will not be shown as several
spaces, but as "^I". A '$' will be placed at the end of the line, so you can
@@ -677,19 +596,19 @@ display of the buffer contents is updated as soon as you go back to Command
mode.
The last line of the window is used for status and other messages. The
-status messages will only be used if an option is on:
+status messages will only be used if an option is on: >
-status message option default Unix default ~
-current mode 'showmode' on on
-command characters 'showcmd' on off
-cursor position 'ruler' off off
+ status message option default Unix default
+ current mode 'showmode' on on
+ command characters 'showcmd' on off
+ cursor position 'ruler' off off
The current mode is "-- INSERT --" or "-- REPLACE --", see |'showmode'|. The
command characters are those that you typed but were not used yet.
If you have a slow terminal you can switch off the status messages to speed
up editing: >
- :set nosc noru nosm
+ :set nosc noru nosm
If there is an error, an error message will be shown for at least one second
(in reverse video).
@@ -704,70 +623,70 @@ small not a single line will fit in it. Make it at least 40 characters wide
to be able to read most messages on the last line.
==============================================================================
-Definitions *definitions* *jargon*
+Definitions *definitions* *jargon*
- buffer Contains lines of text, usually from a file.
- screen The whole area that Nvim uses to display things.
- window A view on a buffer. There can be multiple windows for
- one buffer.
- frame Windows are kept in a tree of frames. Each frame
- contains a column, row, or window ("leaf" frame).
+- buffer: Contains lines of text, usually from a file.
+- screen: The whole area that Nvim uses to display things.
+- window: A view on a buffer. There can be multiple windows for one buffer.
+- frame: Windows are kept in a tree of frames. Each frame contains a column,
+ row, or window ("leaf" frame).
A screen contains one or more windows, separated by status lines and with the
command line at the bottom.
>
- +-------------------------------+
- screen | window 1 | window 2 |
- | | |
- | | |
- |= status line =|= status line =|
- | window 3 |
- | |
- | |
- |==== status line ==============|
- |command line |
- +-------------------------------+
+ +-------------------------------+
+ screen | window 1 | window 2 |
+ | | |
+ | | |
+ |= status line =|= status line =|
+ | window 3 |
+ | |
+ | |
+ |==== 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.
A difference is made between four types of lines:
- buffer lines The lines in the buffer. This is the same as the
- lines as they are read from/written to a file. They
- can be thousands of characters long.
- logical lines The buffer lines with folding applied. Buffer lines
- in a closed fold are changed to a single logical line:
- "+-- 99 lines folded". They can be thousands of
- characters long.
- window lines The lines displayed in a window: A range of logical
- lines with wrapping, line breaks, etc. applied. They
- can only be as long as the width of the window allows,
- longer lines are wrapped or truncated.
- screen lines The lines of the screen that Nvim uses. Consists of
- the window lines of all windows, with status lines
- and the command line added. They can only be as long
- as the width of the screen allows. When the command
- line gets longer it wraps and lines are scrolled to
- make room.
-
-buffer lines logical lines window lines screen lines ~
-
-1. one 1. one 1. +-- folded 1. +-- folded
-2. two 2. +-- folded 2. five 2. five
-3. three 3. five 3. six 3. six
-4. four 4. six 4. seven 4. seven
-5. five 5. seven 5. === status line ===
-6. six 6. aaa
-7. seven 7. bbb
- 8. ccc ccc c
-1. aaa 1. aaa 1. aaa 9. cc
-2. bbb 2. bbb 2. bbb 10. ddd
-3. ccc ccc ccc 3. ccc ccc ccc 3. ccc ccc c 11. ~
-4. ddd 4. ddd 4. cc 12. === status line ===
- 5. ddd 13. (command line)
- 6. ~
+- buffer lines: The lines in the buffer. This is the same as the
+ lines as they are read from/written to a file. They
+ can be thousands of characters long.
+- logical lines: The buffer lines with folding applied. Buffer lines
+ in a closed fold are changed to a single logical line:
+ "+-- 99 lines folded". They can be thousands of
+ characters long.
+- window lines: The lines displayed in a window: A range of logical
+ lines with wrapping, line breaks, etc. applied. They
+ can only be as long as the width of the window allows,
+ longer lines are wrapped or truncated.
+- screen lines: The lines of the screen that Nvim uses. Consists of
+ the window lines of all windows, with status lines
+ and the command line added. They can only be as long
+ as the width of the screen allows. When the command
+ line gets longer it wraps and lines are scrolled to
+ make room.
+>
+ buffer lines logical lines window lines screen lines
+ -----------------------------------------------------------------------
+ 1. one 1. one 1. +-- folded 1. +-- folded
+ 2. two 2. +-- folded 2. five 2. five
+ 3. three 3. five 3. six 3. six
+ 4. four 4. six 4. seven 4. seven
+ 5. five 5. seven 5. === status line ===
+ 6. six 6. aaa
+ 7. seven 7. bbb
+ 8. ccc ccc c
+ 1. aaa 1. aaa 1. aaa 9. cc
+ 2. bbb 2. bbb 2. bbb 10. ddd
+ 3. ccc ccc ccc 3. ccc ccc ccc 3. ccc ccc c 11. ~
+ 4. ddd 4. ddd 4. cc 12. === status line ===
+ 5. ddd 13. (command line)
+ 6. ~
+<
API client ~
All external UIs and remote plugins (as opposed to regular Vim plugins) are
@@ -775,8 +694,8 @@ All external UIs and remote plugins (as opposed to regular Vim plugins) are
to abstract or wrap the RPC API for the convenience of other applications
(just like a REST client or SDK such as boto3 for AWS: you can speak AWS REST
using an HTTP client like curl, but boto3 wraps that in a convenient python
-interface). For example, the Nvim lua-client is an API client:
- https://github.com/neovim/lua-client
+interface). For example, the Nvim node-client is an API client:
+ https://github.com/neovim/node-client
Host ~
@@ -791,4 +710,4 @@ process and communicates with Nvim via the |api|.
==============================================================================
- vim:tw=78:ts=8:noet:ft=help:norl:
+ vim:tw=78:ts=8:et:sw=4:ft=help:norl:
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 64bef849fc..25ef7f24cc 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -28,31 +28,110 @@ Follow these steps to get LSP features:
upstream installation instructions. You can find language servers here:
https://microsoft.github.io/language-server-protocol/implementors/servers/
-2. Use |vim.lsp.start()| to start the LSP server (or attach to an existing
- one) when a file is opened. Example: >lua
- -- Create an event handler for the FileType autocommand
- vim.api.nvim_create_autocmd('FileType', {
- -- This handler will fire when the buffer's 'filetype' is "python"
- pattern = 'python',
- callback = function(args)
- vim.lsp.start({
- name = 'my-server-name',
- cmd = {'name-of-language-server-executable', '--option', 'arg1', 'arg2'},
-
- -- Set the "root directory" to the parent directory of the file in the
- -- current buffer (`args.buf`) that contains either a "setup.py" or a
- -- "pyproject.toml" file. Files that share a root directory will reuse
- -- the connection to the same LSP server.
- root_dir = vim.fs.root(args.buf, {'setup.py', 'pyproject.toml'}),
- })
- end,
- })
+2. Use |vim.lsp.config()| to define a configuration for an LSP client.
+ Example: >lua
+ vim.lsp.config['luals'] = {
+ -- Command and arguments to start the server.
+ cmd = { 'lua-language-server' },
+
+ -- Filetypes to automatically attach to.
+ filetypes = { 'lua' },
+
+ -- Sets the "root directory" to the parent directory of the file in the
+ -- current buffer that contains either a ".luarc.json" or a
+ -- ".luarc.jsonc" file. Files that share a root directory will reuse
+ -- the connection to the same LSP server.
+ root_markers = { '.luarc.json', '.luarc.jsonc' },
+
+ -- Specific settings to send to the server. The schema for this is
+ -- defined by the server. For example the schema for lua-language-server
+ -- can be found here https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json
+ settings = {
+ Lua = {
+ runtime = {
+ version = 'LuaJIT',
+ }
+ }
+ }
+ }
+<
+3. Use |vim.lsp.enable()| to enable a configuration.
+ Example: >lua
+ vim.lsp.enable('luals')
+<
+4. Check that the buffer is attached to the server: >vim
+ :checkhealth vim.lsp
<
-3. Check that the buffer is attached to the server: >vim
- :checkhealth lsp
+5. (Optional) Configure keymaps and autocommands to use LSP features.
+ |lsp-attach|
+
+ *lsp-config*
+
+Configurations for LSP clients is done via |vim.lsp.config()|.
+
+When an LSP client starts, it resolves a configuration by merging
+configurations, in increasing priority, from the following:
+
+1. Configuration defined for the `'*'` name.
+
+2. Configuration from the result of merging all tables returned by
+ `lsp/<name>.lua` files in 'runtimepath' for a server of name `name`.
+
+3. Configurations defined anywhere else.
+
+Note: The merge semantics of configurations follow the behaviour of
+|vim.tbl_deep_extend()|.
-4. (Optional) Configure keymaps and autocommands to use LSP features. |lsp-config|
+Example:
+Given: >lua
+ -- Defined in init.lua
+ vim.lsp.config('*', {
+ capabilities = {
+ textDocument = {
+ semanticTokens = {
+ multilineTokenSupport = true,
+ }
+ }
+ },
+ root_markers = { '.git' },
+ })
+
+ -- Defined in ../lsp/clangd.lua
+ return {
+ cmd = { 'clangd' },
+ root_markers = { '.clangd', 'compile_commands.json' },
+ filetypes = { 'c', 'cpp' },
+ }
+
+ -- Defined in init.lua
+ vim.lsp.config('clangd', {
+ filetypes = { 'c' },
+ })
+<
+Results in the configuration: >lua
+ {
+ -- From the clangd configuration in <rtp>/lsp/clangd.lua
+ cmd = { 'clangd' },
+
+ -- From the clangd configuration in <rtp>/lsp/clangd.lua
+ -- Overrides the * configuration in init.lua
+ root_markers = { '.clangd', 'compile_commands.json' },
+
+ -- From the clangd configuration in init.lua
+ -- Overrides the * configuration in init.lua
+ filetypes = { 'c' },
+
+ -- From the * configuration in init.lua
+ capabilities = {
+ textDocument = {
+ semanticTokens = {
+ multilineTokenSupport = true,
+ }
+ }
+ }
+ }
+<
*lsp-defaults*
When the Nvim LSP client starts it enables diagnostics |vim.diagnostic| (see
|vim.diagnostic.config()| to customize). It also sets various default options,
@@ -98,7 +177,7 @@ To override or delete any of the above defaults, set or unset the options on
end,
})
<
- *lsp-config*
+ *lsp-attach*
To use other LSP features, set keymaps and other buffer options on
|LspAttach|. Not all language servers provide the same capabilities. Use
capability checks to ensure you only use features supported by the language
@@ -107,16 +186,16 @@ server. Example: >lua
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local client = vim.lsp.get_client_by_id(args.data.client_id)
- if client.supports_method('textDocument/implementation') then
+ if client:supports_method('textDocument/implementation') then
-- Create a keymap for vim.lsp.buf.implementation
end
- if client.supports_method('textDocument/completion') then
+ if client:supports_method('textDocument/completion') then
-- Enable auto-completion
vim.lsp.completion.enable(true, client.id, args.buf, {autotrigger = true})
end
- if client.supports_method('textDocument/formatting') then
+ if client:supports_method('textDocument/formatting') then
-- Format the current buffer on save
vim.api.nvim_create_autocmd('BufWritePre', {
buffer = args.buf,
@@ -204,6 +283,7 @@ won't run if your server doesn't support them.
- `'textDocument/diagnostic'`
- `'textDocument/documentHighlight'`
- `'textDocument/documentSymbol'`
+- `'textDocument/foldingRange'`
- `'textDocument/formatting'`
- `'textDocument/hover'`
- `'textDocument/implementation'`
@@ -264,22 +344,17 @@ Each response handler has this signature: >
*lsp-handler-resolution*
Handlers can be set by (in increasing priority):
-- Setting a field in vim.lsp.handlers. *vim.lsp.handlers*
- `vim.lsp.handlers` is a global table that contains the default mapping of
- |lsp-method| names to lsp-handlers.
-
+ *vim.lsp.handlers*
+- Setting a field in `vim.lsp.handlers`. This global table contains the
+ default mappings of |lsp-method| names to handlers. (Note: only for
+ server-to-client requests/notifications, not client-to-server.)
Example: >lua
-
vim.lsp.handlers['textDocument/publishDiagnostics'] = my_custom_diagnostics_handler
<
- Note: this only applies for requests/notifications made by the
- server to the client.
-
-- The {handlers} parameter of |vim.lsp.start()|. This sets the default
- |lsp-handler| for a specific server.
-
+- Passing a {handlers} parameter to |vim.lsp.start()|. This sets the default
+ |lsp-handler| for a specific server. (Note: only for server-to-client
+ requests/notifications, not client-to-server.)
Example: >lua
-
vim.lsp.start {
..., -- Other configuration omitted.
handlers = {
@@ -287,14 +362,9 @@ Handlers can be set by (in increasing priority):
},
}
<
- Note: this only applies for requests/notifications made by the
- server to the client.
-
-- The {handler} parameter of |vim.lsp.buf_request_all()|. This sets
- the |lsp-handler| ONLY for the given request(s).
-
+- Passing a {handler} parameter to |vim.lsp.buf_request_all()|. This sets the
+ |lsp-handler| ONLY for the given request(s).
Example: >lua
-
vim.lsp.buf_request_all(
0,
'textDocument/publishDiagnostics',
@@ -464,7 +534,7 @@ EVENTS *lsp-events*
LspAttach *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. See |lsp-config| for an example.
+ callback in the "data" table. See |lsp-attach| for an example.
LspDetach *LspDetach*
Just before an LSP client detaches from a buffer. The |autocmd-pattern|
@@ -477,7 +547,7 @@ LspDetach *LspDetach*
local client = vim.lsp.get_client_by_id(args.data.client_id)
-- Remove the autocommand to format the buffer on save, if it exists
- if client.supports_method('textDocument/formatting') then
+ if client:supports_method('textDocument/formatting') then
vim.api.nvim_clear_autocmds({
event = 'BufWritePre',
buffer = args.buf,
@@ -589,6 +659,34 @@ LspTokenUpdate *LspTokenUpdate*
==============================================================================
Lua module: vim.lsp *lsp-core*
+*vim.lsp.Config*
+ Extends: |vim.lsp.ClientConfig|
+
+
+ Fields: ~
+ • {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`)
+ See `cmd` in |vim.lsp.ClientConfig|.
+ • {filetypes}? (`string[]`) Filetypes the client will attach to, if
+ activated by `vim.lsp.enable()`. If not provided,
+ then the client will attach to all filetypes.
+ • {root_markers}? (`string[]`) Directory markers (.e.g. '.git/') where
+ the LSP server will base its workspaceFolders,
+ rootUri, and rootPath on initialization. Unused if
+ `root_dir` is provided.
+ • {root_dir}? (`string|fun(cb:fun(string))`) Directory where the
+ LSP server will base its workspaceFolders, rootUri,
+ and rootPath on initialization. If a function, it
+ accepts a single callback argument which must be
+ called with the value of root_dir to use. The LSP
+ server will not be started until the callback is
+ called.
+ • {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): 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.
+
+
buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()*
Implements the `textDocument/did…` notifications required to track a
buffer for any language server.
@@ -688,7 +786,7 @@ commands *vim.lsp.commands*
value is a function which is called if any LSP action (code action, code
lenses, ...) triggers the command.
- If a LSP response contains a command for which no matching entry is
+ If an LSP response contains a command for which no matching entry is
available in this registry, the command will be executed via the LSP
server using `workspace/executeCommand`.
@@ -697,6 +795,108 @@ commands *vim.lsp.commands*
The second argument is the `ctx` of |lsp-handler|
+config({name}, {cfg}) *vim.lsp.config()*
+ Update the configuration for an LSP client.
+
+ Use name '*' to set default configuration for all clients.
+
+ Can also be table-assigned to redefine the configuration for a client.
+
+ Examples:
+ • Add a root marker for all clients: >lua
+ vim.lsp.config('*', {
+ root_markers = { '.git' },
+ })
+<
+ • Add additional capabilities to all clients: >lua
+ vim.lsp.config('*', {
+ capabilities = {
+ textDocument = {
+ semanticTokens = {
+ multilineTokenSupport = true,
+ }
+ }
+ }
+ })
+<
+ • (Re-)define the configuration for clangd: >lua
+ vim.lsp.config.clangd = {
+ cmd = {
+ 'clangd',
+ '--clang-tidy',
+ '--background-index',
+ '--offset-encoding=utf-8',
+ },
+ root_markers = { '.clangd', 'compile_commands.json' },
+ filetypes = { 'c', 'cpp' },
+ }
+<
+ • Get configuration for luals: >lua
+ local cfg = vim.lsp.config.luals
+<
+
+ Parameters: ~
+ • {name} (`string`)
+ • {cfg} (`vim.lsp.Config`) See |vim.lsp.Config|.
+
+enable({name}, {enable}) *vim.lsp.enable()*
+ Enable an LSP server to automatically start when opening a buffer.
+
+ Uses configuration defined with `vim.lsp.config`.
+
+ Examples: >lua
+ vim.lsp.enable('clangd')
+
+ vim.lsp.enable({'luals', 'pyright'})
+<
+
+ Parameters: ~
+ • {name} (`string|string[]`) Name(s) of client(s) to enable.
+ • {enable} (`boolean?`) `true|nil` to enable, `false` to disable.
+
+foldclose({kind}, {winid}) *vim.lsp.foldclose()*
+ Close all {kind} of folds in the the window with {winid}.
+
+ To automatically fold imports when opening a file, you can use an autocmd: >lua
+ vim.api.nvim_create_autocmd('LspNotify', {
+ callback = function(args)
+ if args.data.method == 'textDocument/didOpen' then
+ vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf))
+ end
+ end,
+ })
+<
+
+ Parameters: ~
+ • {kind} (`lsp.FoldingRangeKind`) Kind to close, one of "comment",
+ "imports" or "region".
+ • {winid} (`integer?`) Defaults to the current window.
+
+foldexpr({lnum}) *vim.lsp.foldexpr()*
+ Provides an interface between the built-in client and a `foldexpr`
+ function.
+
+ To use, check for the "textDocument/foldingRange" capability in an
+ |LspAttach| autocommand. Example: >lua
+ vim.api.nvim_create_autocmd('LspAttach', {
+ callback = function(args)
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ if client:supports_method('textDocument/foldingRange') then
+ local win = vim.api.nvim_get_current_win()
+ vim.wo[win][0].foldmethod = 'expr'
+ vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()'
+ end
+ end,
+ })
+<
+
+ Parameters: ~
+ • {lnum} (`integer`) line number
+
+foldtext() *vim.lsp.foldtext()*
+ Provides a `foldtext` function that shows the `collapsedText` retrieved,
+ defaults to the first folded line if `collapsedText` is not provided.
+
formatexpr({opts}) *vim.lsp.formatexpr()*
Provides an interface between the built-in client and a `formatexpr`
function.
@@ -798,12 +998,11 @@ start({config}, {opts}) *vim.lsp.start()*
})
<
- See |vim.lsp.start_client()| for all available options. The most important
+ See |vim.lsp.ClientConfig| for all available options. The most important
are:
• `name` arbitrary name for the LSP client. Should be unique per language
server.
- • `cmd` command string[] or function, described at
- |vim.lsp.start_client()|.
+ • `cmd` command string[] or function.
• `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.root()| to detect the root by traversing the file system upwards
@@ -825,33 +1024,25 @@ start({config}, {opts}) *vim.lsp.start()*
Parameters: ~
• {config} (`vim.lsp.ClientConfig`) Configuration for the server. See
|vim.lsp.ClientConfig|.
- • {opts} (`table?`) Optional keyword arguments
+ • {opts} (`table?`) Optional keyword arguments.
• {reuse_client}?
(`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): 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.
+ re-uses a client if it has the same name and if the given
+ workspace folders (or root_dir) are all included in the
+ client's workspace folders.
• {bufnr}? (`integer`) Buffer handle to attach to if
starting or re-using a client (0 for current).
+ • {attach}? (`boolean`) Whether to attach the client to a
+ buffer (default true). If set to `false`, `reuse_client`
+ and `bufnr` will be ignored.
• {silent}? (`boolean`) Suppress error reporting if the LSP
server fails to start (default false).
Return: ~
(`integer?`) client_id
-start_client({config}) *vim.lsp.start_client()*
- Starts and initializes a client with the given configuration.
-
- Parameters: ~
- • {config} (`vim.lsp.ClientConfig`) Configuration for the server. See
- |vim.lsp.ClientConfig|.
-
- Return (multiple): ~
- (`integer?`) client_id |vim.lsp.get_client_by_id()| Note: client may
- not be fully initialized. Use `on_init` to do any actions once the
- client has been initialized.
- (`string?`) Error message, if any
-
status() *vim.lsp.status()*
Consumes the latest progress messages from all clients and formats them as
a string. Empty if there are no clients or if no new messages
@@ -904,14 +1095,15 @@ Lua module: vim.lsp.client *lsp-client*
• {rpc} (`vim.lsp.rpc.PublicClient`) RPC client
object, for low level interaction with the
client. See |vim.lsp.rpc.start()|.
- • {offset_encoding} (`string`) The encoding used for communicating
- with the server. You can modify this in the
+ • {offset_encoding} (`string`) Called "position encoding" in LSP
+ spec, the encoding used for communicating with
+ the server. You can modify this in the
`config`'s `on_init` method before text is
sent to the server.
• {handlers} (`table<string,lsp.Handler>`) The handlers
used by the client as described in
|lsp-handler|.
- • {requests} (`table<integer,{ type: string, bufnr: integer, method: string}>`)
+ • {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`)
The current pending requests in flight to the
server. Entries are key-value pairs with the
key being the request id while the value is a
@@ -924,11 +1116,13 @@ Lua module: vim.lsp.client *lsp-client*
server.
• {config} (`vim.lsp.ClientConfig`) copy of the table
that was passed by the user to
- |vim.lsp.start_client()|. See
- |vim.lsp.ClientConfig|.
+ |vim.lsp.start()|. See |vim.lsp.ClientConfig|.
• {server_capabilities} (`lsp.ServerCapabilities?`) Response from the
server sent on `initialize` describing the
server's capabilities.
+ • {server_info} (`lsp.ServerInfo?`) Response from the server
+ sent on `initialize` describing information
+ about the server.
• {progress} (`vim.lsp.Client.Progress`) A ring buffer
(|vim.ringbuf()|) containing progress messages
sent by the server. See
@@ -948,9 +1142,9 @@ Lua module: vim.lsp.client *lsp-client*
lenses, ...) triggers the command. Client
commands take precedence over the global
command registry.
- • {settings} (`table`) Map with language server specific
- settings. These are returned to the language
- server if requested via
+ • {settings} (`lsp.LSPObject`) Map with language server
+ specific settings. These are returned to the
+ language server if requested via
`workspace/configuration`. Keys are
case-sensitive.
• {flags} (`table`) A table with flags for the client.
@@ -972,54 +1166,24 @@ Lua module: vim.lsp.client *lsp-client*
• {capabilities} (`lsp.ClientCapabilities`) The capabilities
provided by the client (editor or tool)
• {dynamic_capabilities} (`lsp.DynamicCapabilities`)
- • {request} (`fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`)
- Sends a request to the server. This is a thin
- wrapper around {client.rpc.request} with some
- additional checking. If {handler} is not
- specified and if there's no respective global
- handler, then an error will occur. Returns:
- {status}, {client_id}?. {status} is a boolean
- indicating if the notification was successful.
- If it is `false`, then it will always be
- `false` (the client has shutdown). If {status}
- is `true`, the function returns {request_id}
- as the second result. You can use this with
- `client.cancel_request(request_id)` to cancel
- the request.
- • {request_sync} (`fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError?, result:any}?, string?`)
- err # a dict
- • {notify} (`fun(method: string, params: table?): boolean`)
- Sends a notification to an LSP server.
- Returns: a boolean to indicate if the
- notification was successful. If it is false,
- then it will always be false (the client has
- shutdown).
- • {cancel_request} (`fun(id: integer): boolean`) Cancels a
- request with a given request id. Returns: same
- as `notify()`.
- • {stop} (`fun(force?: boolean)`) Stops a client,
- optionally with force. By default, it will
- just ask the server to shutdown without force.
- If you request to stop a client which has
- previously been requested to shutdown, it will
- automatically escalate and force shutdown.
- • {on_attach} (`fun(bufnr: integer)`) Runs the on_attach
- function from the client's config if it was
- defined. Useful for buffer-local setup.
- • {supports_method} (`fun(method: string, opts?: {bufnr: integer?}): boolean`)
- Checks if a client supports a given method.
- Always returns true for unknown off-spec
- methods. {opts} is a optional
- `{bufnr?: integer}` table. Some language
- server capabilities can be file specific.
- • {is_stopped} (`fun(): boolean`) Checks whether a client is
- stopped. Returns: true if the client is fully
- stopped.
+ • {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`)
+ See |Client:request()|.
+ • {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer?): {err: lsp.ResponseError?, result:any}?, string?`)
+ See |Client:request_sync()|.
+ • {notify} (`fun(self: vim.lsp.Client, method: string, params: table?): boolean`)
+ See |Client:notify()|.
+ • {cancel_request} (`fun(self: vim.lsp.Client, id: integer): boolean`)
+ See |Client:cancel_request()|.
+ • {stop} (`fun(self: vim.lsp.Client, force: boolean?)`)
+ See |Client:stop()|.
+ • {is_stopped} (`fun(self: vim.lsp.Client): boolean`) See
+ |Client:is_stopped()|.
• {exec_cmd} (`fun(self: vim.lsp.Client, command: lsp.Command, context: {bufnr?: integer}?, handler: lsp.Handler?)`)
- Execute a lsp command, either via client
- command function (if available) or via
- workspace/executeCommand (if supported by the
- server)
+ See |Client:exec_cmd()|.
+ • {on_attach} (`fun(self: vim.lsp.Client, bufnr: integer)`)
+ See |Client:on_attach()|.
+ • {supports_method} (`fun(self: vim.lsp.Client, method: string, bufnr: integer?)`)
+ See |Client:supports_method()|.
*vim.lsp.Client.Progress*
Extends: |vim.Ringbuf|
@@ -1073,28 +1237,30 @@ Lua module: vim.lsp.client *lsp-client*
an array.
• {handlers}? (`table<string,function>`) Map of language
server method names to |lsp-handler|
- • {settings}? (`table`) Map with language server specific
- settings. See the {settings} in
+ • {settings}? (`lsp.LSPObject`) Map with language server
+ specific settings. See the {settings} in
|vim.lsp.Client|.
• {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`)
Table that maps string of clientside commands to
user-defined functions. Commands passed to
- start_client take precedence over the global
+ `start()` 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}? (`table`) Values to pass in the initialization
- request as `initializationOptions`. See
- `initialize` in the LSP spec.
+ • {init_options}? (`lsp.LSPObject`) 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}? (`fun(bufnr: integer, filetype: string): string`)
Language ID as string. Defaults to the buffer
filetype.
- • {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) The encoding that
- the LSP server expects. Client does not verify
- this is correct.
+ • {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position
+ encoding" in LSP spec, the encoding that the LSP
+ server expects. Client does not verify this is
+ correct.
• {on_error}? (`fun(code: integer, err: string)`) Callback
invoked when the client operation throws an
error. `code` is a number describing the error.
@@ -1107,9 +1273,9 @@ Lua module: vim.lsp.client *lsp-client*
Callback 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.
+ config that was passed to |vim.lsp.start()|. You
+ can use this to modify parameters before they
+ are sent.
• {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, initialize_result: lsp.InitializeResult)>`)
Callback invoked after LSP "initialize", where
`result` is a table of `capabilities` and
@@ -1150,6 +1316,18 @@ Lua module: vim.lsp.client *lsp-client*
on initialization.
+Client:cancel_request({id}) *Client:cancel_request()*
+ Cancels a request with a given request id.
+
+ Parameters: ~
+ • {id} (`integer`) id of request to cancel
+
+ Return: ~
+ (`boolean`) status indicating if the notification was successful.
+
+ See also: ~
+ • |Client:notify()|
+
Client:exec_cmd({command}, {context}, {handler}) *Client:exec_cmd()*
Execute a lsp command, either via client command function (if available)
or via workspace/executeCommand (if supported by the server)
@@ -1159,6 +1337,96 @@ Client:exec_cmd({command}, {context}, {handler}) *Client:exec_cmd()*
• {context} (`{bufnr?: integer}?`)
• {handler} (`lsp.Handler?`) only called if a server command
+Client:is_stopped() *Client:is_stopped()*
+ Checks whether a client is stopped.
+
+ Return: ~
+ (`boolean`) true if client is stopped or in the process of being
+ stopped; false otherwise
+
+Client:notify({method}, {params}) *Client:notify()*
+ Sends a notification to an LSP server.
+
+ Parameters: ~
+ • {method} (`string`) LSP method name.
+ • {params} (`table?`) LSP request params.
+
+ Return: ~
+ (`boolean`) status indicating if the notification was successful. If
+ it is false, then the client has shutdown.
+
+Client:on_attach({bufnr}) *Client:on_attach()*
+ Runs the on_attach function from the client's config if it was defined.
+ Useful for buffer-local setup.
+
+ Parameters: ~
+ • {bufnr} (`integer`) Buffer number
+
+ *Client:request()*
+Client:request({method}, {params}, {handler}, {bufnr})
+ Sends a request to the server.
+
+ This is a thin wrapper around {client.rpc.request} with some additional
+ checks for capabilities and handler availability.
+
+ Parameters: ~
+ • {method} (`string`) LSP method name.
+ • {params} (`table?`) LSP request params.
+ • {handler} (`lsp.Handler?`) Response |lsp-handler| for this method.
+ • {bufnr} (`integer?`) (default: 0) Buffer handle, or 0 for current.
+
+ Return (multiple): ~
+ (`boolean`) status indicates whether the request was successful. If it
+ is `false`, then it will always be `false` (the client has shutdown).
+ (`integer?`) request_id Can be used with |Client:cancel_request()|.
+ `nil` is request failed.
+
+ See also: ~
+ • |vim.lsp.buf_request_all()|
+
+ *Client:request_sync()*
+Client:request_sync({method}, {params}, {timeout_ms}, {bufnr})
+ Sends a request to the server and synchronously waits for the response.
+
+ This is a wrapper around |Client:request()|
+
+ Parameters: ~
+ • {method} (`string`) LSP method name.
+ • {params} (`table`) LSP request params.
+ • {timeout_ms} (`integer?`) Maximum time in milliseconds to wait for a
+ result. Defaults to 1000
+ • {bufnr} (`integer?`) (default: 0) Buffer handle, or 0 for
+ current.
+
+ Return (multiple): ~
+ (`{err: lsp.ResponseError?, result:any}?`) `result` and `err` from the
+ |lsp-handler|. `nil` is the request was unsuccessful
+ (`string?`) err On timeout, cancel or error, where `err` is a string
+ describing the failure reason.
+
+ See also: ~
+ • |vim.lsp.buf_request_sync()|
+
+Client:stop({force}) *Client:stop()*
+ Stops a client, optionally with force.
+
+ By default, it will just request the server to shutdown without force. If
+ you request to stop a client which has previously been requested to
+ shutdown, it will automatically escalate and force shutdown.
+
+ Parameters: ~
+ • {force} (`boolean?`)
+
+Client:supports_method({method}, {bufnr}) *Client:supports_method()*
+ Checks if a client supports a given method. Always returns true for
+ unknown off-spec methods.
+
+ Note: Some language server capabilities can be file specific.
+
+ Parameters: ~
+ • {method} (`string`)
+ • {bufnr} (`integer?`)
+
==============================================================================
Lua module: vim.lsp.buf *lsp-buf*
@@ -1178,12 +1446,11 @@ Lua module: vim.lsp.buf *lsp-buf*
vim.lsp.buf.definition({ on_list = on_list })
vim.lsp.buf.references(nil, { on_list = on_list })
<
-
- If you prefer loclist instead of qflist: >lua
+ • {loclist}? (`boolean`) Whether to use the |location-list| or the
+ |quickfix| list in the default handler. >lua
vim.lsp.buf.definition({ loclist = true })
- vim.lsp.buf.references(nil, { loclist = true })
+ vim.lsp.buf.references(nil, { loclist = false })
<
- • {loclist}? (`boolean`)
*vim.lsp.LocationOpts*
Extends: |vim.lsp.ListOpts|
@@ -1286,7 +1553,7 @@ document_highlight() *vim.lsp.buf.document_highlight()*
|hl-LspReferenceWrite|
document_symbol({opts}) *vim.lsp.buf.document_symbol()*
- Lists all symbols in the current buffer in the quickfix window.
+ Lists all symbols in the current buffer in the |location-list|.
Parameters: ~
• {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|.
@@ -1312,7 +1579,7 @@ format({opts}) *vim.lsp.buf.format()*
predicate are included. Example: >lua
-- Never request typescript-language-server for formatting
vim.lsp.buf.format {
- filter = function(client) return client.name ~= "tsserver" end
+ filter = function(client) return client.name ~= "ts_ls" end
}
<
• {async}? (`boolean`, default: false) If true the method
@@ -1375,7 +1642,7 @@ references({context}, {opts}) *vim.lsp.buf.references()*
window.
Parameters: ~
- • {context} (`table?`) Context for the request
+ • {context} (`lsp.ReferenceContext?`) Context for the request
• {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|.
See also: ~
@@ -1461,12 +1728,13 @@ get_namespace({client_id}, {is_pull})
client. Defaults to push
*vim.lsp.diagnostic.on_diagnostic()*
-on_diagnostic({_}, {result}, {ctx})
+on_diagnostic({error}, {result}, {ctx})
|lsp-handler| for the method "textDocument/diagnostic"
See |vim.diagnostic.config()| for configuration options.
Parameters: ~
+ • {error} (`lsp.ResponseError?`)
• {result} (`lsp.DocumentDiagnosticReport`)
• {ctx} (`lsp.HandlerContext`)
@@ -1599,12 +1867,12 @@ get({filter}) *vim.lsp.inlay_hint.get()*
local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer
local client = vim.lsp.get_client_by_id(hint.client_id)
- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0)
+ local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0)
local resolved_hint = assert(resp and resp.result, resp.err)
vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding)
location = resolved_hint.label[1].location
- client.request('textDocument/hover', {
+ client:request('textDocument/hover', {
textDocument = { uri = location.uri },
position = location.range.start,
})
@@ -1756,7 +2024,7 @@ Lua module: vim.lsp.util *lsp-util*
• {zindex}? (`integer`) override `zindex`, defaults to 50
• {title}? (`string`)
• {title_pos}? (`'left'|'center'|'right'`)
- • {relative}? (`'mouse'|'cursor'`) (default: `'cursor'`)
+ • {relative}? (`'mouse'|'cursor'|'editor'`) (default: `'cursor'`)
• {anchor_bias}? (`'auto'|'above'|'below'`, default: `'auto'`) -
"auto": place window based on which side of the
cursor has more lines
@@ -1769,7 +2037,7 @@ Lua module: vim.lsp.util *lsp-util*
*vim.lsp.util.apply_text_document_edit()*
-apply_text_document_edit({text_document_edit}, {index}, {offset_encoding})
+apply_text_document_edit({text_document_edit}, {index}, {position_encoding})
Applies a `TextDocumentEdit`, which is a list of changes to a single
document.
@@ -1777,30 +2045,30 @@ apply_text_document_edit({text_document_edit}, {index}, {offset_encoding})
• {text_document_edit} (`lsp.TextDocumentEdit`)
• {index} (`integer?`) Optional index of the edit, if from
a list of edits (or nil, if not from a list)
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`)
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'?`)
See also: ~
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
*vim.lsp.util.apply_text_edits()*
-apply_text_edits({text_edits}, {bufnr}, {offset_encoding})
+apply_text_edits({text_edits}, {bufnr}, {position_encoding})
Applies a list of text edits to a buffer.
Parameters: ~
- • {text_edits} (`lsp.TextEdit[]`)
- • {bufnr} (`integer`) Buffer id
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`)
+ • {text_edits} (`lsp.TextEdit[]`)
+ • {bufnr} (`integer`) Buffer id
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`)
See also: ~
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
*vim.lsp.util.apply_workspace_edit()*
-apply_workspace_edit({workspace_edit}, {offset_encoding})
+apply_workspace_edit({workspace_edit}, {position_encoding})
Applies a `WorkspaceEdit`.
Parameters: ~
- • {workspace_edit} (`lsp.WorkspaceEdit`)
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`) (required)
+ • {workspace_edit} (`lsp.WorkspaceEdit`)
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`) (required)
See also: ~
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
@@ -1812,13 +2080,13 @@ buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()*
• {bufnr} (`integer?`) Buffer id
*vim.lsp.util.buf_highlight_references()*
-buf_highlight_references({bufnr}, {references}, {offset_encoding})
+buf_highlight_references({bufnr}, {references}, {position_encoding})
Shows a list of document highlights for a certain buffer.
Parameters: ~
- • {bufnr} (`integer`) Buffer id
- • {references} (`lsp.DocumentHighlight[]`) objects to highlight
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`)
+ • {bufnr} (`integer`) Buffer id
+ • {references} (`lsp.DocumentHighlight[]`) objects to highlight
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`)
See also: ~
• https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent
@@ -1893,7 +2161,7 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
• 'shiftwidth'
*vim.lsp.util.locations_to_items()*
-locations_to_items({locations}, {offset_encoding})
+locations_to_items({locations}, {position_encoding})
Returns the items with the byte position calculated correctly and in
sorted order, for display in quickfix and location lists.
@@ -1904,9 +2172,9 @@ locations_to_items({locations}, {offset_encoding})
|setloclist()|.
Parameters: ~
- • {locations} (`lsp.Location[]|lsp.LocationLink[]`)
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) default to first
- client of buffer
+ • {locations} (`lsp.Location[]|lsp.LocationLink[]`)
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) default to first
+ client of buffer
Return: ~
(`vim.quickfix.entry[]`) See |setqflist()| for the format
@@ -1941,37 +2209,33 @@ make_formatting_params({options})
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting
*vim.lsp.util.make_given_range_params()*
-make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding})
+make_given_range_params({start_pos}, {end_pos}, {bufnr}, {position_encoding})
Using the given range in the current buffer, creates an object that is
similar to |vim.lsp.util.make_range_params()|.
Parameters: ~
- • {start_pos} (`[integer,integer]?`) {row,col} mark-indexed
- position. Defaults to the start of the last visual
- selection.
- • {end_pos} (`[integer,integer]?`) {row,col} mark-indexed
- position. Defaults to the end of the last visual
- selection.
- • {bufnr} (`integer?`) buffer handle or 0 for current,
- defaults to current
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) defaults to
- `offset_encoding` of first client of `bufnr`
+ • {start_pos} (`[integer,integer]?`) {row,col} mark-indexed
+ position. Defaults to the start of the last
+ visual selection.
+ • {end_pos} (`[integer,integer]?`) {row,col} mark-indexed
+ position. Defaults to the end of the last visual
+ selection.
+ • {bufnr} (`integer?`) buffer handle or 0 for current,
+ defaults to current
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`)
Return: ~
- (`table`) { textDocument = { uri = `current_file_uri` }, range = {
- start = `start_position`, end = `end_position` } }
+ (`{ textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }`)
*vim.lsp.util.make_position_params()*
-make_position_params({window}, {offset_encoding})
+make_position_params({window}, {position_encoding})
Creates a `TextDocumentPositionParams` object for the current buffer and
cursor position.
Parameters: ~
- • {window} (`integer?`) window handle or 0 for current,
- defaults to current
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) defaults to
- `offset_encoding` of first client of buffer of
- `window`
+ • {window} (`integer?`) window handle or 0 for current,
+ defaults to current
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`)
Return: ~
(`lsp.TextDocumentPositionParams`)
@@ -1980,22 +2244,19 @@ make_position_params({window}, {offset_encoding})
• https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
*vim.lsp.util.make_range_params()*
-make_range_params({window}, {offset_encoding})
+make_range_params({window}, {position_encoding})
Using the current position in the current buffer, creates an object that
can be used as a building block for several LSP requests, such as
`textDocument/codeAction`, `textDocument/colorPresentation`,
`textDocument/rangeFormatting`.
Parameters: ~
- • {window} (`integer?`) window handle or 0 for current,
- defaults to current
- • {offset_encoding} (`"utf-8"|"utf-16"|"utf-32"?`) defaults to
- `offset_encoding` of first client of buffer of
- `window`
+ • {window} (`integer?`) window handle or 0 for current,
+ defaults to current
+ • {position_encoding} (`"utf-8"|"utf-16"|"utf-32"`)
Return: ~
- (`table`) { textDocument = { uri = `current_file_uri` }, range = {
- start = `current_position`, end = `current_position` } }
+ (`{ textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }`)
*vim.lsp.util.make_text_document_params()*
make_text_document_params({bufnr})
@@ -2074,17 +2335,17 @@ rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()*
• {ignoreIfExists}? (`boolean`)
*vim.lsp.util.show_document()*
-show_document({location}, {offset_encoding}, {opts})
+show_document({location}, {position_encoding}, {opts})
Shows document and optionally jumps to the location.
Parameters: ~
- • {location} (`lsp.Location|lsp.LocationLink`)
- • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`)
- • {opts} (`table?`) A table with the following fields:
- • {reuse_win}? (`boolean`) Jump to existing window
- if buffer is already open.
- • {focus}? (`boolean`) Whether to focus/jump to
- location if possible. (defaults: true)
+ • {location} (`lsp.Location|lsp.LocationLink`)
+ • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'?`)
+ • {opts} (`table?`) A table with the following fields:
+ • {reuse_win}? (`boolean`) Jump to existing
+ window if buffer is already open.
+ • {focus}? (`boolean`) Whether to focus/jump to
+ location if possible. (defaults: true)
Return: ~
(`boolean`) `true` if succeeded
@@ -2168,14 +2429,15 @@ should_log({level}) *vim.lsp.log.should_log()*
Lua module: vim.lsp.rpc *lsp-rpc*
*vim.lsp.rpc.PublicClient*
+ Client RPC object
Fields: ~
- • {request} (`fun(method: string, params: table?, callback: fun(err: lsp.ResponseError?, result: any), notify_reply_callback: fun(message_id: integer)?):boolean,integer?`)
- see |vim.lsp.rpc.request()|
- • {notify} (`fun(method: string, params: any):boolean`) see
+ • {request} (`fun(method: string, params: table?, callback: fun(err?: lsp.ResponseError, result: any), notify_reply_callback?: fun(message_id: integer)):boolean,integer?`)
+ See |vim.lsp.rpc.request()|
+ • {notify} (`fun(method: string, params: any): boolean`) See
|vim.lsp.rpc.notify()|
- • {is_closing} (`fun(): boolean`)
- • {terminate} (`fun()`)
+ • {is_closing} (`fun(): boolean`) Indicates if the RPC is closing.
+ • {terminate} (`fun()`) Terminates the RPC client.
connect({host_or_path}, {port}) *vim.lsp.rpc.connect()*
@@ -2185,7 +2447,7 @@ connect({host_or_path}, {port}) *vim.lsp.rpc.connect()*
• a host and port via TCP
Return a function that can be passed to the `cmd` field for
- |vim.lsp.start_client()| or |vim.lsp.start()|.
+ |vim.lsp.start()|.
Parameters: ~
• {host_or_path} (`string`) host to connect to or path to a pipe/domain
@@ -2276,12 +2538,7 @@ start({cmd}, {dispatchers}, {extra_spawn_params}) *vim.lsp.rpc.start()*
See |vim.system()|
Return: ~
- (`vim.lsp.rpc.PublicClient`) Client RPC object, with these methods:
- • `notify()` |vim.lsp.rpc.notify()|
- • `request()` |vim.lsp.rpc.request()|
- • `is_closing()` returns a boolean indicating if the RPC is closing.
- • `terminate()` terminates the RPC client. See
- |vim.lsp.rpc.PublicClient|.
+ (`vim.lsp.rpc.PublicClient`) See |vim.lsp.rpc.PublicClient|.
==============================================================================
diff --git a/runtime/doc/lua-guide.txt b/runtime/doc/lua-guide.txt
index b40d5a0791..d0d148f689 100644
--- a/runtime/doc/lua-guide.txt
+++ b/runtime/doc/lua-guide.txt
@@ -153,7 +153,7 @@ its functions if this succeeds and prints an error message otherwise:
if not ok then
print("Module had an error")
else
- mymod.function()
+ mymod.func()
end
<
In contrast to |:source|, |require()| not only searches through all `lua/` directories
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 243c907180..0eca3286d0 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -17,9 +17,9 @@ get an idea of what lurks beneath: >vim
:lua vim.print(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.
+"editor stdlib" (|vimscript-functions| + |Ex-commands|) and the |API|, all of
+which can be used from Lua code (|lua-vimscript| |vim.api|). These three
+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.
@@ -161,7 +161,7 @@ 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" }
@@ -618,20 +618,20 @@ Example: TCP echo-server *tcp-server*
Multithreading *lua-loop-threading*
Plugins can perform work in separate (os-level) threads using the threading
-APIs in luv, for instance `vim.uv.new_thread`. Note that every thread
-gets its own separate Lua interpreter state, with no access to Lua globals
-in the main thread. Neither can the state of the editor (buffers, windows,
-etc) be directly accessed from threads.
+APIs in luv, for instance `vim.uv.new_thread`. Each thread has its own
+separate Lua interpreter state, with no access to Lua globals on the main
+thread. Neither can the editor state (buffers, windows, etc) be directly
+accessed from threads.
-A subset of the `vim.*` API is available in threads. This includes:
+A subset of the `vim.*` stdlib is available in threads, including:
- `vim.uv` 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 |package.path|
- `print()` and `vim.inspect`
- `vim.diff`
-- most utility functions in `vim.*` for working with pure Lua values
- like `vim.split`, `vim.tbl_*`, `vim.list_*`, and so on.
+- Most utility functions in `vim.*` that work with pure Lua values, like
+ `vim.split`, `vim.tbl_*`, `vim.list_*`, etc.
- `vim.is_thread()` returns true from a non-main thread.
@@ -685,6 +685,8 @@ vim.hl.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {opts})
whether the range is end-inclusive
• {priority}? (`integer`, default:
`vim.hl.priorities.user`) Highlight priority
+ • {timeout}? (`integer`, default: -1 no timeout) Time in ms
+ before highlight is cleared
==============================================================================
@@ -811,11 +813,14 @@ vim.json.decode({str}, {opts}) *vim.json.decode()*
Return: ~
(`any`)
-vim.json.encode({obj}) *vim.json.encode()*
+vim.json.encode({obj}, {opts}) *vim.json.encode()*
Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
Parameters: ~
- • {obj} (`any`)
+ • {obj} (`any`)
+ • {opts} (`table<string,any>?`) Options table with keys:
+ • escape_slash: (boolean) (default false) Escape slash
+ characters "/" in string values.
Return: ~
(`string`)
@@ -1083,9 +1088,8 @@ vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()*
|ui-popupmenu| and the sections below for event format for respective
events.
- Callbacks for `msg_show` events are executed in |api-fast| context unless
- Nvim will wait for input, in which case messages should be shown
- immediately.
+ Callbacks for `msg_show` events are executed in |api-fast| context;
+ showing the message should be scheduled.
Excessive errors inside the callback will result in forced detachment.
@@ -1297,7 +1301,7 @@ global value of a |global-local| option, see |:setglobal|.
A special interface |vim.opt| exists for conveniently interacting with list-
-and map-style option from Lua: It allows accessing them as Lua tables and
+and map-style options from Lua: It allows accessing them as Lua tables and
offers object-oriented method for adding and removing entries.
Examples: ~
@@ -1471,10 +1475,8 @@ vim.go *vim.go*
<
vim.o *vim.o*
- 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.
+ Get or set |options|. Works like `:set`, so buffer/window-scoped options
+ target the current buffer/window. Invalid key is an error.
Example: >lua
vim.o.cmdheight = 4
@@ -1505,7 +1507,7 @@ vim.wo[{winid}][{bufnr}] *vim.wo*
Lua module: vim *lua-vim*
vim.cmd({command}) *vim.cmd()*
- Executes Vim script commands.
+ Executes Vimscript (|Ex-commands|).
Note that `vim.cmd` can be indexed with a command name to return a
callable function to the command.
@@ -1539,9 +1541,9 @@ vim.cmd({command}) *vim.cmd()*
Parameters: ~
• {command} (`string|table`) Command(s) to execute. If a string,
- executes multiple lines of Vim script at once. In this
- case, it is an alias to |nvim_exec2()|, where `opts.output`
- is set to false. Thus it works identical to |:source|. If a
+ executes multiple lines of Vimscript at once. In this case,
+ it is an alias to |nvim_exec2()|, where `opts.output` is
+ set to false. Thus it works identical to |:source|. If a
table, executes a single command. In this case, it is an
alias to |nvim_cmd()| where `opts` is empty.
@@ -1805,7 +1807,7 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()*
-- Runs synchronously:
local obj = vim.system({'echo', 'hello'}, { text = true }):wait()
- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' }
+ -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' }
<
See |uv.spawn()| for more details. Note: unlike |uv.spawn()|, vim.system
@@ -1911,6 +1913,11 @@ vim.show_pos({bufnr}, {row}, {col}, {filter}) *vim.show_pos()*
Can also be shown with `:Inspect`. *:Inspect*
+ Example: To bind this function to the vim-scriptease inspired `zS` in
+ Normal mode: >lua
+ vim.keymap.set('n', 'zS', vim.show_pos)
+<
+
Attributes: ~
Since: 0.9.0
@@ -1937,12 +1944,10 @@ vim.show_pos({bufnr}, {row}, {col}, {filter}) *vim.show_pos()*
*vim.Ringbuf*
Fields: ~
- • {clear} (`fun()`) Clear all items
- • {push} (`fun(item: T)`) Adds an item, overriding the oldest item if
- the buffer is full.
- • {pop} (`fun(): T?`) Removes and returns the first unread item
- • {peek} (`fun(): T?`) Returns the first unread item without removing
- it
+ • {clear} (`fun()`) See |Ringbuf:clear()|.
+ • {push} (`fun(item: T)`) See |Ringbuf:push()|.
+ • {pop} (`fun(): T?`) See |Ringbuf:pop()|.
+ • {peek} (`fun(): T?`) See |Ringbuf:peek()|.
Ringbuf:clear() *Ringbuf:clear()*
@@ -2425,7 +2430,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message})
function vim.startswith(s, prefix)
vim.validate('s', s, 'string')
vim.validate('prefix', prefix, 'string')
- ...
+ -- ...
end
<
2. `vim.validate(spec)` (deprecated) where `spec` is of type
@@ -2439,7 +2444,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message})
age={age, 'number'},
hobbies={hobbies, 'table'},
}
- ...
+ -- ...
end
<
@@ -2471,7 +2476,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message})
Parameters: ~
• {name} (`string`) Argument name
- • {value} (`string`) Argument value
+ • {value} (`any`) Argument value
• {validator} (`vim.validate.Validator`)
• (`string|string[]`): Any value that can be returned
from |lua-type()| in addition to `'callable'`:
@@ -2487,22 +2492,24 @@ vim.validate({name}, {value}, {validator}, {optional}, {message})
==============================================================================
Lua module: vim.loader *vim.loader*
-vim.loader.disable() *vim.loader.disable()*
+vim.loader.enable({enable}) *vim.loader.enable()*
WARNING: This feature is experimental/unstable.
- Disables the experimental Lua module loader:
- • removes the loaders
- • adds the default Nvim loader
-
-vim.loader.enable() *vim.loader.enable()*
- WARNING: This feature is experimental/unstable.
+ Enables or disables the experimental Lua module loader:
- Enables the experimental Lua module loader:
- • overrides loadfile
+ Enable (`enable=true`):
+ • overrides |loadfile()|
• adds the Lua loader using the byte-compilation cache
• adds the libs loader
• removes the default Nvim loader
+ Disable (`enable=false`):
+ • removes the loaders
+ • adds the default Nvim loader
+
+ Parameters: ~
+ • {enable} (`boolean?`) true/nil to enable, false to disable
+
vim.loader.find({modname}, {opts}) *vim.loader.find()*
WARNING: This feature is experimental/unstable.
@@ -2926,6 +2933,31 @@ vim.keymap.set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
==============================================================================
Lua module: vim.fs *vim.fs*
+
+ *vim.fs.exists()*
+Use |uv.fs_stat()| to check a file's type, and whether it exists.
+
+Example: >lua
+ if vim.uv.fs_stat(file) then
+ vim.print("file exists")
+ end
+<
+
+
+vim.fs.abspath({path}) *vim.fs.abspath()*
+ Convert path to an absolute path. A tilde (~) character at the beginning
+ of the path is expanded to the user's home directory. Does not check if
+ the path exists, normalize the path, resolve symlinks or hardlinks
+ (including `.` and `..`), or expand environment variables. If the path is
+ already absolute, it is returned unchanged. Also converts `\` path
+ separators to `/`.
+
+ Parameters: ~
+ • {path} (`string`) Path
+
+ Return: ~
+ (`string`) Absolute path
+
vim.fs.basename({file}) *vim.fs.basename()*
Return the basename of the given path
@@ -2953,6 +2985,7 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
• skip: (fun(dir_name: string): boolean)|nil Predicate to
control traversal. Return false to stop searching the
current directory. Only useful when depth > 1
+ • follow: boolean|nil Follow symbolic links. (default: true)
Return: ~
(`Iterator`) over items in {path}. Each iteration yields two values:
@@ -2994,7 +3027,7 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
-- get all files ending with .cpp or .hpp inside lib/
local cpp_hpp = vim.fs.find(function(name, path)
- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$')
+ return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
end, {limit = math.huge, type = 'file'})
<
@@ -3008,8 +3041,10 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
If {names} is a function, it is called for each traversed
item with args:
• name: base name of the current item
- • path: full path of the current item The function should
- return `true` if the given item is considered a match.
+ • path: full path of the current item
+
+ The function should return `true` if the given item is
+ considered a match.
• {opts} (`table`) Optional keyword arguments:
• {path}? (`string`) Path to begin searching from. If
omitted, the |current-directory| is used.
@@ -3023,14 +3058,21 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
• {limit}? (`number`, default: `1`) Stop the search after
finding this many matches. Use `math.huge` to place no
limit on the number of matches.
+ • {follow}? (`boolean`, default: `true`) Follow symbolic
+ links.
Return: ~
(`string[]`) Normalized paths |vim.fs.normalize()| of all matching
items
vim.fs.joinpath({...}) *vim.fs.joinpath()*
- Concatenate directories and/or file paths into a single path with
- normalization (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`)
+ Concatenates partial paths (one absolute or relative path followed by zero
+ or more relative paths). Slashes are normalized: redundant slashes are
+ removed, and (on Windows) backslashes are replaced with forward-slashes.
+
+ Examples:
+ • "foo/", "/bar" => "foo/bar"
+ • Windows: "a\foo\", "\bar" => "a/foo/bar"
Attributes: ~
Since: 0.10.0
@@ -3113,6 +3155,23 @@ vim.fs.parents({start}) *vim.fs.parents()*
(`nil`)
(`string?`)
+vim.fs.relpath({base}, {target}, {opts}) *vim.fs.relpath()*
+ Gets `target` path relative to `base`, or `nil` if `base` is not an
+ ancestor.
+
+ Example: >lua
+ vim.fs.relpath('/var', '/var/lib') -- 'lib'
+ vim.fs.relpath('/var', '/usr/bin') -- nil
+<
+
+ Parameters: ~
+ • {base} (`string`)
+ • {target} (`string`)
+ • {opts} (`table?`) Reserved for future use
+
+ Return: ~
+ (`string?`)
+
vim.fs.rm({path}, {opts}) *vim.fs.rm()*
WARNING: This feature is experimental/unstable.
@@ -3979,6 +4038,7 @@ vim.version.range({spec}) *vim.version.range()*
(`table?`) A table with the following fields:
• {from} (`vim.Version`)
• {to}? (`vim.Version`)
+ • {has} (`fun(self: vim.VersionRange, version: string|vim.Version)`)
See also: ~
• https://github.com/npm/node-semver#ranges
diff --git a/runtime/doc/luvref.txt b/runtime/doc/luvref.txt
index 2be73f0412..3cbdb53e01 100644
--- a/runtime/doc/luvref.txt
+++ b/runtime/doc/luvref.txt
@@ -102,6 +102,7 @@ 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.
+- |luv-constants| — Constants
- |luv-error-handling| — Error handling
- |luv-version-checking| — Version checking
- |uv_loop_t| — Event loop
@@ -131,12 +132,103 @@ module.
- |luv-metrics-operations| — Metrics operations
==============================================================================
+CONSTANTS *luv-constants*
+
+As a Lua library, luv supports and encourages the use of lowercase strings to
+represent options. For example:
+>lua
+ -- signal start with string input
+ uv.signal_start("sigterm", function(signame)
+ print(signame) -- string output: "sigterm"
+ end)
+<
+However, luv also superficially exposes libuv constants in a Lua table at
+`uv.constants` where its keys are uppercase constant names and their associated
+values are integers defined internally by libuv. The values from this table may
+be supported as function arguments, but their use may not change the output
+type. For example:
+>lua
+ -- signal start with integer input
+ uv.signal_start(uv.constants.SIGTERM, function(signame)
+ print(signame) -- string output: "sigterm"
+ end)
+<
+The uppercase constants defined in `uv.constants` that have associated
+lowercase option strings are listed below.
+
+Address Families ~
+
+- `AF_UNIX`: "unix"
+- `AF_INET`: "inet"
+- `AF_INET6`: "inet6"
+- `AF_IPX`: "ipx"
+- `AF_NETLINK`: "netlink"
+- `AF_X25`: "x25"
+- `AF_AX25`: "as25"
+- `AF_ATMPVC`: "atmpvc"
+- `AF_APPLETALK`: "appletalk"
+- `AF_PACKET`: "packet"
+
+Signals ~
+
+- `SIGHUP`: "sighup"
+- `SIGINT`: "sigint"
+- `SIGQUIT`: "sigquit"
+- `SIGILL`: "sigill"
+- `SIGTRAP`: "sigtrap"
+- `SIGABRT`: "sigabrt"
+- `SIGIOT`: "sigiot"
+- `SIGBUS`: "sigbus"
+- `SIGFPE`: "sigfpe"
+- `SIGKILL`: "sigkill"
+- `SIGUSR1`: "sigusr1"
+- `SIGSEGV`: "sigsegv"
+- `SIGUSR2`: "sigusr2"
+- `SIGPIPE`: "sigpipe"
+- `SIGALRM`: "sigalrm"
+- `SIGTERM`: "sigterm"
+- `SIGCHLD`: "sigchld"
+- `SIGSTKFLT`: "sigstkflt"
+- `SIGCONT`: "sigcont"
+- `SIGSTOP`: "sigstop"
+- `SIGTSTP`: "sigtstp"
+- `SIGBREAK`: "sigbreak"
+- `SIGTTIN`: "sigttin"
+- `SIGTTOU`: "sigttou"
+- `SIGURG`: "sigurg"
+- `SIGXCPU`: "sigxcpu"
+- `SIGXFSZ`: "sigxfsz"
+- `SIGVTALRM`: "sigvtalrm"
+- `SIGPROF`: "sigprof"
+- `SIGWINCH`: "sigwinch"
+- `SIGIO`: "sigio"
+- `SIGPOLL`: "sigpoll"
+- `SIGLOST`: "siglost"
+- `SIGPWR`: "sigpwr"
+- `SIGSYS`: "sigsys"
+
+Socket Types ~
+
+- `SOCK_STREAM`: "stream"
+- `SOCK_DGRAM`: "dgram"
+- `SOCK_SEQPACKET`: "seqpacket"
+- `SOCK_RAW`: "raw"
+- `SOCK_RDM`: "rdm"
+
+TTY Modes ~
+
+- `TTY_MODE_NORMAL`: "normal"
+- `TTY_MODE_RAW`: "raw"
+- `TTY_MODE_IO`: "io"
+
+==============================================================================
ERROR HANDLING *luv-error-handling*
-In libuv, errors are negative numbered constants; however, while those errors
-are exposed through `uv.errno`, the functions used to handle them are not
-exposed to luv users. Instead, if an internal error is encountered, the luv
-function will return to the caller an assertable `nil, err, name` tuple.
+In libuv, errors are represented by negative numbered constants. While these
+constants are made available in the `uv.errno` table, they are not returned by
+luv funtions and the libuv functions used to handle them are not exposed.
+Instead, if an internal error is encountered, the failing luv function will
+return to the caller an assertable `nil, err, name` tuple:
- `nil` idiomatically indicates failure
- `err` is a string with the format `{name}: {message}`
@@ -153,9 +245,8 @@ success, or sometimes nothing at all. These cases are documented below.
`uv.errno` *uv.errno*
-A table value which exposes error constants as a map, where the key is the
-error name (without the `UV_` prefix) and its value is a negative number.
-See Libuv's "Error constants" page for further details.
+Below is a list of known error names and error strings. See Libuv's "Error
+constants" page for further details.
(https://docs.libuv.org/en/v1.x/errors.html#error-constants)
- `E2BIG`: argument list too long.
@@ -1154,8 +1245,8 @@ Unix Notes:
-- Create a new signal handler
local signal = uv.new_signal()
-- Define a handler function
- uv.signal_start(signal, "sigint", function(signal)
- print("got " .. signal .. ", shutting down")
+ uv.signal_start(signal, "sigint", function(signame)
+ print("got " .. signame .. ", shutting down")
os.exit(1)
end)
<
@@ -1167,34 +1258,40 @@ uv.new_signal() *uv.new_signal()*
Returns: `uv_signal_t userdata` or `fail`
-uv.signal_start({signal}, {signum}, {callback}) *uv.signal_start()*
+uv.signal_start({signal}, {signame}, {callback}) *uv.signal_start()*
- > method form `signal:start(signum, callback)`
+ > method form `signal:start(signame, callback)`
Parameters:
- `signal`: `uv_signal_t userdata`
- - `signum`: `integer` or `string`
+ - `signame`: `string` or `integer`
- `callback`: `callable`
- - `signum`: `string`
+ - `signame`: `string`
Start the handle with the given callback, watching for the
given signal.
+ See |luv-constants| for supported `signame` input and output
+ values.
+
Returns: `0` or `fail`
*uv.signal_start_oneshot()*
-uv.signal_start_oneshot({signal}, {signum}, {callback})
+uv.signal_start_oneshot({signal}, {signame}, {callback})
- > method form `signal:start_oneshot(signum, callback)`
+ > method form `signal:start_oneshot(signame, callback)`
Parameters:
- `signal`: `uv_signal_t userdata`
- - `signum`: `integer` or `string`
+ - `signame`: `string` or `integer`
- `callback`: `callable`
- - `signum`: `string`
+ - `signame`: `string`
Same functionality as |uv.signal_start()| but the signal
handler is reset the moment the signal is received.
+ See |luv-constants| for supported `signame` input and output
+ values.
+
Returns: `0` or `fail`
uv.signal_stop({signal}) *uv.signal_stop()*
@@ -1349,30 +1446,36 @@ uv.spawn({path}, {options}, {on_exit}) *uv.spawn()*
Returns: `uv_process_t userdata`, `integer`
-uv.process_kill({process}, {signum}) *uv.process_kill()*
+uv.process_kill({process}, {signame}) *uv.process_kill()*
- > method form `process:kill(signum)`
+ > method form `process:kill(signame)`
Parameters:
- `process`: `uv_process_t userdata`
- - `signum`: `integer` or `string` or `nil` (default: `sigterm`)
+ - `signame`: `string` or `integer` or `nil` (default: `sigterm`)
Sends the specified signal to the given process handle. Check
the documentation on |uv_signal_t| for signal support,
specially on Windows.
+ See |luv-constants| for supported `signame` input and output
+ values.
+
Returns: `0` or `fail`
-uv.kill({pid}, {signum}) *uv.kill()*
+uv.kill({pid}, {signame}) *uv.kill()*
Parameters:
- `pid`: `integer`
- - `signum`: `integer` or `string` or `nil` (default: `sigterm`)
+ - `signame`: `string` or `integer` or `nil` (default: `sigterm`)
Sends the specified signal to the given PID. Check the
documentation on |uv_signal_t| for signal support, specially
on Windows.
+ See |luv-constants| for supported `signame` input and output
+ values.
+
Returns: `0` or `fail`
uv.process_get_pid({process}) *uv.process_get_pid()*
@@ -1638,12 +1741,13 @@ TCP handles are used to represent both TCP streams and servers.
uv.new_tcp([{flags}]) *uv.new_tcp()*
Parameters:
- - `flags`: `string` or `nil`
+ - `flags`: `string` or `integer` or `nil`
Creates and initializes a new |uv_tcp_t|. Returns the Lua
- userdata wrapping it. Flags may be a family string: `"unix"`,
- `"inet"`, `"inet6"`, `"ipx"`, `"netlink"`, `"x25"`, `"ax25"`,
- `"atmpvc"`, `"appletalk"`, or `"packet"`.
+ userdata wrapping it.
+
+ If set, `flags` must be a valid address family. See
+ |luv-constants| for supported address family input values.
Returns: `uv_tcp_t userdata` or `fail`
@@ -1744,6 +1848,8 @@ uv.tcp_getpeername({tcp}) *uv.tcp_getpeername()*
Get the address of the peer connected to the handle.
+ See |luv-constants| for supported address `family` output values.
+
Returns: `table` or `fail`
- `ip` : `string`
- `family` : `string`
@@ -1758,6 +1864,8 @@ uv.tcp_getsockname({tcp}) *uv.tcp_getsockname()*
Get the current address to which the handle is bound.
+ See |luv-constants| for supported address `family` output values.
+
Returns: `table` or `fail`
- `ip` : `string`
- `family` : `string`
@@ -1823,8 +1931,7 @@ uv.socketpair([{socktype}, [{protocol}, [{flags1}, [{flags2}]]]])
|uv.tcp_open()|, used with |uv.spawn()|, or for any other
purpose.
- When specified as a string, `socktype` must be one of
- `"stream"`, `"dgram"`, `"raw"`, `"rdm"`, or `"seqpacket"`.
+ See |luv-constants| for supported `socktype` input values.
When `protocol` is set to 0 or nil, it will be automatically
chosen based on the socket's domain and type. When `protocol`
@@ -2184,17 +2291,11 @@ uv.tty_set_mode({tty}, {mode}) *uv.tty_set_mode()*
Parameters:
- `tty`: `uv_tty_t userdata`
- - `mode`: `integer`
+ - `mode`: `string` or `integer`
Set the TTY using the specified terminal mode.
- Parameter `mode` is a C enum with the following values:
-
- - 0 - UV_TTY_MODE_NORMAL: Initial/normal terminal mode
- - 1 - UV_TTY_MODE_RAW: Raw input mode (On Windows,
- ENABLE_WINDOW_INPUT is also enabled)
- - 2 - UV_TTY_MODE_IO: Binary-safe I/O mode for IPC
- (Unix-only)
+ See |luv-constants| for supported TTY mode input values.
Returns: `0` or `fail`
@@ -2265,9 +2366,7 @@ uv.new_udp([{flags}]) *uv.new_udp()*
Creates and initializes a new |uv_udp_t|. Returns the Lua
userdata wrapping it. The actual socket is created lazily.
- When specified, `family` must be one of `"unix"`, `"inet"`,
- `"inet6"`, `"ipx"`, `"netlink"`, `"x25"`, `"ax25"`,
- `"atmpvc"`, `"appletalk"`, or `"packet"`.
+ See |luv-constants| for supported address `family` input values.
When specified, `mmsgs` determines the number of messages able
to be received at one time via `recvmmsg(2)` (the allocated
@@ -2510,6 +2609,51 @@ uv.udp_try_send({udp}, {data}, {host}, {port}) *uv.udp_try_send()*
Returns: `integer` or `fail`
+uv.udp_try_send2({udp}, {messages}, {flags}) *uv.udp_try_send2()*
+
+ > method form `udp:try_send2(messages, flags)`
+
+ Parameters:
+ - `udp`: `uv_udp_t userdata`
+ - `messages`: `table`
+ - `[1, 2, 3, ..., n]` : `table`
+ - `data` : `buffer`
+ - `addr` : `table`
+ - `ip` : `string`
+ - `port` : `integer`
+ - `flags`: `nil` (see below)
+ - `port`: `integer`
+
+ Like `uv.udp_try_send()`, but can send multiple datagrams.
+ Lightweight abstraction around `sendmmsg(2)`, with a
+ `sendmsg(2)` fallback loop for platforms that do not support
+ the former. The `udp` handle must be fully initialized, either
+ from a `uv.udp_bind` call, another call that will bind
+ automatically (`udp_send`, `udp_try_send`, etc), or from
+ `uv.udp_connect`. `messages` should be an array-like table,
+ where `addr` must be specified if the `udp` has not been
+ connected via `udp_connect`. Otherwise, `addr` must be `nil`.
+
+ `flags` is reserved for future extension and must currently be
+ `nil` or `0` or `{}`.
+
+ Returns the number of messages sent successfully. An error will only be returned
+ if the first datagram failed to be sent.
+
+ Returns: `integer` or `fail`
+ >lua
+ -- If client:connect(...) was not called
+ local addr = { ip = "127.0.0.1", port = 1234 }
+ client:try_send2({
+ { data = "Message 1", addr = addr },
+ { data = "Message 2", addr = addr },
+ })
+ -- If client:connect(...) was called
+ client:try_send2({
+ { data = "Message 1" },
+ { data = "Message 2" },
+ })
+<
uv.udp_recv_start({udp}, {callback}) *uv.udp_recv_start()*
> method form `udp:recv_start(callback)`
@@ -2531,6 +2675,8 @@ uv.udp_recv_start({udp}, {callback}) *uv.udp_recv_start()*
been bound with |uv.udp_bind()| it is bound to `0.0.0.0` (the
"all interfaces" IPv4 address) and a random port number.
+ See |luv-constants| for supported address `family` output values.
+
Returns: `0` or `fail`
uv.udp_recv_stop({udp}) *uv.udp_recv_stop()*
@@ -2743,7 +2889,8 @@ uv.fs_open({path}, {flags}, {mode} [, {callback}]) *uv.fs_open()*
Parameters:
- `path`: `string`
- `flags`: `string` or `integer`
- - `mode`: `integer`
+ - `mode`: `integer` (octal `chmod(1)` mode, e.g.
+ `tonumber('644', 8)`)
- `callback`: `callable` (async version) or `nil` (sync
version)
- `err`: `nil` or `string`
@@ -2829,7 +2976,8 @@ uv.fs_mkdir({path}, {mode} [, {callback}]) *uv.fs_mkdir()*
Parameters:
- `path`: `string`
- - `mode`: `integer`
+ - `mode`: `integer` (octal representation of `chmod(1)` mode,
+ e.g. `tonumber('755', 8)`)
- `callback`: `callable` (async version) or `nil` (sync
version)
- `err`: `nil` or `string`
@@ -3078,7 +3226,9 @@ uv.fs_access({path}, {mode} [, {callback}]) *uv.fs_access()*
Parameters:
- `path`: `string`
- - `mode`: `integer`
+ - `mode`: `integer` `string` (a combination of the `'r'`,
+ `'w'` and `'x'` characters denoting the symbolic mode as per
+ `chmod(1)`)
- `callback`: `callable` (async version) or `nil` (sync
version)
- `err`: `nil` or `string`
@@ -3097,7 +3247,8 @@ uv.fs_chmod({path}, {mode} [, {callback}]) *uv.fs_chmod()*
Parameters:
- `path`: `string`
- - `mode`: `integer`
+ - `mode`: `integer` (octal representation of `chmod(1)` mode,
+ e.g. `tonumber('644', 8)`)
- `callback`: `callable` (async version) or `nil` (sync
version)
- `err`: `nil` or `string`
@@ -3480,14 +3631,17 @@ uv.getaddrinfo({host}, {service} [, {hints} [, {callback}]]) *uv.getaddrinfo()*
Equivalent to `getaddrinfo(3)`. Either `node` or `service` may
be `nil` but not both.
- Valid hint strings for the keys that take a string:
- - `family`: `"unix"`, `"inet"`, `"inet6"`, `"ipx"`,
- `"netlink"`, `"x25"`, `"ax25"`, `"atmpvc"`, `"appletalk"`,
- or `"packet"`
- - `socktype`: `"stream"`, `"dgram"`, `"raw"`, `"rdm"`, or
- `"seqpacket"`
- - `protocol`: will be looked up using the `getprotobyname(3)`
- function (examples: `"ip"`, `"icmp"`, `"tcp"`, `"udp"`, etc)
+ See |luv-constants| for supported address `family` input and
+ output values.
+
+ See |luv-constants| for supported `socktype` input and
+ output values.
+
+ When `protocol` is set to `0` or `nil`, it will be
+ automatically chosen based on the socket's domain and type.
+ When `protocol` is specified as a string, it will be looked up
+ using the `getprotobyname(3)` function. Examples: `"ip"`,
+ `"icmp"`, `"tcp"`, `"udp"`, etc.
Returns (sync version): `table` or `fail`
- `[1, 2, 3, ..., n]` : `table`
@@ -3515,9 +3669,7 @@ uv.getnameinfo({address} [, {callback}]) *uv.getnameinfo()*
Equivalent to `getnameinfo(3)`.
- When specified, `family` must be one of `"unix"`, `"inet"`,
- `"inet6"`, `"ipx"`, `"netlink"`, `"x25"`, `"ax25"`,
- `"atmpvc"`, `"appletalk"`, or `"packet"`.
+ See |luv-constants| for supported address `family` input values.
Returns (sync version): `string, string` or `fail`
@@ -3526,7 +3678,7 @@ uv.getnameinfo({address} [, {callback}]) *uv.getnameinfo()*
==============================================================================
THREADING AND SYNCHRONIZATION UTILITIES *luv-threading-and-synchronization-utilities*
-Libuv provides cross-platform implementations for multiple threading an
+Libuv provides cross-platform implementations for multiple threading and
synchronization primitives. The API largely follows the pthreads API.
uv.new_thread([{options}, ] {entry}, {...}) *uv.new_thread()*
@@ -3683,6 +3835,43 @@ uv.thread_join({thread}) *uv.thread_join()*
Returns: `boolean` or `fail`
+uv.thread_detach({thread}) *uv.thread_detach()*
+
+ > method form `thread:detach()`
+
+ Parameters:
+ - `thread`: `luv_thread_t userdata`
+
+ Detaches a thread. Detached threads automatically release
+ their resources upon termination, eliminating the need for the
+ application to call `uv.thread_join`.
+
+ Returns: `boolean` or `fail`
+
+uv.thread_setname({name}) *uv.thread_setname()*
+
+ Parameters:
+ - `name`: `string`
+
+ Sets the name of the current thread. Different platforms
+ define different limits on the max number of characters a
+ thread name can be: Linux, IBM i (16), macOS (64), Windows
+ (32767), and NetBSD (32), etc. The name will be truncated if
+ `name` is larger than the limit of the platform.
+
+ Returns: `0` or `fail`
+
+uv.thread_getname({thread}) *uv.thread_getname()*
+
+ > method form `thread:getname()`
+
+ Parameters:
+ - `thread`: `luv_thread_t userdata`
+
+ Gets the name of the thread specified by `thread`.
+
+ Returns: `string` or `fail`
+
uv.sleep({msec}) *uv.sleep()*
Parameters:
@@ -3796,6 +3985,36 @@ uv.getrusage() *uv.getrusage()*
- `nvcsw` : `integer` (voluntary context switches)
- `nivcsw` : `integer` (involuntary context switches)
+uv.getrusage_thread() *uv.getrusage_thread()*
+ Gets the resource usage measures for the calling thread.
+
+ Note: Not supported on all platforms. May return `ENOTSUP`.
+
+ On macOS and Windows not all fields are set (the unsupported
+ fields are filled with zeroes).
+
+ Returns: `table` or `fail`
+ - `utime` : `table` (user CPU time used)
+ - `sec` : `integer`
+ - `usec` : `integer`
+ - `stime` : `table` (system CPU time used)
+ - `sec` : `integer`
+ - `usec` : `integer`
+ - `maxrss` : `integer` (maximum resident set size)
+ - `ixrss` : `integer` (integral shared memory size)
+ - `idrss` : `integer` (integral unshared data size)
+ - `isrss` : `integer` (integral unshared stack size)
+ - `minflt` : `integer` (page reclaims (soft page faults))
+ - `majflt` : `integer` (page faults (hard page faults))
+ - `nswap` : `integer` (swaps)
+ - `inblock` : `integer` (block input operations)
+ - `oublock` : `integer` (block output operations)
+ - `msgsnd` : `integer` (IPC messages sent)
+ - `msgrcv` : `integer` (IPC messages received)
+ - `nsignals` : `integer` (signals received)
+ - `nvcsw` : `integer` (voluntary context switches)
+ - `nivcsw` : `integer` (involuntary context switches)
+
uv.available_parallelism() *uv.available_parallelism()*
Returns an estimate of the default amount of parallelism a
@@ -3968,6 +4187,8 @@ uv.interface_addresses() *uv.interface_addresses()*
information where fields are `ip`, `family`, `netmask`,
`internal`, and `mac`.
+ See |luv-constants| for supported address `family` output values.
+
Returns: `table`
- `[name(s)]` : `table`
- `ip` : `string`
@@ -4202,6 +4423,67 @@ uv.metrics_info() *uv.metrics_info()*
- `events_waiting` : `integer`
==============================================================================
+STRING MANIPULATION FUNCTIONS *luv-string-manipulation*
+
+These string utilities are needed internally for dealing with Windows, and are
+exported to allow clients to work uniformly with this data when the libuv API
+is not complete.
+
+Notes:
+1. New in luv version 1.49.0.
+2. See the WTF-8 spec (https://simonsapin.github.io/wtf-8/) for information
+ about WTF-8.
+3. Luv uses Lua-style strings, which means that all inputs and return values
+ (UTF-8 or UTF-16 strings) do not include a NUL terminator.
+
+uv.utf16_length_as_wtf8({utf16}) *uv.utf16_length_as_wtf8()*
+
+ Get the length (in bytes) of a UTF-16 (or UCS-2) string
+ `utf16` value after converting it to WTF-8. The endianness of
+ the UTF-16 (or UCS-2) string is assumed to be the same as the
+ native endianness of the platform.
+
+ Parameters:
+ - `utf16`: `string`
+
+ Returns: `integer`
+
+uv.utf16_to_wtf8({utf16}) *uv.utf16_to_wtf8()*
+
+ Convert UTF-16 (or UCS-2) string `utf16` to UTF-8 string. The
+ endianness of the UTF-16 (or UCS-2) string is assumed to be
+ the same as the native endianness of the platform.
+
+ Parameters:
+ - `utf16`: `string`
+
+ Returns: `string`
+
+uv.wtf8_length_as_utf16({wtf16}) *uv.wtf8_length_as_utf16()*
+
+ Get the length (in UTF-16 code units) of a WTF-8 `wtf8` value
+ after converting it to UTF-16 (or UCS-2).
+
+ Note: The number of bytes needed for a UTF-16 (or UCS-2)
+ string is `<number of code units> * 2`.
+
+ Parameters:
+ - `wtf8`: `string`
+
+ Returns: `integer`
+
+uv.wtf8_to_utf16({wtf16}) *uv.wtf8_to_utf16()*
+
+ Convert WTF-8 string in `wtf8` to UTF-16 (or UCS-2) string.
+ The endianness of the UTF-16 (or UCS-2) string is assumed to
+ be the same as the native endianness of the platform.
+
+ Parameters:
+ - `wtf8`: `string`
+
+ Returns: `string`
+
+==============================================================================
CREDITS *luv-credits*
This document is a reformatted version of the LUV documentation, up-to-date
@@ -4211,4 +4493,4 @@ https://github.com/luvit/luv/commit/dcd1a1cad5b05634a7691402d6ca2f214fb4ae76.
Based on https://github.com/nanotee/luv-vimdocs with kind permission.
-vim:tw=78:ts=8:ft=help:norl:
+vim:tw=78:ts=8:sw=2:et:ft=help:norl:
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index 11048aee30..52c04333fc 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -12,7 +12,7 @@ manual.
Type |gO| to see the table of contents.
==============================================================================
-1. Key mapping *key-mapping* *mapping* *macro*
+1. Key mapping *keybind* *key-mapping* *mapping*
Key mapping is used to change the meaning of typed keys. The most common use
is to define a sequence of commands for a function key. Example: >
@@ -1545,7 +1545,7 @@ Your command preview routine must implement this protocol:
3. Add required highlights to the target buffers. If preview buffer is
provided, add required highlights to the preview buffer as well. All
highlights must be added to the preview namespace which is provided as an
- argument to the preview callback (see |nvim_buf_add_highlight()| and
+ argument to the preview callback (see |vim.hl.range()| and
|nvim_buf_set_extmark()| for help on how to add highlights to a namespace).
4. Return an integer (0, 1, 2) which controls how Nvim behaves as follows:
0: No preview is shown.
@@ -1574,13 +1574,12 @@ supports incremental command preview:
if start_idx then
-- Highlight the match
- vim.api.nvim_buf_add_highlight(
+ vim.hl.range(
buf,
preview_ns,
'Substitute',
- line1 + i - 2,
- start_idx - 1,
- end_idx
+ {line1 + i - 2, start_idx - 1},
+ {line1 + i - 2, end_idx},
)
-- Add lines and set highlights in the preview buffer
@@ -1595,13 +1594,12 @@ supports incremental command preview:
false,
{ prefix .. line }
)
- vim.api.nvim_buf_add_highlight(
+ vim.hl.range(
preview_buf,
preview_ns,
'Substitute',
- preview_buf_line,
- #prefix + start_idx - 1,
- #prefix + end_idx
+ {preview_buf_line, #prefix + start_idx - 1},
+ {preview_buf_line, #prefix + end_idx},
)
preview_buf_line = preview_buf_line + 1
end
diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt
index ac151811c2..d6f5aeb354 100644
--- a/runtime/doc/message.txt
+++ b/runtime/doc/message.txt
@@ -27,7 +27,7 @@ depends on the 'shortmess' option.
Clear messages, keeping only the {count} most
recent ones.
-The number of remembered messages is fixed at 200.
+The number of remembered messages is determined by the 'messagesopt' option.
*g<*
The "g<" command can be used to see the last page of previous command output.
@@ -789,6 +789,7 @@ If you accidentally hit <Enter> or <Space> and you want to see the displayed
text then use |g<|. This only works when 'more' is set.
To reduce the number of hit-enter prompts:
+- Set 'messagesopt'.
- Set 'cmdheight' to 2 or higher.
- Add flags to 'shortmess'.
- Reset 'showcmd' and/or 'ruler'.
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 48e13d795e..284be09121 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -115,6 +115,12 @@ This cannot be repeated: >
endif<CR>
Note that when using ":" any motion becomes charwise exclusive.
+ *inclusive-motion-selection-exclusive*
+When 'selection' is "exclusive", |Visual| mode is active and an inclusive
+motion has been used, the cursor position will be adjusted by another
+character to the right, so that the Visual selection includes the expected
+text and can be acted upon.
+
*forced-motion*
FORCING A MOTION TO BE LINEWISE, CHARWISE OR BLOCKWISE
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index d19df84023..12fac28db8 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -11,16 +11,36 @@ For changes in the previous release, see |news-0.10|.
Type |gO| to see the table of contents.
==============================================================================
-BREAKING CHANGES IN HEAD *news-breaking-dev*
+BREAKING CHANGES IN HEAD OR EXPERIMENTAL *news-breaking-dev*
====== Remove this section before release. ======
The following changes to UNRELEASED features were made during the development
cycle (Nvim HEAD, the "master" branch).
+EXPERIMENTS
+
+• Removed `vim.loader.disable()`. Use `vim.loader.enable(false)` instead.
+
+LSP
+
+• `lsp/` runtimepath files should return a table instead of calling
+ |vim.lsp.config()| (or assigning to `vim.lsp.config`). See |lsp-config|
+• `vim.lsp.buf.document_symbol()` uses the |location-list| by default. Use
+ `vim.lsp.buf.document_symbol({ loclist = false })` to use the |quickfix|
+ list.
+
+
OPTIONS
• 'jumpoptions' flag "unload" has been renamed to "clean".
+• The `msghistory` option has been removed in favor of 'messagesopt'.
+
+TREESITTER
+
+• *TSNode:child_containing_descendant()* has been removed in the tree-sitter
+ library and is no longer available; use |TSNode:child_with_descendant()|
+ instead.
==============================================================================
BREAKING CHANGES *news-breaking*
@@ -29,7 +49,6 @@ These changes may require adaptations in your config or plugins.
API
-• Improved API "meta" docstrings and :help documentation.
• `vim.rpcnotify(0)` and `rpcnotify(0)` broadcast to ALL channels. Previously
they would "multicast" only to subscribed channels (controlled by
`nvim_subscribe()`). Plugins and clients that want "multicast" behavior must
@@ -45,19 +64,24 @@ API
• Renamed `nvim__id_dictionary` (unsupported/experimental API) to
`nvim__id_dict`.
+BUILD
+
+On Windows, only building with the UCRT runtime is supported.
+
DEFAULTS
-• |]d-default| and |[d-default| accept a count.
-• |[D-default| and |]D-default| jump to the first and last diagnostic in the
- current buffer, respectively.
+• 'number', 'relativenumber', 'signcolumn', and 'foldcolumn' are disabled in
+ |terminal| buffers. See |terminal-config| for an example of changing these defaults.
DIAGNOSTICS
-• |vim.diagnostic.config()| accepts a "jump" table to specify defaults for
- |vim.diagnostic.jump()|.
• The "underline" diagnostics handler sorts diagnostics by severity when using
the "severity_sort" option.
-
+• Diagnostics are filtered by severity before being passed to a diagnostic
+ handler |diagnostic-handlers|.
+• The "virtual_text" handler is disabled by default. Enable with >lua
+ vim.diagnostic.config({ virtual_text = true })
+<
EDITOR
• The order in which signs are placed was changed. Higher priority signs will
@@ -74,14 +98,24 @@ EVENTS
• |vim.ui_attach()| callbacks for |ui-messages| `msg_show` events are executed in
|api-fast| context.
+• New/enhanced arguments in these existing UI events:
+ • `cmdline_hide`: `abort` argument indicating if the cmdline was aborted.
+ • `cmdline_show`:
+ • Prompts that were previously emitted as `msg_show` events, are now routed
+ through `cmdline_show`.
+ • `hl_id` argument to highlight the prompt text.
+ • `msg_show`:
+ • `history` argument indicating if the message was added to the history.
+ • new message kinds: "bufwrite", "completion", "list_cmd", "lua_print",
+ "search_cmd", "shell_out/err/ret", "undo", "verbose", wildlist".
+
+HIGHLIGHTS
+
+• |TermCursorNC| is removed and no longer supported. Unfocused terminals no
+ longer have a cursor.
LSP
-• Improved rendering of LSP hover docs. |K-lsp-default|
-• |vim.lsp.completion.enable()| gained the `convert` callback which enables
- customizing the transformation of an LSP CompletionItem to |complete-items|.
-• |vim.lsp.diagnostic.from()| can be used to convert a list of
- |vim.Diagnostic| objects into their LSP diagnostic representation.
• |vim.lsp.buf.references()|, |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|,
|vim.lsp.buf.type_definition()|, |vim.lsp.buf.implementation()| and
|vim.lsp.buf.hover()| now support merging the results of multiple clients
@@ -95,14 +129,15 @@ LSP
Instead use: >lua
vim.diagnostic.config(config, vim.lsp.diagnostic.get_namespace(client_id))
<
+• |vim.lsp.util.make_position_params()|, |vim.lsp.util.make_range_params()|
+ and |vim.lsp.util.make_given_range_params()| now require the `position_encoding`
+ parameter.
LUA
• API functions now consistently return an empty dictionary as
|vim.empty_dict()|. Earlier, a |lua-special-tbl| was sometimes used.
-• Command-line completions for: `vim.g`, `vim.t`, `vim.w`, `vim.b`, `vim.v`,
- `vim.o`, `vim.wo`, `vim.bo`, `vim.opt`, `vim.opt_local`, `vim.opt_global`,
- and `vim.fn`.
+• |vim.json.encode()| no longer escapes forward slashes "/" by default
OPTIONS
@@ -134,13 +169,17 @@ TREESITTER
if no languages are explicitly registered.
• |vim.treesitter.language.add()| returns `true` if a parser was loaded
successfully and `nil,errmsg` otherwise instead of throwing an error.
-• New |TSNode:child_with_descendant()|, which is nearly identical to
- |TSNode:child_containing_descendant()| except that it can return the
- descendant itself.
+• |vim.treesitter.get_parser()| and |vim.treesitter.start()| no longer parse
+ the tree before returning. Scripts must call |LanguageTree:parse()| explicitly. >lua
+ local p = vim.treesitter.get_parser(0, 'c')
+ p:parse()
+<
TUI
-• TODO
+• OSC 52 is used as a fallback clipboard provider when no other
+ |clipboard-tool| is found, even when not using SSH |clipboard-osc52|. To
+ disable OSC 52 queries, set the "osc52" key of |g:termfeatures| to false.
VIMSCRIPT
@@ -155,7 +194,15 @@ The following new features were added.
API
+• Improved API "meta" docstrings and :help documentation.
• |nvim__ns_set()| can set properties for a namespace
+• |nvim_echo()| `err` field to print error messages and `chunks` accepts
+ highlight group IDs.
+• |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline".
+• |nvim_buf_set_extmark()| `hl_group` field can be an array of layered groups
+• |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight
+• |nvim_buf_set_extmark()| virt_text_pos accepts `eol_right_align` to
+ allow for right aligned text that truncates before covering up buffer text.
DEFAULTS
@@ -173,6 +220,9 @@ DEFAULTS
on a URL.
• Mouse |popup-menu| includes a "Go to definition" item when LSP is active
in the buffer.
+ • |]d-default| and |[d-default| accept a count.
+ • |[D-default| and |]D-default| jump to the first and last diagnostic in the
+ current buffer, respectively.
• Mappings inspired by Tim Pope's vim-unimpaired:
• |[q|, |]q|, |[Q|, |]Q|, |[CTRL-Q|, |]CTRL-Q| navigate through the |quickfix| list
• |[l|, |]l|, |[L|, |]L|, |[CTRL-L|, |]CTRL-L| navigate through the |location-list|
@@ -187,14 +237,25 @@ DEFAULTS
• `<S-Tab>` in Insert and Select mode maps to `vim.snippet.jump({ direction = -1 })`
when a snippet is active and jumpable backwards.
+DIAGNOSTICS
+
+• |vim.diagnostic.config()| accepts a "jump" table to specify defaults for
+ |vim.diagnostic.jump()|.
+• A "virtual_lines" diagnostic handler was added to render diagnostics using
+ virtual lines below the respective code.
+• The "virtual_text" diagnostic handler accepts a `current_line` option to
+ only show virtual text at the cursor's line.
+
EDITOR
+• Use |g==| in :help docs to execute Lua and Vimscript code examples.
• Improved |paste| handling for redo (dot-repeat) and macros (|recording|):
• Redoing a large paste is significantly faster and ignores 'autoindent'.
• Replaying a macro with |@| also replays pasted text.
• On Windows, filename arguments on the command-line prefixed with "~\" or
"~/" are now expanded to the user's profile directory, not a relative path
to a literal "~" directory.
+• |hl-ComplMatchIns| shows matched text of the currently inserted completion.
• |hl-PmenuMatch| and |hl-PmenuMatchSel| show matched text in completion popup.
EVENTS
@@ -205,6 +266,12 @@ EVENTS
LSP
+• Improved rendering of LSP hover docs. |K-lsp-default|
+• |vim.lsp.completion.enable()| gained the `convert` callback which enables
+ customizing the transformation of an LSP CompletionItem to |complete-items|.
+• |vim.lsp.diagnostic.from()| can be used to convert a list of
+ |vim.Diagnostic| objects into their LSP diagnostic representation.
+• `:checkhealth vim.lsp` displays the server version (if available).
• Completion side effects (including snippet expansion, execution of commands
and application of additional text edits) is now built-in.
• |vim.lsp.util.locations_to_items()| sets `end_col` and `end_lnum` fields.
@@ -218,32 +285,61 @@ LSP
• The client now supports `'utf-8'` and `'utf-32'` position encodings.
• |vim.lsp.buf.hover()| now highlights hover ranges using the
|hl-LspReferenceTarget| highlight group.
+• Functions in |vim.lsp.Client| can now be called as methods.
+• Implemented LSP folding: |vim.lsp.foldexpr()|
+ https://microsoft.github.io/language-server-protocol/specification/#textDocument_foldingRange
+• |vim.lsp.config()| has been added to define default configurations for
+ servers. In addition, configurations can be specified in `lsp/<name>.lua`.
+• |vim.lsp.enable()| has been added to enable servers.
LUA
+• Command-line completions for: `vim.g`, `vim.t`, `vim.w`, `vim.b`, `vim.v`,
+ `vim.o`, `vim.wo`, `vim.bo`, `vim.opt`, `vim.opt_local`, `vim.opt_global`,
+ and `vim.fn`.
• |vim.fs.rm()| can delete files and directories.
• |vim.validate()| now has a new signature which uses less tables,
- is more peformant and easier to read.
+ is more performant and easier to read.
• |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures
supporting two new parameters, `encoding` and `strict_indexing`.
+• |vim.json.encode()| has an option to enable forward slash escaping
+• |vim.fs.abspath()| converts paths to absolute paths.
+• |vim.fs.relpath()| gets relative path compared to base path.
+• |vim.fs.dir()| and |vim.fs.find()| now follow symbolic links by default,
+ the behavior can be turn off using the new `follow` option.
OPTIONS
• 'completeopt' flag "fuzzy" enables |fuzzy-matching| during |ins-completion|.
-• 'msghistory' controls maximum number of messages to remember.
+• 'completeopt' flag "preinsert" highlights text to be inserted.
+• 'messagesopt' configures |:messages| and |hit-enter| prompt.
• 'tabclose' controls which tab page to focus when closing a tab page.
PERFORMANCE
-• TODO
+• Significantly reduced redraw time for long lines with treesitter
+ highlighting.
+• LSP diagnostics and inlay hints are de-duplicated (new requests cancel
+ inflight requests). This greatly improves performance with slow LSP servers.
+• 10x speedup for |vim.treesitter.foldexpr()| (when no parser exists for the
+ buffer).
+• Strong |treesitter-query| caching makes repeat |vim.treesitter.query.get()|
+ and |vim.treesitter.query.parse()| calls significantly faster for large
+ queries.
+• Treesitter highlighting is now asynchronous. To force synchronous parsing,
+ use `vim.g._ts_force_sync_parsing = true`.
+• Treesitter folding is now calculated asynchronously.
PLUGINS
• EditorConfig
• spelling_language property is now supported.
+• 'inccommand' incremental preview can run on 'nomodifiable' buffers and
+ restores their 'modifiable' state
STARTUP
+• |-es| ("script mode") disables shada by default.
• Nvim will fail if the |--listen| or |$NVIM_LISTEN_ADDRESS| address is
invalid, instead of silently skipping an invalid address.
@@ -258,12 +354,36 @@ TERMINAL
'scrollback' are not reflown.
• The |terminal| now supports OSC 8 escape sequences and will display
hyperlinks in supporting host terminals.
+• The |terminal| now uses the actual cursor, rather than a "virtual" cursor.
+ This means that escape codes sent by applications running in a terminal
+ buffer can change the cursor shape and visibility. However, it also
+ means that the |TermCursorNC| highlight group is no longer supported: an
+ unfocused terminal window will have no cursor at all (so there is nothing to
+ highlight).
+• |jobstart()| gained the "term" flag.
+• The |terminal| will send theme update notifications when 'background' is
+ changed and DEC mode 2031 is enabled.
+• The |terminal| has experimental support for the Kitty keyboard protocol
+ (sometimes called "CSI u" key encoding). Only the "Disambiguate escape
+ codes" mode is currently supported.
TREESITTER
• |LanguageTree:node_for_range()| gets anonymous and named nodes for a range
• |vim.treesitter.get_node()| now takes an option `include_anonymous`, default
false, which allows it to return anonymous nodes as well as named nodes.
+• |treesitter-directive-trim!| can trim all whitespace (not just empty lines)
+ from both sides of a node.
+• |vim.treesitter.get_captures_at_pos()| now returns the `id` of each capture
+• New |TSNode:child_with_descendant()|, which efficiently gets the node's
+ child that contains a given node as descendant.
+• |LanguageTree:parse()| optionally supports asynchronous invocation, which is
+ activated by passing the `on_parse` callback parameter.
+• |vim.treesitter.query.set()| can now inherit and/or extend runtime file
+ queries in addition to overriding.
+• |LanguageTree:is_valid()| now accepts a range parameter to narrow the scope
+ of the validity check.
+• |:InspectTree| now shows which nodes are missing.
TUI
@@ -272,6 +392,8 @@ TUI
:lua =vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan)
• |log| messages written by the builtin UI client (TUI, |--remote-ui|) are
now prefixed with "ui" instead of "?".
+• The TUI will re-query the terminal's background color when a theme update
+ notification is received and Nvim will update 'background' accordingly.
UI
@@ -279,7 +401,7 @@ UI
which controls the tool used to open the given path or URL. If you want to
globally set this, you can override vim.ui.open using the same approach
described at |vim.paste()|.
-- `vim.ui.open()` now supports
+• `vim.ui.open()` now supports
[lemonade](https://github.com/lemonade-command/lemonade) as an option for
opening urls/files. This is handy if you are in an ssh connection and use
`lemonade`.
@@ -287,8 +409,16 @@ UI
|hl-PmenuSel| and |hl-PmenuMatch| both inherit from |hl-Pmenu|, and
|hl-PmenuMatchSel| inherits highlights from both |hl-PmenuSel| and
|hl-PmenuMatch|.
-
+• |vim.diagnostic.setqflist()| updates an existing quickfix list with the
+ given title if found
• |ui-messages| content chunks now also contain the highlight group ID.
+• |:checkhealth| can display in a floating window, controlled by the
+ |g:health| variable.
+
+VIMSCRIPT
+
+• |getchar()| and |getcharstr()| have optional {opts} |Dict| argument to control:
+ cursor behavior, return type, and whether to simplify the returned key.
==============================================================================
CHANGED FEATURES *news-changed*
@@ -309,9 +439,14 @@ These existing features changed their behavior.
more emoji characters than before, including those encoded with multiple
emoji codepoints combined with ZWJ (zero width joiner) codepoints.
-• Text in the 'statusline', 'tabline', and 'winbar' now inherits highlights
- from the respective |hl-StatusLine|, |hl-TabLine|, and |hl-WinBar| highlight
- groups.
+ This also applies to :terminal output, where width of cells will be calculated
+ using the upgraded implementation.
+
+• Custom highlights in 'rulerformat', 'statuscolumn', 'statusline', 'tabline',
+ 'winbar', and the sign/number column are stacked with their respective
+ highlight groups, as opposed to |hl-Normal|.
+ This is also reflected in the `highlights` from |nvim_eval_statusline()|,
+ with a new `groups` field containing an array of stacked highlight groups.
• |vim.on_key()| callbacks won't be invoked recursively when a callback itself
consumes input.
@@ -320,6 +455,11 @@ These existing features changed their behavior.
current window, and it no longer throws |E444| when there is only one window
on the screen. Global variable `vim.g.pager` is removed.
+• Default 'titlestring' is now implemented with 'statusline' "%" format items.
+ This means the default, empty value is essentially an alias to:
+ `%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim`. This is only an
+ implementation simplification, not a behavior change.
+
==============================================================================
REMOVED FEATURES *news-removed*
diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt
index 86e344c654..8593511dbc 100644
--- a/runtime/doc/nvim.txt
+++ b/runtime/doc/nvim.txt
@@ -6,21 +6,23 @@
Nvim *nvim* *neovim* *nvim-intro*
-Nvim is based on Vim by Bram Moolenaar.
+Nvim is based on Vim by Bram Moolenaar. Nvim is emphatically a fork of Vim,
+not a clone: compatibility with Vim (especially editor and Vimscript features,
+except |Vim9script|) is maintained where possible. See |vim-differences| for
+the complete reference.
-If you already use Vim see |nvim-from-vim| for a quickstart.
-If you are new to Vim, try the 30-minute tutorial: >vim
+If you already use Vim, see |nvim-from-vim| for a quickstart. If you just
+installed Nvim and have never used it before, watch this 10-minute
+video: https://youtu.be/TQn2hJeHQbM .
- :Tutor<Enter>
-
-Nvim is emphatically a fork of Vim, not a clone: compatibility with Vim
-(especially editor and Vimscript features) is maintained where possible. See
-|vim-differences| for the complete reference of differences from Vim.
+To learn how to use Vim in 30 minutes, try the tutorial: >vim
+ :Tutor<Enter>
+<
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: >vim
@@ -71,4 +73,20 @@ the same Nvim configuration on all of your machines, by creating
source ~/.config/nvim/init.vim
==============================================================================
+What next? *nvim-quickstart*
+
+If you are just trying out Nvim for a few minutes, and want to see the
+extremes of what it can do, try one of these popular "extension packs" or
+"distributions" (Note: Nvim is not affiliated with these projects, and does
+not support them):
+
+- *kickstart* https://github.com/nvim-lua/kickstart.nvim
+- *lazyvim* https://www.lazyvim.org/
+- *nvchad* https://nvchad.com/
+
+However, in general, we recommend (eventually) taking time to learn Nvim from
+its stock configuration, and incrementally setting options and adding plugins
+to your |config| as you find an explicit need to do so.
+
+==============================================================================
vim:tw=78:ts=8:et:ft=help:norl:
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 97d5082e9e..b35049343d 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1559,9 +1559,9 @@ A jump table for the options with a short description can be found at |Q_op|.
a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
- noselect Do not select a match in the menu, force the user to
- select one from the menu. Only works in combination with
- "menu" or "menuone".
+ noselect Same as "noinsert", except that no menu item is
+ pre-selected. If both "noinsert" and "noselect" are
+ present, "noselect" has precedence.
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
@@ -1571,6 +1571,16 @@ A jump table for the options with a short description can be found at |Q_op|.
list of alternatives, but not how the candidates are
collected (using different completion types).
+ nosort Disable sorting of completion candidates based on fuzzy
+ scores when "fuzzy" is enabled. Candidates will appear
+ in their original order.
+
+ preinsert
+ Preinsert the portion of the first candidate word that is
+ not part of the current completion leader and using the
+ |hl-ComplMatchIns| highlight group. Does not work when
+ "fuzzy" is also included.
+
*'completeslash'* *'csl'*
'completeslash' 'csl' string (default "")
local to buffer
@@ -2027,11 +2037,20 @@ A jump table for the options with a short description can be found at |Q_op|.
Option settings for diff mode. It can consist of the following items.
All are optional. Items must be separated by a comma.
- filler Show filler lines, to keep the text
- synchronized with a window that has inserted
- lines at the same position. Mostly useful
- when windows are side-by-side and 'scrollbind'
- is set.
+ algorithm:{text} Use the specified diff algorithm with the
+ internal diff engine. Currently supported
+ algorithms are:
+ myers the default algorithm
+ minimal spend extra time to generate the
+ smallest possible diff
+ patience patience diff algorithm
+ histogram histogram diff algorithm
+
+ closeoff When a window is closed where 'diff' is set
+ and there is only one window remaining in the
+ same tab page with 'diff' set, execute
+ `:diffoff` in that window. This undoes a
+ `:diffsplit` command.
context:{n} Use a context of {n} lines between a change
and a fold that contains unchanged lines.
@@ -2042,6 +2061,23 @@ A jump table for the options with a short description can be found at |Q_op|.
value (999999) to disable folding completely.
See |fold-diff|.
+ filler Show filler lines, to keep the text
+ synchronized with a window that has inserted
+ lines at the same position. Mostly useful
+ when windows are side-by-side and 'scrollbind'
+ is set.
+
+ foldcolumn:{n} Set the 'foldcolumn' option to {n} when
+ starting diff mode. Without this 2 is used.
+
+ followwrap Follow the 'wrap' option and leave as it is.
+
+ horizontal Start diff mode with horizontal splits (unless
+ explicitly specified otherwise).
+
+ hiddenoff Do not use diff mode for a buffer when it
+ becomes hidden.
+
iblank Ignore changes where lines are all blank. Adds
the "-B" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
@@ -2055,6 +2091,17 @@ A jump table for the options with a short description can be found at |Q_op|.
are considered the same. Adds the "-i" flag
to the "diff" command if 'diffexpr' is empty.
+ indent-heuristic
+ Use the indent heuristic for the internal
+ diff library.
+
+ internal Use the internal diff library. This is
+ ignored when 'diffexpr' is set. *E960*
+ When running out of memory when writing a
+ buffer this item will be ignored for diffs
+ involving that buffer. Set the 'verbose'
+ option to see when this happens.
+
iwhite Ignore changes in amount of white space. Adds
the "-b" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
@@ -2074,56 +2121,19 @@ A jump table for the options with a short description can be found at |Q_op|.
of the "diff" command for what this does
exactly.
- horizontal Start diff mode with horizontal splits (unless
- explicitly specified otherwise).
+ linematch:{n} Align and mark changes between the most
+ similar lines between the buffers. When the
+ total number of lines in the diff hunk exceeds
+ {n}, the lines will not be aligned because for
+ very large diff hunks there will be a
+ noticeable lag. A reasonable setting is
+ "linematch:60", as this will enable alignment
+ for a 2 buffer diff hunk of 30 lines each,
+ or a 3 buffer diff hunk of 20 lines each.
vertical Start diff mode with vertical splits (unless
explicitly specified otherwise).
- closeoff When a window is closed where 'diff' is set
- and there is only one window remaining in the
- same tab page with 'diff' set, execute
- `:diffoff` in that window. This undoes a
- `:diffsplit` command.
-
- hiddenoff Do not use diff mode for a buffer when it
- becomes hidden.
-
- foldcolumn:{n} Set the 'foldcolumn' option to {n} when
- starting diff mode. Without this 2 is used.
-
- followwrap Follow the 'wrap' option and leave as it is.
-
- internal Use the internal diff library. This is
- ignored when 'diffexpr' is set. *E960*
- When running out of memory when writing a
- buffer this item will be ignored for diffs
- involving that buffer. Set the 'verbose'
- option to see when this happens.
-
- indent-heuristic
- 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:
- myers the default algorithm
- minimal spend extra time to generate the
- smallest possible diff
- patience patience diff algorithm
- histogram histogram diff algorithm
-
Examples: >vim
set diffopt=internal,filler,context:4
set diffopt=
@@ -2977,7 +2987,7 @@ A jump table for the options with a short description can be found at |Q_op|.
An |OptionSet| autocmd can be used to set it up to match automatically.
*'guicursor'* *'gcr'* *E545* *E546* *E548* *E549*
-'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20")
+'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor")
global
Configures the cursor style for each mode. Works in the GUI and many
terminals. See |tui-cursor-shape|.
@@ -3005,6 +3015,7 @@ A jump table for the options with a short description can be found at |Q_op|.
ci Command-line Insert mode
cr Command-line Replace mode
sm showmatch in Insert mode
+ t Terminal mode
a all modes
The argument-list is a dash separated list of these arguments:
hor{N} horizontal bar, {N} percent of the character height
@@ -3021,7 +3032,8 @@ A jump table for the options with a short description can be found at |Q_op|.
cursor is not shown. Times are in msec. When one of
the numbers is zero, there is no blinking. E.g.: >vim
set guicursor=n:blinkon0
-< - Default is "blinkon0" for each mode.
+<
+ Default is "blinkon0" for each mode.
{group-name}
Highlight group that decides the color and font of the
cursor.
@@ -3197,7 +3209,7 @@ A jump table for the options with a short description can be found at |Q_op|.
global
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
- each of these histories (see |cmdline-editing| and 'msghistory' for
+ each of these histories (see |cmdline-editing| and 'messagesopt' for
the number of messages to remember).
The maximum value is 10000.
@@ -4045,6 +4057,28 @@ A jump table for the options with a short description can be found at |Q_op|.
generated from a list of items, e.g., the Buffers menu. Changing this
option has no direct effect, the menu must be refreshed first.
+ *'messagesopt'* *'mopt'*
+'messagesopt' 'mopt' string (default "hit-enter,history:500")
+ global
+ Option settings for outputting messages. It can consist of the
+ following items. Items must be separated by a comma.
+
+ hit-enter Use a |hit-enter| prompt when the message is longer than
+ 'cmdheight' size.
+
+ wait:{n} Instead of using a |hit-enter| prompt, simply wait for
+ {n} milliseconds so that the user has a chance to read
+ the message. The maximum value of {n} is 10000. Use
+ 0 to disable the wait (but then the user may miss an
+ important message).
+ This item is ignored when "hit-enter" is present, but
+ required when "hit-enter" is not present.
+
+ history:{n} Determines how many entries are remembered in the
+ |:messages| history. The maximum value is 10000.
+ Setting it to zero clears the message history.
+ This item must always be present.
+
*'mkspellmem'* *'msm'*
'mkspellmem' 'msm' string (default "460000,2000,500")
global
@@ -4290,12 +4324,6 @@ A jump table for the options with a short description can be found at |Q_op|.
Defines the maximum time in msec between two mouse clicks for the
second click to be recognized as a multi click.
- *'msghistory'* *'mhi'*
-'msghistory' 'mhi' number (default 500)
- global
- Determines how many entries are remembered in the |:messages| history.
- The maximum value is 10000.
-
*'nrformats'* *'nf'*
'nrformats' 'nf' string (default "bin,hex")
local to buffer
@@ -4639,8 +4667,8 @@ A jump table for the options with a short description can be found at |Q_op|.
'redrawtime' 'rdt' number (default 2000)
global
Time in milliseconds for redrawing the display. Applies to
- 'hlsearch', 'inccommand', |:match| highlighting and syntax
- highlighting.
+ 'hlsearch', 'inccommand', |:match| highlighting, syntax highlighting,
+ and async |LanguageTree:parse()|.
When redrawing takes more than this many milliseconds no further
matches will be highlighted.
For syntax highlighting the time applies per window. When over the
@@ -4794,6 +4822,7 @@ 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|
+ lsp/ LSP client configurations |lsp-config|
lua/ |Lua| plugins
menu.vim GUI menus |menu.vim|
pack/ packages |:packadd|
@@ -4965,6 +4994,8 @@ A jump table for the options with a short description can be found at |Q_op|.
selection.
When "old" is used and 'virtualedit' allows the cursor to move past
the end of line the line break still isn't included.
+ When "exclusive" is used, cursor position in visual mode will be
+ adjusted for inclusive motions |inclusive-motion-selection-exclusive|.
Note that when "exclusive" is used and selecting from the end
backwards, you cannot include the last character of a line, when
starting in Normal mode and 'virtualedit' empty.
@@ -5916,6 +5947,7 @@ A jump table for the options with a short description can be found at |Q_op|.
All fields except the {item} are optional. A single percent sign can
be given as "%%".
+ *stl-%!*
When the option starts with "%!" then it is used as an expression,
evaluated and the result is used as the option value. Example: >vim
set statusline=%!MyStatusLine()
@@ -6590,6 +6622,10 @@ A jump table for the options with a short description can be found at |Q_op|.
expanded according to the rules used for 'statusline'. If it contains
an invalid '%' format, the value is used as-is and no error or warning
will be given when the value is set.
+
+ The default behaviour is equivalent to: >vim
+ set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim
+<
This option cannot be set in a modeline when 'modelineexpr' is off.
Example: >vim
diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt
index 7f0938be05..be913e941e 100644
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -1485,6 +1485,7 @@ criteria:
- Matches at a camel case character (e.g. Case in CamelCase)
- Matches after a path separator or a hyphen.
- The number of unmatched characters in a string.
+ - A full/exact match is preferred.
The matching string with the highest score is returned first.
For example, when you search for the "get pat" string using fuzzy matching, it
diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt
index c8570044e5..96b26d92e7 100644
--- a/runtime/doc/pi_tar.txt
+++ b/runtime/doc/pi_tar.txt
@@ -1,4 +1,4 @@
-*pi_tar.txt* For Vim version 8.2. Last change: 2020 Jan 07
+*pi_tar.txt* Nvim
+====================+
| Tar File Interface |
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index f1b0daee76..69ae0f20d1 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -193,7 +193,7 @@ registers. Nvim looks for these clipboard tools, in order of priority:
- xclip (if $DISPLAY is set)
- lemonade (for SSH) https://github.com/pocke/lemonade
- doitclient (for SSH) https://www.chiark.greenend.org.uk/~sgtatham/doit/
- - win32yank (Windows)
+ - *win32yank* (Windows)
- putclip, getclip (Windows) https://cygwin.com/packages/summary/cygutils.html
- clip, powershell (Windows) https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/clip
- termux (via termux-clipboard-set, termux-clipboard-set)
@@ -259,23 +259,21 @@ For Windows WSL, try this g:clipboard definition:
*clipboard-osc52*
Nvim bundles a clipboard provider that allows copying to the system clipboard
using OSC 52. OSC 52 is an Operating System Command control sequence that
-writes the copied text to the terminal emulator. If the terminal emulator
-supports OSC 52 then it will write the copied text into the system clipboard.
-
-Nvim will attempt to automatically determine if the host terminal emulator
-supports the OSC 52 sequence and enable the OSC 52 clipboard provider if it
-does as long as all of the following are true:
-
- • Nvim is running in the |TUI|
- • |g:clipboard| is unset
- • 'clipboard' is not set to "unnamed" or "unnamedplus"
- • $SSH_TTY is set
-
-If any of the above conditions are not met then the OSC 52 clipboard provider
-will not be used by default and Nvim will fall back to discovering a
-|clipboard-tool| through the usual process.
-
-To force Nvim to use the OSC 52 provider you can use the following
+causes the terminal emulator to write to or read from the system clipboard.
+
+When Nvim is running in the |TUI|, it will automatically attempt to determine if
+the host terminal emulator supports OSC 52. If it does, then Nvim will use OSC
+52 for copying and pasting if no other |clipboard-tool| is found and when
+'clipboard' is unset.
+
+ *g:termfeatures*
+To disable the automatic detection, set the "osc52" key of |g:termfeatures| to
+|v:false| in the |config| file. Example: >lua
+ local termfeatures = vim.g.termfeatures or {}
+ termfeatures.osc52 = false
+ vim.g.termfeatures = termfeatures
+<
+To force Nvim to always use the OSC 52 provider you can use the following
|g:clipboard| definition: >lua
vim.g.clipboard = {
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index b3399b2766..7aeb494437 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -538,9 +538,9 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
< Otherwise it works the same as `:ldo`.
FILTERING A QUICKFIX OR LOCATION LIST:
- *cfilter-plugin* *:Cfilter* *:Lfilter*
+ *cfilter-plugin* *:Cfilter* *:Lfilter* *package-cfilter*
If you have too many entries in a quickfix list, you can use the cfilter
-plugin to reduce the number of entries. Load the plugin with: >
+plugin to reduce the number of entries. Load the plugin with: >vim
packadd cfilter
@@ -1317,9 +1317,235 @@ g:compiler_gcc_ignore_unmatched_lines
JAVAC *compiler-javac*
Commonly used compiler options can be added to 'makeprg' by setting the
-g:javac_makeprg_params variable. For example: >
+b/g:javac_makeprg_params variable. For example: >
+
let g:javac_makeprg_params = "-Xlint:all -encoding utf-8"
-<
+
+MAVEN *compiler-maven*
+
+Commonly used compiler options can be added to 'makeprg' by setting the
+b/g:maven_makeprg_params variable. For example: >
+
+ let g:maven_makeprg_params = "-DskipTests -U -X"
+
+SPOTBUGS *compiler-spotbugs*
+
+SpotBugs is a static analysis tool that can be used to find bugs in Java.
+It scans the Java bytecode of all classes in the currently open buffer.
+(Therefore, `:compiler! spotbugs` is not supported.)
+
+Commonly used compiler options can be added to 'makeprg' by setting the
+"b:" or "g:spotbugs_makeprg_params" variable. For example: >vim
+
+ let b:spotbugs_makeprg_params = "-longBugCodes -effort:max -low"
+
+The global default is "-workHard -experimental".
+
+By default, the class files are searched in the directory where the source
+files are placed. However, typical Java projects use distinct directories
+for source files and class files. To make both known to SpotBugs, assign
+their paths (distinct and relative to their common root directory) to the
+following properties (using the example of a common Maven project): >vim
+
+ let g:spotbugs_properties = {
+ \ 'sourceDirPath': ['src/main/java'],
+ \ 'classDirPath': ['target/classes'],
+ \ 'testSourceDirPath': ['src/test/java'],
+ \ 'testClassDirPath': ['target/test-classes'],
+ \ }
+
+Note that source and class path entries are expected to come in pairs: define
+both "sourceDirPath" and "classDirPath" when you are considering at least one,
+and apply the same logic to "testSourceDirPath" and "testClassDirPath".
+Note that values for the path keys describe only for SpotBugs where to look
+for files; refer to the documentation for particular compiler plugins for more
+information.
+
+The default pre- and post-compiler actions are provided for Ant, Maven, and
+Javac compiler plugins and can be selected by assigning the name of a compiler
+plugin (`ant`, `maven`, or `javac`) to the "compiler" key: >vim
+
+ let g:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ }
+
+This single setting is essentially equivalent to all the settings below, with
+the exception made for the "PreCompilerAction" and "PreCompilerTestAction"
+values: their listed |Funcref|s will obtain no-op implementations whereas the
+implicit Funcrefs of the "compiler" key will obtain the requested defaults if
+available. >vim
+
+ let g:spotbugs_properties = {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ 'sourceDirPath': ['src/main/java'],
+ \ 'classDirPath': ['target/classes'],
+ \ 'testSourceDirPath': ['src/test/java'],
+ \ 'testClassDirPath': ['target/test-classes'],
+ \ }
+
+With default actions, the compiler of choice will attempt to rebuild the class
+files for the buffer (and possibly for the whole project) as soon as a Java
+syntax file is loaded; then, `spotbugs` will attempt to analyze the quality of
+the compilation unit of the buffer.
+
+Vim commands proficient in 'makeprg' [0] can be composed with default actions.
+Begin by considering which of the supported keys, "DefaultPreCompilerCommand",
+"DefaultPreCompilerTestCommand", or "DefaultPostCompilerCommand", you need to
+write an implementation for, observing that each of these keys corresponds to
+a particular "*Action" key. Follow it by defining a new function that always
+declares an only parameter of type string and puts to use a command equivalent
+of |:make|, and assigning its |Funcref| to the selected key. For example:
+>vim
+ function! GenericPostCompilerCommand(arguments) abort
+ execute 'make ' . a:arguments
+ endfunction
+
+ let g:spotbugs_properties = {
+ \ 'DefaultPostCompilerCommand':
+ \ function('GenericPostCompilerCommand'),
+ \ }
+
+When "PostCompilerAction" is available, "PostCompilerActionExecutor" is also
+supported. Its value must be a Funcref pointing to a function that always
+declares a single parameter of type string and decides whether |:execute| can
+be dispatched on its argument, containing a pending post-compiler action,
+after ascertaining the current status of |:cc| (or |:ll|): >vim
+
+ function! GenericPostCompilerActionExecutor(action) abort
+ try
+ cc
+ catch /\<E42:/
+ execute a:action
+ endtry
+ endfunction
+
+Complementary, some or all of the available "Pre*Action"s (or "*Pre*Command"s)
+may run `:doautocmd java_spotbugs_post User` in their implementations before
+|:make| (or its equivalent) to define a once-only |ShellCmdPost| `:autocmd`
+that will arrange for "PostCompilerActionExecutor" to be invoked; and then run
+`:doautocmd java_spotbugs_post ShellCmdPost` to consume this event: >vim
+
+ function! GenericPreCompilerCommand(arguments) abort
+ if !exists('g:spotbugs_compilation_done')
+ doautocmd java_spotbugs_post User
+ execute 'make ' . a:arguments
+ " only run doautocmd when :make was synchronous
+ " see note below
+ doautocmd java_spotbugs_post ShellCmdPost " XXX: (a)
+ let g:spotbugs_compilation_done = 1
+ else
+ cc
+ endif
+ endfunction
+
+ function! GenericPreCompilerTestCommand(arguments) abort
+ if !exists('g:spotbugs_test_compilation_done')
+ doautocmd java_spotbugs_post User
+ execute 'make ' . a:arguments
+ " only run doautocmd when :make was synchronous
+ " see note below
+ doautocmd java_spotbugs_post ShellCmdPost " XXX: (b)
+ let g:spotbugs_test_compilation_done = 1
+ else
+ cc
+ endif
+ endfunction
+
+ let g:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ 'DefaultPreCompilerCommand':
+ \ function('GenericPreCompilerCommand'),
+ \ 'DefaultPreCompilerTestCommand':
+ \ function('GenericPreCompilerTestCommand'),
+ \ 'PostCompilerActionExecutor':
+ \ function('GenericPostCompilerActionExecutor'),
+ \ }
+
+If a command equivalent of `:make` is capable of asynchronous execution and
+consuming `ShellCmdPost` events, `:doautocmd java_spotbugs_post ShellCmdPost`
+must be removed from such "*Action" (or "*Command") implementations (i.e. the
+lines `(a)` and `(b)` in the listed examples) to retain a sequential order for
+non-blocking execution, and any notification (see below) must be suppressed.
+A `ShellCmdPost` `:autocmd` can be associated with any |:augroup| by assigning
+its name to the "augroupForPostCompilerAction" key.
+
+When default actions are not suited to a desired workflow, proceed by writing
+arbitrary functions yourself and matching their Funcrefs to the supported
+keys: "PreCompilerAction", "PreCompilerTestAction", and "PostCompilerAction".
+
+The next example re-implements the default pre-compiler actions for a Maven
+project and requests other default Maven settings with the "compiler" entry:
+>vim
+ function! MavenPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ make compile
+ cc
+ endfunction
+
+ function! MavenPreCompilerTestAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ make test-compile
+ cc
+ endfunction
+
+ let g:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ 'PreCompilerAction':
+ \ function('MavenPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('MavenPreCompilerTestAction'),
+ \ }
+
+Note that all entered custom settings will take precedence over the matching
+default settings in "g:spotbugs_properties".
+Note that it is necessary to notify the plugin of the result of a pre-compiler
+action before further work can be undertaken. Using |:cc| after |:make| (or
+|:ll| after |:lmake|) as the last command of an action is the supported means
+of such communication.
+
+Two commands, "SpotBugsRemoveBufferAutocmd" and "SpotBugsDefineBufferAutocmd",
+are provided to toggle actions for buffer-local autocommands. For example, to
+also run actions on any |BufWritePost| and |Signal| event, add these lines to
+`~/.config/nvim/after/ftplugin/java.vim`: >vim
+
+ if exists(':SpotBugsDefineBufferAutocmd') == 2
+ SpotBugsDefineBufferAutocmd BufWritePost Signal
+ endif
+
+Otherwise, you can turn to `:doautocmd java_spotbugs User` at any time.
+
+The "g:spotbugs_properties" variable is consulted by the Java filetype plugin
+(|ft-java-plugin|) to arrange for the described automation, and, therefore, it
+must be defined before |FileType| events can take place for the buffers loaded
+with Java source files. It could, for example, be set in a project-local
+|vimrc| loaded by [1].
+
+Both "g:spotbugs_properties" and "b:spotbugs_properties" are recognized and
+must be modifiable (|:unlockvar|). The "*Command" entries are always treated
+as global functions to be shared among all Java buffers.
+
+The SpotBugs Java library and, by extension, its distributed shell scripts do
+not support in the `-textui` mode listed pathnames with directory filenames
+that contain blank characters [2]. To work around this limitation, consider
+making a symbolic link to such a directory from a directory that does not have
+blank characters in its name and passing this information to SpotBugs: >vim
+
+ let g:spotbugs_alternative_path = {
+ \ 'fromPath': 'path/to/dir_without_blanks',
+ \ 'toPath': 'path/to/dir with blanks',
+ \ }
+
+[0] https://github.com/Konfekt/vim-compilers
+[1] https://github.com/MarcWeber/vim-addon-local-vimrc
+[2] https://github.com/spotbugs/spotbugs/issues/909
+
GNU MAKE *compiler-make*
Since the default make program is "make", the compiler plugin for make,
@@ -1409,6 +1635,13 @@ Useful values for the 'makeprg' options therefore are:
setlocal makeprg=./alltests.py " Run a testsuite
setlocal makeprg=python\ %:S " Run a single testcase
+PYTEST COMPILER *compiler-pytest*
+Commonly used compiler options can be added to 'makeprg' by setting the
+b/g:pytest_makeprg_params variable. For example: >
+
+ let b:pytest_makeprg_params = "--verbose --no-summary --disable-warnings"
+
+The global default is "--tb=short --quiet"; Python warnings are suppressed.
TEX COMPILER *compiler-tex*
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index abeefb980e..e65caa72ed 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -111,7 +111,7 @@ To abort this type CTRL-C twice.
==============================================================================
Complex repeats *complex-repeat*
- *q* *recording*
+ *q* *recording* *macro*
q{0-9a-zA-Z"} Record typed characters into register {0-9a-zA-Z"}
(uppercase to append). The 'q' command is disabled
while executing a register, and it doesn't work inside
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index 9d74f1f376..9895b606fd 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -10,7 +10,7 @@ Sign Support Features *sign-support*
Type |gO| to see the table of contents.
==============================================================================
-1. Introduction *sign-intro* *signs*
+1. Introduction *sign-intro* *signs* *gutter*
When a debugger or other IDE tool is driving an editor it needs to be able
to give specific highlights which quickly tell the user useful information
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 3b0fa2b371..d8d5e42397 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -164,8 +164,7 @@ argument.
you can overwrite a file by adding an exclamation mark to
the Ex command, as in ":w!". The 'readonly' option can be
reset with ":set noro" (see the options chapter, |options|).
- Subsequent edits will not be done in readonly mode. Calling
- the executable "view" has the same effect as the -R argument.
+ Subsequent edits will not be done in readonly mode.
The 'updatecount' option will be set to 10000, meaning that
the swap file will not be updated automatically very often.
See |-M| for disallowing modifications.
@@ -207,12 +206,12 @@ argument.
: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 |-u| was given.
- Swap file is skipped (like |-n|).
- User |shada| is loaded (unless "-i NONE" is given).
+ nvim -es +"verbose echo 'foo'"
+ nvim -V1 -es +"echo 'foo'"
+<
+ Skips user |config| unless |-u| was given.
+ Disables |shada| unless |-i| was given.
+ Disables swapfile (like |-n|).
*-l*
-l {script} [args]
@@ -226,7 +225,8 @@ argument.
arguments. The {script} name is stored at `_G.arg[0]`.
Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to
- output.
+ output, as well as other message-emitting functions like
+ |:echo|.
If {script} prints messages and doesn't cause Nvim to exit,
Nvim ensures output ends with a newline.
@@ -235,6 +235,11 @@ argument.
nvim +q -l foo.lua
< This loads Lua module "bar" before executing "foo.lua": >
nvim +"lua require('bar')" -l foo.lua
+< *lua-shebang*
+ You can set the "shebang" of the script so that Nvim executes
+ the script when called with "./" from a shell (remember to
+ "chmod u+x"): >
+ #!/usr/bin/env -S nvim -l
<
Skips user |config| unless |-u| was given.
Disables plugins unless 'loadplugins' was set.
@@ -243,7 +248,7 @@ argument.
*-ll*
-ll {script} [args]
- Execute a Lua script, similarly to |-l|, but the editor is not
+ Executes a Lua script, similarly to |-l|, but the editor is not
initialized. This gives a Lua environment similar to a worker
thread. See |lua-loop-threading|.
@@ -283,21 +288,18 @@ argument.
command from a script. |debug-mode|
*-n*
--n No |swap-file| will be used. Recovery after a crash will be
- impossible. Handy if you want to view or edit a file on a
- very slow medium (e.g., a floppy).
- Can also be done with ":set updatecount=0". You can switch it
- on again by setting the 'updatecount' option to some value,
- e.g., ":set uc=100".
- 'updatecount' is set to 0 AFTER executing commands from a
- vimrc file, but before the GUI initializations. Thus it
- overrides a setting for 'updatecount' in a vimrc file, but not
- in a gvimrc file. See |startup|.
- When you want to reduce accesses to the disk (e.g., for a
- laptop), don't use "-n", but set 'updatetime' and
- 'updatecount' to very big numbers, and type ":preserve" when
- you want to save your work. This way you keep the possibility
- for crash recovery.
+-n Disables |swap-file| by setting 'updatecount' to 0 (after
+ executing any |vimrc|). Recovery after a crash will be
+ impossible. Improves performance when working with a file on
+ a very slow medium (usb drive, network share).
+
+ Enable it again by setting 'updatecount' to some value, e.g.
+ ":set updatecount=100".
+
+ To reduce accesses to the disk, don't use "-n", but set
+ 'updatetime' and 'updatecount' to very big numbers, and type
+ ":preserve" when you want to save your work. This way you
+ keep the possibility for crash recovery.
*-o*
-o[N] Open N windows, split horizontally. If [N] is not given,
diff --git a/runtime/doc/support.txt b/runtime/doc/support.txt
index a2776fca0d..103fb90b6d 100644
--- a/runtime/doc/support.txt
+++ b/runtime/doc/support.txt
@@ -12,9 +12,10 @@ Support *support*
Supported platforms *supported-platforms*
`System` `Tier` `Versions` `Tested versions`
-Linux 1 >= 2.6.32, glibc >= 2.12 Ubuntu 24.04
-macOS (Intel) 1 >= 11 macOS 13
-macOS (M1) 1 >= 11 macOS 15
+Linux (x86_64) 1 >= 2.6.32, glibc >= 2.12 Ubuntu 24.04
+Linux (arm64) 1 >= 2.6.32, glibc >= 2.12 Ubuntu 24.04
+macOS (x86_64) 1 >= 11 macOS 13
+macOS (arm64) 1 >= 11 macOS 15
Windows 64-bit 1 >= Windows 10 Version 1809 Windows Server 2022
FreeBSD 1 >= 10 FreeBSD 14
OpenBSD 2 >= 7
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 75a855bbdd..75d6d85183 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -414,12 +414,15 @@ There are many types of assembly languages that all use the same file name
extensions. Therefore you will have to select the type yourself, or add a
line in the assembly file that Vim will recognize. Currently these syntax
files are included:
- asm GNU assembly (the default)
+ asm GNU assembly (usually have .s or .S extension and were
+ already built using C compiler such as GCC or CLANG)
asm68k Motorola 680x0 assembly
asmh8300 Hitachi H-8300 version of GNU assembly
ia64 Intel Itanium 64
fasm Flat assembly (https://flatassembler.net)
- masm Microsoft assembly (probably works for any 80x86)
+ masm Microsoft assembly (.masm files are compiled with
+ Microsoft's Macro Assembler. This is only supported
+ for x86, x86_64, ARM and AARCH64 CPU families)
nasm Netwide assembly
tasm Turbo Assembly (with opcodes 80x86 up to Pentium, and
MMX)
@@ -590,6 +593,7 @@ Variable Highlight ~
*c_no_cformat* don't highlight %-formats in strings
*c_no_c99* don't highlight C99 standard items
*c_no_c11* don't highlight C11 standard items
+*c_no_c23* don't highlight C23 standard items
*c_no_bsd* don't highlight BSD specific types
*c_functions* highlight function calls and definitions
*c_function_pointers* highlight function pointers definitions
@@ -1741,6 +1745,16 @@ To disable numbers having their own color add the following to your vimrc: >
If you want quotes to have different highlighting than strings >
let g:jq_quote_highlight = 1
+KCONFIG *ft-kconfig-syntax*
+
+Kconfig syntax highlighting language. For syntax syncing, you can configure
+the following variable (default: 50): >
+
+ let kconfig_minlines = 50
+
+To configure a bit more (heavier) highlighting, set the following variable: >
+
+ let kconfig_syntax_heavy = 1
LACE *lace.vim* *ft-lace-syntax*
@@ -5159,8 +5173,6 @@ EndOfBuffer Filler lines (~) after the end of the buffer.
By default, this is highlighted like |hl-NonText|.
*hl-TermCursor*
TermCursor Cursor in a focused terminal.
- *hl-TermCursorNC*
-TermCursorNC Cursor in an unfocused terminal.
*hl-ErrorMsg*
ErrorMsg Error messages on the command line.
*hl-WinSeparator*
@@ -5242,6 +5254,8 @@ PmenuMatch Popup menu: Matched text in normal item. Combined with
*hl-PmenuMatchSel*
PmenuMatchSel Popup menu: Matched text in selected item. Combined with
|hl-PmenuMatch| and |hl-PmenuSel|.
+ *hl-ComplMatchIns*
+ComplMatchIns Matched text of the currently inserted completion.
*hl-Question*
Question |hit-enter| prompt and yes/no questions.
*hl-QuickFixLine*
diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt
index ed9659d6e7..0ab7151728 100644
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -26,7 +26,7 @@ Start *terminal-start*
There are several ways to create a terminal buffer:
- Run the |:terminal| command.
-- Call the |nvim_open_term()| or |termopen()| function.
+- Call |nvim_open_term()| or `jobstart(…, {'term': v:true})`.
- Edit a "term://" buffer. Examples: >vim
:edit term://bash
:vsplit term://top
@@ -101,13 +101,17 @@ Configuration *terminal-config*
Options: 'modified', 'scrollback'
Events: |TermOpen|, |TermEnter|, |TermLeave|, |TermClose|
-Highlight groups: |hl-TermCursor|, |hl-TermCursorNC|
+Highlight groups: |hl-TermCursor|
Terminal sets local defaults for some options, which may differ from your
global configuration.
- 'list' is disabled
- 'wrap' is disabled
+- 'number' is disabled
+- 'relativenumber' is disabled
+- 'signcolumn' is set to "no"
+- 'foldcolumn' is set to "0"
You can change the defaults with a TermOpen autocommand: >vim
au TermOpen * setlocal list
@@ -161,8 +165,8 @@ directory indicated in the request. >lua
end
})
-To try it out, select the above code and source it with `:'<,'>lua`, then run
-this command in a :terminal buffer: >
+To try it out, select the above code and source it with `:'<,'>lua` (or
+`g==`), then run this command in a :terminal buffer: >
printf "\033]7;file://./foo/bar\033\\"
@@ -203,7 +207,7 @@ Use |jobwait()| to check if the terminal job has finished: >vim
let running = jobwait([&channel], 0)[0] == -1
<
==============================================================================
-:Termdebug plugin *terminal-debug*
+:Termdebug plugin *terminal-debug* *terminal-debugger* *package-termdebug*
The Terminal debugging plugin can be used to debug a program with gdb and view
the source code in a Vim window. Since this is completely contained inside
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 5fc6429f7a..b04f13add5 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -70,7 +70,7 @@ 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
+Note: The predicates listed there 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
@@ -81,8 +81,7 @@ that user config takes precedence over plugins, which take precedence over
queries bundled with Nvim). If a query should extend other queries instead
of replacing them, use |treesitter-query-modeline-extends|.
-See |lua-treesitter-query| for the list of available methods for working with
-treesitter queries from Lua.
+The Lua interface is described at |lua-treesitter-query|.
TREESITTER QUERY PREDICATES *treesitter-predicates*
@@ -158,12 +157,12 @@ The following predicates are built in:
(field_identifier) @method)) @_parent
(#has-parent? @_parent template_method function_declarator))
<
- *lua-treesitter-not-predicate*
+ *treesitter-predicate-not*
Each predicate has a `not-` prefixed predicate that is just the negation of
the predicate.
- *lua-treesitter-all-predicate*
- *lua-treesitter-any-predicate*
+ *treesitter-predicate-all*
+ *treesitter-predicate-any*
Queries can use quantifiers to capture multiple nodes. When a capture contains
multiple nodes, predicates match only if ALL nodes contained by the capture
match the predicate. Some predicates (`eq?`, `match?`, `lua-match?`,
@@ -245,15 +244,32 @@ The following directives are built in:
(#gsub! @_node ".*%.(.*)" "%1")
<
`trim!` *treesitter-directive-trim!*
- Trim blank lines from the end of the node. This will set a new
- `metadata[capture_id].range`.
+ Trims whitespace from the node. Sets a new
+ `metadata[capture_id].range`. Takes a capture ID and, optionally, four
+ integers to customize trimming behavior (`1` meaning trim, `0` meaning
+ don't trim). When only given a capture ID, trims blank lines (lines
+ that contain only whitespace, or are empty) from the end of the node
+ (for backwards compatibility). Can trim all whitespace from both sides
+ of the node if parameters are given.
+ Examples: >query
+ ; only trim blank lines from the end of the node
+ ; (equivalent to (#trim! @fold 0 0 1 0))
+ (#trim! @fold)
+
+ ; trim blank lines from both sides of the node
+ (#trim! @fold 1 0 1 0)
+
+ ; trim all whitespace around the node
+ (#trim! @fold 1 1 1 1)
+<
Parameters: ~
{capture_id}
+ {trim_start_linewise}
+ {trim_start_charwise}
+ {trim_end_linewise} (default `1` if only given {capture_id})
+ {trim_end_charwise}
- Example: >query
- (#trim! @fold)
-<
Further directives can be added via |vim.treesitter.query.add_directive()|.
Use |vim.treesitter.query.list_directives()| to list all available directives.
@@ -481,7 +497,7 @@ 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: >query
- (fenced_code_block_delimiter @conceal (#set! conceal ""))
+ ((fenced_code_block_delimiter) @conceal (#set! conceal ""))
<
It is also possible to replace a node with a single character, which (unlike
legacy syntax) can be given a custom highlight. For example, the following
@@ -492,6 +508,13 @@ still highlighted the same as other operators: >query
<
Conceals specified in this way respect 'conceallevel'.
+Note that although you can use any string for `conceal`, only the first
+character will be used: >query
+
+ ; identifiers will be concealed with 'f'.
+ ((identifier) @conceal (#set! conceal "foo"))
+<
+
*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
@@ -556,6 +579,10 @@ associated with patterns:
• `injection.parent` - indicates that the captured node’s text should
be parsed with the same language as the node's parent LanguageTree.
+Injection queries are currently run over the entire buffer, which can be slow
+for large buffers. To disable injections for, e.g., `c`, just place an
+empty `queries/c/injections.scm` file in your 'runtimepath'.
+
==============================================================================
VIM.TREESITTER *lua-treesitter*
@@ -816,7 +843,13 @@ TSNode:range({include_bytes}) *TSNode:range()*
• end byte (if {include_bytes} is `true`)
Parameters: ~
- • {include_bytes} (`boolean?`)
+ • {include_bytes} (`false?`)
+
+ Return (multiple): ~
+ (`integer`)
+ (`integer`)
+ (`integer`)
+ (`integer`)
TSNode:sexpr() *TSNode:sexpr()*
Get an S-expression representing the node as a string.
@@ -885,8 +918,8 @@ 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).
+ string, the capture's language, a table of metadata (`priority`,
+ `conceal`, ...; empty if none are defined), and the id of the capture.
Parameters: ~
• {bufnr} (`integer`) Buffer number (0 for current buffer)
@@ -894,7 +927,7 @@ get_captures_at_pos({bufnr}, {row}, {col})
• {col} (`integer`) Position column
Return: ~
- (`{capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[]`)
+ (`{capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata, id: integer}[]`)
get_node({opts}) *vim.treesitter.get_node()*
Returns the smallest named node at the given position
@@ -926,7 +959,7 @@ get_node_range({node_or_range}) *vim.treesitter.get_node_range()*
Returns the node's range or an unpacked range table
Parameters: ~
- • {node_or_range} (`TSNode|table`) Node or table of positions
+ • {node_or_range} (`TSNode|Range4`) Node or table of positions
Return (multiple): ~
(`integer`) start_row
@@ -1074,6 +1107,9 @@ start({bufnr}, {lang}) *vim.treesitter.start()*
required for some plugins. In this case, add `vim.bo.syntax = 'on'` after
the call to `start`.
+ Note: By default, the highlighter parses code asynchronously, using a
+ segment time of 3ms.
+
Example: >lua
vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex',
callback = function(args)
@@ -1098,7 +1134,7 @@ stop({bufnr}) *vim.treesitter.stop()*
==============================================================================
-Lua module: vim.treesitter.language *lua-treesitter-language*
+Lua module: vim.treesitter.language *treesitter-language*
add({lang}, {opts}) *vim.treesitter.language.add()*
Load parser with name {lang}
@@ -1162,7 +1198,7 @@ inspect({lang}) *vim.treesitter.language.inspect()*
• {lang} (`string`) Language
Return: ~
- (`table`)
+ (`TSLangInfo`)
register({lang}, {filetype}) *vim.treesitter.language.register()*
Register a parser named {lang} to be used for {filetype}(s).
@@ -1178,6 +1214,10 @@ register({lang}, {filetype}) *vim.treesitter.language.register()*
==============================================================================
Lua module: vim.treesitter.query *lua-treesitter-query*
+This Lua |treesitter-query| interface allows you to create queries and use
+them to parse text. See |vim.treesitter.query.parse()| for a working example.
+
+
*vim.treesitter.query.add_directive()*
add_directive({name}, {handler}, {opts})
Adds a new directive to be used in queries
@@ -1308,21 +1348,30 @@ omnifunc({findstart}, {base}) *vim.treesitter.query.omnifunc()*
• {base} (`string`)
parse({lang}, {query}) *vim.treesitter.query.parse()*
- 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
- the `iter_captures` and `iter_matches` methods.
+ Parses a {query} string and returns a `Query` object
+ (|lua-treesitter-query|), which can be used to search the tree for the
+ query patterns (via |Query:iter_captures()|, |Query:iter_matches()|), or
+ inspect the query via these fields:
+ • `captures`: a list of unique capture names defined in the query (alias:
+ `info.captures`).
+ • `info.patterns`: information about predicates.
- Exposes `info` and `captures` with additional context about {query}.
- • `captures` contains the list of unique capture names defined in {query}.
- • `info.captures` also points to `captures`.
- • `info.patterns` contains information about predicates.
+ Example: >lua
+ local query = vim.treesitter.query.parse('vimdoc', [[
+ ; query
+ ((h1) @str
+ (#trim! @str 1 1 1 1))
+ ]])
+ local tree = vim.treesitter.get_parser():parse()[1]
+ for id, node, metadata in query:iter_captures(tree:root(), 0) do
+ -- Print the node name and source text.
+ vim.print({node:type(), vim.treesitter.get_node_text(node, vim.api.nvim_get_current_buf())})
+ end
+<
Parameters: ~
• {lang} (`string`) Language to use for the query
- • {query} (`string`) Query in s-expr syntax
+ • {query} (`string`) Query text, in s-expr syntax
Return: ~
(`vim.treesitter.Query`) Parsed query
@@ -1332,18 +1381,23 @@ parse({lang}, {query}) *vim.treesitter.query.parse()*
*Query:iter_captures()*
Query:iter_captures({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
- ensure to use a freshly parsed tree consistent with the current text of
- the buffer (if relevant). {start} and {stop} 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 {stop} row values are used from the given node.
-
- The iterator returns four values: a numeric id identifying the capture,
- the captured node, metadata from any directives processing the match, and
- the match itself. The following example shows how to get captures by name: >lua
+ Iterates over all captures from all matches in {node}.
+
+ {source} is required 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} and {stop} 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 {stop} row values are used from the given
+ node.
+
+ The iterator returns four values:
+ 1. the numeric id identifying the capture
+ 2. the captured node
+ 3. metadata from any directives processing the match
+ 4. the match itself
+
+ Example: how to get captures by name: >lua
for id, node, metadata, match 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:
@@ -1367,8 +1421,8 @@ Query:iter_captures({node}, {source}, {start}, {stop})
Defaults to `node:end_()`.
Return: ~
- (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch`)
- capture id, capture node, metadata, match
+ (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`)
+ capture id, capture node, metadata, match, tree
*Query:iter_matches()*
Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
@@ -1388,7 +1442,7 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
-- `node` was captured by the `name` capture in the match
local node_data = metadata[id] -- Node level metadata
- ... use the info here ...
+ -- ... use the info here ...
end
end
end
@@ -1413,14 +1467,24 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts})
compatibility and will be removed in a future release.
Return: ~
- (`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata`)
- pattern id, match, metadata
+ (`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree`)
+ pattern id, match, metadata, tree
set({lang}, {query_name}, {text}) *vim.treesitter.query.set()*
Sets the runtime query named {query_name} for {lang}
- This allows users to override any runtime files and/or configuration set
- by plugins.
+ This allows users to override or extend any runtime files and/or
+ configuration set by plugins.
+
+ For example, you could enable spellchecking of `C` identifiers with the
+ following code: >lua
+ vim.treesitter.query.set(
+ 'c',
+ 'highlights',
+ [[;inherits c
+ (identifier) @spell]])
+ ]])
+<
Parameters: ~
• {lang} (`string`) Language to use for the query
@@ -1429,7 +1493,7 @@ set({lang}, {query_name}, {text}) *vim.treesitter.query.set()*
==============================================================================
-Lua module: vim.treesitter.languagetree *lua-treesitter-languagetree*
+Lua module: vim.treesitter.languagetree *treesitter-languagetree*
A *LanguageTree* contains a tree of parsers: the root treesitter parser for
{lang} and any "injected" language parsers, which themselves may inject other
@@ -1465,6 +1529,9 @@ analysis on a tree should use a timer to throttle too frequent updates.
LanguageTree:children() *LanguageTree:children()*
Returns a map of language to child tree.
+ Return: ~
+ (`table<string,vim.treesitter.LanguageTree>`)
+
LanguageTree:contains({range}) *LanguageTree:contains()*
Determines whether {range} is contained in the |LanguageTree|.
@@ -1514,7 +1581,8 @@ LanguageTree:invalidate({reload}) *LanguageTree:invalidate()*
Parameters: ~
• {reload} (`boolean?`)
-LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()*
+ *LanguageTree:is_valid()*
+LanguageTree:is_valid({exclude_children}, {range})
Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()|
reflects the latest state of the source. If invalid, user should call
|LanguageTree:parse()|.
@@ -1522,6 +1590,7 @@ LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()*
Parameters: ~
• {exclude_children} (`boolean?`) whether to ignore the validity of
children (default `false`)
+ • {range} (`Range?`) range to check for validity
Return: ~
(`boolean`)
@@ -1529,6 +1598,9 @@ LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()*
LanguageTree:lang() *LanguageTree:lang()*
Gets the language of this tree node.
+ Return: ~
+ (`string`)
+
*LanguageTree:language_for_range()*
LanguageTree:language_for_range({range})
Gets the appropriate language that contains {range}.
@@ -1577,7 +1649,13 @@ LanguageTree:node_for_range({range}, {opts})
Return: ~
(`TSNode?`)
-LanguageTree:parse({range}) *LanguageTree:parse()*
+LanguageTree:parent() *LanguageTree:parent()*
+ Returns the parent tree. `nil` for the root tree.
+
+ Return: ~
+ (`vim.treesitter.LanguageTree?`)
+
+LanguageTree:parse({range}, {on_parse}) *LanguageTree:parse()*
Recursively parse all regions in the language tree using
|treesitter-parsers| for the corresponding languages and run injection
queries on the parsed trees to determine whether child trees should be
@@ -1588,14 +1666,27 @@ LanguageTree:parse({range}) *LanguageTree:parse()*
if {range} is `true`).
Parameters: ~
- • {range} (`boolean|Range?`) Parse this range in the parser's source.
- Set to `true` to run a complete parse of the source (Note:
- Can be slow!) Set to `false|nil` to only parse regions with
- empty ranges (typically only the root tree without
- injections).
+ • {range} (`boolean|Range?`) Parse this range in the parser's
+ source. Set to `true` to run a complete parse of the
+ source (Note: Can be slow!) Set to `false|nil` to only
+ parse regions with empty ranges (typically only the root
+ tree without injections).
+ • {on_parse} (`fun(err?: string, trees?: table<integer, TSTree>)?`)
+ Function invoked when parsing completes. When provided and
+ `vim.g._ts_force_sync_parsing` is not set, parsing will
+ run asynchronously. The first argument to the function is
+ a string representing the error type, in case of a failure
+ (currently only possible for timeouts). The second
+ argument is the list of trees returned by the parse (upon
+ success), or `nil` if the parse timed out (determined by
+ 'redrawtime').
+
+ If parsing was still able to finish synchronously (within
+ 3ms), `parse()` returns the list of trees. Otherwise, it
+ returns `nil`.
Return: ~
- (`table<integer, TSTree>`)
+ (`table<integer, TSTree>?`)
*LanguageTree:register_cbs()*
LanguageTree:register_cbs({cbs}, {recursive})
@@ -1624,6 +1715,9 @@ LanguageTree:register_cbs({cbs}, {recursive})
LanguageTree:source() *LanguageTree:source()*
Returns the source content of the language tree (bufnr or string).
+ Return: ~
+ (`integer|string`)
+
*LanguageTree:tree_for_range()*
LanguageTree:tree_for_range({range}, {opts})
Gets the tree that contains {range}.
diff --git a/runtime/doc/tui.txt b/runtime/doc/tui.txt
index 9493f91b1e..96ac54abc5 100644
--- a/runtime/doc/tui.txt
+++ b/runtime/doc/tui.txt
@@ -280,191 +280,5 @@ colours of whitespace are immaterial, in practice they change the colours of
cursors and selections that cross them. This may have a visible, but minor,
effect on some UIs.
-==============================================================================
-Using the mouse *mouse-using*
-
- *mouse-mode-table* *mouse-overview*
-Overview of what the mouse buttons do, when 'mousemodel' is "extend":
-
- *<S-LeftMouse>* *<A-RightMouse>* *<S-RightMouse>* *<RightDrag>*
- *<RightRelease>* *<LeftDrag>*
-Normal Mode: >
- event position selection change action
- cursor window
- ---------------------------------------------------------------------------
- <LeftMouse> yes end yes
- <C-LeftMouse> yes end yes "CTRL-]" (2)
- <S-LeftMouse> yes no change yes "*" (2)
- <LeftDrag> yes start or extend (1) no
- <LeftRelease> yes start or extend (1) no
- <MiddleMouse> yes if not active no put
- <MiddleMouse> yes if active no yank and put
- <RightMouse> yes start or extend yes
- <A-RightMouse> yes start or extend blockw. yes
- <S-RightMouse> yes no change yes "#" (2)
- <C-RightMouse> no no change no "CTRL-T"
- <RightDrag> yes extend no
- <RightRelease> yes extend no
-
-Insert or Replace Mode: >
- event position selection change action
- cursor window
- ---------------------------------------------------------------------------
- <LeftMouse> yes (cannot be active) yes
- <C-LeftMouse> yes (cannot be active) yes "CTRL-O^]" (2)
- <S-LeftMouse> yes (cannot be active) yes "CTRL-O*" (2)
- <LeftDrag> yes start or extend (1) no like CTRL-O (1)
- <LeftRelease> yes start or extend (1) no like CTRL-O (1)
- <MiddleMouse> no (cannot be active) no put register
- <RightMouse> yes start or extend yes like CTRL-O
- <A-RightMouse> yes start or extend blockw. yes
- <S-RightMouse> yes (cannot be active) yes "CTRL-O#" (2)
- <C-RightMouse> no (cannot be active) no "CTRL-O CTRL-T"
-
-In a help window: >
- event position selection change action
- cursor window
- ---------------------------------------------------------------------------
- <2-LeftMouse> yes (cannot be active) no "^]" (jump to help tag)
-
-When 'mousemodel' is "popup", these are different:
-
- *<A-LeftMouse>*
-Normal Mode: >
- event position selection change action
- cursor window
- ---------------------------------------------------------------------------
- <S-LeftMouse> yes start or extend (1) no
- <A-LeftMouse> yes start/extend blockw no
- <RightMouse> no popup menu no
-
-Insert or Replace Mode: >
- event position selection change action
- cursor window
- ---------------------------------------------------------------------------
- <S-LeftMouse> yes start or extend (1) no like CTRL-O (1)
- <A-LeftMouse> yes start/extend blockw no
- <RightMouse> no popup menu no
-
-(1) only if mouse pointer moved since press
-(2) only if click is in same buffer
-
-Clicking the left mouse button causes the cursor to be positioned. If the
-click is in another window that window is made the active window. When
-editing the command-line the cursor can only be positioned on the
-command-line. When in Insert mode Vim remains in Insert mode. If 'scrolloff'
-is set, and the cursor is positioned within 'scrolloff' lines from the window
-border, the text is scrolled.
-
-A selection can be started by pressing the left mouse button on the first
-character, moving the mouse to the last character, then releasing the mouse
-button. You will not always see the selection until you release the button,
-only in some versions (GUI, Win32) will the dragging be shown immediately.
-Note that you can make the text scroll by moving the mouse at least one
-character in the first/last line in the window when 'scrolloff' is non-zero.
-
-In Normal, Visual and Select mode clicking the right mouse button causes the
-Visual area to be extended. When 'mousemodel' is "popup", the left button has
-to be used while keeping the shift key pressed. When clicking in a window
-which is editing another buffer, the Visual or Select mode is stopped.
-
-In Normal, Visual and Select mode clicking the right mouse button with the alt
-key pressed causes the Visual area to become blockwise. When 'mousemodel' is
-"popup" the left button has to be used with the alt key. Note that this won't
-work on systems where the window manager consumes the mouse events when the
-alt key is pressed (it may move the window).
-
- *double-click* *<2-LeftMouse>* *<3-LeftMouse>* *<4-LeftMouse>*
-Double, triple and quadruple clicks are supported when the GUI is active, for
-Win32 and for an xterm. For selecting text, extra clicks extend the
-selection: >
-
- click select
- ---------------------------------
- double word or % match
- triple line
- quadruple rectangular block
-
-Exception: In a Help window a double click jumps to help for the word that is
-clicked on.
-
-A double click on a word selects that word. 'iskeyword' is used to specify
-which characters are included in a word. A double click on a character
-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: >vim
- :map <2-LeftMouse> :exe "tag " .. expand("<cword>")<CR>
-
-Dragging the mouse with a double click (button-down, button-up, button-down
-and then drag) will result in whole words to be selected. This continues
-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
-'selectmode' option contains "mouse".
-
- *X1Mouse* *X1Drag* *X1Release*
- *X2Mouse* *X2Drag* *X2Release*
- *<MiddleRelease>* *<MiddleDrag>*
-Mouse clicks can be mapped. The codes for mouse clicks are: >
- code mouse button normal action
- ---------------------------------------------------------------------------
- <LeftMouse> left pressed set cursor position
- <LeftDrag> left moved while pressed extend selection
- <LeftRelease> left released set selection end
- <MiddleMouse> middle pressed paste text at cursor position
- <MiddleDrag> middle moved while pressed -
- <MiddleRelease> middle released -
- <RightMouse> right pressed extend selection
- <RightDrag> right moved while pressed extend selection
- <RightRelease> right released set selection end
- <X1Mouse> X1 button pressed -
- <X1Drag> X1 moved while pressed -
- <X1Release> X1 button release -
- <X2Mouse> X2 button pressed -
- <X2Drag> X2 moved while pressed -
- <X2Release> X2 button release -
-
-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: >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). >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: >vim
- :noremap <LeftMouse> <RightMouse>
- :noremap <LeftDrag> <RightDrag>
- :noremap <LeftRelease> <RightRelease>
- :noremap <RightMouse> <LeftMouse>
- :noremap <RightDrag> <LeftDrag>
- :noremap <RightRelease> <LeftRelease>
- :noremap g<LeftMouse> <C-RightMouse>
- :noremap g<RightMouse> <C-LeftMouse>
- :noremap! <LeftMouse> <RightMouse>
- :noremap! <LeftDrag> <RightDrag>
- :noremap! <LeftRelease> <RightRelease>
- :noremap! <RightMouse> <LeftMouse>
- :noremap! <RightDrag> <LeftDrag>
- :noremap! <RightRelease> <LeftRelease>
-<
+
vim:et:sw=2:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 4e8253c47a..d8a0e3d0d7 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -562,7 +562,7 @@ with the following possible keys:
"ui": Builtin UI highlight. |highlight-groups|
"syntax": Highlight applied to a buffer by a syntax declaration or
other runtime/plugin functionality such as
- |nvim_buf_add_highlight()|
+ |nvim_buf_set_extmark()|
"terminal": highlight from a process running in a |terminal-emulator|.
Contains no further semantic information.
`ui_name`: Highlight name from |highlight-groups|. Only for "ui" kind.
@@ -715,7 +715,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, hl_id] ~
content: List of [attrs, string]
[[{}, "t"], [attrs, "est"], ...]
@@ -728,8 +728,8 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
`firstc` and `prompt` are text, that if non-empty should be
displayed in front of the command line. `firstc` always indicates
built-in command lines such as `:` (ex command) and `/` `?` (search),
- while `prompt` is an |input()| prompt. `indent` tells how many spaces
- the content should be indented.
+ while `prompt` is an |input()| prompt, highlighted with `hl_id`.
+ `indent` tells how many spaces the content should be indented.
The Nvim command line can be invoked recursively, for instance by
typing `<c-r>=` at the command line prompt. The `level` field is used
@@ -749,8 +749,9 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|.
Should be hidden at next cmdline_show.
-["cmdline_hide"] ~
- Hide the cmdline.
+["cmdline_hide", abort] ~
+ Hide the cmdline. `abort` is true if the cmdline is hidden after an
+ aborting condition (|c_Esc| or |c_CTRL-C|).
["cmdline_block_show", lines] ~
Show a block of context to the current command line. For example if
@@ -783,24 +784,33 @@ will be set to zero, but can be changed and used for the replacing cmdline or
message window. 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, history] ~
Display a message to the user.
kind
Name indicating the message kind:
- "" (empty) Unknown (consider a feature-request: |bugs|)
+ "" (empty) Unknown (consider a |feature-request|)
+ "bufwrite" |:write| message
"confirm" |confirm()| or |:confirm| dialog
- "confirm_sub" |:substitute| confirm dialog |:s_c|
"emsg" Error (|errors|, internal error, |:throw|, …)
"echo" |:echo| message
"echomsg" |:echomsg| message
"echoerr" |:echoerr| message
+ "completion" |ins-completion-menu| message
+ "list_cmd" List output for various commands (|:ls|, |:set|, …)
"lua_error" Error in |:lua| code
"lua_print" |print()| from |:lua| code
"rpc_error" Error response from |rpcrequest()|
"return_prompt" |press-enter| prompt after a multiple messages
"quickfix" Quickfix navigation message
+ "search_cmd" Entered search command
"search_count" Search count message ("S" flag of 'shortmess')
+ "shell_err" |:!cmd| shell stderr output
+ "shell_out" |:!cmd| shell stdout output
+ "shell_ret" |:!cmd| shell return code
+ "undo" |:undo| and |:redo| message
+ "verbose" 'verbose' message
+ "wildlist" 'wildmode' "list" message
"wmsg" Warning ("search hit BOTTOM", |W10|, …)
New kinds may be added in the future; clients should treat unknown
kinds as the empty kind.
@@ -819,6 +829,9 @@ must handle.
true: Replace the message in the most-recent `msg_show` call,
but any other visible message should still remain.
+ history
+ True if the message was added to the |:messages| history.
+
["msg_clear"] ~
Clear all messages currently displayed by "msg_show". (Messages sent
by other "msg_" events below will not be affected).
diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt
index 1fc612de26..41cee4e540 100644
--- a/runtime/doc/usr_02.txt
+++ b/runtime/doc/usr_02.txt
@@ -684,6 +684,13 @@ Summary: *help-summary* >
:help E128
< takes you to the |:function| command
+27) Documentation for packages distributed with Vim have the form
+ package-<name>. So >
+ :help package-termdebug
+<
+ will bring you to the help section for the included termdebug plugin and
+ how to enable it.
+
==============================================================================
diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt
index 698d1207d3..d75438cd22 100644
--- a/runtime/doc/usr_05.txt
+++ b/runtime/doc/usr_05.txt
@@ -235,7 +235,7 @@ an archive or as a repository. For an archive you can follow these steps:
else.
-Adding nohlsearch package *nohlsearch-install*
+Adding nohlsearch package *nohlsearch-install* *package-nohlsearch*
Load the plugin with this command: >
packadd nohlsearch
diff --git a/runtime/doc/usr_10.txt b/runtime/doc/usr_10.txt
index 2f55aadef0..98337e4b76 100644
--- a/runtime/doc/usr_10.txt
+++ b/runtime/doc/usr_10.txt
@@ -192,7 +192,7 @@ following: >
Vim finds the first occurrence of "Professor" and displays the text it is
about to change. You get the following prompt: >
- replace with Teacher (y/n/a/q/l/^E/^Y)?
+ replace with Teacher? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)
At this point, you must enter one of the following answers:
diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt
index 955d2ae5f0..8dbe1332b5 100644
--- a/runtime/doc/usr_25.txt
+++ b/runtime/doc/usr_25.txt
@@ -190,15 +190,15 @@ This results in the following:
story. ~
-JUSTIFYING TEXT
+JUSTIFYING TEXT *justify* *:Justify* *Justify()* *package-justify*
Vim has no built-in way of justifying text. However, there is a neat macro
package that does the job. To use this package, execute the following
-command: >
+command: >vim
:packadd justify
-Or put this line in your |vimrc|: >
+Or put this line in your |vimrc|: >vim
packadd! justify
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 3202a70b76..f958491ccf 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1103,7 +1103,8 @@ Various: *various-functions*
did_filetype() check if a FileType autocommand was used
eventhandler() check if invoked by an event handler
getpid() get process ID of Vim
- getscriptinfo() get list of sourced vim scripts
+ getscriptinfo() get list of sourced Vim scripts
+ getstacktrace() get current stack trace of Vim scripts
libcall() call a function in an external library
libcallnr() idem, returning a number
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 1ded5154a7..662519d415 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -282,6 +282,16 @@ gx Opens the current filepath or URL (decided by
requires using the call operator (&). >
:!Write-Output "1`n2" | & "C:\Windows\System32\sort.exe" /r
<
+ Vim builds command line using options 'shell', 'shcf',
+ 'sxq' and 'shq' in the following order:
+ `&sh &shcf &sxq &shq {cmd} &shq &sxq`
+ So setting both 'sxq' and 'shq' is possible but rarely
+ useful. Additional escaping inside `{cmd}` may also
+ be due to 'sxe' option.
+
+ Also, all |cmdline-special| characters in {cmd} are
+ replaced by Vim before passing them to shell.
+
*E34*
Any "!" in {cmd} is replaced with the previous
external command (see also 'cpoptions'), unless
@@ -524,7 +534,8 @@ gO Show a filetype-specific, navigable "outline" of the
current buffer. For example, in a |help| buffer this
shows the table of contents.
- Currently works in |help| and |:Man| buffers.
+ Works in |help| and |:Man| buffers, or any buffer with
+ an active |LSP| client (|lsp-defaults|).
[N]gs *gs* *:sl* *:sleep*
:[N]sl[eep] [N][m] Do nothing for [N] seconds, or [N] milliseconds if [m]
@@ -541,8 +552,12 @@ gO Show a filetype-specific, navigable "outline" of the
Queued messages are processed during the sleep.
*:sl!* *:sleep!*
-:[N]sl[eep]! [N][m] Same as above. Unlike Vim, it does not hide the
- cursor. |vim-differences|
+:[N]sl[eep]! [N][m] Same as above, but hide the cursor.
+
+ *g==*
+g== Executes the current code block.
+
+ Works in |help| buffers.
==============================================================================
2. Using Vim like less or more *less*
diff --git a/runtime/doc/vietnamese.txt b/runtime/doc/vietnamese.txt
new file mode 100644
index 0000000000..ed3fe9bc68
--- /dev/null
+++ b/runtime/doc/vietnamese.txt
@@ -0,0 +1,73 @@
+*vietnamese.txt* Nvim
+
+
+ VIM REFERENCE MANUAL by Phạm Bình An
+
+ Type |gO| to see the table of contents.
+
+===============================================================================
+1. Introduction
+ *vietnamese-intro*
+Vim supports Vietnamese language in the following ways:
+
+- Built-in |vietnamese-keymap|, which allows you to type Vietnamese characters
+ in |Insert-mode| and |search-commands| using US keyboard layout.
+- Localization in Vietnamese. See |vietnamese-l10n|
+
+===============================================================================
+2. Vietnamese keymaps
+ *vietnamese-keymap*
+To switch between languages you can use your system native keyboard switcher,
+or use one of the Vietnamese keymaps included in the Vim distribution, like
+below >
+ :set keymap=vietnamese-telex_utf-8
+<
+See |'keymap'| for more information.
+
+In the latter case, you can type Vietnamese even if you do not have a
+Vietnamese input method engine (IME) or you want Vim to be independent from a
+system-wide keyboard settings (when |'imdisable'| is set). You can also |:map|
+a key to switch between keyboards.
+
+Vim comes with the following Vietnamese keymaps:
+- *vietnamese-telex_utf-8* Telex input method, |UTF-8| encoding.
+- *vietnamese-viqr_utf-8* VIQR input method, |UTF-8| encoding.
+- *vietnamese-vni_utf-8* VNI input method, |UTF-8| encoding.
+
+ *vietnamese-ime_diff*
+Since these keymaps were designed to be minimalistic, they do not support all
+features of the corresponding input methods. The differences are described
+below:
+
+- You can only type each character individually, entering the base letter first
+ and then the diacritics later. For example, to type the word `nến` using
+ |vietnamese-vni_utf-8|, you must type `ne61n`, not `nen61` or `ne6n1`
+- For characters with more than 1 diacritic, you need to type vowel mark before
+ tone mark. For example, to type `ồ` using |vietnamese-telex_utf-8|, you need
+ to type `oof`, not `ofo`.
+- With |vietnamese-telex_utf-8|, you need to type all uppercase letters to
+ produce uppercase characters with diacritics. For example, `Ừ` must be typed
+ as `UWF`.
+- With |vietnamese-telex_utf-8|, the escape character `\` from VNI is added,
+ hence the confusing `ooo` input to type `oo` is removed, which could lead to
+ ambiguities. For example, to type the word `Đoòng`, you would type
+ `DDo\ofng`.
+- Simple Telex (both v1 and v2), including the `w[]{}` style, is not
+ supported.
+- Removing diacritics using `z` in Telex or `0` in VNI and VIQR is not supported.
+
+===============================================================================
+3. Localization
+ *vietnamese-l10n*
+Vim |messages| are also available in Vietnamese. If you wish to see messages
+in Vietnamese, you can run the command |:language| with an argument being the
+name of the Vietnamese locale. For example, >
+ :language vi_VN
+< or >
+ :language vi_VN.utf-8
+<
+Note that the name of the Vietnamese locale may vary depending on your system.
+See |mbyte-first| for details.
+
+===============================================================================
+vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 8fa94a2601..eae341da49 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -24,7 +24,7 @@ User configuration and data files are found in standard |base-directories|
session information. |shada|
==============================================================================
-Defaults *nvim-defaults*
+Defaults *defaults* *nvim-defaults*
- Filetype detection is enabled by default. This can be disabled by adding
":filetype off" to |init.vim|.
@@ -39,7 +39,6 @@ Defaults *nvim-defaults*
- 'autoindent' is enabled
- 'autoread' is enabled (works in all UIs, including terminal)
- 'background' defaults to "dark" (unless set automatically by the terminal/UI)
-- 'backspace' defaults to "indent,eol,start"
- 'backupdir' defaults to .,~/.local/state/nvim/backup// (|xdg|), auto-created
- 'belloff' defaults to "all"
- 'comments' includes "fb:•"
@@ -89,7 +88,6 @@ Defaults *nvim-defaults*
- 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created
- 'viewoptions' includes "unix,slash", excludes "options"
- 'viminfo' includes "!"
-- 'wildmenu' is enabled
- 'wildoptions' defaults to "pum,tagfile"
- |editorconfig| plugin is enabled, .editorconfig settings are applied.
@@ -124,7 +122,7 @@ fully disable the mouse or popup-menu, do any of the following:
<
To remove the default popup-menu without disabling mouse: >vim
aunmenu PopUp
- autocmd! nvim_popupmenu
+ autocmd! nvim.popupmenu
To remove only the "How-to disable mouse" menu item (and its separator): >vim
aunmenu PopUp.How-to\ disable\ mouse
@@ -172,7 +170,7 @@ DEFAULT AUTOCOMMANDS
Default autocommands exist in the following groups. Use ":autocmd! {group}" to
remove them and ":autocmd {group}" to see how they're defined.
-nvim_terminal:
+nvim.terminal:
- BufReadCmd: Treats "term://" buffers as |terminal| buffers. |terminal-start|
- TermClose: A |terminal| buffer started with no arguments (which thus uses
'shell') and which exits with no error is closed automatically.
@@ -188,13 +186,17 @@ nvim_terminal:
- 'textwidth' set to 0
- 'nowrap'
- 'nolist'
+ - 'nonumber'
+ - 'norelativenumber'
+ - 'signcolumn' set to "no"
+ - 'foldcolumn' set to "0"
- 'winhighlight' uses |hl-StatusLineTerm| and |hl-StatusLineTermNC| in
place of |hl-StatusLine| and |hl-StatusLineNC|
-nvim_cmdwin:
+nvim.cmdwin:
- CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|.
-nvim_swapfile:
+nvim.swapfile:
- SwapExists: Skips the swapfile prompt (sets |v:swapchoice| to "e") when the
swapfile is owned by a running Nvim process. Shows |W325| "Ignoring
swapfile…" message.
@@ -289,7 +291,8 @@ Commands:
User commands can support |:command-preview| to show results as you type
- |:write| with "++p" flag creates parent directories.
-Events:
+Events (autocommands):
+- Fixed inconsistent behavior in execution of nested autocommands #23368
- |RecordingEnter|
- |RecordingLeave|
- |SearchWrapped|
@@ -297,6 +300,8 @@ Events:
- |TabNewEntered|
- |TermClose|
- |TermOpen|
+- |TermResponse| is fired for any OSC sequence received from the terminal,
+ instead of the Primary Device Attributes response. |v:termresponse|
- |UIEnter|
- |UILeave|
@@ -323,7 +328,6 @@ Highlight groups:
- |hl-MsgSeparator| highlights separator for scrolled messages
- |hl-Substitute|
- |hl-TermCursor|
-- |hl-TermCursorNC|
- |hl-WinSeparator| highlights window separators
- |hl-Whitespace| highlights 'listchars' whitespace
- |hl-WinBar| highlights 'winbar'
@@ -343,11 +347,14 @@ Options:
- `:set {option}<` removes local value for all |global-local| options.
- `:setlocal {option}<` copies global value to local value for all options.
+- 'ambiwidth' cannot be set to empty.
- 'autoread' works in the terminal (if it supports "focus" events)
+- 'background' cannot be set to empty.
- 'cpoptions' flags: |cpo-_|
-- 'diffopt' "linematch" feature
+- 'eadirection' cannot be set to empty.
- 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The
user is prompted whether to trust the file.
+- 'fileformat' cannot be set to empty.
- 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown",
"vertleft", "vertright", "verthoriz"
- 'foldcolumn' supports up to 9 dynamic/fixed columns
@@ -359,14 +366,17 @@ Options:
- "clean" removes unloaded buffers from the jumplist.
- the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|.
- 'laststatus' global statusline support
+- 'mousemodel' cannot be set to empty.
- 'mousescroll' amount to scroll by when scrolling with a mouse
- 'pumblend' pseudo-transparent popupmenu
- 'scrollback'
- 'shortmess'
- "F" flag does not affect output from autocommands.
- "q" flag fully hides macro recording message.
-- 'signcolumn' supports up to 9 dynamic/fixed columns
+- 'showcmdloc' cannot be set to empty.
+- 'signcolumn' can show multiple signs (dynamic or fixed columns)
- 'statuscolumn' full control of columns using 'statusline' format
+- 'splitkeep' cannot be set to empty.
- 'tabline' middle-click on tabpage label closes tabpage,
and %@Func@foo%X can call any function on mouse-click
- 'termpastefilter'
@@ -374,6 +384,10 @@ Options:
- 'winblend' pseudo-transparency in floating windows |api-floatwin|
- 'winhighlight' window-local highlights
+Performance:
+- Signs are implemented using Nvim's internal "marktree" (btree) structure.
+- Folds are not updated during insert-mode.
+
Providers:
- If a Python interpreter is available on your `$PATH`, |:python| and
|:python3| are always available. See |provider-python|.
@@ -391,6 +405,7 @@ Shell:
- |system()| does not support writing/reading "backgrounded" commands. |E5677|
Signs:
+- 'signcolumn' can show multiple signs.
- Signs are removed if the associated line is deleted.
- Signs placed twice with the same identifier in the same group are moved.
@@ -400,6 +415,8 @@ Startup:
- |-es| and |-Es| have improved behavior:
- Quits automatically, don't need "-c qa!".
- Skips swap-file dialog.
+ - Optimized for non-interactive scripts: disables swapfile, shada.
+- |-l| Executes Lua scripts non-interactively.
- |-s| reads Normal commands from stdin if the script name is "-".
- Reading text (instead of commands) from stdin |--|:
- works by default: "-" file is optional
@@ -412,9 +429,12 @@ TUI:
<
*'term'* *E529* *E530* *E531*
- 'term' reflects the terminal type derived from |$TERM| and other environment
- checks. For debugging only; not reliable during startup. >vim
- :echo &term
-- "builtin_x" means one of the |builtin-terms| was chosen, because the expected
+ checks. Use `:echo &term` to get its value. For debugging only; not
+ reliable during startup.
+ - Note: If you want to detect when Nvim is running in a terminal, use
+ `has('gui_running')` |has()| or see |nvim_list_uis()| for an example of
+ how to inspect the UI channel.
+- "builtin_x" means one of the |builtin-terms| was chosen, because the expected
terminfo file was not found on the system.
- Nvim will use 256-colour capability on Linux virtual terminals. Vim uses
only 8 colours plus bright foreground on Linux VTs.
@@ -445,6 +465,7 @@ Upstreamed features *nvim-upstreamed*
These Nvim features were later integrated into Vim.
+- 'diffopt' "linematch" feature
- 'fillchars' flags: "eob"
- 'jumpoptions' "stack" behavior
- 'wildoptions' flags: "pum" enables popupmenu for wildmode completion
@@ -583,9 +604,6 @@ Mappings:
Motion:
- The |jumplist| avoids useless/phantom jumps.
-Performance:
-- Folds are not updated during insert-mode.
-
Syntax highlighting:
- syncolor.vim has been removed. Nvim now sets up default highlighting groups
automatically for both light and dark backgrounds, regardless of whether or
@@ -610,11 +628,10 @@ Working directory (Vim implemented some of these after Nvim):
- `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global
working directory. Use `getcwd(-1, -1)` to get the global working directory.
-Autocommands:
-- Fixed inconsistent behavior in execution of nested autocommands:
- https://github.com/neovim/neovim/issues/23368
-- |TermResponse| is fired for any OSC sequence received from the terminal,
- instead of the Primary Device Attributes response. |v:termresponse|
+Options:
+- 'titlestring' uses printf-style '%' items (see: 'statusline') to implement
+ the default behaviour. The implementation is equivalent to setting
+ 'titlestring' to `%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim`.
==============================================================================
Missing features *nvim-missing*
@@ -663,7 +680,6 @@ Commands:
- :promptrepl
- :scriptversion (always version 1)
- :shell
-- :sleep! (does not hide the cursor; same as :sleep)
- :smile
- :tearoff
- :cstag
@@ -683,7 +699,7 @@ Cscope:
https://github.com/dhananjaylatkar/cscope_maps.nvim
Eval:
-- Vim9script
+- *Vim9script* (the Vim 9+ flavor of Vimscript) is not supported.
- *cscope_connection()*
- *err_teapot()*
- *js_encode()*
diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt
index 0c349c4e57..0ebb54e38a 100644
--- a/runtime/doc/vvars.txt
+++ b/runtime/doc/vvars.txt
@@ -6,7 +6,8 @@
Predefined variables *vvars*
-Some variables can be set by the user, but the type cannot be changed.
+Most variables are read-only, when a variable can be set by the user, it will
+be mentioned at the variable description below. The type cannot be changed.
Type |gO| to see the table of contents.
@@ -189,11 +190,14 @@ v:event
changing window (or tab) on |DirChanged|.
status Job status or exit code, -1 means "unknown". |TermClose|
reason Reason for completion being done. |CompleteDone|
+ complete_word The word that was selected, empty if abandoned complete.
+ complete_type See |complete_info_mode|
*v:exception* *exception-variable*
v:exception
The value of the exception most recently caught and not
- finished. See also |v:throwpoint| and |throw-variables|.
+ finished. See also |v:stacktrace|, |v:throwpoint|, and
+ |throw-variables|.
Example: >vim
try
throw "oops"
@@ -584,6 +588,13 @@ v:shell_error
endif
<
+ *v:stacktrace* *stacktrace-variable*
+v:stacktrace
+ The stack trace of the exception most recently caught and
+ not finished. Refer to |getstacktrace()| for the structure of
+ stack trace. See also |v:exception|, |v:throwpoint|, and
+ |throw-variables|.
+
*v:statusmsg* *statusmsg-variable*
v:statusmsg
Last given status message.
@@ -677,7 +688,7 @@ v:this_session
v:throwpoint
The point where the exception most recently caught and not
finished was thrown. Not set when commands are typed. See
- also |v:exception| and |throw-variables|.
+ also |v:exception|, |v:stacktrace|, and |throw-variables|.
Example: >vim
try
throw "oops"
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 24aa7b1b11..6dd90f7e49 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -941,8 +941,8 @@ set in the preview window to be able to recognize it. The 'winfixheight'
option is set to have it keep the same height when opening/closing other
windows.
- *:pta* *:ptag*
-:pta[g][!] [tagname]
+ *:pt* *:ptag*
+:pt[ag][!] [tagname]
Does ":tag[!] [tagname]" and shows the found tag in a
"Preview" window without changing the current buffer or cursor
position. If a "Preview" window already exists, it is re-used
@@ -978,6 +978,14 @@ CTRL-W g } *CTRL-W_g}*
it. Make the new Preview window (if required) N high. If N is
not given, 'previewheight' is used.
+ *:pb* *:pbuffer*
+:[N]pb[uffer][!] [+cmd] [N]
+ Edit buffer [N] from the buffer list in the preview window.
+ If [N] is not given, the current buffer remains being edited.
+ See |:buffer-!| for [!]. This will also edit a buffer that is
+ not in the buffer list, without setting the 'buflisted' flag.
+ Also see |+cmd|.
+
*:ped* *:pedit*
:ped[it][!] [++opt] [+cmd] {file}
Edit {file} in the preview window. The preview window is
@@ -985,6 +993,8 @@ CTRL-W g } *CTRL-W_g}*
position isn't changed. Useful example: >
:pedit +/fputc /usr/include/stdio.h
<
+ Also see |++opt| and |+cmd|.
+
*:ps* *:psearch*
:[range]ps[earch][!] [count] [/]pattern[/]
Works like |:ijump| but shows the found match in the preview
diff --git a/runtime/filetype.lua b/runtime/filetype.lua
index 797033da06..730991a00c 100644
--- a/runtime/filetype.lua
+++ b/runtime/filetype.lua
@@ -13,8 +13,8 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, {
end
local ft, on_detect = vim.filetype.match({
-- The unexpanded file name is needed here. #27914
- -- Neither args.file nor args.match are guaranteed to be unexpanded.
- filename = vim.fn.bufname(args.buf),
+ -- However, bufname() can't be used, as it doesn't work with :doautocmd. #31306
+ filename = args.file,
buf = args.buf,
})
if not ft then
diff --git a/runtime/ftplugin/c.vim b/runtime/ftplugin/c.vim
index 8b2b784eb4..3b05ce182a 100644
--- a/runtime/ftplugin/c.vim
+++ b/runtime/ftplugin/c.vim
@@ -39,7 +39,7 @@ endif
" When the matchit plugin is loaded, this makes the % command skip parens and
" braces in comments properly.
if !exists("b:match_words")
- let b:match_words = '^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>'
+ let b:match_words = '^\s*#\s*if\%(\|def\|ndef\)\>:^\s*#\s*elif\%(\|def\|ndef\)\>:^\s*#\s*else\>:^\s*#\s*endif\>'
let b:match_skip = 's:comment\|string\|character\|special'
let b:undo_ftplugin ..= " | unlet! b:match_skip b:match_words"
endif
diff --git a/runtime/ftplugin/c3.vim b/runtime/ftplugin/c3.vim
new file mode 100644
index 0000000000..6db665a03a
--- /dev/null
+++ b/runtime/ftplugin/c3.vim
@@ -0,0 +1,14 @@
+" Vim filetype plugin
+" Language: C3
+" Maintainer: Turiiya <34311583+ttytm@users.noreply.github.com>
+" Last Change: 2024 Nov 24
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setl comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
+setl commentstring=//\ %s
+
+let b:undo_ftplugin = 'setl com< cms<'
diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim
index 00f24a2912..cc53d723c2 100644
--- a/runtime/ftplugin/checkhealth.vim
+++ b/runtime/ftplugin/checkhealth.vim
@@ -1,6 +1,5 @@
" Vim filetype plugin
" Language: Nvim :checkhealth buffer
-" Last Change: 2022 Nov 10
if exists("b:did_ftplugin")
finish
diff --git a/runtime/ftplugin/dockerfile.vim b/runtime/ftplugin/dockerfile.vim
index 2e3c447b59..e45bf4c1d8 100644
--- a/runtime/ftplugin/dockerfile.vim
+++ b/runtime/ftplugin/dockerfile.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: Dockerfile
" Maintainer: Honza Pokorny <http://honza.ca>
-" Last Change: 2014 Aug 29
+" Last Change: 2024 Dec 20
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -11,6 +11,6 @@ endif
" Don't load another plugin for this buffer
let b:did_ftplugin = 1
-let b:undo_ftplugin = "setl commentstring<"
-
setlocal commentstring=#\ %s
+
+let b:undo_ftplugin = "setl commentstring<"
diff --git a/runtime/ftplugin/editorconfig.vim b/runtime/ftplugin/editorconfig.vim
index 6d437351eb..1693a95c0b 100644
--- a/runtime/ftplugin/editorconfig.vim
+++ b/runtime/ftplugin/editorconfig.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: EditorConfig
" Maintainer: Riley Bruins <ribru17@gmail.com>
-" Last Change: 2024 Jul 06
+" Last Change: 2025 Jan 10
if exists('b:did_ftplugin')
finish
@@ -10,4 +10,6 @@ let b:did_ftplugin = 1
setl comments=:#,:; commentstring=#\ %s
-let b:undo_ftplugin = 'setl com< cms<'
+setl omnifunc=syntaxcomplete#Complete
+
+let b:undo_ftplugin = 'setl com< cms< ofu<'
diff --git a/runtime/ftplugin/gel.vim b/runtime/ftplugin/gel.vim
new file mode 100644
index 0000000000..b1f4def2b8
--- /dev/null
+++ b/runtime/ftplugin/gel.vim
@@ -0,0 +1,13 @@
+" Vim filetype plugin file
+" Language: TI Code Composer Studio General Extension Language
+" Document: https://downloads.ti.com/ccs/esd/documents/users_guide/ccs_debug-gel.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 25
+
+if exists("b:did_ftplugin") | finish | endif
+let b:did_ftplugin = 1
+
+setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
+setlocal commentstring=/*\ %s\ */
+
+let b:undo_ftplugin = "setl commentstring< comments<"
diff --git a/runtime/ftplugin/graphql.vim b/runtime/ftplugin/graphql.vim
index 56f6e36e20..1717ebf2cc 100644
--- a/runtime/ftplugin/graphql.vim
+++ b/runtime/ftplugin/graphql.vim
@@ -1,13 +1,22 @@
" Vim filetype plugin
" Language: graphql
-" Maintainer: Riley Bruins <ribru17@gmail.com>
-" Last Change: 2024 May 18
+" Maintainer: Jon Parise <jon@indelible.org>
+" Filenames: *.graphql *.graphqls *.gql
+" URL: https://github.com/jparise/vim-graphql
+" License: MIT <https://opensource.org/license/mit>
+" Last Change: 2024 Dec 21
if exists('b:did_ftplugin')
finish
endif
let b:did_ftplugin = 1
-setl comments=:# commentstring=#\ %s
+setlocal comments=:#
+setlocal commentstring=#\ %s
+setlocal formatoptions-=t
+setlocal iskeyword+=$,@-@
+setlocal softtabstop=2
+setlocal shiftwidth=2
+setlocal expandtab
-let b:undo_ftplugin = 'setl com< cms<'
+let b:undo_ftplugin = 'setlocal com< cms< fo< isk< sts< sw< et<'
diff --git a/runtime/ftplugin/help.lua b/runtime/ftplugin/help.lua
index 8d991be0e4..a6169a1d9d 100644
--- a/runtime/ftplugin/help.lua
+++ b/runtime/ftplugin/help.lua
@@ -31,5 +31,56 @@ vim.keymap.set('n', 'gO', function()
require('vim.vimhelp').show_toc()
end, { buffer = 0, silent = true })
-vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n exe "nunmap <buffer> gO"'
+-- Add "runnables" for Lua/Vimscript code examples.
+---@type table<integer, { lang: string, code: string }>
+local code_blocks = {}
+local tree = vim.treesitter.get_parser():parse()[1]
+local query = vim.treesitter.query.parse(
+ 'vimdoc',
+ [[
+ (codeblock
+ (language) @_lang
+ .
+ (code) @code
+ (#any-of? @_lang "lua" "vim")
+ (#set! @code lang @_lang))
+]]
+)
+local run_message_ns = vim.api.nvim_create_namespace('nvim.vimdoc.run_message')
+
+vim.api.nvim_buf_clear_namespace(0, run_message_ns, 0, -1)
+for _, match, metadata in query:iter_matches(tree:root(), 0, 0, -1) do
+ for id, nodes in pairs(match) do
+ local name = query.captures[id]
+ local node = nodes[1]
+ local start, _, end_ = node:parent():range()
+
+ if name == 'code' then
+ vim.api.nvim_buf_set_extmark(0, run_message_ns, start, 0, {
+ virt_text = { { 'Run with `g==`', 'LspCodeLens' } },
+ })
+ local code = vim.treesitter.get_node_text(node, 0)
+ local lang_node = match[metadata[id].lang][1] --[[@as TSNode]]
+ local lang = vim.treesitter.get_node_text(lang_node, 0)
+ for i = start + 1, end_ do
+ code_blocks[i] = { lang = lang, code = code }
+ end
+ end
+ end
+end
+
+vim.keymap.set('n', 'g==', function()
+ local pos = vim.api.nvim_win_get_cursor(0)[1]
+ local code_block = code_blocks[pos]
+ if not code_block then
+ vim.print('No code block found')
+ elseif code_block.lang == 'lua' then
+ vim.cmd.lua(code_block.code)
+ elseif code_block.lang == 'vim' then
+ vim.cmd(code_block.code)
+ end
+end, { buffer = true })
+
+vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '')
+ .. '\n exe "nunmap <buffer> gO" | exe "nunmap <buffer> g=="'
vim.b.undo_ftplugin = vim.b.undo_ftplugin .. ' | call v:lua.vim.treesitter.stop()'
diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim
index 55b358374f..cfd25bce6c 100644
--- a/runtime/ftplugin/java.vim
+++ b/runtime/ftplugin/java.vim
@@ -3,7 +3,7 @@
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
" Former Maintainer: Dan Sharp
" Repository: https://github.com/zzzyxwvut/java-vim.git
-" Last Change: 2024 Sep 26
+" Last Change: 2024 Dec 25
" 2024 Jan 14 by Vim Project (browsefilter)
" 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring')
@@ -90,10 +90,269 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
endif
endif
+"""" Support pre- and post-compiler actions for SpotBugs.
+if (!empty(get(g:, 'spotbugs_properties', {})) ||
+ \ !empty(get(b:, 'spotbugs_properties', {}))) &&
+ \ filereadable($VIMRUNTIME . '/compiler/spotbugs.vim')
+
+ function! s:SpotBugsGetProperty(name, default) abort
+ return get(
+ \ {s:spotbugs_properties_scope}spotbugs_properties,
+ \ a:name,
+ \ a:default)
+ endfunction
+
+ function! s:SpotBugsHasProperty(name) abort
+ return has_key(
+ \ {s:spotbugs_properties_scope}spotbugs_properties,
+ \ a:name)
+ endfunction
+
+ function! s:SpotBugsGetProperties() abort
+ return {s:spotbugs_properties_scope}spotbugs_properties
+ endfunction
+
+ " Work around ":bar"s and ":autocmd"s.
+ function! JavaFileTypeExecuteActionOnce(cleanup_cmd, action_cmd) abort
+ try
+ execute a:cleanup_cmd
+ finally
+ execute a:action_cmd
+ endtry
+ endfunction
+
+ if exists("b:spotbugs_properties")
+ let s:spotbugs_properties_scope = 'b:'
+
+ " Merge global entries, if any, in buffer-local entries, favouring
+ " defined buffer-local ones.
+ call extend(
+ \ b:spotbugs_properties,
+ \ get(g:, 'spotbugs_properties', {}),
+ \ 'keep')
+ elseif exists("g:spotbugs_properties")
+ let s:spotbugs_properties_scope = 'g:'
+ endif
+
+ let s:commands = {}
+
+ for s:name in ['DefaultPreCompilerCommand',
+ \ 'DefaultPreCompilerTestCommand',
+ \ 'DefaultPostCompilerCommand']
+ if s:SpotBugsHasProperty(s:name)
+ let s:commands[s:name] = remove(
+ \ s:SpotBugsGetProperties(),
+ \ s:name)
+ endif
+ endfor
+
+ if s:SpotBugsHasProperty('compiler')
+ " XXX: Postpone loading the script until all state, if any, has been
+ " collected.
+ if !empty(s:commands)
+ let g:spotbugs#state = {
+ \ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'),
+ \ 'commands': copy(s:commands),
+ \ }
+ else
+ let g:spotbugs#state = {
+ \ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'),
+ \ }
+ endif
+
+ " Merge default entries in global (or buffer-local) entries, favouring
+ " defined global (or buffer-local) ones.
+ call extend(
+ \ {s:spotbugs_properties_scope}spotbugs_properties,
+ \ spotbugs#DefaultProperties(),
+ \ 'keep')
+ elseif !empty(s:commands)
+ " XXX: Postpone loading the script until all state, if any, has been
+ " collected.
+ let g:spotbugs#state = {'commands': copy(s:commands)}
+ endif
+
+ unlet s:commands s:name
+ let s:request = 0
+
+ if s:SpotBugsHasProperty('PostCompilerAction')
+ let s:request += 4
+ endif
+
+ if s:SpotBugsHasProperty('PreCompilerTestAction')
+ let s:dispatcher = printf('call call(%s, [])',
+ \ string(s:SpotBugsGetProperties().PreCompilerTestAction))
+ let s:request += 2
+ endif
+
+ if s:SpotBugsHasProperty('PreCompilerAction')
+ let s:dispatcher = printf('call call(%s, [])',
+ \ string(s:SpotBugsGetProperties().PreCompilerAction))
+ let s:request += 1
+ endif
+
+ " Adapt the tests for "s:FindClassFiles()" from "compiler/spotbugs.vim".
+ if (s:request == 3 || s:request == 7) &&
+ \ (!empty(s:SpotBugsGetProperty('sourceDirPath', [])) &&
+ \ !empty(s:SpotBugsGetProperty('classDirPath', [])) &&
+ \ !empty(s:SpotBugsGetProperty('testSourceDirPath', [])) &&
+ \ !empty(s:SpotBugsGetProperty('testClassDirPath', [])))
+ function! s:DispatchAction(paths_action_pairs) abort
+ let name = expand('%:p')
+
+ for [paths, Action] in a:paths_action_pairs
+ for path in paths
+ if name =~# (path . '.\{-}\.java\=$')
+ call Action()
+ return
+ endif
+ endfor
+ endfor
+ endfunction
+
+ let s:dir_cnt = min([
+ \ len(s:SpotBugsGetProperties().sourceDirPath),
+ \ len(s:SpotBugsGetProperties().classDirPath)])
+ let s:test_dir_cnt = min([
+ \ len(s:SpotBugsGetProperties().testSourceDirPath),
+ \ len(s:SpotBugsGetProperties().testClassDirPath)])
+
+ " Do not break up path pairs with filtering!
+ let s:dispatcher = printf('call s:DispatchAction(%s)',
+ \ string([[s:SpotBugsGetProperties().sourceDirPath[0 : s:dir_cnt - 1],
+ \ s:SpotBugsGetProperties().PreCompilerAction],
+ \ [s:SpotBugsGetProperties().testSourceDirPath[0 : s:test_dir_cnt - 1],
+ \ s:SpotBugsGetProperties().PreCompilerTestAction]]))
+ unlet s:test_dir_cnt s:dir_cnt
+ endif
+
+ if exists("s:dispatcher")
+ function! s:ExecuteActions(pre_action, post_action) abort
+ try
+ execute a:pre_action
+ catch /\<E42:/
+ execute a:post_action
+ endtry
+ endfunction
+ endif
+
+ if s:request
+ if exists("b:spotbugs_syntax_once") || empty(join(getline(1, 8), ''))
+ let s:actions = [{'event': 'User'}]
+ else
+ " XXX: Handle multiple FileType events when vimrc contains more
+ " than one filetype setting for the language, e.g.:
+ " :filetype plugin indent on
+ " :autocmd BufRead,BufNewFile *.java setlocal filetype=java ...
+ " XXX: DO NOT ADD b:spotbugs_syntax_once TO b:undo_ftplugin !
+ let b:spotbugs_syntax_once = 1
+ let s:actions = [{
+ \ 'event': 'Syntax',
+ \ 'once': 1,
+ \ }, {
+ \ 'event': 'User',
+ \ }]
+ endif
+
+ for s:idx in range(len(s:actions))
+ if s:request == 7 || s:request == 6 || s:request == 5
+ let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)',
+ \ string(s:dispatcher),
+ \ string(printf('compiler spotbugs | call call(%s, [])',
+ \ string(s:SpotBugsGetProperties().PostCompilerAction))))
+ elseif s:request == 4
+ let s:actions[s:idx].cmd = printf(
+ \ 'compiler spotbugs | call call(%s, [])',
+ \ string(s:SpotBugsGetProperties().PostCompilerAction))
+ elseif s:request == 3 || s:request == 2 || s:request == 1
+ let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)',
+ \ string(s:dispatcher),
+ \ string('compiler spotbugs'))
+ else
+ let s:actions[s:idx].cmd = ''
+ endif
+ endfor
+
+ if !exists("#java_spotbugs")
+ augroup java_spotbugs
+ augroup END
+ endif
+
+ " The events are defined in s:actions.
+ silent! autocmd! java_spotbugs User <buffer>
+ silent! autocmd! java_spotbugs Syntax <buffer>
+
+ for s:action in s:actions
+ if has_key(s:action, 'once')
+ execute printf('autocmd java_spotbugs %s <buffer> ' .
+ \ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
+ \ s:action.event,
+ \ string(printf('autocmd! java_spotbugs %s <buffer>',
+ \ s:action.event)),
+ \ string(s:action.cmd))
+ else
+ execute printf('autocmd java_spotbugs %s <buffer> %s',
+ \ s:action.event,
+ \ s:action.cmd)
+ endif
+ endfor
+
+ if s:SpotBugsHasProperty('PostCompilerActionExecutor') &&
+ \ (s:request == 7 || s:request == 6 ||
+ \ s:request == 5 || s:request == 4)
+ let s:augroup = s:SpotBugsGetProperty(
+ \ 'augroupForPostCompilerAction',
+ \ 'java_spotbugs_post')
+ let s:augroup = !empty(s:augroup) ? s:augroup : 'java_spotbugs_post'
+
+ for s:candidate in ['java_spotbugs_post', s:augroup]
+ if !exists("#" . s:candidate)
+ execute printf('augroup %s | augroup END', s:candidate)
+ endif
+ endfor
+
+ silent! autocmd! java_spotbugs_post User <buffer>
+
+ " Define a User ":autocmd" to define a once-only ShellCmdPost
+ " ":autocmd" that will invoke "PostCompilerActionExecutor" and let
+ " it decide whether to proceed with ":compiler spotbugs" etc.; and
+ " seek explicit synchronisation with ":doautocmd ShellCmdPost" by
+ " omitting "nested" for "java_spotbugs_post" and "java_spotbugs".
+ execute printf('autocmd java_spotbugs_post User <buffer> ' .
+ \ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
+ \ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)),
+ \ string(printf('autocmd %s ShellCmdPost <buffer> ' .
+ \ 'call JavaFileTypeExecuteActionOnce(%s, %s)',
+ \ s:augroup,
+ \ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)),
+ \ string(printf('call call(%s, [%s])',
+ \ string(s:SpotBugsGetProperties().PostCompilerActionExecutor),
+ \ string(printf('compiler spotbugs | call call(%s, [])',
+ \ string(s:SpotBugsGetProperties().PostCompilerAction))))))))
+ endif
+
+ unlet! s:candidate s:augroup s:action s:actions s:idx s:dispatcher
+ endif
+
+ delfunction s:SpotBugsGetProperties
+ delfunction s:SpotBugsHasProperty
+ delfunction s:SpotBugsGetProperty
+ unlet! s:request s:spotbugs_properties_scope
+endif
+
+function! JavaFileTypeCleanUp() abort
+ setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr<
+ unlet! b:browsefilter
+
+ " The concatenated ":autocmd" removals may be misparsed as an ":autocmd".
+ " A _once-only_ ShellCmdPost ":autocmd" is always a call-site definition.
+ silent! autocmd! java_spotbugs User <buffer>
+ silent! autocmd! java_spotbugs Syntax <buffer>
+ silent! autocmd! java_spotbugs_post User <buffer>
+endfunction
+
" Undo the stuff we changed.
-let b:undo_ftplugin = "setlocal suffixes< suffixesadd<" .
- \ " formatoptions< comments< commentstring< path< includeexpr<" .
- \ " | unlet! b:browsefilter"
+let b:undo_ftplugin = 'call JavaFileTypeCleanUp() | delfunction JavaFileTypeCleanUp'
" See ":help vim9-mix".
if !has("vim9script")
@@ -114,6 +373,21 @@ if exists("s:zip_func_upgradable")
setlocal suffixesadd<
endif
+if exists("*s:DispatchAction")
+ def! s:DispatchAction(paths_action_pairs: list<list<any>>)
+ const name: string = expand('%:p')
+
+ for [paths: list<string>, Action: func: any] in paths_action_pairs
+ for path in paths
+ if name =~# (path .. '.\{-}\.java\=$')
+ Action()
+ return
+ endif
+ endfor
+ endfor
+ enddef
+endif
+
" Restore the saved compatibility options.
let &cpo = s:save_cpo
unlet s:save_cpo
diff --git a/runtime/ftplugin/jj.vim b/runtime/ftplugin/jjdescription.vim
index cc5d700a30..cc5d700a30 100644
--- a/runtime/ftplugin/jj.vim
+++ b/runtime/ftplugin/jjdescription.vim
diff --git a/runtime/ftplugin/just.vim b/runtime/ftplugin/just.vim
new file mode 100644
index 0000000000..6f2acddf96
--- /dev/null
+++ b/runtime/ftplugin/just.vim
@@ -0,0 +1,17 @@
+" Vim ftplugin file
+" Language: Justfile
+" Maintainer: Peter Benjamin <@pbnj>
+" Last Change: 2025 Jan 19
+" Credits: The original author, Noah Bogart <https://github.com/NoahTheDuke/vim-just/>
+
+" Only do this when not done yet for this buffer
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal iskeyword+=-
+setlocal comments=n:#
+setlocal commentstring=#\ %s
+
+let b:undo_ftplugin = "setlocal iskeyword< comments< commentstring<"
diff --git a/runtime/ftplugin/karel.vim b/runtime/ftplugin/karel.vim
new file mode 100644
index 0000000000..8ccc2b32ce
--- /dev/null
+++ b/runtime/ftplugin/karel.vim
@@ -0,0 +1,16 @@
+" Vim filetype plugin file
+" Language: KAREL
+" Last Change: 2024-11-18
+" Maintainer: Kirill Morozov <kirill@robotix.pro>
+" Credits: Patrick Meiser-Knosowski for the initial implementation.
+
+if exists("b:did_ftplugin")
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal comments=:--
+setlocal commentstring=--\ %s
+setlocal suffixesadd+=.kl,.KL
+
+let b:undo_ftplugin = "setlocal com< cms< sua<"
diff --git a/runtime/ftplugin/kconfig.vim b/runtime/ftplugin/kconfig.vim
index 767490701b..1c2857ec50 100644
--- a/runtime/ftplugin/kconfig.vim
+++ b/runtime/ftplugin/kconfig.vim
@@ -2,7 +2,7 @@
" Vim syntax file
" Maintainer: Christian Brabandt <cb@256bit.org>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2024-04-12
+" Latest Revision: 2025 Jan 20
" License: Vim (see :h license)
" Repository: https://github.com/chrisbra/vim-kconfig
@@ -19,4 +19,5 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql
" For matchit.vim
if exists("loaded_matchit")
let b:match_words = '^\<menu\>:\<endmenu\>,^\<if\>:\<endif\>,^\<choice\>:\<endchoice\>'
+ let b:undo_ftplugin .= "| unlet! b:match_words"
endif
diff --git a/runtime/ftplugin/lnk.vim b/runtime/ftplugin/lnk.vim
new file mode 100644
index 0000000000..8b280d9836
--- /dev/null
+++ b/runtime/ftplugin/lnk.vim
@@ -0,0 +1,14 @@
+" Vim filetype plugin file
+" Language: TI linker command file
+" Document: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_Linker-Command-File-Primer.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 31
+
+if exists("b:did_ftplugin") | finish | endif
+let b:did_ftplugin = 1
+
+setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
+setlocal commentstring=/*\ %s\ */
+setlocal iskeyword+=.
+
+let b:undo_ftplugin = "setl commentstring< comments< iskeyword<"
diff --git a/runtime/ftplugin/lnkmap.vim b/runtime/ftplugin/lnkmap.vim
new file mode 100644
index 0000000000..46fb070e71
--- /dev/null
+++ b/runtime/ftplugin/lnkmap.vim
@@ -0,0 +1,16 @@
+" Vim filetype plugin file
+" Language: TI Linker map
+" Document: https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 25
+
+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 iskeyword<"
+
+setl iskeyword+=.
diff --git a/runtime/ftplugin/opencl.vim b/runtime/ftplugin/opencl.vim
new file mode 100644
index 0000000000..e8570fbe95
--- /dev/null
+++ b/runtime/ftplugin/opencl.vim
@@ -0,0 +1,12 @@
+" Vim filetype plugin file
+" Language: OpenCL
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Nov 19
+
+if exists("b:did_ftplugin") | finish | endif
+let b:did_ftplugin = 1
+
+setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
+setlocal commentstring=/*\ %s\ */ define& include&
+
+let b:undo_ftplugin = "setl commentstring< comments<"
diff --git a/runtime/ftplugin/proto.vim b/runtime/ftplugin/proto.vim
new file mode 100644
index 0000000000..585f4461d3
--- /dev/null
+++ b/runtime/ftplugin/proto.vim
@@ -0,0 +1,18 @@
+" Vim filetype plugin
+" Language: Protobuf
+" Maintainer: David Pedersen <limero@me.com>
+" Last Change: 2024 Dec 09
+
+if exists('b:did_ftplugin')
+ finish
+endif
+let b:did_ftplugin = 1
+
+setlocal formatoptions-=t formatoptions+=croql
+
+setlocal comments=s1:/*,mb:*,ex:*/,://
+setlocal commentstring=//\ %s
+
+let b:undo_ftplugin = "setlocal formatoptions< comments< commentstring<"
+
+" vim: sw=2 sts=2 et
diff --git a/runtime/ftplugin/ptx.vim b/runtime/ftplugin/ptx.vim
new file mode 100644
index 0000000000..12b127c8fc
--- /dev/null
+++ b/runtime/ftplugin/ptx.vim
@@ -0,0 +1,16 @@
+" Vim filetype plugin file
+" Language: Nvidia PTX (Parellel Thread Execution)
+" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com>
+" Last Change: 2024-12-05
+
+if exists("b:did_ftplugin")
+ finish
+endif
+
+let b:did_ftplugin = 1
+
+" Comments in PTX follow C/C++ syntax
+" See: https://docs.nvidia.com/cuda/parallel-thread-execution/#syntax
+setlocal commentstring=//\ %s
+
+let b:undo_ftplugin = 'setl commentstring<'
diff --git a/runtime/ftplugin/python.vim b/runtime/ftplugin/python.vim
index c000296726..6f20468896 100644
--- a/runtime/ftplugin/python.vim
+++ b/runtime/ftplugin/python.vim
@@ -3,8 +3,9 @@
" Maintainer: Tom Picton <tom@tompicton.com>
" Previous Maintainer: James Sully <sullyj3@gmail.com>
" Previous Maintainer: Johannes Zellner <johannes@zellner.org>
-" Last Change: 2024/05/13
-" https://github.com/tpict/vim-ftplugin-python
+" Repository: https://github.com/tpict/vim-ftplugin-python
+" Last Change: 2024/05/13
+" 2024 Nov 30 use pytest compiler (#16130)
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
@@ -134,6 +135,11 @@ elseif executable('python')
setlocal keywordprg=python\ -m\ pydoc
endif
+if expand('%:t') =~# '\v^test_.*\.py$|_test\.py$' && executable('pytest')
+ compiler pytest
+ let &l:makeprg .= ' %:S'
+endif
+
" Script for filetype switching to undo the local stuff we may have changed
let b:undo_ftplugin = 'setlocal cinkeys<'
\ . '|setlocal comments<'
@@ -148,6 +154,7 @@ let b:undo_ftplugin = 'setlocal cinkeys<'
\ . '|setlocal softtabstop<'
\ . '|setlocal suffixesadd<'
\ . '|setlocal tabstop<'
+ \ . '|setlocal makeprg<'
\ . '|silent! nunmap <buffer> [M'
\ . '|silent! nunmap <buffer> [['
\ . '|silent! nunmap <buffer> []'
diff --git a/runtime/ftplugin/query.lua b/runtime/ftplugin/query.lua
index 32d615c65c..aa5cac2f07 100644
--- a/runtime/ftplugin/query.lua
+++ b/runtime/ftplugin/query.lua
@@ -1,6 +1,5 @@
-- Neovim filetype plugin file
-- Language: Treesitter query
--- Last Change: 2024 Jul 03
if vim.b.did_ftplugin == 1 then
return
@@ -22,7 +21,7 @@ local query_lint_on = vim.g.query_lint_on or { 'BufEnter', 'BufWrite' }
if not vim.b.disable_query_linter and #query_lint_on > 0 then
vim.api.nvim_create_autocmd(query_lint_on, {
- group = vim.api.nvim_create_augroup('querylint', { clear = false }),
+ group = vim.api.nvim_create_augroup('nvim.querylint', { clear = false }),
buffer = buf,
callback = function()
vim.treesitter.query.lint(buf)
diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim
index 4c7695dcc6..54ae73b675 100644
--- a/runtime/ftplugin/sh.vim
+++ b/runtime/ftplugin/sh.vim
@@ -5,6 +5,7 @@
" Contributor: Enno Nagel <ennonagel+vim@gmail.com>
" Eisuke Kawashima
" Last Change: 2024 Sep 19 by Vim Project (compiler shellcheck)
+" 2024 Dec 29 by Vim Project (improve setting shellcheck compiler)
if exists("b:did_ftplugin")
finish
@@ -44,7 +45,11 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
let b:undo_ftplugin ..= " | unlet! b:browsefilter"
endif
-if get(b:, "is_bash", 0)
+let s:is_sh = get(b:, "is_sh", get(g:, "is_sh", 0))
+let s:is_bash = get(b:, "is_bash", get(g:, "is_bash", 0))
+let s:is_kornshell = get(b:, "is_kornshell", get(g:, "is_kornshell", 0))
+
+if s:is_bash
if exists(':terminal') == 2
command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""'
else
@@ -52,14 +57,21 @@ if get(b:, "is_bash", 0)
endif
setlocal keywordprg=:ShKeywordPrg
let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer ShKeywordPrg"
+endif
+if (s:is_sh || s:is_bash || s:is_kornshell) && executable('shellcheck')
if !exists('current_compiler')
compiler shellcheck
+ let b:undo_ftplugin ..= ' | compiler make'
+ endif
+elseif s:is_bash
+ if !exists('current_compiler')
+ compiler bash
+ let b:undo_ftplugin ..= ' | compiler make'
endif
- let b:undo_ftplugin .= ' | compiler make'
endif
let &cpo = s:save_cpo
-unlet s:save_cpo
+unlet s:save_cpo s:is_sh s:is_bash s:is_kornshell
" vim: nowrap sw=2 sts=2 ts=8 noet:
diff --git a/runtime/ftplugin/shaderslang.vim b/runtime/ftplugin/shaderslang.vim
new file mode 100644
index 0000000000..f3d1ab8c1c
--- /dev/null
+++ b/runtime/ftplugin/shaderslang.vim
@@ -0,0 +1,54 @@
+" Vim filetype plugin file
+" Language: Slang
+" Maintainer: Austin Shijo <epestr@proton.me>
+" Last Change: 2025 Jan 05
+
+" 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
+
+" Using line continuation here.
+let s:cpo_save = &cpo
+set cpo-=C
+
+let b:undo_ftplugin = "setl fo< com< cms< inc<"
+
+" 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
+
+" Set comment string (Slang uses C-style comments)
+setlocal commentstring=//\ %s
+
+" Set 'comments' to format dashed lists in comments
+setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,://
+
+" When the matchit plugin is loaded, this makes the % command skip parens and
+" braces in comments properly, and adds support for shader-specific keywords
+if exists("loaded_matchit")
+ " Add common shader control structures
+ let b:match_words = '{\|^\s*\<\(if\|for\|while\|switch\|struct\|class\)\>:}\|^\s*\<break\>,' ..
+ \ '^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>,' ..
+ \ '\[:\]'
+ let b:match_skip = 's:comment\|string\|character\|special'
+ let b:match_ignorecase = 0
+ let b:undo_ftplugin ..= " | unlet! b:match_skip b:match_words b:match_ignorecase"
+endif
+
+" Win32 and GTK can filter files in the browse dialog
+if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
+ let b:browsefilter = "Slang Source Files (*.slang)\t*.slang\n"
+ if has("win32")
+ let b:browsefilter ..= "All Files (*.*)\t*\n"
+ else
+ let b:browsefilter ..= "All Files (*)\t*\n"
+ endif
+ let b:undo_ftplugin ..= " | unlet! b:browsefilter"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/ftplugin/tiasm.vim b/runtime/ftplugin/tiasm.vim
new file mode 100644
index 0000000000..13a3dc64f7
--- /dev/null
+++ b/runtime/ftplugin/tiasm.vim
@@ -0,0 +1,18 @@
+" Vim filetype plugin file
+" Language: TI linear assembly language
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2025 Jan 08
+
+if exists("b:did_ftplugin") | finish | endif
+let b:did_ftplugin = 1
+
+setlocal comments=:;
+setlocal commentstring=;\ %s
+
+let b:undo_ftplugin = "setl commentstring< comments<"
+
+if exists("loaded_matchit")
+ let b:match_words = '^\s\+\.if\>:^\s\+\.elseif:^\s\+\.else\>:^\s\+\.endif\>,^\s\+\.group:^\s\+\.gmember:^\s\+\.endgroup,^\s\+\.loop:^\s\+\.break:^\s\+\.endloop,^\s\+\.macro:^\s\+\.mexit:^\s\+\.endm,^\s\+\.asmfunc:^\s\+\.endasmfunc,^\s\+\.c\?struct:^\s\+\.endstruct,^\s\+\.c\?union:^\s\+\.endunion,^\s\+\.c\?proc:^\s\+\.return:^\s\+\.endproc'
+ let b:match_ignorecase = 1
+ let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words"
+endif
diff --git a/runtime/ftplugin/typst.vim b/runtime/ftplugin/typst.vim
index 3841e427f3..08929bbdbe 100644
--- a/runtime/ftplugin/typst.vim
+++ b/runtime/ftplugin/typst.vim
@@ -1,7 +1,8 @@
" Vim filetype plugin file
" Language: Typst
-" Maintainer: Gregory Anders
-" Last Change: 2024 Oct 21
+" Previous Maintainer: Gregory Anders
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Last Change: 2024 Dec 09
" Based on: https://github.com/kaarmu/typst.vim
if exists('b:did_ftplugin')
@@ -11,10 +12,14 @@ let b:did_ftplugin = 1
setlocal commentstring=//\ %s
setlocal comments=s1:/*,mb:*,ex:*/,://
-setlocal formatoptions+=croq
+setlocal formatoptions+=croqn
+" Numbered Lists
+setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s*
+" Unordered (-), Ordered (+) and definition (/) Lists
+setlocal formatlistpat+=\\\|^\\s*[-+/\]\\s\\+
setlocal suffixesadd=.typ
-let b:undo_ftplugin = 'setl cms< com< fo< sua<'
+let b:undo_ftplugin = 'setl cms< com< fo< flp< sua<'
if get(g:, 'typst_conceal', 0)
setlocal conceallevel=2
diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim
index b5e8e693f6..6ba057fc03 100644
--- a/runtime/ftplugin/vim.vim
+++ b/runtime/ftplugin/vim.vim
@@ -1,9 +1,9 @@
" Vim filetype plugin
" Language: Vim
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2024 Apr 13
-" 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring')
+" Last Change: 2025 Jan 06
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
+" Contributors: Riley Bruins <ribru17@gmail.com> ('commentstring')
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -103,8 +103,8 @@ if exists("loaded_matchit")
\ '\<try\>:\%(\%(^\||\)\s*\)\@<=\<cat\%[ch]\>:\%(\%(^\||\)\s*\)\@<=\<fina\%[lly]\>:\%(\%(^\||\)\s*\)\@<=\<endt\%[ry]\>,' ..
\ '\<aug\%[roup]\s\+\%(END\>\)\@!\S:\<aug\%[roup]\s\+END\>,' ..
\ '\<class\>:\<endclass\>,' ..
- \ '\<inte\%[rface]\>:\<endinterface\>,' ..
- \ '\<enu\%[m]\>:\<endenum\>,'
+ \ '\<interface\>:\<endinterface\>,' ..
+ \ '\<enum\>:\<endenum\>'
" Ignore syntax region commands and settings, any 'en*' would clobber
" if-endif.
diff --git a/runtime/indent/graphql.vim b/runtime/indent/graphql.vim
new file mode 100644
index 0000000000..dc0769b9a8
--- /dev/null
+++ b/runtime/indent/graphql.vim
@@ -0,0 +1,92 @@
+" Vim indent file
+" Language: graphql
+" Maintainer: Jon Parise <jon@indelible.org>
+" Filenames: *.graphql *.graphqls *.gql
+" URL: https://github.com/jparise/vim-graphql
+" License: MIT <https://opensource.org/license/mit>
+" Last Change: 2024 Dec 21
+
+" Set our local options if indentation hasn't already been set up.
+" This generally means we've been detected as the primary filetype.
+if !exists('b:did_indent')
+ setlocal autoindent
+ setlocal nocindent
+ setlocal nolisp
+ setlocal nosmartindent
+
+ setlocal indentexpr=GetGraphQLIndent()
+ setlocal indentkeys=0{,0},0),0[,0],0#,!^F,o,O
+
+ let b:did_indent = 1
+endif
+
+" If our indentation function already exists, we have nothing more to do.
+if exists('*GetGraphQLIndent')
+ finish
+endif
+
+let s:cpo_save = &cpoptions
+set cpoptions&vim
+
+" searchpair() skip expression that matches in comments and strings.
+let s:pair_skip_expr =
+ \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "comment\\|string"'
+
+" Check if the character at lnum:col is inside a string.
+function s:InString(lnum, col)
+ return synIDattr(synID(a:lnum, a:col, 1), 'name') ==# 'graphqlString'
+endfunction
+
+function GetGraphQLIndent()
+ " If this is the first non-blank line, we have nothing more to do because
+ " all of our indentation rules are based on matching against earlier lines.
+ let l:prevlnum = prevnonblank(v:lnum - 1)
+ if l:prevlnum == 0
+ return 0
+ endif
+
+ " If the previous line isn't GraphQL, assume we're part of a template
+ " string and indent this new line within it.
+ let l:stack = map(synstack(l:prevlnum, 1), "synIDattr(v:val, 'name')")
+ if get(l:stack, -1) !~# '^graphql'
+ return indent(l:prevlnum) + shiftwidth()
+ endif
+
+ let l:line = getline(v:lnum)
+
+ " If this line contains just a closing bracket, find its matching opening
+ " bracket and indent the closing bracket to match.
+ let l:col = matchend(l:line, '^\s*[]})]')
+ if l:col > 0 && !s:InString(v:lnum, l:col)
+ call cursor(v:lnum, l:col)
+
+ let l:bracket = l:line[l:col - 1]
+ if l:bracket ==# '}'
+ let l:matched = searchpair('{', '', '}', 'bW', s:pair_skip_expr)
+ elseif l:bracket ==# ']'
+ let l:matched = searchpair('\[', '', '\]', 'bW', s:pair_skip_expr)
+ elseif l:bracket ==# ')'
+ let l:matched = searchpair('(', '', ')', 'bW', s:pair_skip_expr)
+ else
+ let l:matched = -1
+ endif
+
+ return l:matched > 0 ? indent(l:matched) : virtcol('.') - 1
+ endif
+
+ " If we're inside of a multiline string, continue with the same indentation.
+ if s:InString(v:lnum, matchend(l:line, '^\s*') + 1)
+ return indent(v:lnum)
+ endif
+
+ " If the previous line ended with an opening bracket, indent this line.
+ if getline(l:prevlnum) =~# '\%(#.*\)\@<![[{(]\s*$'
+ return indent(l:prevlnum) + shiftwidth()
+ endif
+
+ " Default to the existing indentation level.
+ return indent(l:prevlnum)
+endfunction
+
+let &cpoptions = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/indent/just.vim b/runtime/indent/just.vim
new file mode 100644
index 0000000000..d7f82b118f
--- /dev/null
+++ b/runtime/indent/just.vim
@@ -0,0 +1,51 @@
+" Vim indent file
+" Language: Justfile
+" Maintainer: Peter Benjamin <@pbnj>
+" Last Change: 2025 Jan 19
+" Credits: The original author, Noah Bogart <https://github.com/NoahTheDuke/vim-just/>
+
+" Only load this indent file when no other was loaded yet.
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=GetJustfileIndent()
+setlocal indentkeys=0},0),!^F,o,O,0=''',0=\"\"\"
+
+let b:undo_indent = "setlocal indentexpr< indentkeys<"
+
+if exists("*GetJustfileIndent")
+ finish
+endif
+
+function GetJustfileIndent()
+ if v:lnum < 2
+ return 0
+ endif
+
+ let prev_line = getline(v:lnum - 1)
+ let last_indent = indent(v:lnum - 1)
+
+ if getline(v:lnum) =~ "\\v^\\s+%([})]|'''$|\"\"\"$)"
+ return last_indent - shiftwidth()
+ elseif prev_line =~ '\V#'
+ return last_indent
+ elseif prev_line =~ "\\v%([:{(]|^.*\\S.*%([^']'''|[^\"]\"\"\"))\\s*$"
+ return last_indent + shiftwidth()
+ elseif prev_line =~ '\\$'
+ if v:lnum == 2 || getline(v:lnum - 2) !~ '\\$'
+ if prev_line =~ '\v:\=@!'
+ return last_indent + shiftwidth() + shiftwidth()
+ else
+ return last_indent + shiftwidth()
+ endif
+ endif
+ elseif v:lnum > 2 && getline(v:lnum - 2) =~ '\\$'
+ return last_indent - shiftwidth()
+ elseif prev_line =~ '\v:\s*%(\h|\()' && prev_line !~ '\V:='
+ return last_indent + shiftwidth()
+ endif
+
+ return last_indent
+endfunction
diff --git a/runtime/indent/query.lua b/runtime/indent/query.lua
index c5b4f1f03d..17b8e9d2ac 100644
--- a/runtime/indent/query.lua
+++ b/runtime/indent/query.lua
@@ -1,6 +1,5 @@
-- Neovim indent file
-- Language: Treesitter query
--- Last Change: 2024 Jul 03
-- it's a lisp!
vim.cmd([[runtime! indent/lisp.vim]])
diff --git a/runtime/indent/typst.vim b/runtime/indent/typst.vim
index 6aaa04a53a..c990b968c8 100644
--- a/runtime/indent/typst.vim
+++ b/runtime/indent/typst.vim
@@ -1,7 +1,8 @@
" Vim indent file
" Language: Typst
-" Maintainer: Gregory Anders <greg@gpanders.com>
-" Last Change: 2024-07-14
+" Previous Maintainer: Gregory Anders
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Last Change: 2024 Dec 09
" Based on: https://github.com/kaarmu/typst.vim
if exists('b:did_indent')
diff --git a/runtime/lua/_vim9script.lua b/runtime/lua/_vim9script.lua
index 23c078cb87..29bafc53b2 100644
--- a/runtime/lua/_vim9script.lua
+++ b/runtime/lua/_vim9script.lua
@@ -145,7 +145,7 @@ local vim9 = (function()
-- 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', {})
+ local group = vim.api.nvim_create_augroup('nvim.vim9script_source', {})
vim.api.nvim_create_autocmd('SourceCmd', {
pattern = '*.vim',
group = group,
diff --git a/runtime/lua/coxpcall.lua b/runtime/lua/coxpcall.lua
index 6b179f1ef0..43e321eac3 100644
--- a/runtime/lua/coxpcall.lua
+++ b/runtime/lua/coxpcall.lua
@@ -1,6 +1,10 @@
-------------------------------------------------------------------------------
+-- (Not needed for LuaJIT or Lua 5.2+)
+--
-- Coroutine safe xpcall and pcall versions
--
+-- https://keplerproject.github.io/coxpcall/
+--
-- Encapsulates the protected calls with a coroutine based loop, so errors can
-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
-- yielding inside the call to pcall or xpcall.
@@ -27,22 +31,23 @@ end
-- No need to do anything if pcall and xpcall are already safe.
if isCoroutineSafe(pcall) and isCoroutineSafe(xpcall) then
- copcall = pcall
- coxpcall = xpcall
+ _G.copcall = pcall
+ _G.coxpcall = xpcall
return { pcall = pcall, xpcall = xpcall, running = coroutine.running }
end
-------------------------------------------------------------------------------
-- Implements xpcall with coroutines
-------------------------------------------------------------------------------
-local performResume, handleReturnValue
+local performResume
local oldpcall, oldxpcall = pcall, xpcall
local pack = table.pack or function(...) return {n = select("#", ...), ...} end
local unpack = table.unpack or unpack
local running = coroutine.running
+--- @type table<thread,thread>
local coromap = setmetatable({}, { __mode = "k" })
-function handleReturnValue(err, co, status, ...)
+local function handleReturnValue(err, co, status, ...)
if not status then
return false, err(debug.traceback(co, (...)), ...)
end
@@ -57,11 +62,12 @@ function performResume(err, co, ...)
return handleReturnValue(err, co, coroutine.resume(co, ...))
end
+--- @diagnostic disable-next-line: unused-vararg
local function id(trace, ...)
return trace
end
-function coxpcall(f, err, ...)
+function _G.coxpcall(f, err, ...)
local current = running()
if not current then
if err == id then
@@ -84,6 +90,7 @@ function coxpcall(f, err, ...)
end
end
+--- @param coro? thread
local function corunning(coro)
if coro ~= nil then
assert(type(coro)=="thread", "Bad argument; expected thread, got: "..type(coro))
@@ -101,7 +108,7 @@ end
-- Implements pcall with coroutines
-------------------------------------------------------------------------------
-function copcall(f, ...)
+function _G.copcall(f, ...)
return coxpcall(f, id, ...)
end
diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua
index e65d267a70..2093c4eb5c 100644
--- a/runtime/lua/editorconfig.lua
+++ b/runtime/lua/editorconfig.lua
@@ -151,7 +151,7 @@ function properties.trim_trailing_whitespace(bufnr, val)
)
if val == 'true' then
vim.api.nvim_create_autocmd('BufWritePre', {
- group = 'editorconfig',
+ group = 'nvim.editorconfig',
buffer = bufnr,
callback = function()
local view = vim.fn.winsaveview()
@@ -163,7 +163,7 @@ function properties.trim_trailing_whitespace(bufnr, val)
else
vim.api.nvim_clear_autocmds({
event = 'BufWritePre',
- group = 'editorconfig',
+ group = 'nvim.editorconfig',
buffer = bufnr,
})
end
@@ -180,7 +180,7 @@ function properties.insert_final_newline(bufnr, val)
local endofline = val == 'true'
if vim.bo[bufnr].endofline ~= endofline then
vim.api.nvim_create_autocmd('BufWritePre', {
- group = 'editorconfig',
+ group = 'nvim.editorconfig',
buffer = bufnr,
once = true,
callback = function()
diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua
index 114c94f9e5..96cfdf523d 100644
--- a/runtime/lua/man.lua
+++ b/runtime/lua/man.lua
@@ -1,93 +1,84 @@
local api, fn = vim.api, vim.fn
-local FIND_ARG = '-w'
-local localfile_arg = true -- Always use -l if possible. #6683
-
----@type table[]
-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.
----@param cmd string[]
----@param silent boolean?
----@param env? table<string,string|number>
----@return string
+--- Run a system command and timeout after 10 seconds.
+--- @param cmd string[]
+--- @param silent boolean?
+--- @param env? table<string,string|number>
+--- @return string
local function system(cmd, silent, env)
local r = vim.system(cmd, { env = env, timeout = 10000 }):wait()
- if r.code ~= 0 and not silent then
- local cmd_str = table.concat(cmd, ' ')
- man_error(string.format("command error '%s': %s", cmd_str, r.stderr))
+ if not silent then
+ if r.code ~= 0 then
+ local cmd_str = table.concat(cmd, ' ')
+ error(string.format("command error '%s': %s", cmd_str, r.stderr))
+ end
+ assert(r.stdout ~= '')
end
return assert(r.stdout)
end
----@param line string
----@param linenr integer
-local function highlight_line(line, linenr)
- ---@type string[]
+--- @enum Man.Attribute
+local Attrs = {
+ None = 0,
+ Bold = 1,
+ Underline = 2,
+ Italic = 3,
+}
+
+--- @param line string
+--- @param row integer
+--- @param hls {attr:Man.Attribute,row:integer,start:integer,final:integer}[]
+--- @return string
+local function render_line(line, row, hls)
+ --- @type string[]
local chars = {}
local prev_char = ''
local overstrike, escape, osc8 = false, false, false
- ---@type table<integer,{attr:integer,start:integer,final:integer}>
- local hls = {} -- Store highlight groups as { attr, start, final }
-
- local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3
- local hl_groups = { [BOLD] = 'manBold', [UNDERLINE] = 'manUnderline', [ITALIC] = 'manItalic' }
- local attr = NONE
+ local attr = Attrs.None
local byte = 0 -- byte offset
- local function end_attr_hl(attr_)
- for i, hl in ipairs(hls) do
- if hl.attr == attr_ and hl.final == -1 then
- hl.final = byte
- hls[i] = hl
- end
- end
- end
+ local hls_start = #hls + 1
+ --- @param code integer
local function add_attr_hl(code)
local continue_hl = true
if code == 0 then
- attr = NONE
+ attr = Attrs.None
continue_hl = false
elseif code == 1 then
- attr = BOLD
+ attr = Attrs.Bold
elseif code == 22 then
- attr = BOLD
+ attr = Attrs.Bold
continue_hl = false
elseif code == 3 then
- attr = ITALIC
+ attr = Attrs.Italic
elseif code == 23 then
- attr = ITALIC
+ attr = Attrs.Italic
continue_hl = false
elseif code == 4 then
- attr = UNDERLINE
+ attr = Attrs.Underline
elseif code == 24 then
- attr = UNDERLINE
+ attr = Attrs.Underline
continue_hl = false
else
- attr = NONE
+ attr = Attrs.None
return
end
if continue_hl then
- hls[#hls + 1] = { attr = attr, start = byte, final = -1 }
+ hls[#hls + 1] = { attr = attr, row = row, start = byte, final = -1 }
else
- if attr == NONE then
- for a, _ in pairs(hl_groups) do
- end_attr_hl(a)
+ for _, a in pairs(attr == Attrs.None and Attrs or { attr }) do
+ for i = hls_start, #hls do
+ if hls[i].attr == a and hls[i].final == -1 then
+ hls[i].final = byte
+ end
end
- else
- end_attr_hl(attr)
end
end
end
@@ -100,11 +91,11 @@ local function highlight_line(line, linenr)
if overstrike then
local last_hl = hls[#hls]
if char == prev_char then
- if char == '_' and attr == ITALIC and last_hl and last_hl.final == byte then
+ if char == '_' and attr == Attrs.Italic and last_hl and last_hl.final == byte then
-- This underscore is in the middle of an italic word
- attr = ITALIC
+ attr = Attrs.Italic
else
- attr = BOLD
+ attr = Attrs.Bold
end
elseif prev_char == '_' then
-- Even though underline is strictly what this should be. <bs>_ was used by nroff to
@@ -113,26 +104,26 @@ local function highlight_line(line, linenr)
-- See:
-- - https://unix.stackexchange.com/questions/274658/purpose-of-ascii-text-with-overstriking-file-format/274795#274795
-- - https://cmd.inp.nsk.su/old/cmd2/manuals/unix/UNIX_Unleashed/ch08.htm
- -- attr = UNDERLINE
- attr = ITALIC
+ -- attr = Attrs.Underline
+ attr = Attrs.Italic
elseif prev_char == '+' and char == 'o' then
-- bullet (overstrike text '+^Ho')
- attr = BOLD
+ attr = Attrs.Bold
char = '·'
elseif prev_char == '·' and char == 'o' then
-- bullet (additional handling for '+^H+^Ho^Ho')
- attr = BOLD
+ attr = Attrs.Bold
char = '·'
else
-- use plain char
- attr = NONE
+ attr = Attrs.None
end
-- Grow the previous highlight group if possible
if last_hl and last_hl.attr == attr and last_hl.final == byte then
last_hl.final = byte + #char
else
- hls[#hls + 1] = { attr = attr, start = byte, final = byte + #char }
+ hls[#hls + 1] = { attr = attr, row = row, start = byte, final = byte + #char }
end
overstrike = false
@@ -151,14 +142,15 @@ local function highlight_line(line, linenr)
-- We only want to match against SGR sequences, which consist of ESC
-- followed by '[', then a series of parameter and intermediate bytes in
-- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117)
- ---@type string?
+ --- @type string?
local sgr = prev_char:match('^%[([\032-\063]*)m$')
-- Ignore escape sequences with : characters, as specified by ITU's T.416
-- Open Document Architecture and interchange format.
- if sgr and not string.find(sgr, ':') then
- local match ---@type string?
+ if sgr and not sgr:find(':') then
+ local match --- @type string?
while sgr and #sgr > 0 do
-- Match against SGR parameters, which may be separated by ';'
+ --- @type string?, string?
match, sgr = sgr:match('^(%d*);?(.*)')
add_attr_hl(match + 0) -- coerce to number
end
@@ -184,55 +176,43 @@ local function highlight_line(line, linenr)
end
end
- for _, hl in ipairs(hls) do
- if hl.attr ~= NONE then
- buf_hls[#buf_hls + 1] = {
- 0,
- -1,
- hl_groups[hl.attr],
- linenr - 1,
- hl.start,
- hl.final,
- }
- end
- end
-
return table.concat(chars, '')
end
+local HlGroups = {
+ [Attrs.Bold] = 'manBold',
+ [Attrs.Underline] = 'manUnderline',
+ [Attrs.Italic] = 'manItalic',
+}
+
local function highlight_man_page()
local mod = vim.bo.modifiable
vim.bo.modifiable = true
local lines = api.nvim_buf_get_lines(0, 0, -1, false)
+
+ --- @type {attr:Man.Attribute,row:integer,start:integer,final:integer}[]
+ local hls = {}
+
for i, line in ipairs(lines) do
- lines[i] = highlight_line(line, i)
+ lines[i] = render_line(line, i - 1, hls)
end
+
api.nvim_buf_set_lines(0, 0, -1, false, lines)
- for _, args in ipairs(buf_hls) do
- api.nvim_buf_add_highlight(unpack(args))
+ for _, hl in ipairs(hls) do
+ if hl.attr ~= Attrs.None then
+ --- @diagnostic disable-next-line: deprecated
+ api.nvim_buf_add_highlight(0, -1, HlGroups[hl.attr], hl.row, hl.start, hl.final)
+ end
end
- buf_hls = {}
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
----@param str string
----@return string
-local function spaces_to_underscores(str)
- local res = str:gsub('%s', '_')
- return res
-end
-
----@param sect string|nil
----@param name string|nil
----@param silent boolean
-local function get_path(sect, name, silent)
+--- @param name? string
+--- @param sect? string
+local function get_path(name, sect)
name = name or ''
sect = sect or ''
-- Some man implementations (OpenBSD) return all available paths from the
@@ -253,14 +233,14 @@ local function get_path(sect, name, silent)
--
-- 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 ---@type string[]
+ local cmd --- @type string[]
if sect == '' then
- cmd = { 'man', FIND_ARG, name }
+ cmd = { 'man', '-w', name }
else
- cmd = { 'man', FIND_ARG, sect, name }
+ cmd = { 'man', '-w', sect, name }
end
- local lines = system(cmd, silent)
+ local lines = system(cmd, true)
local results = vim.split(lines, '\n', { trimempty = true })
if #results == 0 then
@@ -276,87 +256,73 @@ local function get_path(sect, name, silent)
end
-- find any that match the specified name
- ---@param v string
+ --- @param v string
local namematches = vim.tbl_filter(function(v)
local tail = fn.fnamemodify(v, ':t')
- return string.find(tail, name, 1, true)
+ return tail:find(name, 1, true) ~= nil
end, results) or {}
local sectmatches = {}
if #namematches > 0 and sect ~= '' then
- ---@param v string
+ --- @param v string
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\+$]], '', '')
+ return (sectmatches[1] or namematches[1] or results[1]):gsub('\n+$', '')
end
----@param text string
----@param pat_or_re string
-local function matchstr(text, pat_or_re)
- local re = type(pat_or_re) == 'string' and vim.regex(pat_or_re) or pat_or_re
-
- ---@type integer, integer
- local s, e = re:match_str(text)
-
- if s == nil then
- return
+--- Attempt to extract the name and sect out of 'name(sect)'
+--- otherwise just return the largest string of valid characters in ref
+--- @param ref string
+--- @return string? name
+--- @return string? sect
+--- @return string? err
+local function parse_ref(ref)
+ if ref == '' or ref:sub(1, 1) == '-' then
+ return nil, nil, ('invalid manpage reference "%s"'):format(ref)
end
- return text:sub(vim.str_utfindex(text, 'utf-32', s) + 1, vim.str_utfindex(text, 'utf-32', e))
-end
+ -- match "<name>(<sect>)"
+ -- note: name can contain spaces
+ local name, sect = ref:match('([^()]+)%(([^()]+)%)')
+ if name then
+ -- 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 name, sect:lower()
+ end
--- attempt to extract the name and sect out of 'name(sect)'
--- otherwise just return the largest string of valid characters in ref
----@param ref string
----@return string, string
-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 '', 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 = parts[1]
- return sect, name
+ name = ref:match('[^()]+')
+ if not name then
+ return nil, nil, ('invalid manpage reference "%s"'):format(ref)
+ end
+ return name
end
--- find_path 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.
--- 4. If a path still wasn't found, return nil.
----@param sect string?
----@param name string
-function M.find_path(sect, name)
+--- 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.
+--- 4. If a path still wasn't found, return nil.
+--- @param name string?
+--- @param sect string?
+--- @return string? path
+function M._find_path(name, sect)
if sect and sect ~= '' then
- local ret = get_path(sect, name, true)
+ local ret = get_path(name, sect)
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)
+ for sec in vim.gsplit(vim.b.man_default_sects, ',', { trimempty = true }) do
+ local ret = get_path(name, sec)
if ret then
return ret
end
@@ -364,17 +330,18 @@ function M.find_path(sect, name)
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
+ local ret = get_path(name)
+ if ret then
+ return ret
end
-- if that still didn't work, we will check for $MANSECT and try again with it
-- unset
if vim.env.MANSECT then
+ --- @type string
local mansect = vim.env.MANSECT
vim.env.MANSECT = nil
- local res = get_path('', name, true)
+ local res = get_path(name)
vim.env.MANSECT = mansect
if res then
return res
@@ -385,24 +352,27 @@ function M.find_path(sect, name)
return nil
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'.
----@param path string
----@return string, string
-local function extract_sect_and_name_path(path)
+--- 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'.
+--- @param path string
+--- @return string name
+--- @return string sect
+local function parse_path(path)
local tail = fn.fnamemodify(path, ':t')
- if EXT_RE:match_str(path) then -- valid extensions
+ if
+ path:match('%.[glx]z$')
+ or path:match('%.bz2$')
+ or path:match('%.lzma$')
+ or path:match('%.Z$')
+ then
tail = fn.fnamemodify(tail, ':r')
end
- local name, sect = tail:match('^(.+)%.([^.]+)$')
- return sect, name
+ return tail:match('^(.+)%.([^.]+)$')
end
----@return boolean
+--- @return boolean
local function find_man()
if vim.bo.filetype == 'man' then
return true
@@ -430,22 +400,32 @@ local function set_options()
vim.bo.filetype = 'man'
end
----@param path string
----@param silent boolean?
----@return string
+--- Always use -l if possible. #6683
+--- @type boolean?
+local localfile_arg
+
+--- @param path string
+--- @param silent boolean?
+--- @return string
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 ---@type integer|string
+ local manwidth --- @type integer|string
if (vim.g.man_hardwrap or 1) ~= 1 then
manwidth = 999
elseif vim.env.MANWIDTH then
- manwidth = vim.env.MANWIDTH
+ manwidth = vim.env.MANWIDTH --- @type string|integer
else
manwidth = api.nvim_win_get_width(0) - vim.o.wrapmargin
end
+ if localfile_arg == nil then
+ local mpath = get_path('man')
+ -- Check for -l support.
+ localfile_arg = (mpath and system({ 'man', '-l', mpath }, true) or '') ~= ''
+ 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).
@@ -458,47 +438,17 @@ local function get_page(path, silent)
})
end
----@param lnum integer
----@return string
-local function getline(lnum)
- ---@diagnostic disable-next-line
- return fn.getline(lnum)
-end
-
----@param page string
-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 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.
- -- Use try/catch to avoid setting v:errmsg.
- vim.cmd([[
- try
- keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g
- catch
- endtry
- ]])
- vim.cmd('1') -- Move cursor to first line
- highlight_man_page()
- set_options()
-end
-
+--- @param path string
+--- @param psect string
local function format_candidate(path, psect)
- if matchstr(path, [[\.\%(pdf\|in\)$]]) then -- invalid extensions
+ if vim.endswith(path, '.pdf') or vim.endswith(path, '.in') then
+ -- invalid extensions
return ''
end
- local sect, name = extract_sect_and_name_path(path)
+ local name, sect = parse_path(path)
if sect == psect then
return name
- elseif sect and name and matchstr(sect, psect .. '.\\+$') then -- invalid extensions
+ elseif sect:match(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)
@@ -506,63 +456,54 @@ local function format_candidate(path, psect)
return ''
end
----@generic T
----@param list T[]
----@param elem T
----@return T[]
-local function move_elem_to_head(list, elem)
- ---@diagnostic disable-next-line:no-unknown
- local list1 = vim.tbl_filter(function(v)
- return v ~= elem
- end, list)
- return { elem, unpack(list1) }
-end
-
----@param sect string
----@param name string
----@return string[]
-local function get_paths(sect, name)
+--- @param name string
+--- @param sect? string
+--- @return string[] paths
+--- @return string? err
+local function get_paths(name, sect)
-- Try several sources for getting the list man directories:
- -- 1. `man -w` (works on most systems)
- -- 2. `manpath`
+ -- 1. `manpath -q`
+ -- 2. `man -w` (works on most systems)
-- 3. $MANPATH
- local mandirs_raw = vim.F.npcall(system, { 'man', FIND_ARG })
- or vim.F.npcall(system, { 'manpath', '-q' })
+ --
+ -- Note we prefer `manpath -q` because `man -w`:
+ -- - does not work on MacOS 14 and later.
+ -- - only returns '/usr/bin/man' on MacOS 13 and earlier.
+ --- @type string?
+ local mandirs_raw = vim.F.npcall(system, { 'manpath', '-q' })
+ or vim.F.npcall(system, { 'man', '-w' })
or vim.env.MANPATH
if not mandirs_raw then
- man_error("Could not determine man directories from: 'man -w', 'manpath' or $MANPATH")
+ return {}, "Could not determine man directories from: 'man -w', 'manpath' or $MANPATH"
end
local mandirs = table.concat(vim.split(mandirs_raw, '[:\n]', { trimempty = true }), ',')
- ---@type string[]
+
+ sect = sect or ''
+
+ --- @type string[]
local paths = fn.globpath(mandirs, 'man[^\\/]*/' .. name .. '*.' .. sect .. '*', false, true)
-- Prioritize the result from find_path as it obeys b:man_default_sects.
- local first = M.find_path(sect, name)
+ local first = M._find_path(name, sect)
if first then
- paths = move_elem_to_head(paths, first)
+ --- @param v string
+ paths = vim.tbl_filter(function(v)
+ return v ~= first
+ end, paths)
+ table.insert(paths, 1, first)
end
return paths
end
----@param sect string
----@param psect string
----@param name string
----@return string[]
-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)
----@param arg_lead string
----@param cmd_line string
-function M.man_complete(arg_lead, cmd_line, _)
+--- @param arg_lead string
+--- @param cmd_line string
+--- @return string? sect
+--- @return string? psect
+--- @return string? name
+local function parse_cmdline(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
@@ -572,13 +513,13 @@ function M.man_complete(arg_lead, cmd_line, _)
end
if #args > 3 then
- return {}
+ return
end
if #args == 1 then
-- returning full completion is laggy. Require some arg_lead to complete
- -- return complete('', '', '')
- return {}
+ -- return '', '', ''
+ return
end
if arg_lead:match('^[^()]+%([^()]*$') then
@@ -587,18 +528,19 @@ function M.man_complete(arg_lead, cmd_line, _)
-- 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]
+ -- See extract_sect_and_name_ref on why :lower()
local sect = (tmp[2] or ''):lower()
- return complete(sect, '', name)
+ return 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 {}
+ return
end
if #args == 2 then
- ---@type string, string
+ --- @type string, string
local name, sect
if arg_lead == '' then
-- cursor (|) is at ':Man 1 |'
@@ -614,52 +556,77 @@ function M.man_complete(arg_lead, cmd_line, _)
name = arg_lead
sect = ''
end
- return complete(sect, sect, name)
+ return sect, sect, name
end
if not arg_lead:match('[^()]+$') then
-- cursor (|) is at ':Man 3 printf |' or ':Man 3 (pr)i|'
- return {}
+ return
end
-- cursor (|) is at ':Man 3 pri|'
- local name = arg_lead
- local sect = args[2]:lower()
- return complete(sect, sect, name)
+ local name, sect = arg_lead, args[2]:lower()
+ return sect, sect, name
end
----@param pattern string
----@return {name:string,filename:string,cmd:string}[]
-function M.goto_tag(pattern, _, _)
- local sect, name = extract_sect_and_name_ref(pattern)
+--- @param arg_lead string
+--- @param cmd_line string
+function M.man_complete(arg_lead, cmd_line)
+ local sect, psect, name = parse_cmdline(arg_lead, cmd_line)
+ if not (sect and psect and name) then
+ return {}
+ end
- local paths = get_paths(sect, name)
- ---@type {name:string,title:string}[]
- local structured = {}
+ local pages = get_paths(name, sect)
- 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 .. ')',
- }
+ -- We check for duplicates in case the same manpage in different languages
+ -- was found.
+ local pages_fmt = {} --- @type string[]
+ local pages_fmt_keys = {} --- @type table<string,true>
+ for _, v in ipairs(pages) do
+ local x = format_candidate(v, psect)
+ local xl = x:lower() -- ignore case when searching avoiding duplicates
+ if not pages_fmt_keys[xl] then
+ pages_fmt[#pages_fmt + 1] = x
+ pages_fmt_keys[xl] = true
end
end
+ table.sort(pages_fmt)
+
+ return pages_fmt
+end
+
+--- @param pattern string
+--- @return {name:string,filename:string,cmd:string}[]
+function M.goto_tag(pattern, _, _)
+ local name, sect, err = parse_ref(pattern)
+ if err then
+ error(err)
+ end
+
+ local paths, err2 = get_paths(assert(name), sect)
+ if err2 then
+ error(err2)
+ end
+
+ --- @type table[]
+ local ret = {}
- ---@param entry {name:string,title:string}
- return vim.tbl_map(function(entry)
- return {
- name = entry.name,
- filename = 'man://' .. entry.title,
+ for _, path in ipairs(paths) do
+ local pname, psect = parse_path(path)
+ ret[#ret + 1] = {
+ name = pname,
+ filename = ('man://%s(%s)'):format(pname, psect),
cmd = '1',
}
- end, structured)
+ end
+
+ return ret
end
--- Called when Nvim is invoked as $MANPAGER.
+--- Called when Nvim is invoked as $MANPAGER.
function M.init_pager()
- if getline(1):match('^%s*$') then
+ if fn.getline(1):match('^%s*$') then
api.nvim_buf_set_lines(0, 0, 1, false, {})
else
vim.cmd('keepjumps 1')
@@ -667,9 +634,10 @@ function M.init_pager()
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(getline(1), [[^[^)]\+)]]) or '', ' ', '_', 'g')
- local ok, res = pcall(extract_sect_and_name_ref, ref)
- vim.b.man_sect = ok and res or ''
+ --- @type string
+ local ref = (fn.getline(1):match('^[^)]+%)') or ''):gsub(' ', '_')
+ local _, sect, err = pcall(parse_ref, ref)
+ vim.b.man_sect = err ~= nil and sect or ''
if not fn.bufname('%'):match('man://') then -- Avoid duplicate buffers, E95.
vim.cmd.file({ 'man://' .. fn.fnameescape(ref):lower(), mods = { silent = true } })
@@ -678,50 +646,64 @@ function M.init_pager()
set_options()
end
----@param count integer
----@param args string[]
+--- Combine the name and sect into a manpage reference so that all
+--- verification/extraction can be kept in a single function.
+--- @param args string[]
+--- @return string? ref
+local function ref_from_args(args)
+ if #args <= 1 then
+ return args[1]
+ elseif args[1]:match('^%d$') or args[1]:match('^%d%a') or args[1]:match('^%a$') then
+ -- NB: Valid sections are not only digits, but also:
+ -- - <digit><word> (see POSIX mans),
+ -- - and even <letter> and <word> (see, for example, by tcl/tk)
+ -- NB2: don't optimize to :match("^%d"), as it will match manpages like
+ -- 441toppm and others whose name starts with digit
+ local sect = args[1]
+ table.remove(args, 1)
+ local name = table.concat(args, ' ')
+ return ('%s(%s)'):format(name, sect)
+ end
+
+ return table.concat(args, ' ')
+end
+
+--- @param count integer
+--- @param args string[]
+--- @return string? err
function M.open_page(count, smods, args)
- local ref ---@type string
- if #args == 0 then
+ local ref = ref_from_args(args)
+ if not ref 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[1]:match('^%d$') or args[1]:match('^%d%a') or args[1]:match('^%a$') then
- -- NB: Valid sections are not only digits, but also:
- -- - <digit><word> (see POSIX mans),
- -- - and even <letter> and <word> (see, for example, by tcl/tk)
- -- NB2: don't optimize to :match("^%d"), as it will match manpages like
- -- 441toppm and others whose name starts with digit
- local sect = args[1]
- table.remove(args, 1)
- local name = table.concat(args, ' ')
- ref = ('%s(%s)'):format(name, sect)
- else
- ref = table.concat(args, ' ')
+ return 'no identifier under cursor'
end
end
- local sect, name = extract_sect_and_name_ref(ref)
+ local name, sect, err = parse_ref(ref)
+ if err then
+ return err
+ end
+ assert(name)
+
if count >= 0 then
sect = tostring(count)
end
-- Try both spaces and underscores, use the first that exists.
- local path = M.find_path(sect, name)
- if path == nil then
- path = M.find_path(sect, spaces_to_underscores(name))
- if path == nil then
- man_error('no manual entry for ' .. name)
+ local path = M._find_path(name, sect)
+ if not path then
+ --- 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
+ path = M._find_path(name:gsub('%s', '_'), sect)
+ if not path then
+ return 'no manual entry for ' .. name
end
end
- sect, name = extract_sect_and_name_path(path)
+ name, sect = parse_path(path)
local buf = api.nvim_get_current_buf()
local save_tfu = vim.bo[buf].tagfunc
vim.bo[buf].tagfunc = "v:lua.require'man'.goto_tag"
@@ -744,24 +726,51 @@ function M.open_page(count, smods, args)
if not ok then
error(ret)
- else
- set_options()
end
+ set_options()
vim.b.man_sect = sect
end
--- Called when a man:// buffer is opened.
+--- Called when a man:// buffer is opened.
+--- @return string? err
function M.read_page(ref)
- local sect, name = extract_sect_and_name_ref(ref)
- local path = M.find_path(sect, name)
- if path == nil then
- man_error('no manual entry for ' .. name)
+ local name, sect, err = parse_ref(ref)
+ if err then
+ return err
+ end
+
+ local path = M._find_path(name, sect)
+ if not path then
+ return 'no manual entry for ' .. name
end
- sect = extract_sect_and_name_path(path)
+
+ local _, sect1 = parse_path(path)
local page = get_page(path)
- vim.b.man_sect = sect
- put_page(page)
+
+ vim.b.man_sect = sect1
+ 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.
+ -- Use try/catch to avoid setting v:errmsg.
+ vim.cmd([[
+ try
+ keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g
+ catch
+ endtry
+ ]])
+ vim.cmd('1') -- Move cursor to first line
+ highlight_man_page()
+ set_options()
end
function M.show_toc()
@@ -773,29 +782,18 @@ function M.show_toc()
return
end
- ---@type {bufnr:integer, lnum:integer, text:string}[]
+ --- @type {bufnr:integer, lnum:integer, text:string}[]
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 = getline(lnum)
- if section_title_re:match_str(text) then
- -- if text is a section title
+ local text = fn.getline(lnum)
+ if text:match('^%s+[-+]%S') or text:match('^ %S') or text:match('^%S') then
toc[#toc + 1] = {
bufnr = 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 = bufnr,
- lnum = lnum,
- text = ' ' .. fn.substitute(text, [[^\s*\(.\{-}\)\s*$]], [[\1]], ''),
+ text = text:gsub('^%s+', ''):gsub('%s+$', ''),
}
end
lnum = fn.nextnonblank(lnum + 1)
@@ -807,19 +805,4 @@ function M.show_toc()
vim.w.qf_toc = bufname
end
-local function init()
- local path = get_path('', 'man', true)
- local page ---@type string?
- 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/tohtml.lua b/runtime/lua/tohtml.lua
index ed42b28725..4415a8cdca 100644
--- a/runtime/lua/tohtml.lua
+++ b/runtime/lua/tohtml.lua
@@ -317,7 +317,7 @@ end
--- @return nil|integer
local function register_hl(state, hl)
if type(hl) == 'table' then
- hl = hl[#hl]
+ hl = hl[#hl] --- @type string|integer
end
if type(hl) == 'nil' then
return
@@ -492,7 +492,7 @@ local function _styletable_extmarks_highlight(state, extmark, namespaces)
end
---TODO(altermo) LSP semantic tokens (and some other extmarks) are only
---generated in visible lines, and not in the whole buffer.
- if (namespaces[extmark[4].ns_id] or ''):find('vim_lsp_semantic_tokens') then
+ if (namespaces[extmark[4].ns_id] or ''):find('nvim.lsp.semantic_tokens') then
notify('lsp semantic tokens are not supported, HTML may be incorrect')
return
end
@@ -514,7 +514,7 @@ local function _styletable_extmarks_virt_text(state, extmark, namespaces)
end
---TODO(altermo) LSP semantic tokens (and some other extmarks) are only
---generated in visible lines, and not in the whole buffer.
- if (namespaces[extmark[4].ns_id] or ''):find('vim_lsp_inlayhint') then
+ if (namespaces[extmark[4].ns_id] or ''):find('nvim.lsp.inlayhint') then
notify('lsp inlay hints are not supported, HTML may be incorrect')
return
end
@@ -1162,7 +1162,9 @@ local function extend_pre(out, state)
s = s .. _pre_text_to_html(state, row)
end
local true_line_len = #line + 1
- for k in pairs(style_line) do
+ for k in
+ pairs(style_line --[[@as table<string,any>]])
+ do
if type(k) == 'number' and k > true_line_len then
true_line_len = k
end
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 06f6ed6829..69204e3fe6 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -49,10 +49,10 @@ do
vim.keymap.set('x', '*', function()
return _visual_search('/')
- end, { desc = ':help v_star-default', expr = true, silent = true })
+ end, { desc = ':help v_star-default', expr = true, replace_keycodes = false })
vim.keymap.set('x', '#', function()
return _visual_search('?')
- end, { desc = ':help v_#-default', expr = true, silent = true })
+ end, { desc = ':help v_#-default', expr = true, replace_keycodes = false })
end
--- Map Y to y$. This mimics the behavior of D and C. See |Y-default|
@@ -222,9 +222,9 @@ do
--- Execute a command and print errors without a stacktrace.
--- @param opts table Arguments to |nvim_cmd()|
local function cmd(opts)
- local _, err = pcall(vim.api.nvim_cmd, opts, {})
- if err then
- vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1))
+ local ok, err = pcall(vim.api.nvim_cmd, opts, {})
+ if not ok then
+ vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true })
end
end
@@ -412,7 +412,7 @@ do
end
end
- local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim_popupmenu', {})
+ local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim.popupmenu', {})
vim.api.nvim_create_autocmd('MenuPopup', {
pattern = '*',
group = nvim_popupmenu_augroup,
@@ -429,13 +429,13 @@ end
--- Default autocommands. See |default-autocmds|
do
- local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim_terminal', {})
+ local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim.terminal', {})
vim.api.nvim_create_autocmd('BufReadCmd', {
pattern = 'term://*',
group = nvim_terminal_augroup,
desc = 'Treat term:// buffers as terminal buffers',
nested = true,
- command = "if !exists('b:term_title')|call termopen(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
+ command = "if !exists('b:term_title')|call jobstart(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'term': v:true, 'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})",
})
vim.api.nvim_create_autocmd({ 'TermClose' }, {
@@ -492,6 +492,10 @@ do
vim.bo.textwidth = 0
vim.wo[0][0].wrap = false
vim.wo[0][0].list = false
+ vim.wo[0][0].number = false
+ vim.wo[0][0].relativenumber = false
+ vim.wo[0][0].signcolumn = 'no'
+ vim.wo[0][0].foldcolumn = '0'
-- This is gross. Proper list options support when?
local winhl = vim.o.winhighlight
@@ -505,14 +509,14 @@ do
vim.api.nvim_create_autocmd('CmdwinEnter', {
pattern = '[:>]',
desc = 'Limit syntax sync to maxlines=1 in the command window',
- group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
+ group = vim.api.nvim_create_augroup('nvim.cmdwin', {}),
command = 'syntax sync minlines=1 maxlines=1',
})
vim.api.nvim_create_autocmd('SwapExists', {
pattern = '*',
desc = 'Skip the swapfile prompt when the swapfile is owned by a running Nvim process',
- group = vim.api.nvim_create_augroup('nvim_swapfile', {}),
+ group = vim.api.nvim_create_augroup('nvim.swapfile', {}),
callback = function()
local info = vim.fn.swapinfo(vim.v.swapname)
local user = vim.uv.os_get_passwd().username
@@ -539,15 +543,16 @@ do
end
if tty then
- local group = vim.api.nvim_create_augroup('nvim_tty', {})
+ local group = vim.api.nvim_create_augroup('nvim.tty', {})
--- Set an option after startup (so that OptionSet is fired), but only if not
--- already set by the user.
---
--- @param option string Option name
--- @param value any Option value
- local function setoption(option, value)
- if vim.api.nvim_get_option_info2(option, {}).was_set then
+ --- @param force boolean? Always set the value, even if already set
+ local function setoption(option, value, force)
+ if not force and vim.api.nvim_get_option_info2(option, {}).was_set then
-- Don't do anything if option is already set
return
end
@@ -563,7 +568,7 @@ do
once = true,
nested = true,
callback = function()
- setoption(option, value)
+ setoption(option, value, force)
end,
})
end
@@ -645,11 +650,15 @@ do
return nil, nil, nil
end
- local timer = assert(vim.uv.new_timer())
-
+ -- This autocommand updates the value of 'background' anytime we receive
+ -- an OSC 11 response from the terminal emulator. If the user has set
+ -- 'background' explicitly then we will delete this autocommand,
+ -- effectively disabling automatic background setting.
+ local force = false
local id = vim.api.nvim_create_autocmd('TermResponse', {
group = group,
nested = true,
+ desc = "Update the value of 'background' automatically based on the terminal emulator's background color",
callback = function(args)
local resp = args.data ---@type string
local r, g, b = parseosc11(resp)
@@ -661,27 +670,33 @@ do
if rr and gg and bb then
local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
local bg = luminance < 0.5 and 'dark' or 'light'
- setoption('background', bg)
+ setoption('background', bg, force)
+
+ -- On the first query response, don't force setting the option in
+ -- case the user has already set it manually. If they have, then
+ -- this autocommand will be deleted. If they haven't, then we do
+ -- want to force setting the option to override the value set by
+ -- this autocommand.
+ if not force then
+ force = true
+ end
end
+ end
+ end,
+ })
- return true
+ vim.api.nvim_create_autocmd('VimEnter', {
+ group = group,
+ nested = true,
+ once = true,
+ callback = function()
+ if vim.api.nvim_get_option_info2('background', {}).was_set then
+ vim.api.nvim_del_autocmd(id)
end
end,
})
io.stdout:write('\027]11;?\007')
-
- timer:start(1000, 0, function()
- -- Delete the autocommand if no response was received
- vim.schedule(function()
- -- Suppress error if autocommand has already been deleted
- pcall(vim.api.nvim_del_autocmd, id)
- end)
-
- if not timer:is_closing() then
- timer:close()
- end
- end)
end
--- If the TUI (term_has_truecolor) was able to determine that the host
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 44f17b3f85..a77ea9bb91 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -58,6 +58,7 @@ vim._extra = {
--- @private
vim.log = {
+ --- @enum vim.log.levels
levels = {
TRACE = 0,
DEBUG = 1,
@@ -92,7 +93,7 @@ local utfs = {
---
--- -- Runs synchronously:
--- local obj = vim.system({'echo', 'hello'}, { text = true }):wait()
---- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' }
+--- -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' }
---
--- ```
---
@@ -390,7 +391,7 @@ end
local VIM_CMD_ARG_MAX = 20
---- Executes Vim script commands.
+--- Executes Vimscript (|Ex-commands|).
---
--- Note that `vim.cmd` can be indexed with a command name to return a callable function to the
--- command.
@@ -424,8 +425,9 @@ local VIM_CMD_ARG_MAX = 20
--- vim.cmd.colorscheme('blue')
--- ```
---
+---@diagnostic disable-next-line: undefined-doc-param
---@param command string|table Command(s) to execute.
---- If a string, executes multiple lines of Vim script at once. In this
+--- If a string, executes multiple lines of Vimscript at once. In this
--- case, it is an alias to |nvim_exec2()|, where `opts.output` is set
--- to false. Thus it works identical to |:source|.
--- If a table, executes a single command. In this case, it is an alias
@@ -440,10 +442,12 @@ vim.cmd = setmetatable({}, {
return ''
end
end,
+ --- @param t table<string,function>
__index = function(t, command)
t[command] = function(...)
- local opts
+ local opts --- @type vim.api.keyset.cmd
if select('#', ...) == 1 and type(select(1, ...)) == 'table' then
+ --- @type vim.api.keyset.cmd
opts = select(1, ...)
-- Move indexed positions in opts to opt.args
@@ -454,6 +458,7 @@ vim.cmd = setmetatable({}, {
break
end
opts.args[i] = opts[i]
+ --- @diagnostic disable-next-line: no-unknown
opts[i] = nil
end
end
@@ -528,7 +533,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive)
end
if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then
- pos1, pos2 = pos2, pos1
+ pos1, pos2 = pos2, pos1 --- @type [integer, integer], [integer, integer]
end
-- getpos() may return {0,0,0,0}
@@ -620,13 +625,8 @@ end
---@param opts table|nil Optional parameters. Unused by default.
---@diagnostic disable-next-line: unused-local
function vim.notify(msg, level, opts) -- luacheck: no unused args
- if level == vim.log.levels.ERROR then
- vim.api.nvim_err_writeln(msg)
- elseif level == vim.log.levels.WARN then
- vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {})
- else
- vim.api.nvim_echo({ { msg } }, true, {})
- end
+ local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } }
+ vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR })
end
do
@@ -705,6 +705,7 @@ function vim._on_key(buf, typed_buf)
local discard = false
for k, v in pairs(on_key_cbs) do
local fn = v[1]
+ --- @type boolean, any
local ok, rv = xpcall(function()
return fn(buf, typed_buf)
end, debug.traceback)
@@ -832,6 +833,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing)
-- Return (multiple): ~
-- (`integer`) UTF-32 index
-- (`integer`) UTF-16 index
+ --- @diagnostic disable-next-line: redundant-return-value
return col32, col16
end
@@ -1004,7 +1006,7 @@ function vim._expand_pat(pat, env)
or vim.v == final_env and { 'v:', 'var' }
or { nil, nil }
)
- assert(prefix, "Can't resolve final_env")
+ assert(prefix and type, "Can't resolve final_env")
local vars = vim.fn.getcompletion(prefix .. match_part, type) --- @type string[]
insert_keys(vim
.iter(vars)
diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua
index fccf4b8dbe..35063dffca 100644
--- a/runtime/lua/vim/_inspector.lua
+++ b/runtime/lua/vim/_inspector.lua
@@ -1,3 +1,5 @@
+--- @diagnostic disable:no-unknown
+
--- @class vim._inspector.Filter
--- @inlinedoc
---
@@ -53,7 +55,7 @@ function vim.inspect_pos(bufnr, row, col, filter)
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
+ bufnr = vim._resolve_bufnr(bufnr)
local results = {
treesitter = {}, --- @type table[]
@@ -78,6 +80,7 @@ function vim.inspect_pos(bufnr, row, col, filter)
-- treesitter
if filter.treesitter then
for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do
+ --- @diagnostic disable-next-line: inject-field
capture.hl_group = '@' .. capture.capture .. '.' .. capture.lang
results.treesitter[#results.treesitter + 1] = resolve_hl(capture)
end
@@ -128,13 +131,13 @@ function vim.inspect_pos(bufnr, row, col, filter)
if filter.semantic_tokens then
results.semantic_tokens = vim.tbl_filter(function(extmark)
- return extmark.ns:find('vim_lsp_semantic_tokens') == 1
+ return extmark.ns:find('nvim.lsp.semantic_tokens') == 1
end, extmarks)
end
if filter.extmarks then
results.extmarks = vim.tbl_filter(function(extmark)
- return extmark.ns:find('vim_lsp_semantic_tokens') ~= 1
+ return extmark.ns:find('nvim.lsp.semantic_tokens') ~= 1
and (filter.extmarks == 'all' or extmark.opts.hl_group)
end, extmarks)
end
@@ -146,6 +149,13 @@ end
---
---Can also be shown with `:Inspect`. [:Inspect]()
---
+---Example: To bind this function to the vim-scriptease
+---inspired `zS` in Normal mode:
+---
+---```lua
+---vim.keymap.set('n', 'zS', vim.show_pos)
+---```
+---
---@since 11
---@param bufnr? integer defaults to the current buffer
---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor
@@ -171,7 +181,7 @@ function vim.show_pos(bufnr, row, col, filter)
if data.hl_group ~= data.hl_group_link then
append('links to ', 'MoreMsg')
append(data.hl_group_link, data.hl_group_link)
- append(' ')
+ append(' ')
end
if comment then
append(comment, 'Comment')
@@ -184,7 +194,14 @@ function vim.show_pos(bufnr, row, col, filter)
append('Treesitter', 'Title')
nl()
for _, capture in ipairs(items.treesitter) do
- item(capture, capture.lang)
+ item(
+ capture,
+ string.format(
+ 'priority: %d language: %s',
+ capture.metadata.priority or vim.hl.priorities.treesitter,
+ capture.lang
+ )
+ )
end
nl()
end
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 3c9b9d4f44..3d10729d23 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -3,6 +3,10 @@
-- DO NOT EDIT
error('Cannot require a meta file')
+--- This file embeds vimdoc as the function descriptions
+--- so ignore any doc related errors.
+--- @diagnostic disable: undefined-doc-name,luadoc-miss-symbol
+
vim.api = {}
--- @private
@@ -163,35 +167,14 @@ function vim.api.nvim__stats() end
--- @return any
function vim.api.nvim__unpack(str) end
---- Adds a highlight to buffer.
----
---- Useful for plugins that dynamically generate highlights to a buffer
---- (like a semantic highlighter or linter). The function adds a single
---- highlight to a buffer. Unlike `matchaddpos()` highlights follow changes to
---- line numbering (as lines are inserted/removed above the highlighted line),
---- like signs and marks do.
----
---- Namespaces are used for batch deletion/updating of a set of highlights. To
---- create a namespace, use `nvim_create_namespace()` which returns a namespace
---- id. Pass it in to this function as `ns_id` to add highlights to the
---- namespace. All highlights in the same namespace can then be cleared with
---- single call to `nvim_buf_clear_namespace()`. If the highlight never will be
---- deleted by an API call, pass `ns_id = -1`.
----
---- As a shorthand, `ns_id = 0` can be used to create a new namespace for the
---- highlight, the allocated id is then returned. If `hl_group` is the empty
---- string no highlight is added, but a new `ns_id` is still returned. This is
---- supported for backwards compatibility, new code should use
---- `nvim_create_namespace()` to create a new empty namespace.
----
---- @param buffer integer Buffer handle, or 0 for current buffer
---- @param ns_id integer namespace to use or -1 for ungrouped highlight
---- @param hl_group string Name of the highlight group to use
---- @param line integer Line to highlight (zero-indexed)
---- @param col_start integer Start of (byte-indexed) column range to highlight
---- @param col_end integer End of (byte-indexed) column range to highlight,
---- or -1 to highlight to end of line
---- @return integer # The ns_id that was used
+--- @deprecated
+--- @param buffer integer
+--- @param ns_id integer
+--- @param hl_group string
+--- @param line integer
+--- @param col_start integer
+--- @param col_end integer
+--- @return integer
function vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) end
--- Activates buffer-update events on a channel, or as Lua callbacks.
@@ -272,12 +255,12 @@ function vim.api.nvim_buf_attach(buffer, send_buffer, opts) end
--- This temporarily switches current buffer to "buffer".
--- If the current window already shows "buffer", the window is not switched.
--- If a window inside the current tabpage (including a float) already shows the
---- buffer, then one of these windows will be set as current window temporarily.
+--- buffer, then one of those windows will be set as current window temporarily.
--- Otherwise a temporary scratch window (called the "autocmd window" for
--- historical reasons) will be used.
---
--- This is useful e.g. to call Vimscript functions that only work with the
---- current buffer/window currently, like `termopen()`.
+--- current buffer/window currently, like `jobstart(…, {'term': v:true})`.
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param fun function Function to call inside the buffer (currently Lua callable
@@ -452,7 +435,7 @@ function vim.api.nvim_buf_get_extmarks(buffer, ns_id, start, end_, opts) end
---
--- @param buffer integer Buffer handle, or 0 for current buffer
--- @param mode string Mode short-name ("n", "i", "v", ...)
---- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings.
+--- @return vim.api.keyset.get_keymap[] # Array of |maparg()|-like dictionaries describing mappings.
--- The "buffer" key holds the associated buffer handle.
function vim.api.nvim_buf_get_keymap(buffer, mode) end
@@ -595,6 +578,9 @@ function vim.api.nvim_buf_line_count(buffer) end
--- - hl_group : highlight group used for the text range. This and below
--- highlight groups can be supplied either as a string or as an integer,
--- the latter of which can be obtained using `nvim_get_hl_id_by_name()`.
+---
+--- Multiple highlight groups can be stacked by passing an array (highest
+--- priority last).
--- - hl_eol : when true, for a multiline highlight covering the
--- EOL of a line, continue the highlight for the rest
--- of the screen line (just like for diff and
@@ -607,6 +593,15 @@ function vim.api.nvim_buf_line_count(buffer) end
--- (highest priority last).
--- - virt_text_pos : position of virtual text. Possible values:
--- - "eol": right after eol character (default).
+--- - "eol_right_align": display right aligned in the window
+--- unless the virtual text is longer than
+--- the space available. If the virtual
+--- text is too long, it is truncated to
+--- fit in the window after the EOL
+--- character. If the line is wrapped, the
+--- virtual text is shown after the end of
+--- the line rather than the previous
+--- screen line.
--- - "overlay": display over the specified column, without
--- shifting the underlying text.
--- - "right_align": display right aligned in the window.
@@ -885,10 +880,8 @@ function vim.api.nvim_cmd(cmd, opts) end
---
--- On execution error: fails with Vimscript error, updates v:errmsg.
---
---- Prefer using `nvim_cmd()` or `nvim_exec2()` over this. To evaluate multiple lines of Vim script
---- or an Ex command directly, use `nvim_exec2()`. To construct an Ex command using a structured
---- format and then execute it, use `nvim_cmd()`. To modify an Ex command before evaluating it, use
---- `nvim_parse_cmd()` in conjunction with `nvim_cmd()`.
+--- Prefer `nvim_cmd()` or `nvim_exec2()` instead. To modify an Ex command in a structured way
+--- before executing it, modify the result of `nvim_parse_cmd()` then pass it to `nvim_cmd()`.
---
--- @param command string Ex command string
function vim.api.nvim_command(command) end
@@ -963,9 +956,9 @@ function vim.api.nvim_create_augroup(name, opts) end
--- - 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>]
+--- - file: (string) [<afile>] (not expanded to a full path)
+--- - match: (string) [<amatch>] (expanded to a full path)
+--- - buf: (number) [<abuf>]
--- - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
--- - command (string) optional: Vim command to execute on event. Cannot be used with
--- {callback}
@@ -989,7 +982,7 @@ function vim.api.nvim_create_buf(listed, scratch) end
--- 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()`.
+--- `nvim_buf_set_extmark()`.
---
--- Namespaces can be named or anonymous. If `name` matches an existing
--- namespace, the associated id is returned. If `name` is an empty string
@@ -1012,7 +1005,7 @@ function vim.api.nvim_create_namespace(name) end
--- ```
---
--- @param name string Name of the new user command. Must begin with an uppercase letter.
---- @param command any Replacement command to execute when this user command is executed. When called
+--- @param command string|fun(args: vim.api.keyset.create_user_command.command_args) 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
@@ -1099,29 +1092,28 @@ function vim.api.nvim_del_user_command(name) end
--- @param name string Variable name
function vim.api.nvim_del_var(name) end
---- Echo a message.
+--- Prints a message given by a list of `[text, hl_group]` "chunks".
+---
+--- Example:
+--- ```lua
+--- vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {})
+--- ```
---
---- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a
---- text chunk with specified highlight group name or ID.
---- `hl_group` element can be omitted for no highlight.
+--- @param chunks any[] List of `[text, hl_group]` pairs, where each is a `text` string highlighted by
+--- the (optional) name or ID `hl_group`.
--- @param history boolean if true, add to `message-history`.
--- @param opts vim.api.keyset.echo_opts Optional parameters.
---- - verbose: Message is 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.
+--- - err: Treat the message like `:echoerr`. Sets `hl_group` to `hl-ErrorMsg` by default.
+--- - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
+--- will write the message to the "log" file instead of standard output.
function vim.api.nvim_echo(chunks, history, opts) end
---- Writes a message to the Vim error buffer. Does not append "\n", the
---- message is buffered (won't display) until a linefeed is written.
----
---- @param str string Message
+--- @deprecated
+--- @param str string
function vim.api.nvim_err_write(str) end
---- Writes a message to the Vim error buffer. Appends "\n", so the buffer is
---- flushed (and displayed).
----
---- @see vim.api.nvim_err_write
---- @param str string Message
+--- @deprecated
+--- @param str string
function vim.api.nvim_err_writeln(str) end
--- Evaluates a Vimscript `expression`. Dicts and Lists are recursively expanded.
@@ -1152,7 +1144,9 @@ function vim.api.nvim_eval(expr) end
--- the "highlights" key in {opts} is true. Each element of the array is a
--- |Dict| with these keys:
--- - start: (number) Byte index (0-based) of first character that uses the highlight.
---- - group: (string) Name of highlight group.
+--- - group: (string) Name of highlight group. May be removed in the future, use
+--- `groups` instead.
+--- - groups: (array) Names of stacked highlight groups (highest priority last).
function vim.api.nvim_eval_statusline(str, opts) end
--- @deprecated
@@ -1256,31 +1250,34 @@ function vim.api.nvim_get_all_options_info() end
--- match any combination of them.
---
--- @param opts vim.api.keyset.get_autocmds Dict 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`.
---- Cannot be used with {buffer}
---- - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+--- - buffer: (integer) Buffer number or list of buffer numbers for buffer local autocommands
--- `autocmd-buflocal`. Cannot be used with {pattern}
+--- - event: (string|table) event or events to match against `autocmd-events`.
+--- - id: (integer) Autocommand ID to match.
+--- - group: (string|table) the autocommand group name or id to match against.
+--- - pattern: (string|table) pattern or patterns to match against `autocmd-pattern`.
+--- Cannot be used with {buffer}
--- @return vim.api.keyset.get_autocmds.ret[] # Array of autocommands matching the criteria, with each item
--- containing the following fields:
---- - id (number): the autocommand id (only when defined with the API).
---- - group (integer): the autocommand group id.
---- - group_name (string): the autocommand group name.
---- - desc (string): the autocommand description.
---- - event (string): the autocommand event.
---- - command (string): the autocommand command. Note: this will be empty if a callback is set.
---- - callback (function|string|nil): Lua function or name of a Vim script function
+--- - buffer: (integer) the buffer number.
+--- - buflocal: (boolean) true if the autocommand is buffer local.
+--- - command: (string) the autocommand command. Note: this will be empty if a callback is set.
+--- - callback: (function|string|nil): Lua function or name of a Vim script function
--- which is executed when this autocommand is triggered.
---- - once (boolean): whether the autocommand is only run once.
---- - pattern (string): the autocommand pattern.
+--- - desc: (string) the autocommand description.
+--- - event: (string) the autocommand event.
+--- - id: (integer) the autocommand id (only when defined with the API).
+--- - group: (integer) the autocommand group id.
+--- - group_name: (string) the autocommand group name.
+--- - once: (boolean) whether the autocommand is only run once.
+--- - pattern: (string) the autocommand pattern.
--- If the autocommand is buffer local |autocmd-buffer-local|:
---- - buflocal (boolean): true if the autocommand is buffer local.
---- - buffer (number): the buffer number.
function vim.api.nvim_get_autocmds(opts) end
--- Gets information about a channel.
---
+--- See `nvim_list_uis()` for an example of how to get channel info.
+---
--- @param chan integer channel_id, or 0 for current channel
--- @return table<string,any> # Channel info dict with these keys:
--- - "id" Channel id.
@@ -1298,8 +1295,8 @@ function vim.api.nvim_get_autocmds(opts) end
--- "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g.
--- for conpty on Windows).
--- - "buffer" (optional) Buffer connected to |terminal| instance.
---- - "client" (optional) Info about the peer (client on the other end of the RPC channel),
---- which it provided via |nvim_set_client_info()|.
+--- - "client" (optional) Info about the peer (client on the other end of the channel), as set
+--- by |nvim_set_client_info()|.
---
function vim.api.nvim_get_chan_info(chan) end
@@ -1416,7 +1413,7 @@ function vim.api.nvim_get_hl_ns(opts) end
--- Gets a list of global (non-buffer-local) `mapping` definitions.
---
--- @param mode string Mode short-name ("n", "i", "v", ...)
---- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings.
+--- @return vim.api.keyset.get_keymap[] # Array of |maparg()|-like dictionaries describing mappings.
--- The "buffer" key is always zero.
function vim.api.nvim_get_keymap(mode) end
@@ -1621,6 +1618,14 @@ function vim.api.nvim_list_tabpages() end
--- Gets a list of dictionaries representing attached UIs.
---
+--- Example: The Nvim builtin `TUI` sets its channel info as described in `startup-tui`. In
+--- particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by
+--- inspecting the client name of each UI:
+---
+--- ```lua
+--- vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name)
+--- ```
+---
--- @return any[] # Array of UI dictionaries, each with these keys:
--- - "height" Requested height of the UI
--- - "width" Requested width of the UI
@@ -1640,21 +1645,17 @@ function vim.api.nvim_list_wins() end
--- @return any
function vim.api.nvim_load_context(dict) end
---- Notify the user with a message
----
---- Relays the call to vim.notify . By default forwards your message in the
---- echo area but can be overridden to trigger desktop notifications.
----
---- @param msg string Message to display to the user
---- @param log_level integer The log level
---- @param opts table<string,any> Reserved for future use.
+--- @deprecated
+--- @param msg string
+--- @param log_level integer
+--- @param opts table<string,any>
--- @return any
function vim.api.nvim_notify(msg, log_level, opts) end
--- Open a terminal instance in a buffer
---
--- By default (and currently the only option) the terminal will not be
---- connected to an external process. Instead, input send on the channel
+--- connected to an external process. Instead, input sent on the channel
--- will be echoed directly by the terminal. This is useful to display
--- ANSI terminal sequences returned as part of a rpc message, or similar.
---
@@ -1665,6 +1666,19 @@ function vim.api.nvim_notify(msg, log_level, opts) end
--- Then `nvim_chan_send()` can be called immediately to process sequences
--- in a virtual terminal having the intended size.
---
+--- Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you
+--- can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]()
+--- [terminal-scrollback-pager]()
+---
+--- ```lua
+--- vim.api.nvim_create_user_command('TermHl', function()
+--- local b = vim.api.nvim_create_buf(false, true)
+--- local chan = vim.api.nvim_open_term(b, {})
+--- vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
+--- vim.api.nvim_win_set_buf(0, b)
+--- end, { desc = 'Highlights ANSI termcodes in curbuf' })
+--- ```
+---
--- @param buffer integer the buffer to use (expected to be empty)
--- @param opts vim.api.keyset.open_term Optional parameters.
--- - on_input: Lua callback for input sent, i e keypresses in terminal
@@ -1738,10 +1752,12 @@ function vim.api.nvim_open_term(buffer, opts) end
--- @param config vim.api.keyset.win_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
+--- - "cursor" Cursor position in current window.
+--- - "editor" The global editor grid.
+--- - "laststatus" 'laststatus' if present, or last row.
+--- - "mouse" Mouse position.
+--- - "tabline" Tabline if present, or first row.
+--- - "win" Window given by the `win` field, or current window.
--- - win: `window-ID` window to split, or relative window when creating a
--- float (relative="win").
--- - anchor: Decides which corner of the float to place at (row,col):
@@ -1849,10 +1865,8 @@ function vim.api.nvim_open_term(buffer, opts) end
--- @return integer # Window handle, or 0 on error
function vim.api.nvim_open_win(buffer, enter, config) end
---- Writes a message to the Vim output buffer. Does not append "\n", the
---- message is buffered (won't display) until a linefeed is written.
----
---- @param str string Message
+--- @deprecated
+--- @param str string
function vim.api.nvim_out_write(str) end
--- Parse command line.
@@ -2124,8 +2138,8 @@ function vim.api.nvim_set_current_win(window) end
--- ```
--- ["start", tick]
--- ```
---- - on_buf: called for each buffer being redrawn (before
---- window callbacks)
+--- - on_buf: called for each buffer being redrawn (once per edit,
+--- before window callbacks)
--- ```
--- ["buf", bufnr, tick]
--- ```
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index bf184dee2d..4d0665872b 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -4,11 +4,11 @@
error('Cannot require a meta file')
--- @class vim.api.keyset.buf_attach
---- @field on_lines? function
---- @field on_bytes? function
---- @field on_changedtick? function
---- @field on_detach? function
---- @field on_reload? function
+--- @field on_lines? fun(_: "lines", bufnr: integer, changedtick: integer, first: integer, last_old: integer, last_new: integer, byte_count: integer, deleted_codepoints?: integer, deleted_codeunits?: integer): boolean?
+--- @field on_bytes? fun(_: "bytes", bufnr: integer, changedtick: integer, start_row: integer, start_col: integer, start_byte: integer, old_end_row: integer, old_end_col: integer, old_end_byte: integer, new_end_row: integer, new_end_col: integer, new_end_byte: integer): boolean?
+--- @field on_changedtick? fun(_: "changedtick", bufnr: integer, changedtick: integer)
+--- @field on_detach? fun(_: "detach", bufnr: integer)
+--- @field on_reload? fun(_: "reload", bufnr: integer)
--- @field utf_sizes? boolean
--- @field preview? boolean
@@ -18,9 +18,9 @@ error('Cannot require a meta file')
--- @class vim.api.keyset.clear_autocmds
--- @field buffer? integer
---- @field event? any
---- @field group? any
---- @field pattern? any
+--- @field event? string|string[]
+--- @field group? integer|string
+--- @field pattern? string|string[]
--- @class vim.api.keyset.cmd
--- @field cmd? string
@@ -28,12 +28,12 @@ error('Cannot require a meta file')
--- @field count? integer
--- @field reg? string
--- @field bang? boolean
---- @field args? any[]
+--- @field args? string[]
--- @field magic? table<string,any>
--- @field mods? table<string,any>
---- @field nargs? any
---- @field addr? any
---- @field nextcmd? any
+--- @field nargs? integer|string
+--- @field addr? string
+--- @field nextcmd? string
--- @class vim.api.keyset.cmd_magic
--- @field file? boolean
@@ -72,22 +72,23 @@ error('Cannot require a meta file')
--- @field info? string
--- @class vim.api.keyset.context
---- @field types? any[]
+--- @field types? string[]
--- @class vim.api.keyset.create_augroup
---- @field clear? any
+--- @field clear? boolean
--- @class vim.api.keyset.create_autocmd
--- @field buffer? integer
---- @field callback? any
+--- @field callback? string|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean?)
--- @field command? string
--- @field desc? string
---- @field group? any
+--- @field group? integer|string
--- @field nested? boolean
--- @field once? boolean
---- @field pattern? any
+--- @field pattern? string|string[]
--- @class vim.api.keyset.echo_opts
+--- @field err? boolean
--- @field verbose? boolean
--- @class vim.api.keyset.empty
@@ -103,19 +104,20 @@ error('Cannot require a meta file')
--- @class vim.api.keyset.exec_autocmds
--- @field buffer? integer
---- @field group? any
+--- @field group? integer|string
--- @field modeline? boolean
---- @field pattern? any
+--- @field pattern? string|string[]
--- @field data? any
--- @class vim.api.keyset.exec_opts
--- @field output? boolean
--- @class vim.api.keyset.get_autocmds
---- @field event? any
---- @field group? any
---- @field pattern? any
---- @field buffer? any
+--- @field event? string|string[]
+--- @field group? integer|string
+--- @field pattern? string|string[]
+--- @field buffer? integer|integer[]
+--- @field id? integer
--- @class vim.api.keyset.get_commands
--- @field builtin? boolean
@@ -154,17 +156,17 @@ error('Cannot require a meta file')
--- @field altfont? boolean
--- @field nocombine? boolean
--- @field default? boolean
---- @field cterm? any
---- @field foreground? any
---- @field fg? any
---- @field background? any
---- @field bg? any
---- @field ctermfg? any
---- @field ctermbg? any
---- @field special? any
---- @field sp? any
---- @field link? any
---- @field global_link? any
+--- @field cterm? integer|string
+--- @field foreground? integer|string
+--- @field fg? integer|string
+--- @field background? integer|string
+--- @field bg? integer|string
+--- @field ctermfg? integer|string
+--- @field ctermbg? integer|string
+--- @field special? integer|string
+--- @field sp? integer|string
+--- @field link? integer|string
+--- @field global_link? integer|string
--- @field fallback? boolean
--- @field blend? integer
--- @field fg_indexed? boolean
@@ -201,7 +203,7 @@ error('Cannot require a meta file')
--- @field wins? any[]
--- @class vim.api.keyset.open_term
---- @field on_input? function
+--- @field on_input? fun(_: "input", term: integer, bufnr: integer, data: any)
--- @field force_crlf? boolean
--- @class vim.api.keyset.option
@@ -227,20 +229,20 @@ error('Cannot require a meta file')
--- @field do_source? boolean
--- @class vim.api.keyset.set_decoration_provider
---- @field on_start? function
---- @field on_buf? function
---- @field on_win? function
---- @field on_line? function
---- @field on_end? function
---- @field _on_hl_def? function
---- @field _on_spell_nav? function
+--- @field on_start? fun(_: "start", tick: integer): boolean?
+--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer)
+--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer): boolean?
+--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer): boolean?
+--- @field on_end? fun(_: "end", tick: integer)
+--- @field _on_hl_def? fun(_: "hl_def")
+--- @field _on_spell_nav? fun(_: "spell_nav")
--- @class vim.api.keyset.set_extmark
--- @field id? integer
--- @field end_line? integer
--- @field end_row? integer
--- @field end_col? integer
---- @field hl_group? number|string
+--- @field hl_group? any
--- @field virt_text? any[]
--- @field virt_text_pos? string
--- @field virt_text_win_col? integer
@@ -258,10 +260,10 @@ error('Cannot require a meta file')
--- @field virt_lines_leftcol? boolean
--- @field strict? boolean
--- @field sign_text? string
---- @field sign_hl_group? number|string
---- @field number_hl_group? number|string
---- @field line_hl_group? number|string
---- @field cursorline_hl_group? number|string
+--- @field sign_hl_group? integer|string
+--- @field number_hl_group? integer|string
+--- @field line_hl_group? integer|string
+--- @field cursorline_hl_group? integer|string
--- @field conceal? string
--- @field spell? boolean
--- @field ui_watched? boolean
@@ -292,7 +294,7 @@ error('Cannot require a meta file')
--- @field relative? string
--- @field split? string
--- @field win? integer
---- @field bufpos? any[]
+--- @field bufpos? integer[]
--- @field external? boolean
--- @field focusable? boolean
--- @field mouse? boolean
@@ -315,12 +317,12 @@ error('Cannot require a meta file')
--- @field end_vcol? integer
--- @class vim.api.keyset.xdl_diff
---- @field on_hunk? function
+--- @field on_hunk? fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer?
--- @field result_type? string
--- @field algorithm? string
--- @field ctxlen? integer
--- @field interhunkctxlen? integer
---- @field linematch? any
+--- @field linematch? boolean|integer
--- @field ignore_whitespace? boolean
--- @field ignore_whitespace_change? boolean
--- @field ignore_whitespace_change_at_eol? boolean
diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua
index 81bce50746..fbef6fa3bc 100644
--- a/runtime/lua/vim/_meta/api_keysets_extra.lua
+++ b/runtime/lua/vim/_meta/api_keysets_extra.lua
@@ -73,6 +73,51 @@ error('Cannot require a meta file')
--- @field buflocal? boolean
--- @field buffer? integer
+--- @class vim.api.keyset.create_autocmd.callback_args
+--- @field id integer autocommand id
+--- @field event string name of the triggered event |autocmd-events|
+--- @field group? integer autocommand group id, if any
+--- @field match string expanded value of <amatch>
+--- @field buf integer expanded value of <abuf>
+--- @field file string expanded value of <afile>
+--- @field data? any arbitrary data passed from |nvim_exec_autocmds()| *event-data*
+
+--- @class vim.api.keyset.create_user_command.command_args
+--- @field name string Command name
+---
+--- The args passed to the command, if any <args>
+--- @field args string
+---
+--- The args split by unescaped whitespace
+--- (when more than one argument is allowed), if any <f-args>
+--- @field fargs string[]
+---
+--- Number of arguments |:command-nargs|
+--- @field nargs string
+---
+--- "true" if the command was executed with a ! modifier <bang>
+--- @field bang boolean
+---
+--- The starting line of the command range <line1>
+--- @field line1 integer
+---
+--- The final line of the command range <line2>
+--- @field line2 integer
+---
+--- The number of items in the command range: 0, 1, or 2 <range>
+--- @field range integer
+---
+--- Any count supplied <count>
+--- @field count integer
+--- The optional register, if specified <reg>
+--- @field reg string
+--- Command modifiers, if any <mods>
+--- @field mods string
+---
+--- Command modifiers in a structured format. Has the same structure as the
+--- "mods" key of |nvim_parse_cmd()|.
+--- @field smods table
+
--- @class vim.api.keyset.command_info
--- @field name string
--- @field definition string
@@ -114,6 +159,7 @@ error('Cannot require a meta file')
--- @field bg? integer
--- @field sp? integer
--- @field default? true
+--- @field link? string
--- @field blend? integer
--- @field cterm? vim.api.keyset.hl_info.cterm
@@ -127,6 +173,26 @@ error('Cannot require a meta file')
--- @field force? true
--- @field cterm? vim.api.keyset.hl_info.cterm
+--- @class vim.api.keyset.get_keymap
+--- @field abbr? 0|1
+--- @field buffer? 0|1
+--- @field callback? function
+--- @field desc? string
+--- @field expr? 0|1
+--- @field lhs? string
+--- @field lhsraw? string
+--- @field lhsrawalt? string
+--- @field lnum? integer
+--- @field mode? string
+--- @field mode_bits? integer
+--- @field noremap? 0|1
+--- @field nowait? 0|1
+--- @field rhs? string
+--- @field script? 0|1
+--- @field scriptversion? integer
+--- @field sid? integer
+--- @field silent? 0|1
+
--- @class vim.api.keyset.get_mode
--- @field blocking boolean
--- @field mode string
diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua
index b8779b66fe..9fa2e242c4 100644
--- a/runtime/lua/vim/_meta/builtin.lua
+++ b/runtime/lua/vim/_meta/builtin.lua
@@ -233,9 +233,8 @@ function vim.wait(time, callback, interval, fast_only) end
--- {callback} receives event name plus additional parameters. See |ui-popupmenu|
--- and the sections below for event format for respective events.
---
---- Callbacks for `msg_show` events are executed in |api-fast| context unless
---- Nvim will wait for input, in which case messages should be shown
---- immediately.
+--- Callbacks for `msg_show` events are executed in |api-fast| context; showing
+--- the message should be scheduled.
---
--- Excessive errors inside the callback will result in forced detachment.
---
diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
index 07d89aafc8..0d59de5fa6 100644
--- a/runtime/lua/vim/_meta/json.lua
+++ b/runtime/lua/vim/_meta/json.lua
@@ -25,15 +25,18 @@ vim.json = {}
---
---@param str string Stringified JSON data.
---@param opts? table<string,any> Options table with keys:
---- - luanil: (table) Table with keys:
---- * object: (boolean) When true, converts `null` in JSON objects
---- to Lua `nil` instead of |vim.NIL|.
---- * array: (boolean) When true, converts `null` in JSON arrays
---- to Lua `nil` instead of |vim.NIL|.
+--- - luanil: (table) Table with keys:
+--- - object: (boolean) When true, converts `null` in JSON objects
+--- to Lua `nil` instead of |vim.NIL|.
+--- - array: (boolean) When true, converts `null` in JSON arrays
+--- to Lua `nil` instead of |vim.NIL|.
---@return any
function vim.json.decode(str, opts) end
--- Encodes (or "packs") Lua object {obj} as JSON in a Lua string.
---@param obj any
+---@param opts? table<string,any> Options table with keys:
+--- - escape_slash: (boolean) (default false) Escape slash
+--- characters "/" in string values.
---@return string
-function vim.json.encode(obj) end
+function vim.json.encode(obj, opts) end
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index cb783720ac..52c556867f 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -52,7 +52,7 @@ vim.go.ari = vim.go.allowrevins
--- set to one of CJK locales. See Unicode Standard Annex #11
--- (https://www.unicode.org/reports/tr11).
---
---- @type string
+--- @type 'single'|'double'
vim.o.ambiwidth = "single"
vim.o.ambw = vim.o.ambiwidth
vim.go.ambiwidth = vim.o.ambiwidth
@@ -208,7 +208,7 @@ vim.go.awa = vim.go.autowriteall
--- will change. To use other settings, place ":highlight" commands AFTER
--- the setting of the 'background' option.
---
---- @type string
+--- @type 'light'|'dark'
vim.o.background = "dark"
vim.o.bg = vim.o.background
vim.go.background = vim.o.background
@@ -595,7 +595,7 @@ vim.wo.briopt = vim.wo.breakindentopt
--- This option is used together with 'buftype' and 'swapfile' to specify
--- special kinds of buffers. See `special-buffers`.
---
---- @type string
+--- @type ''|'hide'|'unload'|'delete'|'wipe'
vim.o.bufhidden = ""
vim.o.bh = vim.o.bufhidden
vim.bo.bufhidden = vim.o.bufhidden
@@ -658,7 +658,7 @@ vim.bo.bl = vim.bo.buflisted
--- without saving. For writing there must be matching `BufWriteCmd|,
--- |FileWriteCmd` or `FileAppendCmd` autocommands.
---
---- @type string
+--- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt'
vim.o.buftype = ""
vim.o.bt = vim.o.buftype
vim.bo.buftype = vim.o.buftype
@@ -1086,9 +1086,9 @@ vim.go.cia = vim.go.completeitemalign
--- a match from the menu. Only works in combination with
--- "menu" or "menuone". No effect if "longest" is present.
---
---- noselect Do not select a match in the menu, force the user to
---- select one from the menu. Only works in combination with
---- "menu" or "menuone".
+--- noselect Same as "noinsert", except that no menu item is
+--- pre-selected. If both "noinsert" and "noselect" are
+--- present, "noselect" has precedence.
---
--- fuzzy Enable `fuzzy-matching` for completion candidates. This
--- allows for more flexible and intuitive matching, where
@@ -1098,6 +1098,16 @@ vim.go.cia = vim.go.completeitemalign
--- list of alternatives, but not how the candidates are
--- collected (using different completion types).
---
+--- nosort Disable sorting of completion candidates based on fuzzy
+--- scores when "fuzzy" is enabled. Candidates will appear
+--- in their original order.
+---
+--- preinsert
+--- Preinsert the portion of the first candidate word that is
+--- not part of the current completion leader and using the
+--- `hl-ComplMatchIns` highlight group. Does not work when
+--- "fuzzy" is also included.
+---
--- @type string
vim.o.completeopt = "menu,preview"
vim.o.cot = vim.o.completeopt
@@ -1118,7 +1128,7 @@ vim.go.cot = vim.go.completeopt
--- For Insert mode completion the buffer-local value is used. For
--- command line completion the global value is used.
---
---- @type string
+--- @type ''|'slash'|'backslash'
vim.o.completeslash = ""
vim.o.csl = vim.o.completeslash
vim.bo.completeslash = vim.o.completeslash
@@ -1621,11 +1631,20 @@ vim.go.dex = vim.go.diffexpr
--- Option settings for diff mode. It can consist of the following items.
--- All are optional. Items must be separated by a comma.
---
---- filler Show filler lines, to keep the text
---- synchronized with a window that has inserted
---- lines at the same position. Mostly useful
---- when windows are side-by-side and 'scrollbind'
---- is set.
+--- algorithm:{text} Use the specified diff algorithm with the
+--- internal diff engine. Currently supported
+--- algorithms are:
+--- myers the default algorithm
+--- minimal spend extra time to generate the
+--- smallest possible diff
+--- patience patience diff algorithm
+--- histogram histogram diff algorithm
+---
+--- closeoff When a window is closed where 'diff' is set
+--- and there is only one window remaining in the
+--- same tab page with 'diff' set, execute
+--- `:diffoff` in that window. This undoes a
+--- `:diffsplit` command.
---
--- context:{n} Use a context of {n} lines between a change
--- and a fold that contains unchanged lines.
@@ -1636,6 +1655,23 @@ vim.go.dex = vim.go.diffexpr
--- value (999999) to disable folding completely.
--- See `fold-diff`.
---
+--- filler Show filler lines, to keep the text
+--- synchronized with a window that has inserted
+--- lines at the same position. Mostly useful
+--- when windows are side-by-side and 'scrollbind'
+--- is set.
+---
+--- foldcolumn:{n} Set the 'foldcolumn' option to {n} when
+--- starting diff mode. Without this 2 is used.
+---
+--- followwrap Follow the 'wrap' option and leave as it is.
+---
+--- horizontal Start diff mode with horizontal splits (unless
+--- explicitly specified otherwise).
+---
+--- hiddenoff Do not use diff mode for a buffer when it
+--- becomes hidden.
+---
--- iblank Ignore changes where lines are all blank. Adds
--- the "-B" flag to the "diff" command if
--- 'diffexpr' is empty. Check the documentation
@@ -1649,6 +1685,17 @@ vim.go.dex = vim.go.diffexpr
--- are considered the same. Adds the "-i" flag
--- to the "diff" command if 'diffexpr' is empty.
---
+--- indent-heuristic
+--- Use the indent heuristic for the internal
+--- diff library.
+---
+--- internal Use the internal diff library. This is
+--- ignored when 'diffexpr' is set. *E960*
+--- When running out of memory when writing a
+--- buffer this item will be ignored for diffs
+--- involving that buffer. Set the 'verbose'
+--- option to see when this happens.
+---
--- iwhite Ignore changes in amount of white space. Adds
--- the "-b" flag to the "diff" command if
--- 'diffexpr' is empty. Check the documentation
@@ -1668,56 +1715,19 @@ vim.go.dex = vim.go.diffexpr
--- of the "diff" command for what this does
--- exactly.
---
---- horizontal Start diff mode with horizontal splits (unless
---- explicitly specified otherwise).
+--- linematch:{n} Align and mark changes between the most
+--- similar lines between the buffers. When the
+--- total number of lines in the diff hunk exceeds
+--- {n}, the lines will not be aligned because for
+--- very large diff hunks there will be a
+--- noticeable lag. A reasonable setting is
+--- "linematch:60", as this will enable alignment
+--- for a 2 buffer diff hunk of 30 lines each,
+--- or a 3 buffer diff hunk of 20 lines each.
---
--- vertical Start diff mode with vertical splits (unless
--- explicitly specified otherwise).
---
---- closeoff When a window is closed where 'diff' is set
---- and there is only one window remaining in the
---- same tab page with 'diff' set, execute
---- `:diffoff` in that window. This undoes a
---- `:diffsplit` command.
----
---- hiddenoff Do not use diff mode for a buffer when it
---- becomes hidden.
----
---- foldcolumn:{n} Set the 'foldcolumn' option to {n} when
---- starting diff mode. Without this 2 is used.
----
---- followwrap Follow the 'wrap' option and leave as it is.
----
---- internal Use the internal diff library. This is
---- ignored when 'diffexpr' is set. *E960*
---- When running out of memory when writing a
---- buffer this item will be ignored for diffs
---- involving that buffer. Set the 'verbose'
---- option to see when this happens.
----
---- indent-heuristic
---- 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:
---- myers the default algorithm
---- minimal spend extra time to generate the
---- smallest possible diff
---- patience patience diff algorithm
---- histogram histogram diff algorithm
----
--- Examples:
---
--- ```vim
@@ -1824,7 +1834,7 @@ vim.go.dy = vim.go.display
--- hor horizontally, height of windows is not affected
--- both width and height of windows is affected
---
---- @type string
+--- @type 'both'|'ver'|'hor'
vim.o.eadirection = "both"
vim.o.ead = vim.o.eadirection
vim.go.eadirection = vim.o.eadirection
@@ -2126,7 +2136,7 @@ vim.go.fencs = vim.go.fileencodings
--- option is set, because the file would be different when written.
--- This option cannot be changed when 'modifiable' is off.
---
---- @type string
+--- @type 'unix'|'dos'|'mac'
vim.o.fileformat = "unix"
vim.o.ff = vim.o.fileformat
vim.bo.fileformat = vim.o.fileformat
@@ -2382,7 +2392,7 @@ vim.go.fcl = vim.go.foldclose
--- "[1-9]": to display a fixed number of columns
--- See `folding`.
---
---- @type string
+--- @type 'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
vim.o.foldcolumn = "0"
vim.o.fdc = vim.o.foldcolumn
vim.wo.foldcolumn = vim.o.foldcolumn
@@ -2479,7 +2489,7 @@ vim.wo.fmr = vim.wo.foldmarker
--- `fold-syntax` syntax Syntax highlighting items specify folds.
--- `fold-diff` diff Fold text that is not changed.
---
---- @type string
+--- @type 'manual'|'expr'|'marker'|'indent'|'syntax'|'diff'
vim.o.foldmethod = "manual"
vim.o.fdm = vim.o.foldmethod
vim.wo.foldmethod = vim.o.foldmethod
@@ -2783,6 +2793,7 @@ vim.go.gp = vim.go.grepprg
--- ci Command-line Insert mode
--- cr Command-line Replace mode
--- sm showmatch in Insert mode
+--- t Terminal mode
--- a all modes
--- The argument-list is a dash separated list of these arguments:
--- hor{N} horizontal bar, {N} percent of the character height
@@ -2802,7 +2813,8 @@ vim.go.gp = vim.go.grepprg
--- ```vim
--- set guicursor=n:blinkon0
--- ```
---- - Default is "blinkon0" for each mode.
+---
+--- Default is "blinkon0" for each mode.
--- {group-name}
--- Highlight group that decides the color and font of the
--- cursor.
@@ -2848,7 +2860,7 @@ vim.go.gp = vim.go.grepprg
---
---
--- @type string
-vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20"
+vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor"
vim.o.gcr = vim.o.guicursor
vim.go.guicursor = vim.o.guicursor
vim.go.gcr = vim.go.guicursor
@@ -3016,7 +3028,7 @@ vim.go.hid = vim.go.hidden
--- A history of ":" commands, and a history of previous search patterns
--- is remembered. This option decides how many entries may be stored in
---- each of these histories (see `cmdline-editing` and 'msghistory' for
+--- each of these histories (see `cmdline-editing` and 'messagesopt' for
--- the number of messages to remember).
--- The maximum value is 10000.
---
@@ -3142,7 +3154,7 @@ vim.bo.ims = vim.bo.imsearch
--- 'redrawtime') then 'inccommand' is automatically disabled until
--- `Command-line-mode` is done.
---
---- @type string
+--- @type 'nosplit'|'split'|''
vim.o.inccommand = "nosplit"
vim.o.icm = vim.o.inccommand
vim.go.inccommand = vim.o.inccommand
@@ -4084,6 +4096,31 @@ vim.o.mis = vim.o.menuitems
vim.go.menuitems = vim.o.menuitems
vim.go.mis = vim.go.menuitems
+--- Option settings for outputting messages. It can consist of the
+--- following items. Items must be separated by a comma.
+---
+--- hit-enter Use a `hit-enter` prompt when the message is longer than
+--- 'cmdheight' size.
+---
+--- wait:{n} Instead of using a `hit-enter` prompt, simply wait for
+--- {n} milliseconds so that the user has a chance to read
+--- the message. The maximum value of {n} is 10000. Use
+--- 0 to disable the wait (but then the user may miss an
+--- important message).
+--- This item is ignored when "hit-enter" is present, but
+--- required when "hit-enter" is not present.
+---
+--- history:{n} Determines how many entries are remembered in the
+--- `:messages` history. The maximum value is 10000.
+--- Setting it to zero clears the message history.
+--- This item must always be present.
+---
+--- @type string
+vim.o.messagesopt = "hit-enter,history:500"
+vim.o.mopt = vim.o.messagesopt
+vim.go.messagesopt = vim.o.messagesopt
+vim.go.mopt = vim.go.messagesopt
+
--- Parameters for `:mkspell`. This tunes when to start compressing the
--- word tree. Compression can be slow when there are many words, but
--- it's needed to avoid running out of memory. The amount of memory used
@@ -4327,7 +4364,7 @@ vim.go.mh = vim.go.mousehide
--- "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
--- "g<RightMouse>" is "<C-RightMouse> ("CTRL-T")
---
---- @type string
+--- @type 'extend'|'popup'|'popup_setpos'
vim.o.mousemodel = "popup_setpos"
vim.o.mousem = vim.o.mousemodel
vim.go.mousemodel = vim.o.mousemodel
@@ -4379,15 +4416,6 @@ vim.o.mouset = vim.o.mousetime
vim.go.mousetime = vim.o.mousetime
vim.go.mouset = vim.go.mousetime
---- Determines how many entries are remembered in the `:messages` history.
---- The maximum value is 10000.
----
---- @type integer
-vim.o.msghistory = 500
-vim.o.mhi = vim.o.msghistory
-vim.go.msghistory = vim.o.msghistory
-vim.go.mhi = vim.go.msghistory
-
--- This defines what bases Vim will consider for numbers when using the
--- CTRL-A and CTRL-X commands for adding to and subtracting from a number
--- respectively; see `CTRL-A` for more info on these commands.
@@ -4827,8 +4855,8 @@ vim.go.redrawdebug = vim.o.redrawdebug
vim.go.rdb = vim.go.redrawdebug
--- Time in milliseconds for redrawing the display. Applies to
---- 'hlsearch', 'inccommand', `:match` highlighting and syntax
---- highlighting.
+--- 'hlsearch', 'inccommand', `:match` highlighting, syntax highlighting,
+--- and async `LanguageTree:parse()`.
--- When redrawing takes more than this many milliseconds no further
--- matches will be highlighted.
--- For syntax highlighting the time applies per window. When over the
@@ -4994,6 +5022,7 @@ vim.go.ruf = vim.go.rulerformat
--- indent/ indent scripts `indent-expression`
--- keymap/ key mapping files `mbyte-keymap`
--- lang/ menu translations `:menutrans`
+--- lsp/ LSP client configurations `lsp-config`
--- lua/ `Lua` plugins
--- menu.vim GUI menus `menu.vim`
--- pack/ packages `:packadd`
@@ -5197,11 +5226,13 @@ vim.go.sect = vim.go.sections
--- selection.
--- When "old" is used and 'virtualedit' allows the cursor to move past
--- the end of line the line break still isn't included.
+--- When "exclusive" is used, cursor position in visual mode will be
+--- adjusted for inclusive motions `inclusive-motion-selection-exclusive`.
--- Note that when "exclusive" is used and selecting from the end
--- backwards, you cannot include the last character of a line, when
--- starting in Normal mode and 'virtualedit' empty.
---
---- @type string
+--- @type 'inclusive'|'exclusive'|'old'
vim.o.selection = "inclusive"
vim.o.sel = vim.o.selection
vim.go.selection = vim.o.selection
@@ -5767,7 +5798,7 @@ vim.go.sc = vim.go.showcmd
--- place the text. Without a custom 'statusline' or 'tabline' it will be
--- displayed in a convenient location.
---
---- @type string
+--- @type 'last'|'statusline'|'tabline'
vim.o.showcmdloc = "last"
vim.o.sloc = vim.o.showcmdloc
vim.go.showcmdloc = vim.o.showcmdloc
@@ -5899,7 +5930,7 @@ vim.go.siso = vim.go.sidescrolloff
--- "number" display signs in the 'number' column. If the number
--- column is not present, then behaves like "auto".
---
---- @type string
+--- @type 'yes'|'no'|'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'yes:1'|'yes:2'|'yes:3'|'yes:4'|'yes:5'|'yes:6'|'yes:7'|'yes:8'|'yes:9'|'number'
vim.o.signcolumn = "auto"
vim.o.scl = vim.o.signcolumn
vim.wo.signcolumn = vim.o.signcolumn
@@ -6207,7 +6238,7 @@ vim.go.sb = vim.go.splitbelow
--- with the previous cursor position. For "screen", the text cannot always
--- be kept on the same screen line when 'wrap' is enabled.
---
---- @type string
+--- @type 'cursor'|'screen'|'topline'
vim.o.splitkeep = "cursor"
vim.o.spk = vim.o.splitkeep
vim.go.splitkeep = vim.o.splitkeep
@@ -6310,6 +6341,7 @@ vim.wo.stc = vim.wo.statuscolumn
--- All fields except the {item} are optional. A single percent sign can
--- be given as "%%".
---
+--- *stl-%!*
--- When the option starts with "%!" then it is used as an expression,
--- evaluated and the result is used as the option value. Example:
---
@@ -6854,7 +6886,7 @@ vim.go.tbs = vim.go.tagbsearch
--- match Match case
--- smart Ignore case unless an upper case letter is used
---
---- @type string
+--- @type 'followic'|'ignore'|'match'|'followscs'|'smart'
vim.o.tagcase = "followic"
vim.o.tc = vim.o.tagcase
vim.bo.tagcase = vim.o.tagcase
@@ -7122,6 +7154,13 @@ vim.go.titleold = vim.o.titleold
--- expanded according to the rules used for 'statusline'. If it contains
--- an invalid '%' format, the value is used as-is and no error or warning
--- will be given when the value is set.
+---
+--- The default behaviour is equivalent to:
+---
+--- ```vim
+--- set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim
+--- ```
+---
--- This option cannot be set in a modeline when 'modelineexpr' is off.
---
--- Example:
@@ -7729,7 +7768,7 @@ vim.go.wop = vim.go.wildoptions
--- key is never used for the menu.
--- This option is not used for <F10>; on Win32.
---
---- @type string
+--- @type 'yes'|'menu'|'no'
vim.o.winaltkeys = "menu"
vim.o.wak = vim.o.winaltkeys
vim.go.winaltkeys = vim.o.winaltkeys
diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua
index 5eb15e1eee..098c0e907a 100644
--- a/runtime/lua/vim/_meta/vimfn.lua
+++ b/runtime/lua/vim/_meta/vimfn.lua
@@ -1023,16 +1023,22 @@ function vim.fn.complete_check() end
--- See |complete_info_mode| for the values.
--- pum_visible |TRUE| if popup menu is visible.
--- See |pumvisible()|.
---- items List of completion matches. Each item is a
---- dictionary containing the entries "word",
+--- items List of all completion candidates. Each item
+--- is a dictionary containing the entries "word",
--- "abbr", "menu", "kind", "info" and "user_data".
--- See |complete-items|.
+--- matches Same as "items", but only returns items that
+--- are matching current query. If both "matches"
+--- and "items" are in "what", the returned list
+--- will still be named "items", but each item
+--- will have an additional "match" field.
--- selected Selected item index. First index is zero.
--- Index is -1 if no item is selected (showing
--- typed text only, or the last completion after
--- no item is selected when using the <Up> or
--- <Down> keys)
---- inserted Inserted string. [NOT IMPLEMENTED YET]
+--- completed Return a dictionary containing the entries of
+--- the currently selected index item.
--- preview_winid Info floating preview window id.
--- preview_bufnr Info floating preview buffer id.
---
@@ -1147,8 +1153,9 @@ function vim.fn.confirm(msg, choices, default, type) end
--- A |Dictionary| is copied in a similar way as a |List|.
--- Also see |deepcopy()|.
---
---- @param expr any
---- @return any
+--- @generic T
+--- @param expr T
+--- @return T
function vim.fn.copy(expr) end
--- Return the cosine of {expr}, measured in radians, as a |Float|.
@@ -1228,7 +1235,7 @@ function vim.fn.ctxpush(types) end
---
--- @param context table
--- @param index? integer
---- @return any
+--- @return integer
function vim.fn.ctxset(context, index) end
--- Returns the size of the |context-stack|.
@@ -1308,9 +1315,10 @@ function vim.fn.debugbreak(pid) end
--- {noref} set to 1 will fail.
--- Also see |copy()|.
---
---- @param expr any
+--- @generic T
+--- @param expr T
--- @param noref? boolean
---- @return any
+--- @return T
function vim.fn.deepcopy(expr, noref) end
--- Without {flags} or with {flags} empty: Deletes the file by the
@@ -1421,7 +1429,7 @@ function vim.fn.dictwatcherdel(dict, pattern, callback) end
--- editing another buffer to set 'filetype' and load a syntax
--- file.
---
---- @return any
+--- @return integer
function vim.fn.did_filetype() end
--- Returns the number of filler lines above line {lnum}.
@@ -1433,7 +1441,7 @@ function vim.fn.did_filetype() end
--- Returns 0 if the current window is not in diff mode.
---
--- @param lnum integer
---- @return any
+--- @return integer
function vim.fn.diff_filler(lnum) end
--- Returns the highlight ID for diff mode at line {lnum} column
@@ -1468,7 +1476,7 @@ function vim.fn.diff_hlID(lnum, col) end
--- <
---
--- @param chars string
---- @return any
+--- @return string
function vim.fn.digraph_get(chars) end
--- Return a list of digraphs. If the {listall} argument is given
@@ -1486,7 +1494,7 @@ function vim.fn.digraph_get(chars) end
--- <
---
--- @param listall? boolean
---- @return any
+--- @return string[][]
function vim.fn.digraph_getlist(listall) end
--- Add digraph {chars} to the list. {chars} must be a string
@@ -1538,7 +1546,7 @@ function vim.fn.digraph_setlist(digraphlist) end
--- - A |Blob| is empty when its length is zero.
---
--- @param expr any
---- @return any
+--- @return integer
function vim.fn.empty(expr) end
--- Return all of environment variables as dictionary. You can
@@ -1561,7 +1569,7 @@ function vim.fn.environ() end
---
--- @param string string
--- @param chars string
---- @return any
+--- @return string
function vim.fn.escape(string, chars) end
--- Evaluate {string} and return the result. Especially useful to
@@ -2368,7 +2376,7 @@ function vim.fn.foldtextresult(lnum) end
---
--- @param expr1 string|table
--- @param expr2 string|function
---- @return any
+--- @return string|table
function vim.fn.foreach(expr1, expr2) end
--- Get the full command name from a short abbreviated command
@@ -2675,7 +2683,7 @@ function vim.fn.getbufinfo(dict) end
--- @param buf integer|string
--- @param lnum integer
--- @param end_? integer
---- @return any
+--- @return string[]
function vim.fn.getbufline(buf, lnum, end_) end
--- Just like `getbufline()` but only get one line and return it
@@ -2740,12 +2748,14 @@ function vim.fn.getcellwidths() end
function vim.fn.getchangelist(buf) end
--- Get a single character from the user or input stream.
---- If {expr} is omitted, wait until a character is available.
+--- If {expr} is omitted or is -1, wait until a character is
+--- available.
--- If {expr} is 0, only get a character when one is available.
--- Return zero otherwise.
--- If {expr} is 1, only check if a character is available, it is
--- not consumed. Return zero if no character available.
---- If you prefer always getting a string use |getcharstr()|.
+--- If you prefer always getting a string use |getcharstr()|, or
+--- specify |FALSE| as "number" in {opts}.
---
--- Without {expr} and when {expr} is 0 a whole character or
--- special key is returned. If it is a single character, the
@@ -2755,7 +2765,8 @@ function vim.fn.getchangelist(buf) end
--- starting with 0x80 (decimal: 128). This is the same value as
--- the String "\<Key>", e.g., "\<Left>". The returned value is
--- also a String when a modifier (shift, control, alt) was used
---- that is not included in the character.
+--- that is not included in the character. |keytrans()| can also
+--- be used to convert a returned String into a readable form.
---
--- When {expr} is 0 and Esc is typed, there will be a short delay
--- while Vim waits to see if this is the start of an escape
@@ -2767,6 +2778,32 @@ function vim.fn.getchangelist(buf) end
---
--- Use getcharmod() to obtain any additional modifiers.
---
+--- The optional argument {opts} is a Dict and supports the
+--- following items:
+---
+--- cursor A String specifying cursor behavior
+--- when waiting for a character.
+--- "hide": hide the cursor.
+--- "keep": keep current cursor unchanged.
+--- "msg": move cursor to message area.
+--- (default: automagically decide
+--- between "keep" and "msg")
+---
+--- number If |TRUE|, return a Number when getting
+--- a single character.
+--- If |FALSE|, the return value is always
+--- converted to a String, and an empty
+--- String (instead of 0) is returned when
+--- no character is available.
+--- (default: |TRUE|)
+---
+--- simplify If |TRUE|, include modifiers in the
+--- character if possible. E.g., return
+--- the same value for CTRL-I and <Tab>.
+--- If |FALSE|, don't include modifiers in
+--- the character.
+--- (default: |TRUE|)
+---
--- When the user clicks a mouse button, the mouse event will be
--- returned. The position can then be found in |v:mouse_col|,
--- |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -2803,9 +2840,10 @@ function vim.fn.getchangelist(buf) end
--- endfunction
--- <
---
---- @param expr? 0|1
---- @return integer
-function vim.fn.getchar(expr) end
+--- @param expr? -1|0|1
+--- @param opts? table
+--- @return integer|string
+function vim.fn.getchar(expr, opts) end
--- The result is a Number which is the state of the modifiers for
--- the last obtained character with getchar() or in another way.
@@ -2864,20 +2902,13 @@ function vim.fn.getcharpos(expr) end
--- @return table
function vim.fn.getcharsearch() end
---- Get a single character from the user or input stream as a
---- string.
---- If {expr} is omitted, wait until a character is available.
---- If {expr} is 0 or false, only get a character when one is
---- available. Return an empty string otherwise.
---- If {expr} is 1 or true, only check if a character is
---- available, it is not consumed. Return an empty string
---- if no character is available.
---- Otherwise this works like |getchar()|, except that a number
---- result is converted to a string.
----
---- @param expr? 0|1
+--- The same as |getchar()|, except that this always returns a
+--- String, and "number" isn't allowed in {opts}.
+---
+--- @param expr? -1|0|1
+--- @param opts? table
--- @return string
-function vim.fn.getcharstr(expr) end
+function vim.fn.getcharstr(expr, opts) end
--- Return completion pattern of the current command-line.
--- Only works when the command line is being edited, thus
@@ -2943,7 +2974,7 @@ function vim.fn.getcmdprompt() end
--- Also see |getcmdpos()|, |setcmdpos()|, |getcmdline()| and
--- |setcmdline()|.
---
---- @return any
+--- @return integer
function vim.fn.getcmdscreenpos() end
--- Return the current command-line type. Possible return values
@@ -3763,6 +3794,20 @@ function vim.fn.getregtype(regname) end
--- @return vim.fn.getscriptinfo.ret[]
function vim.fn.getscriptinfo(opts) end
+--- Returns the current stack trace of Vim scripts.
+--- Stack trace is a |List|, of which each item is a |Dictionary|
+--- with the following items:
+--- funcref The funcref if the stack is at a function,
+--- otherwise this item is omitted.
+--- event The string of the event description if the
+--- stack is at an autocmd event, otherwise this
+--- item is omitted.
+--- lnum The line number in the script on the stack.
+--- filepath The file path of the script on the stack.
+---
+--- @return table[]
+function vim.fn.getstacktrace() end
+
--- If {tabnr} is not specified, then information about all the
--- tab pages is returned as a |List|. Each List item is a
--- |Dictionary|. Otherwise, {tabnr} specifies the tab page
@@ -3869,7 +3914,7 @@ function vim.fn.gettagstack(winnr) end
--- strings.
---
--- @param text string
---- @return any
+--- @return string
function vim.fn.gettext(text) end
--- Returns information about windows as a |List| with Dictionaries.
@@ -3885,6 +3930,8 @@ function vim.fn.gettext(text) end
--- botline last complete displayed buffer line
--- bufnr number of buffer in the window
--- height window height (excluding winbar)
+--- leftcol first column displayed; only used when
+--- 'wrap' is off
--- loclist 1 if showing a location list
--- quickfix 1 if quickfix or location list window
--- terminal 1 if a terminal window
@@ -4018,7 +4065,7 @@ function vim.fn.glob(expr, nosuf, list, alllinks) end
--- a backslash usually means a path separator.
---
--- @param string string
---- @return any
+--- @return string
function vim.fn.glob2regpat(string) end
--- Perform glob() for String {expr} on all directories in {path}
@@ -4352,7 +4399,7 @@ function vim.fn.hostname() end
--- @param string string
--- @param from string
--- @param to string
---- @return any
+--- @return string
function vim.fn.iconv(string, from, to) end
--- Returns a |String| which is a unique identifier of the
@@ -4372,7 +4419,7 @@ function vim.fn.iconv(string, from, to) end
--- reuse identifiers of the garbage-collected ones.
---
--- @param expr any
---- @return any
+--- @return string
function vim.fn.id(expr) end
--- The result is a Number, which is indent of line {lnum} in the
@@ -4416,7 +4463,7 @@ function vim.fn.indent(lnum) end
--- @param expr any
--- @param start? integer
--- @param ic? boolean
---- @return any
+--- @return integer
function vim.fn.index(object, expr, start, ic) end
--- Returns the index of an item in {object} where {expr} is
@@ -4460,14 +4507,14 @@ function vim.fn.index(object, expr, start, ic) end
--- @param object any
--- @param expr any
--- @param opts? table
---- @return any
+--- @return integer
function vim.fn.indexof(object, expr, opts) end
---
--- @param prompt string
--- @param text? string
--- @param completion? string
---- @return any
+--- @return string
function vim.fn.input(prompt, text, completion) end
--- The result is a String, which is whatever the user typed on
@@ -4581,7 +4628,7 @@ function vim.fn.input(prompt, text, completion) end
--- <
---
--- @param opts table
---- @return any
+--- @return string
function vim.fn.input(opts) end
--- @deprecated
@@ -4616,7 +4663,7 @@ function vim.fn.inputlist(textlist) end
--- called. Calling it more often is harmless though.
--- Returns TRUE when there is nothing to restore, FALSE otherwise.
---
---- @return any
+--- @return integer
function vim.fn.inputrestore() end
--- Preserve typeahead (also from mappings) and clear it, so that
@@ -4626,7 +4673,7 @@ function vim.fn.inputrestore() end
--- many inputrestore() calls.
--- Returns TRUE when out of memory, FALSE otherwise.
---
---- @return any
+--- @return integer
function vim.fn.inputsave() end
--- This function acts much like the |input()| function with but
@@ -4641,7 +4688,7 @@ function vim.fn.inputsave() end
---
--- @param prompt string
--- @param text? string
---- @return any
+--- @return string
function vim.fn.inputsecret(prompt, text) end
--- When {object} is a |List| or a |Blob| insert {item} at the start
@@ -4687,8 +4734,8 @@ function vim.fn.interrupt() end
--- let bits = invert(bits)
--- <
---
---- @param expr number
---- @return any
+--- @param expr integer
+--- @return integer
function vim.fn.invert(expr) end
--- The result is a Number, which is |TRUE| when {path} is an
@@ -4767,7 +4814,7 @@ function vim.fn.isnan(expr) end
--- cases, items() returns a List with the index and the value at
--- the index.
---
---- @param dict any
+--- @param dict table
--- @return any
function vim.fn.items(dict) end
@@ -4801,7 +4848,7 @@ function vim.fn.jobresize(job, width, height) end
--- @return any
function vim.fn.jobsend(...) end
---- Note: Prefer |vim.system()| in Lua (unless using the `pty` option).
+--- Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`).
---
--- Spawns {cmd} as a job.
--- If {cmd} is a List it runs directly (no 'shell').
@@ -4809,8 +4856,11 @@ function vim.fn.jobsend(...) end
--- call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
--- <(See |shell-unquoting| for details.)
---
---- Example: >vim
---- call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
+--- Example: start a job and handle its output: >vim
+--- call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}})
+--- <
+--- Example: start a job in a |terminal| connected to the current buffer: >vim
+--- call jobstart(['nvim', '-h'], {'term':v:true})
--- <
--- Returns |job-id| on success, 0 on invalid arguments (or job
--- table is full), -1 if {cmd}[0] or 'shell' is not executable.
@@ -4875,6 +4925,10 @@ function vim.fn.jobsend(...) end
--- stdin: (string) Either "pipe" (default) to connect the
--- job's stdin to a channel or "null" to disconnect
--- stdin.
+--- term: (boolean) Spawns {cmd} in a new pseudo-terminal session
+--- connected to the current (unmodified) buffer. Implies "pty".
+--- Default "height" and "width" are set to the current window
+--- dimensions. |jobstart()|. Defaults $TERM to "xterm-256color".
--- width: (number) Width of the `pty` terminal.
---
--- {opts} is passed as |self| dictionary to the callback; the
@@ -4888,7 +4942,7 @@ function vim.fn.jobsend(...) end
---
--- @param cmd string|string[]
--- @param opts? table
---- @return any
+--- @return integer
function vim.fn.jobstart(cmd, opts) end
--- Stop |job-id| {id} by sending SIGTERM to the job process. If
@@ -4901,7 +4955,7 @@ function vim.fn.jobstart(cmd, opts) end
--- exited or stopped.
---
--- @param id integer
---- @return any
+--- @return integer
function vim.fn.jobstop(id) end
--- Waits for jobs and their |on_exit| handlers to complete.
@@ -4926,7 +4980,7 @@ function vim.fn.jobstop(id) end
---
--- @param jobs integer[]
--- @param timeout? integer
---- @return any
+--- @return integer[]
function vim.fn.jobwait(jobs, timeout) end
--- Join the items in {list} together into one String.
@@ -4941,7 +4995,7 @@ function vim.fn.jobwait(jobs, timeout) end
---
--- @param list any[]
--- @param sep? string
---- @return any
+--- @return string
function vim.fn.join(list, sep) end
--- Convert {expr} from JSON object. Accepts |readfile()|-style
@@ -4974,14 +5028,14 @@ function vim.fn.json_decode(expr) end
--- |Blob|s are converted to arrays of the individual bytes.
---
--- @param expr any
---- @return any
+--- @return string
function vim.fn.json_encode(expr) end
--- Return a |List| with all the keys of {dict}. The |List| is in
--- arbitrary order. Also see |items()| and |values()|.
---
--- @param dict table
---- @return any
+--- @return string[]
function vim.fn.keys(dict) end
--- Turn the internal byte representation of keys into a form that
@@ -4991,7 +5045,7 @@ function vim.fn.keys(dict) end
--- < <C-Home>
---
--- @param string string
---- @return any
+--- @return string
function vim.fn.keytrans(string) end
--- @deprecated
@@ -5010,8 +5064,8 @@ function vim.fn.last_buffer_nr() end
--- |Dictionary| is returned.
--- Otherwise an error is given and returns zero.
---
---- @param expr any
---- @return any
+--- @param expr any[]
+--- @return integer
function vim.fn.len(expr) end
--- Call function {funcname} in the run-time library {libname}
@@ -5122,7 +5176,7 @@ function vim.fn.line2byte(lnum) end
--- When {lnum} is invalid, -1 is returned.
---
--- @param lnum integer
---- @return any
+--- @return integer
function vim.fn.lispindent(lnum) end
--- Return a Blob concatenating all the number values in {list}.
@@ -5135,7 +5189,7 @@ function vim.fn.lispindent(lnum) end
--- |blob2list()| does the opposite.
---
--- @param list any[]
---- @return any
+--- @return string
function vim.fn.list2blob(list) end
--- Convert each number in {list} to a character string can
@@ -5155,13 +5209,13 @@ function vim.fn.list2blob(list) end
---
--- @param list any[]
--- @param utf8? boolean
---- @return any
+--- @return string
function vim.fn.list2str(list, utf8) end
--- Return the current time, measured as seconds since 1st Jan
--- 1970. See also |strftime()|, |strptime()| and |getftime()|.
---
---- @return any
+--- @return integer
function vim.fn.localtime() end
--- Return the natural logarithm (base e) of {expr} as a |Float|.
@@ -5175,7 +5229,7 @@ function vim.fn.localtime() end
--- < 5.0
---
--- @param expr number
---- @return any
+--- @return number
function vim.fn.log(expr) end
--- Return the logarithm of Float {expr} to base 10 as a |Float|.
@@ -5188,7 +5242,7 @@ function vim.fn.log(expr) end
--- < -2.0
---
--- @param expr number
---- @return any
+--- @return number
function vim.fn.log10(expr) end
--- {expr1} must be a |List|, |String|, |Blob| or |Dictionary|.
@@ -5950,7 +6004,7 @@ function vim.fn.matchstrpos(expr, pat, start, count) end
--- an error. An empty |List| or |Dictionary| results in zero.
---
--- @param expr any
---- @return any
+--- @return number
function vim.fn.max(expr) end
--- Returns a |List| of |Dictionaries| describing |menus| (defined
@@ -6088,7 +6142,7 @@ function vim.fn.menu_info(name, mode) end
--- an error. An empty |List| or |Dictionary| results in zero.
---
--- @param expr any
---- @return any
+--- @return number
function vim.fn.min(expr) end
--- Create directory {name}.
@@ -6133,7 +6187,7 @@ function vim.fn.min(expr) end
--- @param name string
--- @param flags? string
--- @param prot? string
---- @return any
+--- @return integer
function vim.fn.mkdir(name, flags, prot) end
--- Return a string that indicates the current mode.
@@ -6296,7 +6350,7 @@ function vim.fn.msgpackparse(data) end
--- See also |prevnonblank()|.
---
--- @param lnum integer
---- @return any
+--- @return integer
function vim.fn.nextnonblank(lnum) end
--- Return a string with a single character, which has the number
@@ -6315,7 +6369,7 @@ function vim.fn.nextnonblank(lnum) end
---
--- @param expr integer
--- @param utf8? boolean
---- @return any
+--- @return string
function vim.fn.nr2char(expr, utf8) end
--- Bitwise OR on the two arguments. The arguments are converted
@@ -6349,7 +6403,7 @@ vim.fn['or'] = function(expr, expr1) end
---
--- @param path string
--- @param len? integer
---- @return any
+--- @return string
function vim.fn.pathshorten(path, len) end
--- Evaluate |perl| expression {expr} and return its result
@@ -6383,7 +6437,7 @@ function vim.fn.perleval(expr) end
---
--- @param x number
--- @param y number
---- @return any
+--- @return number
function vim.fn.pow(x, y) end
--- Return the line number of the first line at or above {lnum}
@@ -6395,7 +6449,7 @@ function vim.fn.pow(x, y) end
--- Also see |nextnonblank()|.
---
--- @param lnum integer
---- @return any
+--- @return integer
function vim.fn.prevnonblank(lnum) end
--- Return a String with {fmt}, where "%" items are replaced by
@@ -7014,10 +7068,11 @@ function vim.fn.readfile(fname, type, max) end
--- echo reduce('xyz', { acc, val -> acc .. ',' .. val })
--- <
---
+--- @generic T
--- @param object any
---- @param func function
+--- @param func fun(accumulator: T, current: any): any
--- @param initial? any
---- @return any
+--- @return T
function vim.fn.reduce(object, func, initial) end
--- Returns the single letter name of the register being executed.
@@ -7170,7 +7225,7 @@ function vim.fn.remove(dict, key) end
---
--- @param from string
--- @param to string
---- @return any
+--- @return integer
function vim.fn.rename(from, to) end
--- Repeat {expr} {count} times and return the concatenated
@@ -7200,7 +7255,7 @@ vim.fn['repeat'] = function(expr, count) end
--- path name) and also keeps a trailing path separator.
---
--- @param filename string
---- @return any
+--- @return string
function vim.fn.resolve(filename) end
--- Reverse the order of items in {object}. {object} can be a
@@ -7213,8 +7268,9 @@ function vim.fn.resolve(filename) end
--- let revlist = reverse(copy(mylist))
--- <
---
---- @param object any
---- @return any
+--- @generic T
+--- @param object T[]
+--- @return T[]
function vim.fn.reverse(object) end
--- Round off {expr} to the nearest integral value and return it
@@ -7231,7 +7287,7 @@ function vim.fn.reverse(object) end
--- < -5.0
---
--- @param expr number
---- @return any
+--- @return number
function vim.fn.round(expr) end
--- Sends {event} to {channel} via |RPC| and returns immediately.
@@ -7242,9 +7298,9 @@ function vim.fn.round(expr) end
---
--- @param channel integer
--- @param event string
---- @param args? any
---- @return any
-function vim.fn.rpcnotify(channel, event, args) end
+--- @param ... any
+--- @return integer
+function vim.fn.rpcnotify(channel, event, ...) end
--- Sends a request to {channel} to invoke {method} via
--- |RPC| and blocks until a response is received.
@@ -7254,9 +7310,9 @@ function vim.fn.rpcnotify(channel, event, args) end
---
--- @param channel integer
--- @param method string
---- @param args? any
+--- @param ... any
--- @return any
-function vim.fn.rpcrequest(channel, method, args) end
+function vim.fn.rpcrequest(channel, method, ...) end
--- @deprecated
--- Deprecated. Replace >vim
@@ -7300,7 +7356,7 @@ function vim.fn.rubyeval(expr) end
---
--- @param row integer
--- @param col integer
---- @return any
+--- @return integer
function vim.fn.screenattr(row, col) end
--- The result is a Number, which is the character at position
@@ -7314,7 +7370,7 @@ function vim.fn.screenattr(row, col) end
---
--- @param row integer
--- @param col integer
---- @return any
+--- @return integer
function vim.fn.screenchar(row, col) end
--- The result is a |List| of Numbers. The first number is the same
@@ -7325,7 +7381,7 @@ function vim.fn.screenchar(row, col) end
---
--- @param row integer
--- @param col integer
---- @return any
+--- @return integer[]
function vim.fn.screenchars(row, col) end
--- The result is a Number, which is the current screen column of
@@ -7342,7 +7398,7 @@ function vim.fn.screenchars(row, col) end
--- noremap GG <Cmd>echom screencol()<CR>
--- <
---
---- @return any
+--- @return integer[]
function vim.fn.screencol() end
--- The result is a Dict with the screen position of the text
@@ -7381,7 +7437,7 @@ function vim.fn.screenpos(winid, lnum, col) end
---
--- Note: Same restrictions as with |screencol()|.
---
---- @return any
+--- @return integer
function vim.fn.screenrow() end
--- The result is a String that contains the base character and
@@ -7393,7 +7449,7 @@ function vim.fn.screenrow() end
---
--- @param row integer
--- @param col integer
---- @return any
+--- @return string
function vim.fn.screenstring(row, col) end
--- Search for regexp pattern {pattern}. The search starts at the
@@ -7505,7 +7561,7 @@ function vim.fn.screenstring(row, col) end
--- @param stopline? integer
--- @param timeout? integer
--- @param skip? string|function
---- @return any
+--- @return integer
function vim.fn.search(pattern, flags, stopline, timeout, skip) end
--- Get or update the last search count, like what is displayed
@@ -7798,7 +7854,7 @@ function vim.fn.searchpos(pattern, flags, stopline, timeout, skip) end
--- echo serverlist()
--- <
---
---- @return any
+--- @return string[]
function vim.fn.serverlist() end
--- Opens a socket or named pipe at {address} and listens for
@@ -7835,7 +7891,7 @@ function vim.fn.serverlist() end
--- <
---
--- @param address? string
---- @return any
+--- @return string
function vim.fn.serverstart(address) end
--- Closes the pipe or socket at {address}.
@@ -7844,7 +7900,7 @@ function vim.fn.serverstart(address) end
--- address in |serverlist()|.
---
--- @param address string
---- @return any
+--- @return integer
function vim.fn.serverstop(address) end
--- Set line {lnum} to {text} in buffer {buf}. This works like
@@ -7874,7 +7930,7 @@ function vim.fn.serverstop(address) end
--- @param buf integer|string
--- @param lnum integer
--- @param text string|string[]
---- @return any
+--- @return integer
function vim.fn.setbufline(buf, lnum, text) end
--- Set option or local variable {varname} in buffer {buf} to
@@ -7979,7 +8035,7 @@ function vim.fn.setcharsearch(dict) end
---
--- @param str string
--- @param pos? integer
---- @return any
+--- @return integer
function vim.fn.setcmdline(str, pos) end
--- Set the cursor position in the command line to byte position
@@ -8289,7 +8345,7 @@ function vim.fn.setpos(expr, list) end
--- @param list vim.quickfix.entry[]
--- @param action? string
--- @param what? vim.fn.setqflist.what
---- @return any
+--- @return integer
function vim.fn.setqflist(list, action, what) end
--- Set the register {regname} to {value}.
@@ -8442,7 +8498,7 @@ function vim.fn.setwinvar(nr, varname, val) end
--- checksum of {string}.
---
--- @param string string
---- @return any
+--- @return string
function vim.fn.sha256(string) end
--- Escape {string} for use as a shell command argument.
@@ -8478,7 +8534,7 @@ function vim.fn.sha256(string) end
---
--- @param string string
--- @param special? boolean
---- @return any
+--- @return string
function vim.fn.shellescape(string, special) end
--- Returns the effective value of 'shiftwidth'. This is the
@@ -8930,7 +8986,7 @@ function vim.fn.sign_unplacelist(list) end
--- links before simplifying the path name, use |resolve()|.
---
--- @param filename string
---- @return any
+--- @return string
function vim.fn.simplify(filename) end
--- Return the sine of {expr}, measured in radians, as a |Float|.
@@ -8943,7 +8999,7 @@ function vim.fn.simplify(filename) end
--- < 0.763301
---
--- @param expr number
---- @return any
+--- @return number
function vim.fn.sin(expr) end
--- Return the hyperbolic sine of {expr} as a |Float| in the range
@@ -9077,10 +9133,11 @@ function vim.fn.sockconnect(mode, address, opts) end
--- eval mylist->sort({i1, i2 -> i1 - i2})
--- <
---
---- @param list any
+--- @generic T
+--- @param list T[]
--- @param how? string|function
--- @param dict? any
---- @return any
+--- @return T[]
function vim.fn.sort(list, how, dict) end
--- Return the sound-folded equivalent of {word}. Uses the first
@@ -9091,7 +9148,7 @@ function vim.fn.sort(list, how, dict) end
--- the method can be quite slow.
---
--- @param word string
---- @return any
+--- @return string
function vim.fn.soundfold(word) end
--- Without argument: The result is the badly spelled word under
@@ -9144,7 +9201,7 @@ function vim.fn.spellbadword(sentence) end
--- @param word string
--- @param max? integer
--- @param capital? boolean
---- @return any
+--- @return string[]
function vim.fn.spellsuggest(word, max, capital) end
--- Make a |List| out of {string}. When {pattern} is omitted or
@@ -9174,7 +9231,7 @@ function vim.fn.spellsuggest(word, max, capital) end
--- @param string string
--- @param pattern? string
--- @param keepempty? boolean
---- @return any
+--- @return string[]
function vim.fn.split(string, pattern, keepempty) end
--- Return the non-negative square root of Float {expr} as a
@@ -9326,6 +9383,7 @@ function vim.fn.str2float(string, quoted) end
--- and exists only for backwards-compatibility.
--- With UTF-8 composing characters are handled properly: >vim
--- echo str2list("á") " returns [97, 769]
+--- <
---
--- @param string string
--- @param utf8? boolean
@@ -10160,23 +10218,12 @@ function vim.fn.tanh(expr) end
--- @return string
function vim.fn.tempname() end
---- Spawns {cmd} in a new pseudo-terminal session connected
---- to the current (unmodified) buffer. Parameters and behavior
---- are the same as |jobstart()| except "pty", "width", "height",
---- and "TERM" are ignored: "height" and "width" are taken from
---- the current window. Note that termopen() implies a "pty" arg
---- to jobstart(), and thus has the implications documented at
---- |jobstart()|.
----
---- Returns the same values as jobstart().
----
---- Terminal environment is initialized as in |jobstart-env|,
---- except $TERM is set to "xterm-256color". Full behavior is
---- described in |terminal|.
+--- @deprecated
+--- Use |jobstart()| with `{term: v:true}` instead.
---
--- @param cmd string|string[]
--- @param opts? table
---- @return any
+--- @return integer
function vim.fn.termopen(cmd, opts) end
--- Return a list with information about timers.
@@ -10576,7 +10623,7 @@ function vim.fn.virtcol(expr, list, winid) end
--- @param winid integer
--- @param lnum integer
--- @param col integer
---- @return any
+--- @return integer
function vim.fn.virtcol2col(winid, lnum, col) end
--- The result is a String, which describes the last Visual mode
@@ -10597,7 +10644,7 @@ function vim.fn.virtcol2col(winid, lnum, col) end
--- the old value is returned. See |non-zero-arg|.
---
--- @param expr? boolean
---- @return any
+--- @return string
function vim.fn.visualmode(expr) end
--- Waits until {condition} evaluates to |TRUE|, where {condition}
@@ -10714,7 +10761,7 @@ function vim.fn.win_id2tabwin(expr) end
--- Return 0 if the window cannot be found in the current tabpage.
---
--- @param expr integer
---- @return any
+--- @return integer
function vim.fn.win_id2win(expr) end
--- Move window {nr}'s vertical separator (i.e., the right border)
@@ -10868,7 +10915,7 @@ function vim.fn.winheight(nr) end
--- <
---
--- @param tabnr? integer
---- @return any
+--- @return any[]
function vim.fn.winlayout(tabnr) end
--- The result is a Number, which is the screen line of the cursor
@@ -10912,7 +10959,7 @@ function vim.fn.winline() end
--- <
---
--- @param arg? string|integer
---- @return any
+--- @return integer
function vim.fn.winnr(arg) end
--- Returns a sequence of |:resize| commands that should restore
@@ -10925,7 +10972,7 @@ function vim.fn.winnr(arg) end
--- exe cmd
--- <
---
---- @return any
+--- @return string
function vim.fn.winrestcmd() end
--- Uses the |Dictionary| returned by |winsaveview()| to restore
@@ -10990,7 +11037,7 @@ function vim.fn.winsaveview() end
--- option.
---
--- @param nr integer
---- @return any
+--- @return integer
function vim.fn.winwidth(nr) end
--- The result is a dictionary of byte/chars/word statistics for
@@ -11075,7 +11122,7 @@ function vim.fn.writefile(object, fname, flags) end
--- let bits = xor(bits, 0x80)
--- <
---
---- @param expr number
---- @param expr1 number
---- @return any
+--- @param expr integer
+--- @param expr1 integer
+--- @return integer
function vim.fn.xor(expr, expr1) end
diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
index 8784fdbac9..c1b8695bbf 100644
--- a/runtime/lua/vim/_meta/vvars.lua
+++ b/runtime/lua/vim/_meta/vvars.lua
@@ -15,7 +15,7 @@ vim.v.argv = ...
--- Argument for evaluating 'formatexpr' and used for the typed
--- character when using <expr> in an abbreviation `:map-<expr>`.
--- It is also used by the `InsertCharPre` and `InsertEnter` events.
---- @type any
+--- @type string
vim.v.char = ...
--- The name of the character encoding of a file to be converted.
@@ -60,7 +60,7 @@ vim.v.collate = ...
--- mode.
--- Note: Plugins can modify the value to emulate the builtin
--- `CompleteDone` event behavior.
---- @type any
+--- @type vim.v.completed_item
vim.v.completed_item = ...
--- The count given for the last Normal mode command. Can be used
@@ -90,7 +90,7 @@ vim.v.count1 = ...
--- This variable can not be set directly, use the `:language`
--- command.
--- See `multi-lang`.
---- @type any
+--- @type string
vim.v.ctype = ...
--- Normally zero. When a deadly signal is caught it's set to
@@ -197,11 +197,14 @@ vim.v.errors = ...
--- changing window (or tab) on `DirChanged`.
--- status Job status or exit code, -1 means "unknown". `TermClose`
--- reason Reason for completion being done. `CompleteDone`
---- @type any
+--- complete_word The word that was selected, empty if abandoned complete.
+--- complete_type See `complete_info_mode`
+--- @type vim.v.event
vim.v.event = ...
--- The value of the exception most recently caught and not
---- finished. See also `v:throwpoint` and `throw-variables`.
+--- finished. See also `v:stacktrace`, `v:throwpoint`, and
+--- `throw-variables`.
--- Example:
---
--- ```vim
@@ -223,7 +226,7 @@ vim.v.exception = ...
--- ```vim
--- :au VimLeave * echo "Exit value is " .. v:exiting
--- ```
---- @type any
+--- @type integer?
vim.v.exiting = ...
--- Special value used to put "false" in JSON and msgpack. See
@@ -419,7 +422,7 @@ vim.v.mouse_winid = ...
--- and `msgpackdump()`. All types inside dictionary are fixed
--- (not editable) empty lists. To check whether some list is one
--- of msgpack types, use `is` operator.
---- @type any
+--- @type table
vim.v.msgpack_types = ...
--- Special value used to put "null" in JSON and NIL in msgpack.
@@ -563,7 +566,7 @@ vim.v.relnum = ...
--- typed command.
--- This can be used to find out why your script causes the
--- hit-enter prompt.
---- @type any
+--- @type string
vim.v.scrollstart = ...
--- Search direction: 1 after a forward search, 0 after a
@@ -614,6 +617,13 @@ vim.v.servername = ...
--- @type integer
vim.v.shell_error = ...
+--- The stack trace of the exception most recently caught and
+--- not finished. Refer to `getstacktrace()` for the structure of
+--- stack trace. See also `v:exception`, `v:throwpoint`, and
+--- `throw-variables`.
+--- @type table[]
+vim.v.stacktrace = ...
+
--- Last given status message.
--- Modifiable (can be set).
--- @type string
@@ -705,18 +715,18 @@ vim.v.termrequest = ...
vim.v.termresponse = ...
--- Must be set before using `test_garbagecollect_now()`.
---- @type any
+--- @type integer
vim.v.testing = ...
--- Full filename of the last loaded or saved session file.
--- Empty when no session file has been saved. See `:mksession`.
--- Modifiable (can be set).
---- @type any
+--- @type string
vim.v.this_session = ...
--- The point where the exception most recently caught and not
--- finished was thrown. Not set when commands are typed. See
---- also `v:exception` and `throw-variables`.
+--- also `v:exception`, `v:stacktrace`, and `throw-variables`.
--- Example:
---
--- ```vim
@@ -728,7 +738,7 @@ vim.v.this_session = ...
--- ```
---
--- Output: "Exception from test.vim, line 2"
---- @type any
+--- @type string
vim.v.throwpoint = ...
--- Special value used to put "true" in JSON and msgpack. See
diff --git a/runtime/lua/vim/_meta/vvars_extra.lua b/runtime/lua/vim/_meta/vvars_extra.lua
new file mode 100644
index 0000000000..7ef3021e89
--- /dev/null
+++ b/runtime/lua/vim/_meta/vvars_extra.lua
@@ -0,0 +1,77 @@
+--- @meta _
+error('Cannot require a meta file')
+
+--- Extra types for vim.v dictionary fields
+
+--- @class vim.v.completed_item
+--- @field word? string the text that will be inserted, mandatory
+--- abbreviation of "word"; when not empty it is used in the menu instead of "word"
+--- @field abbr? string
+--- extra text for the popup menu, displayed after "word" or "abbr"
+--- @field menu? string
+--- more information about the item, can be displayed in a preview window
+--- @field info? string
+--- @field kind? string single letter indicating the type of completion
+--- when non-zero case is to be ignored when comparing items to be equal; when
+--- omitted zero is used, thus items that only differ in case are added
+--- @field icase? integer
+--- when non-zero, always treat this item to be equal when comparing. Which
+--- means, "equal=1" disables filtering of this item.
+--- @field equal? integer
+--- when non-zero this match will be added even when an item with the same word
+--- is already present.
+--- @field dup? integer
+--- when non-zero this match will be added even when it is an empty string
+--- @field empty? integer
+--- custom data which is associated with the item and available
+--- in |v:completed_item|; it can be any type; defaults to an empty string
+--- @field user_data? any
+--- an additional highlight group whose attributes are combined
+--- with |hl-PmenuSel| and |hl-Pmenu| or |hl-PmenuMatchSel| and |hl-PmenuMatch|
+--- highlight attributes in the popup menu to apply cterm and gui properties
+--- (with higher priority) like strikethrough to the completion items abbreviation
+--- @field abbr_hlgroup? string
+--- an additional highlight group specifically for setting the highlight
+--- attributes of the completion kind. When this field is present, it will
+--- override the |hl-PmenuKind| highlight group, allowing for the customization
+--- of ctermfg and guifg properties for the completion kind
+--- @field kind_hlgroup? string
+
+--- @class vim.v.event
+--- Whether the event triggered during an aborting condition (e.g. |c_Esc| or
+--- |c_CTRL-C| for |CmdlineLeave|).
+--- @field abort? boolean
+--- @field chan? integer See |channel-id|
+--- @field info? table Dict of arbitrary event data.
+--- @field cmdlevel? integer Level of cmdline.
+--- @field cmdtype? string Type of cmdline, |cmdline-char|.
+--- @field cwd? string Current working directory.
+--- @field inclusive? boolean Motion is |inclusive|, else exclusive.
+--- @field scope? string Event-specific scope name.
+--- Current |operator|. Also set for Ex commands (unlike |v:operator|). For
+--- example if |TextYankPost| is triggered by the |:yank| Ex command then
+--- `v:event.operator` is "y".
+--- @field operator? string
+--- Text stored in the register as a |readfile()|-style list of lines.
+--- @field regcontents? string
+--- Requested register (e.g "x" for "xyy) or the empty string for an unnamed operation.
+--- @field regname? string
+--- @field regtype? string Type of register as returned by |getregtype()|.
+--- @field visual? boolean Selection is visual (as opposed to, e.g., via motion).
+--- @field completed_item? vim.v.completed_item
+--- Current selected complete item on |CompleteChanged|, Is `{}` when no
+--- complete item selected.
+--- @field height? integer
+--- @field width? integer Height of popup menu on |CompleteChanged|
+--- @field row? integer Width of popup menu on |CompleteChanged|
+--- Col count of popup menu on |CompleteChanged|, relative to screen.
+--- @field col? integer
+--- @field size? integer Total number of completion items on |CompleteChanged|.
+--- Is |v:true| if popup menu have scrollbar, or |v:false| if not.
+--- @field scrollbar? boolean
+--- Is |v:true| if the event fired while changing window (or tab) on |DirChanged|.
+--- @field changed_window? boolean
+--- @field status? boolean Job status or exit code, -1 means "unknown". |TermClose|
+--- @field reason? string Reason for completion being done. |CompleteDone|
+--- The word that was selected, empty if abandoned complete. @field complete_word? string
+--- @field complete_type? string See |complete_info_mode|
diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua
index 77d7054626..973ad87ee8 100644
--- a/runtime/lua/vim/_options.lua
+++ b/runtime/lua/vim/_options.lua
@@ -229,10 +229,8 @@ end
--- global value of a |global-local| option, see |:setglobal|.
--- </pre>
---- 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.
+--- Get or set |options|. Works like `:set`, so buffer/window-scoped options target the current
+--- buffer/window. Invalid key is an error.
---
--- Example:
---
@@ -690,6 +688,7 @@ local function remove_value(info, current, new)
end
local function create_option_accessor(scope)
+ --- @diagnostic disable-next-line: no-unknown
local option_mt
local function make_option(name, value)
@@ -698,6 +697,7 @@ local function create_option_accessor(scope)
if type(value) == 'table' and getmetatable(value) == option_mt then
assert(name == value._name, "must be the same value, otherwise that's weird.")
+ --- @diagnostic disable-next-line: no-unknown
value = value._value
end
@@ -721,6 +721,7 @@ local function create_option_accessor(scope)
end,
append = function(self, right)
+ --- @diagnostic disable-next-line: no-unknown
self._value = add_value(self._info, self._value, right)
self:_set()
end,
@@ -730,6 +731,7 @@ local function create_option_accessor(scope)
end,
prepend = function(self, right)
+ --- @diagnostic disable-next-line: no-unknown
self._value = prepend_value(self._info, self._value, right)
self:_set()
end,
@@ -739,6 +741,7 @@ local function create_option_accessor(scope)
end,
remove = function(self, right)
+ --- @diagnostic disable-next-line: no-unknown
self._value = remove_value(self._info, self._value, right)
self:_set()
end,
@@ -770,7 +773,7 @@ end
---
---
--- A special interface |vim.opt| exists for conveniently interacting with list-
---- and map-style option from Lua: It allows accessing them as Lua tables and
+--- and map-style options from Lua: It allows accessing them as Lua tables and
--- offers object-oriented method for adding and removing entries.
---
--- Examples: ~
diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua
index ce5dbffeaa..157172447a 100644
--- a/runtime/lua/vim/_system.lua
+++ b/runtime/lua/vim/_system.lua
@@ -47,15 +47,6 @@ local function close_handle(handle)
end
end
----@param state vim.SystemState
-local function close_handles(state)
- close_handle(state.handle)
- close_handle(state.stdin)
- close_handle(state.stdout)
- close_handle(state.stderr)
- close_handle(state.timer)
-end
-
--- @class vim.SystemObj
--- @field cmd string[]
--- @field pid integer
@@ -88,7 +79,8 @@ function SystemObj:_timeout(signal)
self:kill(signal or SIG.TERM)
end
-local MAX_TIMEOUT = 2 ^ 31
+-- Use max 32-bit signed int value to avoid overflow on 32-bit systems. #31633
+local MAX_TIMEOUT = 2 ^ 31 - 1
--- @param timeout? integer
--- @return vim.SystemCompleted
@@ -132,9 +124,7 @@ function SystemObj:write(data)
-- (https://github.com/neovim/neovim/pull/17620#discussion_r820775616)
stdin:write('', function()
stdin:shutdown(function()
- if stdin then
- stdin:close()
- end
+ close_handle(stdin)
end)
end)
end
@@ -146,25 +136,52 @@ function SystemObj:is_closing()
return handle == nil or handle:is_closing() or false
end
----@param output fun(err:string?, data: string?)|false
----@return uv.uv_stream_t?
----@return fun(err:string?, data: string?)? Handler
-local function setup_output(output)
- if output == nil then
- return assert(uv.new_pipe(false)), nil
+--- @param output? uv.read_start.callback|false
+--- @param text? boolean
+--- @return uv.uv_stream_t? pipe
+--- @return uv.read_start.callback? handler
+--- @return string[]? data
+local function setup_output(output, text)
+ if output == false then
+ return
end
+ local bucket --- @type string[]?
+ local handler --- @type uv.read_start.callback
+
if type(output) == 'function' then
- return assert(uv.new_pipe(false)), output
+ handler = output
+ else
+ bucket = {}
+ handler = function(err, data)
+ if err then
+ error(err)
+ end
+ if text and data then
+ bucket[#bucket + 1] = data:gsub('\r\n', '\n')
+ else
+ bucket[#bucket + 1] = data
+ end
+ end
end
- assert(output == false)
- return nil, nil
+ local pipe = assert(uv.new_pipe(false))
+
+ --- @type uv.read_start.callback
+ local function handler_with_close(err, data)
+ handler(err, data)
+ if data == nil then
+ pipe:read_stop()
+ pipe:close()
+ end
+ end
+
+ return pipe, handler_with_close, bucket
end
----@param input string|string[]|true|nil
----@return uv.uv_stream_t?
----@return string|string[]?
+--- @param input? string|string[]|boolean
+--- @return uv.uv_stream_t?
+--- @return string|string[]?
local function setup_input(input)
if not input then
return
@@ -208,28 +225,6 @@ local function setup_env(env, clear_env)
return renv
end
---- @param stream uv.uv_stream_t
---- @param text? boolean
---- @param bucket string[]
---- @return fun(err: string?, data: string?)
-local function default_handler(stream, text, bucket)
- return function(err, data)
- if err then
- error(err)
- end
- if data ~= nil then
- if text then
- bucket[#bucket + 1] = data:gsub('\r\n', '\n')
- else
- bucket[#bucket + 1] = data
- end
- else
- stream:read_stop()
- stream:close()
- end
- end
-end
-
local is_win = vim.fn.has('win32') == 1
local M = {}
@@ -255,9 +250,9 @@ local function spawn(cmd, opts, on_exit, on_error)
return handle, pid_or_err --[[@as integer]]
end
----@param timeout integer
----@param cb fun()
----@return uv.uv_timer_t
+--- @param timeout integer
+--- @param cb fun()
+--- @return uv.uv_timer_t
local function timer_oneshot(timeout, cb)
local timer = assert(uv.new_timer())
timer:start(timeout, 0, function()
@@ -273,7 +268,12 @@ end
--- @param signal integer
--- @param on_exit fun(result: vim.SystemCompleted)?
local function _on_exit(state, code, signal, on_exit)
- close_handles(state)
+ close_handle(state.handle)
+ close_handle(state.stdin)
+ close_handle(state.timer)
+
+ -- #30846: Do not close stdout/stderr here, as they may still have data to
+ -- read. They will be closed in uv.read_start on EOF.
local check = assert(uv.new_check())
check:start(function()
@@ -311,6 +311,15 @@ local function _on_exit(state, code, signal, on_exit)
end)
end
+--- @param state vim.SystemState
+local function _on_error(state)
+ close_handle(state.handle)
+ close_handle(state.stdin)
+ close_handle(state.stdout)
+ close_handle(state.stderr)
+ close_handle(state.timer)
+end
+
--- Run a system command
---
--- @param cmd string[]
@@ -324,8 +333,8 @@ function M.run(cmd, opts, on_exit)
opts = opts or {}
- local stdout, stdout_handler = setup_output(opts.stdout)
- local stderr, stderr_handler = setup_output(opts.stderr)
+ local stdout, stdout_handler, stdout_data = setup_output(opts.stdout, opts.text)
+ local stderr, stderr_handler, stderr_data = setup_output(opts.stderr, opts.text)
local stdin, towrite = setup_input(opts.stdin)
--- @type vim.SystemState
@@ -335,7 +344,9 @@ function M.run(cmd, opts, on_exit)
timeout = opts.timeout,
stdin = stdin,
stdout = stdout,
+ stdout_data = stdout_data,
stderr = stderr,
+ stderr_data = stderr_data,
}
--- @diagnostic disable-next-line:missing-fields
@@ -350,17 +361,15 @@ function M.run(cmd, opts, on_exit)
}, function(code, signal)
_on_exit(state, code, signal, on_exit)
end, function()
- close_handles(state)
+ _on_error(state)
end)
- if stdout then
- state.stdout_data = {}
- stdout:read_start(stdout_handler or default_handler(stdout, opts.text, state.stdout_data))
+ if stdout and stdout_handler then
+ stdout:read_start(stdout_handler)
end
- if stderr then
- state.stderr_data = {}
- stderr:read_start(stderr_handler or default_handler(stderr, opts.text, state.stderr_data))
+ if stderr and stderr_handler then
+ stderr:read_start(stderr_handler)
end
local obj = new_systemobj(state)
diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua
index 4fb8c6a686..621945aedd 100644
--- a/runtime/lua/vim/diagnostic.lua
+++ b/runtime/lua/vim/diagnostic.lua
@@ -2,6 +2,20 @@ local api, if_nil = vim.api, vim.F.if_nil
local M = {}
+--- @param title string
+--- @return integer?
+local function get_qf_id_for_title(title)
+ local lastqflist = vim.fn.getqflist({ nr = '$' })
+ for i = 1, lastqflist.nr do
+ local qflist = vim.fn.getqflist({ nr = i, id = 0, title = 0 })
+ if qflist.title == title then
+ return qflist.id
+ end
+ end
+
+ return nil
+end
+
--- [diagnostic-structure]()
---
--- Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based
@@ -56,9 +70,13 @@ local M = {}
--- Use virtual text for diagnostics. If multiple diagnostics are set for a
--- namespace, one prefix per diagnostic + the last diagnostic message are
--- shown.
---- (default: `true`)
+--- (default: `false`)
--- @field virtual_text? boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText
---
+--- Use virtual lines for diagnostics.
+--- (default: `false`)
+--- @field virtual_lines? boolean|vim.diagnostic.Opts.VirtualLines|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualLines
+---
--- Use signs for diagnostics |diagnostic-signs|.
--- (default: `true`)
--- @field signs? boolean|vim.diagnostic.Opts.Signs|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Signs
@@ -87,6 +105,7 @@ local M = {}
--- @field update_in_insert boolean
--- @field underline vim.diagnostic.Opts.Underline
--- @field virtual_text vim.diagnostic.Opts.VirtualText
+--- @field virtual_lines vim.diagnostic.Opts.VirtualLines
--- @field signs vim.diagnostic.Opts.Signs
--- @field severity_sort {reverse?:boolean}
@@ -131,10 +150,11 @@ local M = {}
--- Overrides the setting from |vim.diagnostic.config()|.
--- @field source? boolean|'if_many'
---
---- A function that takes a diagnostic as input and returns a string.
---- The return value is the text used to display the diagnostic.
+--- A function that takes a diagnostic as input and returns a string or nil.
+--- If the return value is nil, the diagnostic is not displayed by the handler.
+--- Else the output text is used to display the diagnostic.
--- Overrides the setting from |vim.diagnostic.config()|.
---- @field format? fun(diagnostic:vim.Diagnostic): string
+--- @field format? fun(diagnostic:vim.Diagnostic): string?
---
--- Prefix each diagnostic in the floating window:
--- - If a `function`, {i} is the index of the diagnostic being evaluated and
@@ -170,6 +190,10 @@ local M = {}
--- severity |diagnostic-severity|
--- @field severity? vim.diagnostic.SeverityFilter
---
+--- Only show diagnostics for the current line.
+--- (default `false`)
+--- @field current_line? boolean
+---
--- Include the diagnostic source in virtual text. Use `'if_many'` to only
--- show sources if there is more than one diagnostic source in the buffer.
--- Otherwise, any truthy value means to always show the diagnostic source.
@@ -188,7 +212,7 @@ local M = {}
--- This can be used to render an LSP diagnostic error code.
--- @field suffix? string|(fun(diagnostic:vim.Diagnostic): string)
---
---- The return value is the text used to display the diagnostic. Example:
+--- If not nil, the return value is the text used to display the diagnostic. Example:
--- ```lua
--- function(diagnostic)
--- if diagnostic.severity == vim.diagnostic.severity.ERROR then
@@ -197,7 +221,8 @@ local M = {}
--- return diagnostic.message
--- end
--- ```
---- @field format? fun(diagnostic:vim.Diagnostic): string
+--- If the return value is nil, the diagnostic is not displayed by the handler.
+--- @field format? fun(diagnostic:vim.Diagnostic): string?
---
--- See |nvim_buf_set_extmark()|.
--- @field hl_mode? 'replace'|'combine'|'blend'
@@ -206,7 +231,7 @@ local M = {}
--- @field virt_text? [string,any][]
---
--- See |nvim_buf_set_extmark()|.
---- @field virt_text_pos? 'eol'|'overlay'|'right_align'|'inline'
+--- @field virt_text_pos? 'eol'|'eol_right_align'|'inline'|'overlay'|'right_align'
---
--- See |nvim_buf_set_extmark()|.
--- @field virt_text_win_col? integer
@@ -214,6 +239,17 @@ local M = {}
--- See |nvim_buf_set_extmark()|.
--- @field virt_text_hide? boolean
+--- @class vim.diagnostic.Opts.VirtualLines
+---
+--- Only show diagnostics for the current line.
+--- (default: `false`)
+--- @field current_line? boolean
+---
+--- A function that takes a diagnostic as input and returns a string or nil.
+--- If the return value is nil, the diagnostic is not displayed by the handler.
+--- Else the output text is used to display the diagnostic.
+--- @field format? fun(diagnostic:vim.Diagnostic): string?
+
--- @class vim.diagnostic.Opts.Signs
---
--- Only show virtual text for diagnostics matching the given
@@ -298,7 +334,8 @@ M.severity = {
local global_diagnostic_options = {
signs = true,
underline = true,
- virtual_text = true,
+ virtual_text = false,
+ virtual_lines = false,
float = true,
update_in_insert = false,
severity_sort = false,
@@ -342,7 +379,7 @@ local bufnr_and_namespace_cacher_mt = {
-- bufnr -> ns -> Diagnostic[]
local diagnostic_cache = {} --- @type table<integer,table<integer,vim.Diagnostic[]>>
do
- local group = api.nvim_create_augroup('DiagnosticBufWipeout', {})
+ local group = api.nvim_create_augroup('nvim.diagnostic.buf_wipeout', {})
setmetatable(diagnostic_cache, {
--- @param t table<integer,vim.Diagnostic[]>
--- @param bufnr integer
@@ -473,15 +510,21 @@ local function prefix_source(diagnostics)
end, diagnostics)
end
+--- @param format fun(vim.Diagnostic): string?
--- @param diagnostics vim.Diagnostic[]
--- @return vim.Diagnostic[]
local function reformat_diagnostics(format, diagnostics)
vim.validate('format', format, 'function')
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
- local formatted = vim.deepcopy(diagnostics, true)
- for _, diagnostic in ipairs(formatted) do
- diagnostic.message = format(diagnostic)
+ local formatted = {}
+ for _, diagnostic in ipairs(diagnostics) do
+ local message = format(diagnostic)
+ if message ~= nil then
+ local formatted_diagnostic = vim.deepcopy(diagnostic, true)
+ formatted_diagnostic.message = message
+ table.insert(formatted, formatted_diagnostic)
+ end
end
return formatted
end
@@ -567,17 +610,11 @@ end
-- TODO(lewis6991): these highlight maps can only be indexed with an integer, however there usage
-- implies they can be indexed with any vim.diagnostic.Severity
local virtual_text_highlight_map = make_highlight_map('VirtualText')
+local virtual_lines_highlight_map = make_highlight_map('VirtualLines')
local underline_highlight_map = make_highlight_map('Underline')
local floating_highlight_map = make_highlight_map('Floating')
local sign_highlight_map = make_highlight_map('Sign')
-local function get_bufnr(bufnr)
- if not bufnr or bufnr == 0 then
- return api.nvim_get_current_buf()
- end
- return bufnr
-end
-
--- @param diagnostics vim.Diagnostic[]
--- @return table<integer,vim.Diagnostic[]>
local function diagnostic_lines(diagnostics)
@@ -597,6 +634,26 @@ local function diagnostic_lines(diagnostics)
return diagnostics_by_line
end
+--- @param diagnostics table<integer, vim.Diagnostic[]>
+--- @return vim.Diagnostic[]
+local function diagnostics_at_cursor(diagnostics)
+ local lnum = api.nvim_win_get_cursor(0)[1] - 1
+
+ if diagnostics[lnum] ~= nil then
+ return diagnostics[lnum]
+ end
+
+ local cursor_diagnostics = {}
+ for _, line_diags in pairs(diagnostics) do
+ for _, diag in ipairs(line_diags) do
+ if diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum then
+ table.insert(cursor_diagnostics, diag)
+ end
+ end
+ end
+ return cursor_diagnostics
+end
+
--- @param namespace integer
--- @param bufnr integer
--- @param diagnostics vim.Diagnostic[]
@@ -640,7 +697,7 @@ end
--- @param namespace integer
--- @param bufnr? integer
local function save_extmarks(namespace, bufnr)
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
if not diagnostic_attached_buffers[bufnr] then
api.nvim_buf_attach(bufnr, false, {
on_lines = function(_, _, _, _, _, last)
@@ -812,7 +869,7 @@ local function get_diagnostics(bufnr, opts, clamp)
end
end
elseif namespace == nil then
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
for iter_namespace in pairs(diagnostic_cache[bufnr]) do
add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace])
end
@@ -823,7 +880,7 @@ local function get_diagnostics(bufnr, opts, clamp)
end
end
else
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
for _, iter_namespace in ipairs(namespace) do
add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace] or {})
end
@@ -847,13 +904,34 @@ local function set_list(loclist, opts)
-- numbers beyond the end of the buffer
local diagnostics = get_diagnostics(bufnr, opts --[[@as vim.diagnostic.GetOpts]], false)
local items = M.toqflist(diagnostics)
+ local qf_id = nil
if loclist then
- vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items })
+ vim.fn.setloclist(winnr, {}, 'u', { title = title, items = items })
else
- vim.fn.setqflist({}, ' ', { title = title, items = items })
+ qf_id = get_qf_id_for_title(title)
+
+ -- If we already have a diagnostics quickfix, update it rather than creating a new one.
+ -- This avoids polluting the finite set of quickfix lists, and preserves the currently selected
+ -- entry.
+ vim.fn.setqflist({}, qf_id and 'u' or ' ', {
+ title = title,
+ items = items,
+ id = qf_id,
+ })
end
+
if open then
- api.nvim_command(loclist and 'lwindow' or 'botright cwindow')
+ if not loclist then
+ -- First navigate to the diagnostics quickfix list.
+ --- @type integer
+ local nr = vim.fn.getqflist({ id = qf_id, nr = 0 }).nr
+ api.nvim_command(('silent %dchistory'):format(nr))
+
+ -- Now open the quickfix list.
+ api.nvim_command('botright cwindow')
+ else
+ api.nvim_command('lwindow')
+ end
end
end
@@ -1081,7 +1159,7 @@ function M.set(namespace, bufnr, diagnostics, opts)
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true)
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
if vim.tbl_isempty(diagnostics) then
diagnostic_cache[bufnr][namespace] = nil
@@ -1361,17 +1439,13 @@ M.handlers.signs = {
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true)
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
if not api.nvim_buf_is_loaded(bufnr) then
return
end
- if opts.signs and opts.signs.severity then
- diagnostics = filter_by_severity(opts.signs.severity, diagnostics)
- end
-
-- 10 is the default sign priority when none is explicitly specified
local priority = opts.signs and opts.signs.priority or 10
local get_priority = severity_to_extmark_priority(priority, opts)
@@ -1379,7 +1453,7 @@ M.handlers.signs = {
local ns = M.get_namespace(namespace)
if not ns.user_data.sign_ns then
ns.user_data.sign_ns =
- api.nvim_create_namespace(string.format('%s/diagnostic/signs', ns.name))
+ api.nvim_create_namespace(string.format('nvim.%s.diagnostic.signs', ns.name))
end
-- Handle legacy diagnostic sign definitions
@@ -1467,21 +1541,17 @@ M.handlers.underline = {
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true)
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
if not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
- if opts.underline and opts.underline.severity then
- diagnostics = filter_by_severity(opts.underline.severity, diagnostics)
- end
-
local ns = M.get_namespace(namespace)
if not ns.user_data.underline_ns then
ns.user_data.underline_ns =
- api.nvim_create_namespace(string.format('%s/diagnostic/underline', ns.name))
+ api.nvim_create_namespace(string.format('nvim.%s.diagnostic.underline', ns.name))
end
local underline_ns = ns.user_data.underline_ns
@@ -1524,6 +1594,28 @@ M.handlers.underline = {
end,
}
+--- @param namespace integer
+--- @param bufnr integer
+--- @param diagnostics table<integer, vim.Diagnostic[]>
+--- @param opts vim.diagnostic.Opts.VirtualText
+local function render_virtual_text(namespace, bufnr, diagnostics, opts)
+ api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
+
+ for line, line_diagnostics in pairs(diagnostics) do
+ local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts)
+
+ if virt_texts then
+ api.nvim_buf_set_extmark(bufnr, namespace, line, 0, {
+ hl_mode = opts.hl_mode or 'combine',
+ virt_text = virt_texts,
+ virt_text_pos = opts.virt_text_pos,
+ virt_text_hide = opts.virt_text_hide,
+ virt_text_win_col = opts.virt_text_win_col,
+ })
+ end
+ end
+end
+
M.handlers.virtual_text = {
show = function(namespace, bufnr, diagnostics, opts)
vim.validate('namespace', namespace, 'number')
@@ -1531,14 +1623,13 @@ M.handlers.virtual_text = {
vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true)
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
if not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
- local severity --- @type vim.diagnostic.SeverityFilter?
if opts.virtual_text then
if opts.virtual_text.format then
diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics)
@@ -1549,36 +1640,51 @@ M.handlers.virtual_text = {
then
diagnostics = prefix_source(diagnostics)
end
- if opts.virtual_text.severity then
- severity = opts.virtual_text.severity
- end
end
local ns = M.get_namespace(namespace)
if not ns.user_data.virt_text_ns then
ns.user_data.virt_text_ns =
- api.nvim_create_namespace(string.format('%s/diagnostic/virtual_text', ns.name))
+ api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_text', ns.name))
+ end
+ if not ns.user_data.virt_text_augroup then
+ ns.user_data.virt_text_augroup = api.nvim_create_augroup(
+ string.format('nvim.%s.diagnostic.virt_text', ns.name),
+ { clear = true }
+ )
end
- local virt_text_ns = ns.user_data.virt_text_ns
- local buffer_line_diagnostics = diagnostic_lines(diagnostics)
- for line, line_diagnostics in pairs(buffer_line_diagnostics) do
- if severity then
- line_diagnostics = filter_by_severity(severity, line_diagnostics)
- end
- local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text)
-
- if virt_texts then
- api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, {
- hl_mode = opts.virtual_text.hl_mode or 'combine',
- virt_text = virt_texts,
- virt_text_pos = opts.virtual_text.virt_text_pos,
- virt_text_hide = opts.virtual_text.virt_text_hide,
- virt_text_win_col = opts.virtual_text.virt_text_win_col,
- })
- end
+ api.nvim_clear_autocmds({ group = ns.user_data.virt_text_augroup, buffer = bufnr })
+
+ local line_diagnostics = diagnostic_lines(diagnostics)
+
+ if opts.virtual_text.current_line == true then
+ api.nvim_create_autocmd('CursorMoved', {
+ buffer = bufnr,
+ group = ns.user_data.virt_text_augroup,
+ callback = function()
+ local lnum = api.nvim_win_get_cursor(0)[1] - 1
+ render_virtual_text(
+ ns.user_data.virt_text_ns,
+ bufnr,
+ { [lnum] = diagnostics_at_cursor(line_diagnostics) },
+ opts.virtual_text
+ )
+ end,
+ })
+ -- Also show diagnostics for the current line before the first CursorMoved event.
+ local lnum = api.nvim_win_get_cursor(0)[1] - 1
+ render_virtual_text(
+ ns.user_data.virt_text_ns,
+ bufnr,
+ { [lnum] = diagnostics_at_cursor(line_diagnostics) },
+ opts.virtual_text
+ )
+ else
+ render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, opts.virtual_text)
end
- save_extmarks(virt_text_ns, bufnr)
+
+ save_extmarks(ns.user_data.virt_text_ns, bufnr)
end,
hide = function(namespace, bufnr)
local ns = M.get_namespace(namespace)
@@ -1587,6 +1693,262 @@ M.handlers.virtual_text = {
if api.nvim_buf_is_valid(bufnr) then
api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1)
end
+ api.nvim_clear_autocmds({ group = ns.user_data.virt_text_augroup, buffer = bufnr })
+ end
+ end,
+}
+
+--- Some characters (like tabs) take up more than one cell. Additionally, inline
+--- virtual text can make the distance between 2 columns larger.
+--- A diagnostic aligned under such characters needs to account for that and that
+--- many spaces to its left.
+--- @param bufnr integer
+--- @param lnum integer
+--- @param start_col integer
+--- @param end_col integer
+--- @return integer
+local function distance_between_cols(bufnr, lnum, start_col, end_col)
+ return api.nvim_buf_call(bufnr, function()
+ local s = vim.fn.virtcol({ lnum + 1, start_col })
+ local e = vim.fn.virtcol({ lnum + 1, end_col + 1 })
+ return e - 1 - s
+ end)
+end
+
+--- @param namespace integer
+--- @param bufnr integer
+--- @param diagnostics vim.Diagnostic[]
+local function render_virtual_lines(namespace, bufnr, diagnostics)
+ table.sort(diagnostics, function(d1, d2)
+ if d1.lnum == d2.lnum then
+ return d1.col < d2.col
+ else
+ return d1.lnum < d2.lnum
+ end
+ end)
+
+ api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
+
+ if not next(diagnostics) then
+ return
+ end
+
+ -- This loop reads each line, putting them into stacks with some extra data since
+ -- rendering each line requires understanding what is beneath it.
+ local ElementType = { Space = 1, Diagnostic = 2, Overlap = 3, Blank = 4 } ---@enum ElementType
+ local line_stacks = {} ---@type table<integer, {[1]:ElementType, [2]:string|vim.diagnostic.Severity|vim.Diagnostic}[]>
+ local prev_lnum = -1
+ local prev_col = 0
+ for _, diag in ipairs(diagnostics) do
+ if not line_stacks[diag.lnum] then
+ line_stacks[diag.lnum] = {}
+ end
+
+ local stack = line_stacks[diag.lnum]
+
+ if diag.lnum ~= prev_lnum then
+ table.insert(stack, {
+ ElementType.Space,
+ string.rep(' ', distance_between_cols(bufnr, diag.lnum, 0, diag.col)),
+ })
+ elseif diag.col ~= prev_col then
+ table.insert(stack, {
+ ElementType.Space,
+ string.rep(
+ ' ',
+ -- +1 because indexing starts at 0 in one API but at 1 in the other.
+ -- -1 for non-first lines, since the previous column was already drawn.
+ distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col) - 1
+ ),
+ })
+ else
+ table.insert(stack, { ElementType.Overlap, diag.severity })
+ end
+
+ if diag.message:find('^%s*$') then
+ table.insert(stack, { ElementType.Blank, diag })
+ else
+ table.insert(stack, { ElementType.Diagnostic, diag })
+ end
+
+ prev_lnum, prev_col = diag.lnum, diag.col
+ end
+
+ local chars = {
+ cross = '┼',
+ horizontal = '─',
+ horizontal_up = '┴',
+ up_right = '└',
+ vertical = '│',
+ vertical_right = '├',
+ }
+
+ for lnum, stack in pairs(line_stacks) do
+ local virt_lines = {}
+
+ -- Note that we read in the order opposite to insertion.
+ for i = #stack, 1, -1 do
+ if stack[i][1] == ElementType.Diagnostic then
+ local diagnostic = stack[i][2]
+ local left = {} ---@type {[1]:string, [2]:string}
+ local overlap = false
+ local multi = false
+
+ -- Iterate the stack for this line to find elements on the left.
+ for j = 1, i - 1 do
+ local type = stack[j][1]
+ local data = stack[j][2]
+ if type == ElementType.Space then
+ if multi then
+ ---@cast data string
+ table.insert(left, {
+ string.rep(chars.horizontal, data:len()),
+ virtual_lines_highlight_map[diagnostic.severity],
+ })
+ else
+ table.insert(left, { data, '' })
+ end
+ elseif type == ElementType.Diagnostic then
+ -- If an overlap follows this line, don't add an extra column.
+ if stack[j + 1][1] ~= ElementType.Overlap then
+ table.insert(left, { chars.vertical, virtual_lines_highlight_map[data.severity] })
+ end
+ overlap = false
+ elseif type == ElementType.Blank then
+ if multi then
+ table.insert(
+ left,
+ { chars.horizontal_up, virtual_lines_highlight_map[data.severity] }
+ )
+ else
+ table.insert(left, { chars.up_right, virtual_lines_highlight_map[data.severity] })
+ end
+ multi = true
+ elseif type == ElementType.Overlap then
+ overlap = true
+ end
+ end
+
+ local center_char ---@type string
+ if overlap and multi then
+ center_char = chars.cross
+ elseif overlap then
+ center_char = chars.vertical_right
+ elseif multi then
+ center_char = chars.horizontal_up
+ else
+ center_char = chars.up_right
+ end
+ local center = {
+ {
+ string.format('%s%s', center_char, string.rep(chars.horizontal, 4) .. ' '),
+ virtual_lines_highlight_map[diagnostic.severity],
+ },
+ }
+
+ -- We can draw on the left side if and only if:
+ -- a. Is the last one stacked this line.
+ -- b. Has enough space on the left.
+ -- c. Is just one line.
+ -- d. Is not an overlap.
+ local msg ---@type string
+ if diagnostic.code then
+ msg = string.format('%s: %s', diagnostic.code, diagnostic.message)
+ else
+ msg = diagnostic.message
+ end
+ for msg_line in msg:gmatch('([^\n]+)') do
+ local vline = {}
+ vim.list_extend(vline, left)
+ vim.list_extend(vline, center)
+ vim.list_extend(vline, { { msg_line, virtual_lines_highlight_map[diagnostic.severity] } })
+
+ table.insert(virt_lines, vline)
+
+ -- Special-case for continuation lines:
+ if overlap then
+ center = {
+ { chars.vertical, virtual_lines_highlight_map[diagnostic.severity] },
+ { ' ', '' },
+ }
+ else
+ center = { { ' ', '' } }
+ end
+ end
+ end
+ end
+
+ api.nvim_buf_set_extmark(bufnr, namespace, lnum, 0, { virt_lines = virt_lines })
+ end
+end
+
+M.handlers.virtual_lines = {
+ show = function(namespace, bufnr, diagnostics, opts)
+ vim.validate('namespace', namespace, 'number')
+ vim.validate('bufnr', bufnr, 'number')
+ vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
+ vim.validate('opts', opts, 'table', true)
+
+ bufnr = vim._resolve_bufnr(bufnr)
+ opts = opts or {}
+
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
+ local ns = M.get_namespace(namespace)
+ if not ns.user_data.virt_lines_ns then
+ ns.user_data.virt_lines_ns =
+ api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_lines', ns.name))
+ end
+ if not ns.user_data.virt_lines_augroup then
+ ns.user_data.virt_lines_augroup = api.nvim_create_augroup(
+ string.format('nvim.%s.diagnostic.virt_lines', ns.name),
+ { clear = true }
+ )
+ end
+
+ api.nvim_clear_autocmds({ group = ns.user_data.virt_lines_augroup, buffer = bufnr })
+
+ if opts.virtual_lines.format then
+ diagnostics = reformat_diagnostics(opts.virtual_lines.format, diagnostics)
+ end
+
+ if opts.virtual_lines.current_line == true then
+ -- Create a mapping from line -> diagnostics so that we can quickly get the
+ -- diagnostics we need when the cursor line doesn't change.
+ local line_diagnostics = diagnostic_lines(diagnostics)
+ api.nvim_create_autocmd('CursorMoved', {
+ buffer = bufnr,
+ group = ns.user_data.virt_lines_augroup,
+ callback = function()
+ render_virtual_lines(
+ ns.user_data.virt_lines_ns,
+ bufnr,
+ diagnostics_at_cursor(line_diagnostics)
+ )
+ end,
+ })
+ -- Also show diagnostics for the current line before the first CursorMoved event.
+ render_virtual_lines(
+ ns.user_data.virt_lines_ns,
+ bufnr,
+ diagnostics_at_cursor(line_diagnostics)
+ )
+ else
+ render_virtual_lines(ns.user_data.virt_lines_ns, bufnr, diagnostics)
+ end
+
+ save_extmarks(ns.user_data.virt_lines_ns, bufnr)
+ end,
+ hide = function(namespace, bufnr)
+ local ns = M.get_namespace(namespace)
+ if ns.user_data.virt_lines_ns then
+ diagnostic_cache_extmarks[bufnr][ns.user_data.virt_lines_ns] = {}
+ if api.nvim_buf_is_valid(bufnr) then
+ api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_lines_ns, 0, -1)
+ end
+ api.nvim_clear_autocmds({ group = ns.user_data.virt_lines_augroup, buffer = bufnr })
end
end,
}
@@ -1656,7 +2018,7 @@ function M.hide(namespace, bufnr)
vim.validate('namespace', namespace, 'number', true)
vim.validate('bufnr', bufnr, 'number', true)
- local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
+ local buffers = bufnr and { vim._resolve_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
for _, iter_bufnr in ipairs(buffers) do
local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr])
for _, iter_namespace in ipairs(namespaces) do
@@ -1683,7 +2045,7 @@ function M.is_enabled(filter)
return vim.tbl_isempty(diagnostic_disabled) and not diagnostic_disabled[1]
end
- local bufnr = get_bufnr(filter.bufnr)
+ local bufnr = vim._resolve_bufnr(filter.bufnr)
if type(diagnostic_disabled[bufnr]) == 'table' then
return not diagnostic_disabled[bufnr][filter.ns_id]
end
@@ -1724,7 +2086,7 @@ function M.show(namespace, bufnr, diagnostics, opts)
end
else
-- namespace is nil
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
for iter_namespace in pairs(diagnostic_cache[bufnr]) do
M.show(iter_namespace, bufnr, nil, opts)
end
@@ -1770,7 +2132,8 @@ function M.show(namespace, bufnr, diagnostics, opts)
for handler_name, handler in pairs(M.handlers) do
if handler.show and opts_res[handler_name] then
- handler.show(namespace, bufnr, diagnostics, opts_res)
+ local filtered = filter_by_severity(opts_res[handler_name].severity, diagnostics)
+ handler.show(namespace, bufnr, filtered, opts_res)
end
end
end
@@ -1791,7 +2154,7 @@ function M.open_float(opts, ...)
end
opts = opts or {}
- bufnr = get_bufnr(bufnr or opts.bufnr)
+ bufnr = vim._resolve_bufnr(bufnr or opts.bufnr)
do
-- Resolve options with user settings from vim.diagnostic.config
@@ -1961,17 +2324,24 @@ function M.open_float(opts, ...)
if not opts.focus_id then
opts.focus_id = scope
end
+
+ --- @diagnostic disable-next-line: param-type-mismatch
local float_bufnr, winnr = vim.lsp.util.open_floating_preview(lines, 'plaintext', opts)
+ vim.bo[float_bufnr].path = vim.bo[bufnr].path
+
+ --- @diagnostic disable-next-line: deprecated
+ local add_highlight = api.nvim_buf_add_highlight
+
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)
+ 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)
+ 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)
+ add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1)
end
end
@@ -1993,7 +2363,7 @@ function M.reset(namespace, bufnr)
vim.validate('namespace', namespace, 'number', true)
vim.validate('bufnr', bufnr, 'number', true)
- local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
+ local buffers = bufnr and { vim._resolve_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache)
for _, iter_bufnr in ipairs(buffers) do
local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr])
for _, iter_namespace in ipairs(namespaces) do
@@ -2024,7 +2394,8 @@ end
--- (default: `true`)
--- @field open? boolean
---
---- Title of quickfix list. Defaults to "Diagnostics".
+--- Title of quickfix list. Defaults to "Diagnostics". If there's already a quickfix list with this
+--- title, it's updated. If not, a new quickfix list is created.
--- @field title? string
---
--- See |diagnostic-severity|.
@@ -2131,7 +2502,7 @@ function M.enable(enable, filter)
ns.disabled = not enable
end
else
- bufnr = get_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
if not ns_id then
diagnostic_disabled[bufnr] = (not enable) and true or nil
else
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index e1e73d63fe..cc7358ee49 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -275,6 +275,9 @@ local extension = {
mdh = 'c',
epro = 'c',
qc = 'c',
+ c3 = 'c3',
+ c3i = 'c3',
+ c3t = 'c3',
cabal = 'cabal',
cairo = 'cairo',
capnp = 'capnp',
@@ -300,6 +303,7 @@ local extension = {
cho = 'chordpro',
chordpro = 'chordpro',
ck = 'chuck',
+ cl = detect.cl,
eni = 'cl',
icl = 'clean',
cljx = 'clojure',
@@ -349,6 +353,7 @@ local extension = {
cql = 'cqlang',
crm = 'crm',
cr = 'crystal',
+ cake = 'cs',
csx = 'cs',
cs = 'cs',
csc = 'csc',
@@ -500,6 +505,7 @@ local extension = {
gdshader = 'gdshader',
shader = 'gdshader',
ged = 'gedcom',
+ gel = 'gel',
gmi = 'gemtext',
gemini = 'gemtext',
gift = 'gift',
@@ -588,6 +594,7 @@ local extension = {
hw = detect.hw,
module = detect.hw,
pkg = detect.hw,
+ hy = 'hy',
iba = 'ibasic',
ibi = 'ibasic',
icn = 'icon',
@@ -612,6 +619,7 @@ local extension = {
janet = 'janet',
jav = 'java',
java = 'java',
+ jsh = 'java',
jj = 'javacc',
jjt = 'javacc',
es = 'javascript',
@@ -624,7 +632,7 @@ local extension = {
clp = 'jess',
jgr = 'jgraph',
jinja = 'jinja',
- jjdescription = 'jj',
+ jjdescription = 'jjdescription',
j73 = 'jovial',
jov = 'jovial',
jovial = 'jovial',
@@ -653,6 +661,10 @@ local extension = {
jsp = 'jsp',
jl = 'julia',
just = 'just',
+ Just = 'just',
+ JUST = 'just',
+ kl = 'karel',
+ KL = 'karel',
kdl = 'kdl',
kv = 'kivy',
kix = 'kix',
@@ -666,6 +678,7 @@ local extension = {
k = 'kwt',
ACE = 'lace',
ace = 'lace',
+ lalrpop = 'lalrpop',
latte = 'latte',
lte = 'latte',
ld = 'ld',
@@ -686,13 +699,11 @@ local extension = {
ily = 'lilypond',
liquid = 'liquid',
liq = 'liquidsoap',
- cl = 'lisp',
L = 'lisp',
lisp = 'lisp',
el = 'lisp',
lsp = 'lisp',
asd = 'lisp',
- stsg = 'lisp',
lt = 'lite',
lite = 'lite',
livemd = 'livebook',
@@ -721,14 +732,14 @@ local extension = {
mc = detect.mc,
quake = 'm3quake',
m4 = function(path, bufnr)
- path = path:lower()
- return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4' or nil
+ local pathl = path:lower()
+ return not (pathl:find('html%.m4$') or pathl:find('fvwm2rc')) and 'm4' or nil
end,
eml = 'mail',
mk = detect.make,
mak = detect.make,
page = 'mallard',
- map = 'map',
+ map = detect_line1('^%*+$', 'lnkmap', 'map'),
mws = 'maple',
mpl = 'maple',
mv = 'maple',
@@ -738,6 +749,7 @@ local extension = {
mkd = detect.markdown,
markdown = detect.markdown,
mdown = detect.markdown,
+ masm = 'masm',
mhtml = 'mason',
mason = 'mason',
master = 'master',
@@ -800,6 +812,7 @@ local extension = {
n1ql = 'n1ql',
nql = 'n1ql',
nanorc = 'nanorc',
+ nasm = 'nasm',
NSA = 'natural',
NSC = 'natural',
NSG = 'natural',
@@ -834,6 +847,7 @@ local extension = {
tr = 'nroff',
nsi = 'nsis',
nsh = 'nsis',
+ nt = 'ntriples',
nu = 'nu',
obj = 'obj',
objdump = 'objdump',
@@ -953,11 +967,14 @@ local extension = {
ps1xml = 'ps1xml',
psf = 'psf',
psl = 'psl',
+ ptx = 'ptx',
pug = 'pug',
purs = 'purescript',
arr = 'pyret',
pxd = 'pyrex',
+ pxi = 'pyrex',
pyx = 'pyrex',
+ ['pyx+'] = 'pyrex',
pyw = 'python',
py = 'python',
pyi = 'python',
@@ -1050,16 +1067,17 @@ local extension = {
builder = 'ruby',
rake = 'ruby',
rs = 'rust',
+ sa = detect.sa,
sage = 'sage',
sls = 'salt',
sas = 'sas',
sass = 'sass',
- sa = 'sather',
sbt = 'sbt',
scala = 'scala',
ss = 'scheme',
scm = 'scheme',
sld = 'scheme',
+ stsg = 'scheme',
sce = 'scilab',
sci = 'scilab',
scss = 'scss',
@@ -1082,6 +1100,7 @@ local extension = {
la = 'sh',
lai = 'sh',
mdd = 'sh',
+ slang = 'shaderslang',
sieve = 'sieve',
siv = 'sieve',
sig = detect.sig,
@@ -1224,6 +1243,7 @@ local extension = {
toml = 'toml',
tpp = 'tpp',
treetop = 'treetop',
+ trig = 'trig',
slt = 'tsalt',
tsscl = 'tsscl',
tssgm = 'tssgm',
@@ -1323,6 +1343,7 @@ local extension = {
xlb = 'xml',
xlc = 'xml',
xba = 'xml',
+ slnx = 'xml',
xpm = detect_line1('XPM2', 'xpm2', 'xpm'),
xpm2 = 'xpm2',
xqy = 'xquery',
@@ -1378,7 +1399,7 @@ local extension = {
txt = detect.txt,
xml = detect.xml,
y = detect.y,
- cmd = detect_line1('^/%*', 'rexx', 'dosbatch'),
+ cmd = detect.cmd,
rul = detect.rul,
cpy = detect_line1('^##', 'python', 'cobol'),
dsl = detect_line1('^%s*<!', 'dsl', 'structurizr'),
@@ -1427,6 +1448,7 @@ local filename = {
['/etc/asound.conf'] = 'alsaconf',
['build.xml'] = 'ant',
['.htaccess'] = 'apache',
+ APKBUILD = 'apkbuild',
['apt.conf'] = 'aptconf',
['/.aptitude/config'] = 'aptconf',
['=tagging-method'] = 'arch',
@@ -1485,6 +1507,7 @@ local filename = {
['NEWS.dch'] = 'debchangelog',
['NEWS.Debian'] = 'debchangelog',
['/debian/control'] = 'debcontrol',
+ ['/DEBIAN/control'] = 'debcontrol',
['/debian/copyright'] = 'debcopyright',
['/etc/apt/sources.list'] = 'debsources',
['denyhosts.conf'] = 'denyhosts',
@@ -1532,6 +1555,8 @@ local filename = {
['filter-rules'] = 'elmfilt',
['exim.conf'] = 'exim',
exports = 'exports',
+ fennelrc = 'fennel',
+ ['.fennelrc'] = 'fennel',
['.fetchmailrc'] = 'fetchmail',
fvSchemes = detect.foam,
fvSolution = detect.foam,
@@ -1553,6 +1578,12 @@ local filename = {
['.gitmodules'] = 'gitconfig',
['.gitattributes'] = 'gitattributes',
['.gitignore'] = 'gitignore',
+ ['.ignore'] = 'gitignore',
+ ['.dockerignore'] = 'gitignore',
+ ['.fdignore'] = 'gitignore',
+ ['.npmignore'] = 'gitignore',
+ ['.rgignore'] = 'gitignore',
+ ['.vscodeignore'] = 'gitignore',
['gitolite.conf'] = 'gitolite',
['git-rebase-todo'] = 'gitrebase',
gkrellmrc = 'gkrellmrc',
@@ -1587,6 +1618,7 @@ local filename = {
['/etc/host.conf'] = 'hostconf',
['/etc/hosts.allow'] = 'hostsaccess',
['/etc/hosts.deny'] = 'hostsaccess',
+ ['.hy-history'] = 'hy',
['hyprland.conf'] = 'hyprlang',
['hyprpaper.conf'] = 'hyprlang',
['hypridle.conf'] = 'hyprlang',
@@ -1608,6 +1640,7 @@ local filename = {
['.lintstagedrc'] = 'json',
['deno.lock'] = 'json',
['flake.lock'] = 'json',
+ ['.swcrc'] = 'json',
['.babelrc'] = 'jsonc',
['.eslintrc'] = 'jsonc',
['.hintrc'] = 'jsonc',
@@ -1617,9 +1650,13 @@ local filename = {
['.luaurc'] = 'jsonc',
['.swrc'] = 'jsonc',
['.vsconfig'] = 'jsonc',
+ ['bun.lock'] = 'jsonc',
['.justfile'] = 'just',
+ ['.Justfile'] = 'just',
+ ['.JUSTFILE'] = 'just',
['justfile'] = 'just',
['Justfile'] = 'just',
+ ['JUSTFILE'] = 'just',
Kconfig = 'kconfig',
['Kconfig.debug'] = 'kconfig',
['Config.in'] = 'kconfig',
@@ -1762,6 +1799,7 @@ local filename = {
['Rantfile'] = 'ruby',
Vagrantfile = 'ruby',
['smb.conf'] = 'samba',
+ ['.lips_repl_history'] = 'scheme',
screenrc = 'screen',
['.screenrc'] = 'screen',
['/etc/sensors3.conf'] = 'sensors',
@@ -1783,7 +1821,6 @@ local filename = {
['.kshrc'] = detect.ksh,
['.profile'] = detect.sh,
['/etc/profile'] = detect.sh,
- APKBUILD = detect.bash,
PKGBUILD = detect.bash,
['.tcshrc'] = detect.tcsh,
['tcsh.login'] = detect.tcsh,
@@ -1860,11 +1897,17 @@ local filename = {
['/etc/blkid.tab'] = 'xml',
['/etc/blkid.tab.old'] = 'xml',
['fonts.conf'] = 'xml',
+ ['Directory.Packages.props'] = 'xml',
+ ['Directory.Build.props'] = 'xml',
+ ['Directory.Build.targets'] = 'xml',
['.clangd'] = 'yaml',
['.clang-format'] = 'yaml',
['.clang-tidy'] = 'yaml',
+ ['pixi.lock'] = 'yaml',
['yarn.lock'] = 'yaml',
matplotlibrc = 'yaml',
+ ['.condarc'] = 'yaml',
+ condarc = 'yaml',
zathurarc = 'zathurarc',
['/etc/zprofile'] = 'zsh',
['.zlogin'] = 'zsh',
@@ -2141,8 +2184,8 @@ local pattern = {
['/gitolite%-admin/conf/'] = starsetf('gitolite'),
['/%.i3/config$'] = 'i3config',
['/i3/config$'] = 'i3config',
- ['/supertux2/config$'] = 'lisp',
['/%.mplayer/config$'] = 'mplayerconf',
+ ['/supertux2/config$'] = 'scheme',
['/neofetch/config%.conf$'] = 'sh',
['/%.ssh/config$'] = 'sshconfig',
['/%.sway/config$'] = 'swayconfig',
@@ -2210,8 +2253,10 @@ local pattern = {
['^dictd.*%.conf$'] = 'dictdconf',
['/lxqt/.*%.conf$'] = 'dosini',
['/screengrab/.*%.conf$'] = 'dosini',
+ ['/%.config/fd/ignore$'] = 'gitignore',
['^${GNUPGHOME}/gpg%.conf$'] = 'gpg',
['/boot/grub/grub%.conf$'] = 'grub',
+ ['/hypr/.*%.conf$'] = 'hyprlang',
['^lilo%.conf'] = starsetf('lilo'),
['^named.*%.conf$'] = 'named',
['^rndc.*%.conf$'] = 'named',
@@ -2313,6 +2358,7 @@ local pattern = {
['%.cmake%.in$'] = 'cmake',
['^crontab%.'] = starsetf('crontab'),
['^cvs%d+$'] = 'cvs',
+ ['/DEBIAN/control$'] = 'debcontrol',
['^php%.ini%-'] = 'dosini',
['^php%-fpm%.conf'] = 'dosini',
['^www%.conf'] = 'dosini',
@@ -2338,6 +2384,8 @@ local pattern = {
['%.html%.m4$'] = 'htmlm4',
['^JAM.*%.'] = starsetf('jam'),
['^Prl.*%.'] = starsetf('jam'),
+ ['^${HOME}/.*/Code/User/.*%.json$'] = 'jsonc',
+ ['^${HOME}/.*/VSCodium/User/.*%.json$'] = 'jsonc',
['%.properties_..$'] = 'jproperties',
['%.properties_.._..$'] = 'jproperties',
['%.properties_.._.._'] = starsetf('jproperties'),
diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua
index 98b001bd51..fc0b45ecd8 100644
--- a/runtime/lua/vim/filetype/detect.lua
+++ b/runtime/lua/vim/filetype/detect.lua
@@ -34,6 +34,12 @@ local matchregex = vim.filetype._matchregex
-- can be detected from the first five lines of the file.
--- @type vim.filetype.mapfn
function M.asm(path, bufnr)
+ -- tiasm uses `* comment`
+ local lines = table.concat(getlines(bufnr, 1, 10), '\n')
+ if findany(lines, { '^%*', '\n%*', 'Texas Instruments Incorporated' }) then
+ return 'tiasm'
+ end
+
local syntax = vim.b[bufnr].asmsyntax
if not syntax or syntax == '' then
syntax = M.asm_syntax(path, bufnr)
@@ -181,6 +187,16 @@ function M.changelog(_, bufnr)
end
--- @type vim.filetype.mapfn
+function M.cl(_, bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 4))
+ if lines:match('/%*') then
+ return 'opencl'
+ else
+ return 'lisp'
+ end
+end
+
+--- @type vim.filetype.mapfn
function M.class(_, bufnr)
-- Check if not a Java class (starts with '\xca\xfe\xba\xbe')
if not getline(bufnr, 1):find('^\202\254\186\190') then
@@ -209,6 +225,24 @@ function M.cls(_, bufnr)
return 'st'
end
+--- *.cmd is close to a Batch file, but on OS/2 Rexx files and TI linker command files also use *.cmd.
+--- lnk: `/* comment */`, `// comment`, and `--linker-option=value`
+--- rexx: `/* comment */`, `-- comment`
+--- @type vim.filetype.mapfn
+function M.cmd(_, bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 20))
+ if matchregex(lines, [[MEMORY\|SECTIONS\|\%(^\|\n\)--\S\|\%(^\|\n\)//]]) then
+ return 'lnk'
+ else
+ local line1 = getline(bufnr, 1)
+ if line1:find('^/%*') then
+ return 'rexx'
+ else
+ return 'dosbatch'
+ end
+ end
+end
+
--- @type vim.filetype.mapfn
function M.conf(path, bufnr)
if fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then
@@ -227,7 +261,8 @@ end
--- Debian Control
--- @type vim.filetype.mapfn
function M.control(_, bufnr)
- if getline(bufnr, 1):find('^Source:') then
+ local line1 = getline(bufnr, 1)
+ if line1 and findany(line1, { '^Source:', '^Package:' }) then
return 'debcontrol'
end
end
@@ -722,7 +757,7 @@ function M.html(_, bufnr)
if
matchregex(
line,
- [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content\|{{.*}}]]
+ [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content]]
)
then
return 'htmlangular'
@@ -846,7 +881,7 @@ end
--- (refactor of filetype.vim since the patterns are case-insensitive)
--- @type vim.filetype.mapfn
function M.log(path, _)
- path = path:lower()
+ path = path:lower() --- @type string LuaLS bug
if
findany(
path,
@@ -1132,7 +1167,7 @@ end
--- @type vim.filetype.mapfn
function M.perl(path, bufnr)
local dir_name = vim.fs.dirname(path)
- if fn.expand(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then
+ if fn.fnamemodify(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then
return 'perl'
end
local first_line = getline(bufnr, 1)
@@ -1340,7 +1375,7 @@ end
local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*'
--- @type vim.filetype.mapfn
function M.rules(path)
- path = path:lower()
+ path = path:lower() --- @type string LuaLS bug
if
findany(path, {
'/etc/udev/.*%.rules$',
@@ -1363,7 +1398,7 @@ function M.rules(path)
if not ok then
return 'hog'
end
- local dir = fn.expand(path, ':h')
+ local dir = fn.fnamemodify(path, ':h')
for _, line in ipairs(config_lines) do
local match = line:match(udev_rules_pattern)
if match then
@@ -1395,6 +1430,15 @@ function M.sig(_, bufnr)
end
end
+--- @type vim.filetype.mapfn
+function M.sa(_, bufnr)
+ local lines = table.concat(getlines(bufnr, 1, 4), '\n')
+ if findany(lines, { '^;', '\n;' }) then
+ return 'tiasm'
+ end
+ return 'sather'
+end
+
-- This function checks the first 25 lines of file extension "sc" to resolve
-- detection between scala and SuperCollider
--- @type vim.filetype.mapfn
@@ -1719,7 +1763,7 @@ function M.v(_, bufnr)
return vim.g.filetype_v
end
local in_comment = 0
- for _, line in ipairs(getlines(bufnr, 1, 200)) do
+ for _, line in ipairs(getlines(bufnr, 1, 500)) do
if line:find('^%s*/%*') then
in_comment = 1
end
@@ -1733,7 +1777,7 @@ function M.v(_, bufnr)
or line:find('%(%*') and not line:find('/[/*].*%(%*')
then
return 'coq'
- elseif findany(line, { ';%s*$', ';%s*/[/*]' }) then
+ elseif findany(line, { ';%s*$', ';%s*/[/*]', '^%s*module%s+%w+%s*%(' }) then
return 'verilog'
end
end
@@ -1833,6 +1877,7 @@ local patterns_hashbang = {
ruby = 'ruby',
['node\\(js\\)\\=\\>\\|js\\>'] = { 'javascript', { vim_regex = true } },
['rhino\\>'] = { 'javascript', { vim_regex = true } },
+ just = 'just',
-- BC calculator
['^bc\\>'] = { 'bc', { vim_regex = true } },
['sed\\>'] = { 'sed', { vim_regex = true } },
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index d91eeaf02f..8b4242223a 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -1,3 +1,15 @@
+--- @brief <pre>help
+--- *vim.fs.exists()*
+--- Use |uv.fs_stat()| to check a file's type, and whether it exists.
+---
+--- Example:
+---
+--- >lua
+--- if vim.uv.fs_stat(file) then
+--- vim.print("file exists")
+--- end
+--- <
+
local uv = vim.uv
local M = {}
@@ -93,14 +105,23 @@ function M.basename(file)
return file:match('/$') and '' or (file:match('[^/]*$'))
end
---- Concatenate directories and/or file paths into a single path with normalization
---- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`)
+--- Concatenates partial paths (one absolute or relative path followed by zero or more relative
+--- paths). Slashes are normalized: redundant slashes are removed, and (on Windows) backslashes are
+--- replaced with forward-slashes.
+---
+--- Examples:
+--- - "foo/", "/bar" => "foo/bar"
+--- - Windows: "a\foo\", "\bar" => "a/foo/bar"
---
---@since 12
---@param ... string
---@return string
function M.joinpath(...)
- return (table.concat({ ... }, '/'):gsub('//+', '/'))
+ local path = table.concat({ ... }, '/')
+ if iswin then
+ path = path:gsub('\\', '/')
+ end
+ return (path:gsub('//+', '/'))
end
---@alias Iterator fun(): string?, string?
@@ -115,6 +136,7 @@ end
--- - skip: (fun(dir_name: string): boolean)|nil Predicate
--- to control traversal. Return false to stop searching the current directory.
--- Only useful when depth > 1
+--- - follow: boolean|nil Follow symbolic links. (default: true)
---
---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type".
--- "name" is the basename of the item relative to {path}.
@@ -126,6 +148,7 @@ function M.dir(path, opts)
vim.validate('path', path, 'string')
vim.validate('depth', opts.depth, 'number', true)
vim.validate('skip', opts.skip, 'function', true)
+ vim.validate('follow', opts.follow, 'boolean', true)
path = M.normalize(path)
if not opts.depth or opts.depth == 1 then
@@ -156,7 +179,9 @@ function M.dir(path, opts)
if
opts.depth
and level < opts.depth
- and t == 'directory'
+ and (t == 'directory' or (t == 'link' and opts.follow ~= false and (vim.uv.fs_stat(
+ M.joinpath(path, f)
+ ) or {}).type == 'directory'))
and (not opts.skip or opts.skip(f) ~= false)
then
dirs[#dirs + 1] = { f, level + 1 }
@@ -190,6 +215,10 @@ end
--- Use `math.huge` to place no limit on the number of matches.
--- (default: `1`)
--- @field limit? number
+---
+--- Follow symbolic links.
+--- (default: `true`)
+--- @field follow? boolean
--- Find files or directories (or other items as specified by `opts.type`) in the given path.
---
@@ -213,7 +242,7 @@ end
---
--- -- get all files ending with .cpp or .hpp inside lib/
--- local cpp_hpp = vim.fs.find(function(name, path)
---- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$')
+--- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
--- end, {limit = math.huge, type = 'file'})
--- ```
---
@@ -223,6 +252,7 @@ end
--- If {names} is a function, it is called for each traversed item with args:
--- - name: base name of the current item
--- - path: full path of the current item
+---
--- The function should return `true` if the given item is considered a match.
---
---@param opts vim.fs.find.Opts Optional keyword arguments:
@@ -235,6 +265,7 @@ function M.find(names, opts)
vim.validate('stop', opts.stop, 'string', true)
vim.validate('type', opts.type, 'string', true)
vim.validate('limit', opts.limit, 'number', true)
+ vim.validate('follow', opts.follow, 'boolean', true)
if type(names) == 'string' then
names = { names }
@@ -324,7 +355,14 @@ function M.find(names, opts)
end
end
- if type_ == 'directory' then
+ if
+ type_ == 'directory'
+ or (
+ type_ == 'link'
+ and opts.follow ~= false
+ and (vim.uv.fs_stat(f) or {}).type == 'directory'
+ )
+ then
dirs[#dirs + 1] = f
end
end
@@ -493,6 +531,27 @@ local function path_resolve_dot(path)
return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/')
end
+--- Expand tilde (~) character at the beginning of the path to the user's home directory.
+---
+--- @param path string Path to expand.
+--- @param sep string|nil Path separator to use. Uses os_sep by default.
+--- @return string Expanded path.
+local function expand_home(path, sep)
+ sep = sep or os_sep
+
+ if vim.startswith(path, '~') then
+ local home = uv.os_homedir() or '~' --- @type string
+
+ if home:sub(-1) == sep then
+ home = home:sub(1, -2)
+ end
+
+ path = home .. path:sub(2) --- @type string
+ end
+
+ return path
+end
+
--- @class vim.fs.normalize.Opts
--- @inlinedoc
---
@@ -556,18 +615,12 @@ function M.normalize(path, opts)
return ''
end
- -- Expand ~ to users home directory
- if vim.startswith(path, '~') then
- local home = uv.os_homedir() or '~'
- if home:sub(-1) == os_sep_local then
- home = home:sub(1, -2)
- end
- path = home .. path:sub(2)
- end
+ -- Expand ~ to user's home directory
+ path = expand_home(path, os_sep_local)
-- Expand environment variables if `opts.expand_env` isn't `false`
if opts.expand_env == nil or opts.expand_env then
- path = path:gsub('%$([%w_]+)', uv.os_getenv)
+ path = path:gsub('%$([%w_]+)', uv.os_getenv) --- @type string
end
if win then
@@ -593,8 +646,8 @@ function M.normalize(path, opts)
return prefix .. path
end
- -- Remove extraneous slashes from the prefix
- prefix = prefix:gsub('/+', '/')
+ -- Ensure capital drive and remove extraneous slashes from the prefix
+ prefix = prefix:gsub('^%a:', string.upper):gsub('/+', '/')
end
if not opts._fast then
@@ -667,4 +720,75 @@ function M.rm(path, opts)
end
end
+--- Convert path to an absolute path. A tilde (~) character at the beginning of the path is expanded
+--- to the user's home directory. Does not check if the path exists, normalize the path, resolve
+--- symlinks or hardlinks (including `.` and `..`), or expand environment variables. If the path is
+--- already absolute, it is returned unchanged. Also converts `\` path separators to `/`.
+---
+--- @param path string Path
+--- @return string Absolute path
+function M.abspath(path)
+ vim.validate('path', path, 'string')
+
+ -- Expand ~ to user's home directory
+ path = expand_home(path)
+
+ -- Convert path separator to `/`
+ path = path:gsub(os_sep, '/')
+
+ local prefix = ''
+
+ if iswin then
+ prefix, path = split_windows_path(path)
+ end
+
+ if prefix == '//' or vim.startswith(path, '/') then
+ -- Path is already absolute, do nothing
+ return prefix .. path
+ end
+
+ -- Windows allows paths like C:foo/bar, these paths are relative to the current working directory
+ -- of the drive specified in the path
+ local cwd = (iswin and prefix:match('^%w:$')) and uv.fs_realpath(prefix) or uv.cwd()
+ assert(cwd ~= nil)
+ -- Convert cwd path separator to `/`
+ cwd = cwd:gsub(os_sep, '/')
+
+ -- Prefix is not needed for expanding relative paths, as `cwd` already contains it.
+ return M.joinpath(cwd, path)
+end
+
+--- Gets `target` path relative to `base`, or `nil` if `base` is not an ancestor.
+---
+--- Example:
+---
+--- ```lua
+--- vim.fs.relpath('/var', '/var/lib') -- 'lib'
+--- vim.fs.relpath('/var', '/usr/bin') -- nil
+--- ```
+---
+--- @param base string
+--- @param target string
+--- @param opts table? Reserved for future use
+--- @return string|nil
+function M.relpath(base, target, opts)
+ vim.validate('base', base, 'string')
+ vim.validate('target', target, 'string')
+ vim.validate('opts', opts, 'table', true)
+
+ base = vim.fs.normalize(vim.fs.abspath(base))
+ target = vim.fs.normalize(vim.fs.abspath(target))
+ if base == target then
+ return '.'
+ end
+
+ local prefix = ''
+ if iswin then
+ prefix, base = split_windows_path(base)
+ end
+ base = prefix .. base .. (base ~= '/' and '/' or '')
+
+ return vim.startswith(target, base) and target:sub(#base + 1) or nil
+end
+
return M
diff --git a/runtime/lua/vim/func.lua b/runtime/lua/vim/func.lua
index f71659ffb4..fc8fa62c71 100644
--- a/runtime/lua/vim/func.lua
+++ b/runtime/lua/vim/func.lua
@@ -3,9 +3,6 @@ local M = {}
-- TODO(lewis6991): Private for now until:
-- - There are other places in the codebase that could benefit from this
-- (e.g. LSP), but might require other changes to accommodate.
--- - Invalidation of the cache needs to be controllable. Using weak tables
--- is an acceptable invalidation policy, but it shouldn't be the only
--- one.
-- - I don't think the story around `hash` is completely thought out. We
-- may be able to have a good default hash by hashing each argument,
-- so basically a better 'concat'.
@@ -17,6 +14,10 @@ local M = {}
--- Internally uses a |lua-weaktable| to cache the results of {fn} meaning the
--- cache will be invalidated whenever Lua does garbage collection.
---
+--- The cache can also be manually invalidated by calling `:clear()` on the returned object.
+--- Calling this function with no arguments clears the entire cache; otherwise, the arguments will
+--- be interpreted as function inputs, and only the cache entry at their hash will be cleared.
+---
--- The memoized function returns shared references so be wary about
--- mutating return values.
---
@@ -32,11 +33,12 @@ local M = {}
--- first n arguments passed to {fn}.
---
--- @param fn F Function to memoize.
---- @param strong? boolean Do not use a weak table
+--- @param weak? boolean Use a weak table (default `true`)
--- @return F # Memoized version of {fn}
--- @nodoc
-function M._memoize(hash, fn, strong)
- return require('vim.func._memoize')(hash, fn, strong)
+function M._memoize(hash, fn, weak)
+ -- this is wrapped in a function to lazily require the module
+ return require('vim.func._memoize')(hash, fn, weak)
end
return M
diff --git a/runtime/lua/vim/func/_memoize.lua b/runtime/lua/vim/func/_memoize.lua
index 6e557905a7..c46f878067 100644
--- a/runtime/lua/vim/func/_memoize.lua
+++ b/runtime/lua/vim/func/_memoize.lua
@@ -1,5 +1,7 @@
--- Module for private utility functions
+--- @alias vim.func.MemoObj { _hash: (fun(...): any), _weak: boolean?, _cache: table<any> }
+
--- @param argc integer?
--- @return fun(...): any
local function concat_hash(argc)
@@ -33,29 +35,49 @@ local function resolve_hash(hash)
return hash
end
+--- @param weak boolean?
+--- @return table
+local create_cache = function(weak)
+ return setmetatable({}, {
+ __mode = weak ~= false and 'kv',
+ })
+end
+
--- @generic F: function
--- @param hash integer|string|fun(...): any
--- @param fn F
---- @param strong? boolean
+--- @param weak? boolean
--- @return F
-return function(hash, fn, strong)
+return function(hash, fn, weak)
vim.validate('hash', hash, { 'number', 'string', 'function' })
vim.validate('fn', fn, 'function')
+ vim.validate('weak', weak, 'boolean', true)
- ---@type table<any,table<any,any>>
- local cache = {}
- if not strong then
- setmetatable(cache, { __mode = 'kv' })
- end
-
- hash = resolve_hash(hash)
+ --- @type vim.func.MemoObj
+ local obj = {
+ _cache = create_cache(weak),
+ _hash = resolve_hash(hash),
+ _weak = weak,
+ --- @param self vim.func.MemoObj
+ clear = function(self, ...)
+ if select('#', ...) == 0 then
+ self._cache = create_cache(self._weak)
+ return
+ end
+ local key = self._hash(...)
+ self._cache[key] = nil
+ end,
+ }
- return function(...)
- local key = hash(...)
- if cache[key] == nil then
- cache[key] = vim.F.pack_len(fn(...))
- end
-
- return vim.F.unpack_len(cache[key])
- end
+ return setmetatable(obj, {
+ --- @param self vim.func.MemoObj
+ __call = function(self, ...)
+ local key = self._hash(...)
+ local cache = self._cache
+ if cache[key] == nil then
+ cache[key] = vim.F.pack_len(fn(...))
+ end
+ return vim.F.unpack_len(cache[key])
+ end,
+ })
end
diff --git a/runtime/lua/vim/glob.lua b/runtime/lua/vim/glob.lua
index 4f86d5e1ca..242c70d4b2 100644
--- a/runtime/lua/vim/glob.lua
+++ b/runtime/lua/vim/glob.lua
@@ -53,6 +53,7 @@ function M.to_lpeg(pattern)
end
-- luacheck: pop
+ --- @diagnostic disable-next-line: missing-fields
local p = P({
'Pattern',
Pattern = V('Elem') ^ -1 * V('End'),
diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua
index 52a7a13966..a265e2b901 100644
--- a/runtime/lua/vim/health.lua
+++ b/runtime/lua/vim/health.lua
@@ -11,7 +11,7 @@
--- <
--- Plugin authors are encouraged to write new healthchecks. |health-dev|
---
---- Commands *health-commands*
+--- COMMANDS *health-commands*
---
--- *:che* *:checkhealth*
--- :che[ckhealth] Run all healthchecks.
@@ -39,6 +39,23 @@
--- :checkhealth vim*
--- <
---
+--- USAGE *health-usage*
+---
+--- Local mappings in the healthcheck buffer:
+---
+--- q Closes the window.
+---
+--- Global configuration:
+---
+--- *g:health*
+--- g:health Dictionary with the following optional keys:
+--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in
+--- a floating window instead of the default behavior.
+---
+--- Example: >lua
+--- vim.g.health = { style = 'float' }
+---
+--- --------------------------------------------------------------------------------
--- Create a healthcheck *health-dev*
---
--- Healthchecks are functions that check the user environment, configuration, or
@@ -101,7 +118,7 @@ local function filepath_to_healthcheck(path)
func = 'health#' .. name .. '#check'
filetype = 'v'
else
- local subpath = path:gsub('.*lua/', '')
+ local subpath = path:gsub('.*/lua/', '')
if vim.fs.basename(subpath) == 'health.lua' then
-- */health.lua
name = vim.fs.dirname(subpath)
@@ -109,7 +126,7 @@ local function filepath_to_healthcheck(path)
-- */health/init.lua
name = vim.fs.dirname(vim.fs.dirname(subpath))
end
- name = name:gsub('/', '.')
+ name = assert(name:gsub('/', '.')) --- @type string
func = 'require("' .. name .. '.health").check()'
filetype = 'l'
@@ -218,7 +235,7 @@ local function format_report_message(status, msg, ...)
-- Report each suggestion
for _, v in ipairs(varargs) do
if v then
- output = output .. '\n - ' .. indent_after_line1(v, 6)
+ output = output .. '\n - ' .. indent_after_line1(v, 6) --- @type string
end
end
end
@@ -331,13 +348,31 @@ function M._check(mods, plugin_names)
local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$')
- -- When no command modifiers are used:
- -- - If the current buffer is empty, open healthcheck directly.
- -- - If not specified otherwise open healthcheck in a tab.
- local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer'
-
local bufnr = vim.api.nvim_create_buf(true, true)
- vim.cmd(buf_cmd .. ' ' .. bufnr)
+ if
+ vim.g.health
+ and type(vim.g.health) == 'table'
+ and vim.tbl_get(vim.g.health, 'style') == 'float'
+ then
+ local max_height = math.floor(vim.o.lines * 0.8)
+ local max_width = 80
+ local float_bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', {
+ height = max_height,
+ width = max_width,
+ offset_x = math.floor((vim.o.columns - max_width) / 2),
+ offset_y = math.floor((vim.o.lines - max_height) / 2) - 1,
+ relative = 'editor',
+ })
+ vim.api.nvim_set_current_win(float_winid)
+ vim.bo[float_bufnr].modifiable = true
+ vim.wo[float_winid].list = false
+ else
+ -- When no command modifiers are used:
+ -- - If the current buffer is empty, open healthcheck directly.
+ -- - If not specified otherwise open healthcheck in a tab.
+ local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer'
+ vim.cmd(buf_cmd .. ' ' .. bufnr)
+ end
if vim.fn.bufexists('health://') == 1 then
vim.cmd.bwipe('health://')
@@ -407,6 +442,16 @@ function M._check(mods, plugin_names)
-- Clear the 'Running healthchecks...' message.
vim.cmd.redraw()
vim.print('')
+
+ -- Quit with 'q' inside healthcheck buffers.
+ vim.keymap.set('n', 'q', function()
+ if not pcall(vim.cmd.close) then
+ vim.cmd.bdelete()
+ end
+ end, { buffer = bufnr, silent = true, noremap = true, nowait = true })
+
+ -- Once we're done writing checks, set nomodifiable.
+ vim.bo[bufnr].modifiable = false
end
return M
diff --git a/runtime/lua/vim/health/health.lua b/runtime/lua/vim/health/health.lua
index d226f35f9a..dd6fe7f608 100644
--- a/runtime/lua/vim/health/health.lua
+++ b/runtime/lua/vim/health/health.lua
@@ -183,13 +183,16 @@ end
local function check_rplugin_manifest()
health.start('Remote Plugins')
- local existing_rplugins = {}
- for _, item in ipairs(vim.fn['remote#host#PluginsForHost']('python3')) do
+ local existing_rplugins = {} --- @type table<string,string>
+ --- @type {path:string}[]
+ local items = vim.fn['remote#host#PluginsForHost']('python3')
+ for _, item in ipairs(items) do
existing_rplugins[item.path] = 'python3'
end
local require_update = false
local handle_path = function(path)
+ --- @type string[]
local python_glob = vim.fn.glob(path .. '/rplugin/python*', true, true)
if vim.tbl_isempty(python_glob) then
return
@@ -198,6 +201,7 @@ local function check_rplugin_manifest()
local python_dir = python_glob[1]
local python_version = vim.fs.basename(python_dir)
+ --- @type string[]
local scripts = vim.fn.glob(python_dir .. '/*.py', true, true)
vim.list_extend(scripts, vim.fn.glob(python_dir .. '/*/__init__.py', true, true))
@@ -227,7 +231,10 @@ local function check_rplugin_manifest()
end
end
- for _, path in ipairs(vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)')) do
+ --- @type string[]
+ local paths = vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)')
+
+ for _, path in ipairs(paths) do
handle_path(path)
end
diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua
index 099efa3c61..070748d31e 100644
--- a/runtime/lua/vim/hl.lua
+++ b/runtime/lua/vim/hl.lua
@@ -17,6 +17,9 @@ M.priorities = {
user = 200,
}
+local range_timer --- @type uv.uv_timer_t?
+local range_hl_clear --- @type fun()?
+
--- @class vim.hl.range.Opts
--- @inlinedoc
---
@@ -31,6 +34,10 @@ M.priorities = {
--- Highlight priority
--- (default: `vim.hl.priorities.user`)
--- @field priority? integer
+---
+--- Time in ms before highlight is cleared
+--- (default: -1 no timeout)
+--- @field timeout? integer
--- Apply highlight group to range of text.
---
@@ -45,6 +52,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
local regtype = opts.regtype or 'v'
local inclusive = opts.inclusive or false
local priority = opts.priority or M.priorities.user
+ local timeout = opts.timeout or -1
local v_maxcol = vim.v.maxcol
@@ -100,6 +108,19 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
end
end
+ if range_timer and not range_timer:is_closing() then
+ range_timer:close()
+ assert(range_hl_clear)
+ range_hl_clear()
+ end
+
+ range_hl_clear = function()
+ range_timer = nil
+ range_hl_clear = nil
+ pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns, 0, -1)
+ pcall(vim.api.nvim__ns_set, { wins = {} })
+ end
+
for _, res in ipairs(region) do
local start_row = res[1][2] - 1
local start_col = res[1][3] - 1
@@ -113,11 +134,13 @@ function M.range(bufnr, ns, higroup, start, finish, opts)
strict = false,
})
end
+
+ if timeout ~= -1 then
+ range_timer = vim.defer_fn(range_hl_clear, timeout)
+ end
end
-local yank_ns = api.nvim_create_namespace('hlyank')
-local yank_timer --- @type uv.uv_timer_t?
-local yank_cancel --- @type fun()?
+local yank_ns = api.nvim_create_namespace('nvim.hlyank')
--- Highlight the yanked text during a |TextYankPost| event.
---
@@ -152,31 +175,17 @@ function M.on_yank(opts)
end
local higroup = opts.higroup or 'IncSearch'
- local timeout = opts.timeout or 150
local bufnr = vim.api.nvim_get_current_buf()
local winid = vim.api.nvim_get_current_win()
- if yank_timer then
- yank_timer:close()
- assert(yank_cancel)
- yank_cancel()
- end
vim.api.nvim__ns_set(yank_ns, { wins = { winid } })
M.range(bufnr, yank_ns, higroup, "'[", "']", {
regtype = event.regtype,
inclusive = event.inclusive,
priority = opts.priority or M.priorities.user,
+ timeout = opts.timeout or 150,
})
-
- yank_cancel = function()
- yank_timer = nil
- yank_cancel = nil
- pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1)
- pcall(vim.api.nvim__ns_set, { wins = {} })
- end
-
- yank_timer = vim.defer_fn(yank_cancel, timeout)
end
return M
diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua
index c232f69590..cdf34897d4 100644
--- a/runtime/lua/vim/inspect.lua
+++ b/runtime/lua/vim/inspect.lua
@@ -1,3 +1,4 @@
+--- @diagnostic disable: no-unknown
local inspect = {
_VERSION = 'inspect.lua 3.1.0',
_URL = 'http://github.com/kikito/inspect.lua',
diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua
index 0cce0ab21d..c7158673fe 100644
--- a/runtime/lua/vim/loader.lua
+++ b/runtime/lua/vim/loader.lua
@@ -399,50 +399,57 @@ function M.reset(path)
end
end
---- Enables the experimental Lua module loader:
---- * overrides loadfile
+--- Enables or disables the experimental Lua module loader:
+---
+--- Enable (`enable=true`):
+--- * overrides |loadfile()|
--- * adds the Lua loader using the byte-compilation cache
--- * adds the libs loader
--- * removes the default Nvim loader
---
---- @since 0
-function M.enable()
- if M.enabled then
- return
- end
- M.enabled = true
- vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p')
- _G.loadfile = loadfile_cached
- -- add Lua loader
- table.insert(loaders, 2, loader_cached)
- -- add libs loader
- table.insert(loaders, 3, loader_lib_cached)
- -- remove Nvim loader
- for l, loader in ipairs(loaders) do
- if loader == vim._load_package then
- table.remove(loaders, l)
- break
- end
- end
-end
-
---- Disables the experimental Lua module loader:
+--- Disable (`enable=false`):
--- * removes the loaders
--- * adds the default Nvim loader
---
--- @since 0
-function M.disable()
- if not M.enabled then
+---
+--- @param enable? (boolean) true/nil to enable, false to disable
+function M.enable(enable)
+ enable = enable == nil and true or enable
+ if enable == M.enabled then
return
end
- M.enabled = false
- _G.loadfile = _loadfile
- for l, loader in ipairs(loaders) do
- if loader == loader_cached or loader == loader_lib_cached then
- table.remove(loaders, l)
+ M.enabled = enable
+
+ if enable then
+ vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p')
+ _G.loadfile = loadfile_cached
+ -- add Lua loader
+ table.insert(loaders, 2, loader_cached)
+ -- add libs loader
+ table.insert(loaders, 3, loader_lib_cached)
+ -- remove Nvim loader
+ for l, loader in ipairs(loaders) do
+ if loader == vim._load_package then
+ table.remove(loaders, l)
+ break
+ end
+ end
+ else
+ _G.loadfile = _loadfile
+ for l, loader in ipairs(loaders) do
+ if loader == loader_cached or loader == loader_lib_cached then
+ table.remove(loaders, l)
+ end
end
+ table.insert(loaders, 2, vim._load_package)
end
- table.insert(loaders, 2, vim._load_package)
+end
+
+--- @deprecated
+function M.disable()
+ vim.deprecate('vim.loader.disable', 'vim.loader.enable(false)', '0.12')
+ vim.loader.enable(false)
end
--- Tracks the time spent in a function
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 0de3b4ee4d..a45f9adeb6 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -3,6 +3,7 @@ local validate = vim.validate
local lsp = vim._defer_require('vim.lsp', {
_changetracking = ..., --- @module 'vim.lsp._changetracking'
+ _folding_range = ..., --- @module 'vim.lsp._folding_range'
_snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
_tagfunc = ..., --- @module 'vim.lsp._tagfunc'
_watchfiles = ..., --- @module 'vim.lsp._watchfiles'
@@ -57,6 +58,7 @@ lsp._request_name_to_capability = {
[ms.textDocument_documentHighlight] = { 'documentHighlightProvider' },
[ms.textDocument_documentLink] = { 'documentLinkProvider' },
[ms.textDocument_documentSymbol] = { 'documentSymbolProvider' },
+ [ms.textDocument_foldingRange] = { 'foldingRangeProvider' },
[ms.textDocument_formatting] = { 'documentFormattingProvider' },
[ms.textDocument_hover] = { 'hoverProvider' },
[ms.textDocument_implementation] = { 'implementationProvider' },
@@ -87,18 +89,6 @@ lsp._request_name_to_capability = {
-- TODO improve handling of scratch buffers with LSP attached.
---- Returns the buffer number for the given {bufnr}.
----
----@param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer
----@return integer bufnr
-local function resolve_bufnr(bufnr)
- validate('bufnr', bufnr, 'number', true)
- if bufnr == nil or bufnr == 0 then
- return api.nvim_get_current_buf()
- end
- return bufnr
-end
-
---@private
--- Called by the client when trying to call a method that's not
--- supported in any of the servers registered for the current buffer.
@@ -112,6 +102,22 @@ function lsp._unsupported_method(method)
return msg
end
+---@private
+---@param workspace_folders string|lsp.WorkspaceFolder[]?
+---@return lsp.WorkspaceFolder[]?
+function lsp._get_workspace_folders(workspace_folders)
+ if type(workspace_folders) == 'table' then
+ return workspace_folders
+ elseif type(workspace_folders) == 'string' then
+ return {
+ {
+ uri = vim.uri_from_fname(workspace_folders),
+ name = workspace_folders,
+ },
+ }
+ end
+end
+
local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' }
local format_line_ending = {
@@ -194,34 +200,393 @@ local function reuse_client_default(client, config)
return false
end
- if config.root_dir then
- local root = vim.uri_from_fname(config.root_dir)
- for _, dir in ipairs(client.workspace_folders or {}) do
- -- note: do not need to check client.root_dir since that should be client.workspace_folders[1]
- if root == dir.uri then
- return true
+ local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir)
+
+ if not config_folders or not next(config_folders) then
+ -- Reuse if the client was configured with no workspace folders
+ local client_config_folders =
+ lsp._get_workspace_folders(client.config.workspace_folders or client.config.root_dir)
+ return not client_config_folders or not next(client_config_folders)
+ end
+
+ for _, config_folder in ipairs(config_folders) do
+ local found = false
+ for _, client_folder in ipairs(client.workspace_folders or {}) do
+ if config_folder.uri == client_folder.uri then
+ found = true
+ break
end
end
+ if not found then
+ return false
+ end
end
- -- TODO(lewis6991): also check config.workspace_folders
+ return true
+end
- return false
+--- Reset defaults set by `set_defaults`.
+--- Must only be called if the last client attached to a buffer exits.
+local function reset_defaults(bufnr)
+ if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
+ vim.bo[bufnr].tagfunc = nil
+ end
+ if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
+ vim.bo[bufnr].omnifunc = nil
+ end
+ if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
+ vim.bo[bufnr].formatexpr = nil
+ end
+ vim._with({ buf = bufnr }, function()
+ local keymap = vim.fn.maparg('K', 'n', false, true)
+ if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
+ vim.keymap.del('n', 'K', { buffer = bufnr })
+ end
+ end)
end
---- @class vim.lsp.start.Opts
---- @inlinedoc
+--- @param code integer
+--- @param signal integer
+--- @param client_id integer
+local function on_client_exit(code, signal, client_id)
+ local client = all_clients[client_id]
+
+ vim.schedule(function()
+ for bufnr in pairs(client.attached_buffers) do
+ if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
+ api.nvim_exec_autocmds('LspDetach', {
+ buffer = bufnr,
+ modeline = false,
+ data = { client_id = client_id },
+ })
+ end
+
+ client.attached_buffers[bufnr] = nil
+
+ if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
+ reset_defaults(bufnr)
+ end
+ end
+
+ local namespace = vim.lsp.diagnostic.get_namespace(client_id)
+ vim.diagnostic.reset(namespace)
+ end)
+
+ local name = client.name or 'unknown'
+
+ -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
+ -- autocommands
+ vim.schedule(function()
+ all_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. Check log for errors: %s',
+ name,
+ code,
+ signal,
+ lsp.get_log_path()
+ )
+ vim.notify(msg, vim.log.levels.WARN)
+ end
+ end)
+end
+
+--- Creates and initializes a client with the given configuration.
+--- @param config vim.lsp.ClientConfig Configuration for the server.
+--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
+--- fully initialized. Use `on_init` to do any actions once
+--- the client has been initialized.
+--- @return string? # Error message, if any
+local function create_and_initialize_client(config)
+ local ok, res = pcall(require('vim.lsp.client').create, config)
+ if not ok then
+ return nil, res --[[@as string]]
+ end
+
+ local client = assert(res)
+
+ --- @diagnostic disable-next-line: invisible
+ table.insert(client._on_exit_cbs, on_client_exit)
+
+ all_clients[client.id] = client
+
+ client:initialize()
+
+ return client.id, nil
+end
+
+--- @class vim.lsp.Config : vim.lsp.ClientConfig
+---
+--- See `cmd` in [vim.lsp.ClientConfig].
+--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
+---
+--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`.
+--- If not provided, then the client will attach to all filetypes.
+--- @field filetypes? string[]
+---
+--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders,
+--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided.
+--- @field root_markers? string[]
+---
+--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on
+--- initialization. If a function, it accepts a single callback argument which must be called with
+--- the value of root_dir to use. The LSP server will not be started until the callback is called.
+--- @field root_dir? string|fun(cb:fun(string))
---
--- 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.
--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
+
+--- Update the configuration for an LSP client.
+---
+--- Use name '*' to set default configuration for all clients.
+---
+--- Can also be table-assigned to redefine the configuration for a client.
+---
+--- Examples:
+---
+--- - Add a root marker for all clients:
+--- ```lua
+--- vim.lsp.config('*', {
+--- root_markers = { '.git' },
+--- })
+--- ```
+--- - Add additional capabilities to all clients:
+--- ```lua
+--- vim.lsp.config('*', {
+--- capabilities = {
+--- textDocument = {
+--- semanticTokens = {
+--- multilineTokenSupport = true,
+--- }
+--- }
+--- }
+--- })
+--- ```
+--- - (Re-)define the configuration for clangd:
+--- ```lua
+--- vim.lsp.config.clangd = {
+--- cmd = {
+--- 'clangd',
+--- '--clang-tidy',
+--- '--background-index',
+--- '--offset-encoding=utf-8',
+--- },
+--- root_markers = { '.clangd', 'compile_commands.json' },
+--- filetypes = { 'c', 'cpp' },
+--- }
+--- ```
+--- - Get configuration for luals:
+--- ```lua
+--- local cfg = vim.lsp.config.luals
+--- ```
+---
+--- @param name string
+--- @param cfg vim.lsp.Config
+--- @diagnostic disable-next-line:assign-type-mismatch
+function lsp.config(name, cfg)
+ local _, _ = name, cfg -- ignore unused
+ -- dummy proto for docs
+end
+
+lsp._enabled_configs = {} --- @type table<string,{resolved_config:vim.lsp.Config?}>
+
+--- If a config in vim.lsp.config() is accessed then the resolved config becomes invalid.
+--- @param name string
+local function invalidate_enabled_config(name)
+ if name == '*' then
+ for _, v in pairs(lsp._enabled_configs) do
+ v.resolved_config = nil
+ end
+ elseif lsp._enabled_configs[name] then
+ lsp._enabled_configs[name].resolved_config = nil
+ end
+end
+
+--- @nodoc
+--- @class vim.lsp.config
+--- @field [string] vim.lsp.Config
+--- @field package _configs table<string,vim.lsp.Config>
+lsp.config = setmetatable({ _configs = {} }, {
+ --- @param self vim.lsp.config
+ --- @param name string
+ --- @return vim.lsp.Config
+ __index = function(self, name)
+ validate('name', name, 'string')
+
+ local rconfig = lsp._enabled_configs[name] or {}
+ self._configs[name] = self._configs[name] or {}
+
+ if not rconfig.resolved_config then
+ -- Resolve configs from lsp/*.lua
+ -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
+ local rtp_config = {} ---@type vim.lsp.Config
+ for _, v in ipairs(api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)) do
+ local config = assert(loadfile(v))() ---@type any?
+ if type(config) == 'table' then
+ rtp_config = vim.tbl_deep_extend('force', rtp_config, config)
+ else
+ log.warn(string.format('%s does not return a table, ignoring', v))
+ end
+ end
+
+ rconfig.resolved_config = vim.tbl_deep_extend(
+ 'force',
+ lsp.config._configs['*'] or {},
+ rtp_config,
+ lsp.config._configs[name] or {}
+ )
+ rconfig.resolved_config.name = name
+ end
+
+ return rconfig.resolved_config
+ end,
+
+ --- @param self vim.lsp.config
+ --- @param name string
+ --- @param cfg vim.lsp.Config
+ __newindex = function(self, name, cfg)
+ validate('name', name, 'string')
+ validate('cfg', cfg, 'table')
+ invalidate_enabled_config(name)
+ self._configs[name] = cfg
+ end,
+
+ --- @param self vim.lsp.config
+ --- @param name string
+ --- @param cfg vim.lsp.Config
+ __call = function(self, name, cfg)
+ validate('name', name, 'string')
+ validate('cfg', cfg, 'table')
+ invalidate_enabled_config(name)
+ self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg)
+ end,
+})
+
+local lsp_enable_autocmd_id --- @type integer?
+
+--- @param bufnr integer
+local function lsp_enable_callback(bufnr)
+ -- Only ever attach to buffers that represent an actual file.
+ if vim.bo[bufnr].buftype ~= '' then
+ return
+ end
+
+ --- @param config vim.lsp.Config
+ local function can_start(config)
+ if config.filetypes and not vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then
+ return false
+ elseif type(config.cmd) == 'table' and vim.fn.executable(config.cmd[1]) == 0 then
+ return false
+ end
+
+ return true
+ end
+
+ --- @param config vim.lsp.Config
+ local function start(config)
+ return vim.lsp.start(config, {
+ bufnr = bufnr,
+ reuse_client = config.reuse_client,
+ _root_markers = config.root_markers,
+ })
+ end
+
+ for name in vim.spairs(lsp._enabled_configs) do
+ local config = lsp.config[name]
+ validate('cmd', config.cmd, { 'function', 'table' })
+ validate('cmd', config.reuse_client, 'function', true)
+
+ if can_start(config) then
+ -- Deepcopy config so changes done in the client
+ -- do not propagate back to the enabled configs.
+ config = vim.deepcopy(config)
+
+ if type(config.root_dir) == 'function' then
+ ---@param root_dir string
+ config.root_dir(function(root_dir)
+ config.root_dir = root_dir
+ vim.schedule(function()
+ start(config)
+ end)
+ end)
+ else
+ start(config)
+ end
+ end
+ end
+end
+
+--- Enable an LSP server to automatically start when opening a buffer.
+---
+--- Uses configuration defined with `vim.lsp.config`.
+---
+--- Examples:
+---
+--- ```lua
+--- vim.lsp.enable('clangd')
+---
+--- vim.lsp.enable({'luals', 'pyright'})
+--- ```
+---
+--- @param name string|string[] Name(s) of client(s) to enable.
+--- @param enable? boolean `true|nil` to enable, `false` to disable.
+function lsp.enable(name, enable)
+ validate('name', name, { 'string', 'table' })
+
+ local names = vim._ensure_list(name) --[[@as string[] ]]
+ for _, nm in ipairs(names) do
+ if nm == '*' then
+ error('Invalid name')
+ end
+ lsp._enabled_configs[nm] = enable ~= false and {} or nil
+ end
+
+ if not next(lsp._enabled_configs) then
+ if lsp_enable_autocmd_id then
+ api.nvim_del_autocmd(lsp_enable_autocmd_id)
+ lsp_enable_autocmd_id = nil
+ end
+ return
+ end
+
+ -- Only ever create autocmd once to reuse computation of config merging.
+ lsp_enable_autocmd_id = lsp_enable_autocmd_id
+ or api.nvim_create_autocmd('FileType', {
+ group = api.nvim_create_augroup('nvim.lsp.enable', {}),
+ callback = function(args)
+ lsp_enable_callback(args.buf)
+ end,
+ })
+end
+
+--- @class vim.lsp.start.Opts
+--- @inlinedoc
+---
+--- Predicate used to decide if a client should be re-used. Used on all
+--- running clients. The default implementation re-uses a client if it has the
+--- same name and if the given workspace folders (or root_dir) are all included
+--- in the client's workspace folders.
+--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean
---
--- Buffer handle to attach to if starting or re-using a client (0 for current).
--- @field bufnr? integer
---
+--- Whether to attach the client to a buffer (default true).
+--- If set to `false`, `reuse_client` and `bufnr` will be ignored.
+--- @field attach? boolean
+---
--- Suppress error reporting if the LSP server fails to start (default false).
--- @field silent? boolean
+---
+--- @field package _root_markers? string[]
--- Create a new LSP client and start a language server or reuses an already
--- running client if one is found matching `name` and `root_dir`.
@@ -237,10 +602,10 @@ end
--- })
--- ```
---
---- See |vim.lsp.start_client()| for all available options. The most important are:
+--- See |vim.lsp.ClientConfig| for all available options. The most important are:
---
--- - `name` arbitrary name for the LSP client. Should be unique per language server.
---- - `cmd` command string[] or function, described at |vim.lsp.start_client()|.
+--- - `cmd` command string[] or function.
--- - `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.root()| to detect the root by traversing
--- the file system upwards starting from the current directory until either a `pyproject.toml`
@@ -260,36 +625,46 @@ end
--- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|)
---
--- @param config vim.lsp.ClientConfig Configuration for the server.
---- @param opts vim.lsp.start.Opts? Optional keyword arguments
+--- @param opts vim.lsp.start.Opts? Optional keyword arguments.
--- @return integer? client_id
function lsp.start(config, opts)
opts = opts or {}
local reuse_client = opts.reuse_client or reuse_client_default
- local bufnr = resolve_bufnr(opts.bufnr)
+ local bufnr = vim._resolve_bufnr(opts.bufnr)
+
+ if not config.root_dir and opts._root_markers then
+ config = vim.deepcopy(config)
+ config.root_dir = vim.fs.root(bufnr, opts._root_markers)
+ end
for _, client in pairs(all_clients) do
if reuse_client(client, config) then
+ if opts.attach == false then
+ return client.id
+ end
+
if lsp.buf_attach_client(bufnr, client.id) then
return client.id
- else
- return nil
end
+ return
end
end
- local client_id, err = lsp.start_client(config)
+ local client_id, err = create_and_initialize_client(config)
if err then
if not opts.silent then
vim.notify(err, vim.log.levels.WARN)
end
- return nil
+ return
end
- if client_id and lsp.buf_attach_client(bufnr, client_id) then
+ if opts.attach == false then
return client_id
end
- return nil
+ if client_id and lsp.buf_attach_client(bufnr, client_id) then
+ return client_id
+ end
end
--- Consumes the latest progress messages from all clients and formats them as a string.
@@ -349,17 +724,17 @@ end
---@param bufnr integer
function lsp._set_defaults(client, bufnr)
if
- client.supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc')
+ client:supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc')
then
vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc'
end
if
- client.supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc')
+ client:supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc')
then
vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
end
if
- client.supports_method(ms.textDocument_rangeFormatting)
+ client:supports_method(ms.textDocument_rangeFormatting)
and is_empty_or_default(bufnr, 'formatprg')
and is_empty_or_default(bufnr, 'formatexpr')
then
@@ -367,90 +742,21 @@ function lsp._set_defaults(client, bufnr)
end
vim._with({ buf = bufnr }, function()
if
- client.supports_method(ms.textDocument_hover)
+ client:supports_method(ms.textDocument_hover)
and is_empty_or_default(bufnr, 'keywordprg')
and vim.fn.maparg('K', 'n', false, false) == ''
then
- vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' })
+ vim.keymap.set('n', 'K', function()
+ vim.lsp.buf.hover()
+ end, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' })
end
end)
- if client.supports_method(ms.textDocument_diagnostic) then
+ if client:supports_method(ms.textDocument_diagnostic) then
lsp.diagnostic._enable(bufnr)
end
end
---- Reset defaults set by `set_defaults`.
---- Must only be called if the last client attached to a buffer exits.
-local function reset_defaults(bufnr)
- if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then
- vim.bo[bufnr].tagfunc = nil
- end
- if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then
- vim.bo[bufnr].omnifunc = nil
- end
- if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then
- vim.bo[bufnr].formatexpr = nil
- end
- vim._with({ buf = bufnr }, function()
- local keymap = vim.fn.maparg('K', 'n', false, true)
- if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then
- vim.keymap.del('n', 'K', { buffer = bufnr })
- end
- end)
-end
-
---- @param code integer
---- @param signal integer
---- @param client_id integer
-local function on_client_exit(code, signal, client_id)
- local client = all_clients[client_id]
-
- vim.schedule(function()
- for bufnr in pairs(client.attached_buffers) do
- if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then
- api.nvim_exec_autocmds('LspDetach', {
- buffer = bufnr,
- modeline = false,
- data = { client_id = client_id },
- })
- end
-
- client.attached_buffers[bufnr] = nil
-
- if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then
- reset_defaults(bufnr)
- end
- end
-
- local namespace = vim.lsp.diagnostic.get_namespace(client_id)
- vim.diagnostic.reset(namespace)
- end)
-
- local name = client.name or 'unknown'
-
- -- Schedule the deletion of the client object so that it exists in the execution of LspDetach
- -- autocommands
- vim.schedule(function()
- all_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. Check log for errors: %s',
- name,
- code,
- signal,
- lsp.get_log_path()
- )
- vim.notify(msg, vim.log.levels.WARN)
- end
- end)
-end
-
+--- @deprecated
--- Starts and initializes a client with the given configuration.
--- @param config vim.lsp.ClientConfig Configuration for the server.
--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be
@@ -458,39 +764,26 @@ end
--- the client has been initialized.
--- @return string? # Error message, if any
function lsp.start_client(config)
- local ok, res = pcall(require('vim.lsp.client').create, config)
- if not ok then
- return nil, res --[[@as string]]
- end
-
- local client = assert(res)
-
- --- @diagnostic disable-next-line: invisible
- table.insert(client._on_exit_cbs, on_client_exit)
-
- all_clients[client.id] = client
-
- client:initialize()
-
- return client.id, nil
+ vim.deprecate('vim.lsp.start_client()', 'vim.lsp.start()', '0.13')
+ return create_and_initialize_client(config)
end
---Buffer lifecycle handler for textDocument/didSave
--- @param bufnr integer
local function text_document_did_save_handler(bufnr)
- bufnr = resolve_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local text = once(lsp._buf_get_full_text)
for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do
local name = api.nvim_buf_get_name(bufnr)
local old_name = changetracking._get_and_set_name(client, bufnr, name)
if old_name and name ~= old_name then
- client.notify(ms.textDocument_didClose, {
+ client:notify(ms.textDocument_didClose, {
textDocument = {
uri = vim.uri_from_fname(old_name),
},
})
- client.notify(ms.textDocument_didOpen, {
+ client:notify(ms.textDocument_didOpen, {
textDocument = {
version = 0,
uri = uri,
@@ -506,7 +799,7 @@ local function text_document_did_save_handler(bufnr)
if type(save_capability) == 'table' and save_capability.includeText then
included_text = text(bufnr)
end
- client.notify(ms.textDocument_didSave, {
+ client:notify(ms.textDocument_didSave, {
textDocument = {
uri = uri,
},
@@ -527,10 +820,10 @@ local function buf_detach_client(bufnr, client)
changetracking.reset_buf(client, bufnr)
- if client.supports_method(ms.textDocument_didClose) then
+ if client:supports_method(ms.textDocument_didClose) then
local uri = vim.uri_from_bufnr(bufnr)
local params = { textDocument = { uri = uri } }
- client.notify(ms.textDocument_didClose, params)
+ client:notify(ms.textDocument_didClose, params)
end
client.attached_buffers[bufnr] = nil
@@ -550,7 +843,7 @@ local function buf_attach(bufnr)
attached_buffers[bufnr] = true
local uri = vim.uri_from_bufnr(bufnr)
- local augroup = ('lsp_b_%d_save'):format(bufnr)
+ local augroup = ('nvim.lsp.b_%d_save'):format(bufnr)
local group = api.nvim_create_augroup(augroup, { clear = true })
api.nvim_create_autocmd('BufWritePre', {
group = group,
@@ -564,12 +857,12 @@ local function buf_attach(bufnr)
},
reason = protocol.TextDocumentSaveReason.Manual, ---@type integer
}
- if client.supports_method(ms.textDocument_willSave) then
- client.notify(ms.textDocument_willSave, params)
+ if client:supports_method(ms.textDocument_willSave) then
+ client:notify(ms.textDocument_willSave, params)
end
- if client.supports_method(ms.textDocument_willSaveWaitUntil) then
+ if client:supports_method(ms.textDocument_willSaveWaitUntil) then
local result, err =
- client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf)
+ client:request_sync(ms.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
@@ -603,8 +896,8 @@ local function buf_attach(bufnr)
local params = { textDocument = { uri = uri } }
for _, client in ipairs(clients) do
changetracking.reset_buf(client, bufnr)
- if client.supports_method(ms.textDocument_didClose) then
- client.notify(ms.textDocument_didClose, params)
+ if client:supports_method(ms.textDocument_didClose) then
+ client:notify(ms.textDocument_didClose, params)
end
end
for _, client in ipairs(clients) do
@@ -639,7 +932,7 @@ end
function lsp.buf_attach_client(bufnr, client_id)
validate('bufnr', bufnr, 'number', true)
validate('client_id', client_id, 'number')
- bufnr = resolve_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
if not api.nvim_buf_is_loaded(bufnr) then
log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr))
return false
@@ -662,7 +955,7 @@ function lsp.buf_attach_client(bufnr, client_id)
-- Send didOpen for the client if it is initialized. If it isn't initialized
-- then it will send didOpen on initialize.
if client.initialized then
- client:_on_attach(bufnr)
+ client:on_attach(bufnr)
end
return true
end
@@ -676,7 +969,7 @@ end
function lsp.buf_detach_client(bufnr, client_id)
validate('bufnr', bufnr, 'number', true)
validate('client_id', client_id, 'number')
- bufnr = resolve_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
local client = all_clients[client_id]
if not client or not client.attached_buffers[bufnr] then
@@ -740,13 +1033,13 @@ function lsp.stop_client(client_id, force)
for _, id in ipairs(ids) do
if type(id) == 'table' then
if id.stop then
- id.stop(force)
+ id:stop(force)
end
else
--- @cast id -vim.lsp.Client
local client = all_clients[id]
if client then
- client.stop(force)
+ client:stop(force)
end
end
end
@@ -782,7 +1075,7 @@ function lsp.get_clients(filter)
local clients = {} --- @type vim.lsp.Client[]
- local bufnr = filter.bufnr and resolve_bufnr(filter.bufnr)
+ local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr)
for _, client in pairs(all_clients) do
if
@@ -790,7 +1083,7 @@ function lsp.get_clients(filter)
and (filter.id == nil or client.id == filter.id)
and (filter.bufnr == nil or client.attached_buffers[bufnr])
and (filter.name == nil or client.name == filter.name)
- and (filter.method == nil or client.supports_method(filter.method, { bufnr = filter.bufnr }))
+ and (filter.method == nil or client:supports_method(filter.method, filter.bufnr))
and (filter._uninitialized or client.initialized)
then
clients[#clients + 1] = client
@@ -812,7 +1105,7 @@ api.nvim_create_autocmd('VimLeavePre', {
local active_clients = lsp.get_clients()
log.info('exit_handler', active_clients)
for _, client in pairs(all_clients) do
- client.stop()
+ client:stop()
end
local timeouts = {} --- @type table<integer,integer>
@@ -847,7 +1140,7 @@ api.nvim_create_autocmd('VimLeavePre', {
if not vim.wait(max_timeout, check_clients_closed, poll_time) then
for client_id, client in pairs(active_clients) do
if timeouts[client_id] ~= nil then
- client.stop(true)
+ client:stop(true)
end
end
end
@@ -878,16 +1171,16 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
validate('handler', handler, 'function', true)
validate('on_unsupported', on_unsupported, 'function', true)
- bufnr = resolve_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
local method_supported = false
local clients = lsp.get_clients({ bufnr = bufnr })
local client_request_ids = {} --- @type table<integer,integer>
for _, client in ipairs(clients) do
- if client.supports_method(method, { bufnr = bufnr }) then
+ if client:supports_method(method, bufnr) then
method_supported = true
local cparams = type(params) == 'function' and params(client, bufnr) or params --[[@as table?]]
- local request_success, request_id = client.request(method, cparams, handler, bufnr)
+ local request_success, request_id = client:request(method, cparams, handler, bufnr)
-- This could only fail if the client shut down in the time since we looked
-- it up and we did the request, which should be rare.
if request_success then
@@ -910,7 +1203,7 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported)
local function _cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do
local client = all_clients[client_id]
- client.cancel_request(request_id)
+ client:cancel_request(request_id)
end
end
@@ -1049,7 +1342,7 @@ function lsp.formatexpr(opts)
end
local bufnr = api.nvim_get_current_buf()
for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do
- if client.supports_method(ms.textDocument_rangeFormatting) then
+ if client:supports_method(ms.textDocument_rangeFormatting) then
local params = util.make_formatting_params()
local end_line = vim.fn.getline(end_lnum) --[[@as string]]
local end_col = vim.str_utfindex(end_line, client.offset_encoding)
@@ -1065,7 +1358,7 @@ function lsp.formatexpr(opts)
},
}
local response =
- client.request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr)
+ client:request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr)
if response and response.result then
lsp.util.apply_text_edits(response.result, bufnr, client.offset_encoding)
return 0
@@ -1092,6 +1385,55 @@ function lsp.tagfunc(pattern, flags)
return vim.lsp._tagfunc(pattern, flags)
end
+--- Provides an interface between the built-in client and a `foldexpr` function.
+---
+--- To use, check for the "textDocument/foldingRange" capability in an
+--- |LspAttach| autocommand. Example:
+---
+--- ```lua
+--- vim.api.nvim_create_autocmd('LspAttach', {
+--- callback = function(args)
+--- local client = vim.lsp.get_client_by_id(args.data.client_id)
+--- if client:supports_method('textDocument/foldingRange') then
+--- local win = vim.api.nvim_get_current_win()
+--- vim.wo[win][0].foldmethod = 'expr'
+--- vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()'
+--- end
+--- end,
+--- })
+--- ```
+---
+---@param lnum integer line number
+function lsp.foldexpr(lnum)
+ return vim.lsp._folding_range.foldexpr(lnum)
+end
+
+--- Close all {kind} of folds in the the window with {winid}.
+---
+--- To automatically fold imports when opening a file, you can use an autocmd:
+---
+--- ```lua
+--- vim.api.nvim_create_autocmd('LspNotify', {
+--- callback = function(args)
+--- if args.data.method == 'textDocument/didOpen' then
+--- vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf))
+--- end
+--- end,
+--- })
+--- ```
+---
+---@param kind lsp.FoldingRangeKind Kind to close, one of "comment", "imports" or "region".
+---@param winid? integer Defaults to the current window.
+function lsp.foldclose(kind, winid)
+ return vim.lsp._folding_range.foldclose(kind, winid)
+end
+
+--- Provides a `foldtext` function that shows the `collapsedText` retrieved,
+--- defaults to the first folded line if `collapsedText` is not provided.
+function lsp.foldtext()
+ return vim.lsp._folding_range.foldtext()
+end
+
---Checks whether a client is stopped.
---
---@param client_id (integer)
@@ -1110,7 +1452,7 @@ end
function lsp.buf_get_clients(bufnr)
vim.deprecate('vim.lsp.buf_get_clients()', 'vim.lsp.get_clients()', '0.12')
local result = {} --- @type table<integer,vim.lsp.Client>
- for _, client in ipairs(lsp.get_clients({ bufnr = resolve_bufnr(bufnr) })) do
+ for _, client in ipairs(lsp.get_clients({ bufnr = vim._resolve_bufnr(bufnr) })) do
result[client.id] = client
end
return result
@@ -1164,7 +1506,7 @@ function lsp.for_each_buffer_client(bufnr, fn)
'lsp.get_clients({ bufnr = bufnr }) with regular loop',
'0.12'
)
- bufnr = resolve_bufnr(bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do
fn(client, client.id, bufnr)
@@ -1181,44 +1523,6 @@ function lsp.with(handler, override_config)
end
end
---- Helper function to use when implementing a handler.
---- This will check that all of the keys in the user configuration
---- are valid keys and make sense to include for this handler.
----
---- Will error on invalid keys (i.e. keys that do not exist in the options)
---- @param name string
---- @param options table<string,any>
---- @param user_config table<string,any>
-function lsp._with_extend(name, options, user_config)
- user_config = user_config or {}
-
- local resulting_config = {} --- @type table<string,any>
- for k, v in pairs(user_config) do
- if options[k] == nil then
- error(
- debug.traceback(
- string.format(
- 'Invalid option for `%s`: %s. Valid options are:\n%s',
- name,
- k,
- vim.inspect(vim.tbl_keys(options))
- )
- )
- )
- end
-
- resulting_config[k] = v
- end
-
- for k, v in pairs(options) do
- if resulting_config[k] == nil then
- resulting_config[k] = v
- end
- end
-
- return resulting_config
-end
-
--- Registry for client side commands.
--- This is an extension point for plugins to handle custom commands which are
--- not part of the core language server protocol specification.
@@ -1227,7 +1531,7 @@ end
--- and the value is a function which is called if any LSP action
--- (code action, code lenses, ...) triggers the command.
---
---- If a LSP response contains a command for which no matching entry is
+--- If an LSP response contains a command for which no matching entry is
--- available in this registry, the command will be executed via the LSP server
--- using `workspace/executeCommand`.
---
diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua
index b2be53269f..265a74c8fa 100644
--- a/runtime/lua/vim/lsp/_changetracking.lua
+++ b/runtime/lua/vim/lsp/_changetracking.lua
@@ -18,14 +18,14 @@ local M = {}
---
--- None: One group for all clients
--- Full: One group for all clients
---- Incremental: One group per `offset_encoding`
+--- Incremental: One group per `position_encoding`
---
--- Sending changes can be debounced per buffer. To simplify the implementation the
--- smallest debounce interval is used and we don't group clients by different intervals.
---
--- @class vim.lsp.CTGroup
--- @field sync_kind integer TextDocumentSyncKind, considers config.flags.allow_incremental_sync
---- @field offset_encoding "utf-8"|"utf-16"|"utf-32"
+--- @field position_encoding "utf-8"|"utf-16"|"utf-32"
---
--- @class vim.lsp.CTBufferState
--- @field name string name of the buffer
@@ -40,13 +40,13 @@ local M = {}
--- @class vim.lsp.CTGroupState
--- @field buffers table<integer,vim.lsp.CTBufferState>
--- @field debounce integer debounce duration in ms
---- @field clients table<integer, table> clients using this state. {client_id, client}
+--- @field clients table<integer, vim.lsp.Client> clients using this state. {client_id, client}
---@param group vim.lsp.CTGroup
---@return string
local function group_key(group)
if group.sync_kind == protocol.TextDocumentSyncKind.Incremental then
- return tostring(group.sync_kind) .. '\0' .. group.offset_encoding
+ return tostring(group.sync_kind) .. '\0' .. group.position_encoding
end
return tostring(group.sync_kind)
end
@@ -64,7 +64,7 @@ local state_by_group = setmetatable({}, {
---@param client vim.lsp.Client
---@return vim.lsp.CTGroup
local function get_group(client)
- local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true) --- @type boolean
+ local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true)
local change_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change')
local sync_kind = change_capability or protocol.TextDocumentSyncKind.None
if not allow_inc_sync and change_capability == protocol.TextDocumentSyncKind.Incremental then
@@ -72,7 +72,7 @@ local function get_group(client)
end
return {
sync_kind = sync_kind,
- offset_encoding = client.offset_encoding,
+ position_encoding = client.offset_encoding,
}
end
@@ -273,8 +273,8 @@ local function send_changes(bufnr, sync_kind, state, buf_state)
end
local uri = vim.uri_from_bufnr(bufnr)
for _, client in pairs(state.clients) do
- if not client.is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then
- client.notify(protocol.Methods.textDocument_didChange, {
+ if not client:is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then
+ client:notify(protocol.Methods.textDocument_didChange, {
textDocument = {
uri = uri,
version = util.buf_versions[bufnr],
@@ -310,7 +310,7 @@ local function send_changes_for_group(bufnr, firstline, lastline, new_lastline,
-- The contents would further change and startline/endline may no longer fit
local changes = incremental_changes(
buf_state,
- group.offset_encoding,
+ group.position_encoding,
bufnr,
firstline,
lastline,
diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua
new file mode 100644
index 0000000000..66eb81db6e
--- /dev/null
+++ b/runtime/lua/vim/lsp/_folding_range.lua
@@ -0,0 +1,373 @@
+local util = require('vim.lsp.util')
+local log = require('vim.lsp.log')
+local ms = require('vim.lsp.protocol').Methods
+local api = vim.api
+
+local M = {}
+
+---@class (private) vim.lsp.folding_range.BufState
+---
+---@field version? integer
+---
+--- Never use this directly, `renew()` the cached foldinfo
+--- then use on demand via `row_*` fields.
+---
+--- Index In the form of client_id -> ranges
+---@field client_ranges table<integer, lsp.FoldingRange[]?>
+---
+--- Index in the form of row -> [foldlevel, mark]
+---@field row_level table<integer, [integer, ">" | "<"?]?>
+---
+--- Index in the form of start_row -> kinds
+---@field row_kinds table<integer, table<lsp.FoldingRangeKind, true?>?>>
+---
+--- Index in the form of start_row -> collapsed_text
+---@field row_text table<integer, string?>
+
+---@type table<integer, vim.lsp.folding_range.BufState?>
+local bufstates = {}
+
+--- Renew the cached foldinfo in the buffer.
+---@param bufnr integer
+local function renew(bufnr)
+ local bufstate = assert(bufstates[bufnr])
+
+ ---@type table<integer, [integer, ">" | "<"?]?>
+ local row_level = {}
+ ---@type table<integer, table<lsp.FoldingRangeKind, true?>?>>
+ local row_kinds = {}
+ ---@type table<integer, string?>
+ local row_text = {}
+
+ for _, ranges in pairs(bufstate.client_ranges) do
+ for _, range in ipairs(ranges) do
+ local start_row = range.startLine
+ local end_row = range.endLine
+ -- Adding folds within a single line is not supported by Nvim.
+ if start_row ~= end_row then
+ row_text[start_row] = range.collapsedText
+
+ local kind = range.kind
+ if kind then
+ local kinds = row_kinds[start_row] or {}
+ kinds[kind] = true
+ row_kinds[start_row] = kinds
+ end
+
+ for row = start_row, end_row do
+ local level = row_level[row] or { 0 }
+ level[1] = level[1] + 1
+ row_level[row] = level
+ end
+ row_level[start_row][2] = '>'
+ row_level[end_row][2] = '<'
+ end
+ end
+ end
+
+ bufstate.row_level = row_level
+ bufstate.row_kinds = row_kinds
+ bufstate.row_text = row_text
+end
+
+--- Renew the cached foldinfo then force `foldexpr()` to be re-evaluated,
+--- without opening folds.
+---@param bufnr integer
+local function foldupdate(bufnr)
+ renew(bufnr)
+ for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
+ local wininfo = vim.fn.getwininfo(winid)[1]
+ if wininfo and wininfo.tabnr == vim.fn.tabpagenr() then
+ if vim.wo[winid].foldmethod == 'expr' then
+ vim._foldupdate(winid, 0, api.nvim_buf_line_count(bufnr))
+ end
+ end
+ end
+end
+
+--- Whether `foldupdate()` is scheduled for the buffer with `bufnr`.
+---
+--- Index in the form of bufnr -> true?
+---@type table<integer, true?>
+local scheduled_foldupdate = {}
+
+--- Schedule `foldupdate()` after leaving insert mode.
+---@param bufnr integer
+local function schedule_foldupdate(bufnr)
+ if not scheduled_foldupdate[bufnr] then
+ scheduled_foldupdate[bufnr] = true
+ api.nvim_create_autocmd('InsertLeave', {
+ buffer = bufnr,
+ once = true,
+ callback = function()
+ foldupdate(bufnr)
+ scheduled_foldupdate[bufnr] = nil
+ end,
+ })
+ end
+end
+
+---@param results table<integer,{err: lsp.ResponseError?, result: lsp.FoldingRange[]?}>
+---@type lsp.MultiHandler
+local function multi_handler(results, ctx)
+ local bufnr = assert(ctx.bufnr)
+ -- Handling responses from outdated buffer only causes performance overhead.
+ if util.buf_versions[bufnr] ~= ctx.version then
+ return
+ end
+
+ local bufstate = assert(bufstates[bufnr])
+ for client_id, result in pairs(results) do
+ if result.err then
+ log.error(result.err)
+ else
+ bufstate.client_ranges[client_id] = result.result
+ end
+ end
+ bufstate.version = ctx.version
+
+ if api.nvim_get_mode().mode:match('^i') then
+ -- `foldUpdate()` is guarded in insert mode.
+ schedule_foldupdate(bufnr)
+ else
+ foldupdate(bufnr)
+ end
+end
+
+---@param result lsp.FoldingRange[]?
+---@type lsp.Handler
+local function handler(err, result, ctx)
+ multi_handler({ [ctx.client_id] = { err = err, result = result } }, ctx)
+end
+
+--- Request `textDocument/foldingRange` from the server.
+--- `foldupdate()` is scheduled once after the request is completed.
+---@param bufnr integer
+---@param client? vim.lsp.Client The client whose server supports `foldingRange`.
+local function request(bufnr, client)
+ ---@type lsp.FoldingRangeParams
+ local params = { textDocument = util.make_text_document_params(bufnr) }
+
+ if client then
+ client:request(ms.textDocument_foldingRange, params, handler, bufnr)
+ return
+ end
+
+ if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then
+ return
+ end
+
+ vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, multi_handler)
+end
+
+-- NOTE:
+-- `bufstate` and event hooks are interdependent:
+-- * `bufstate` needs event hooks for correctness.
+-- * event hooks require the previous `bufstate` for updates.
+-- Since they are manually created and destroyed,
+-- we ensure their lifecycles are always synchronized.
+--
+-- TODO(ofseed):
+-- 1. Implement clearing `bufstate` and event hooks
+-- when no clients in the buffer support the corresponding method.
+-- 2. Then generalize this state management to other LSP modules.
+local augroup_setup = api.nvim_create_augroup('nvim.lsp.folding_range.setup', {})
+
+--- Initialize `bufstate` and event hooks, then request folding ranges.
+--- Manage their lifecycle within this function.
+---@param bufnr integer
+---@return vim.lsp.folding_range.BufState?
+local function setup(bufnr)
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
+ -- Register the new `bufstate`.
+ bufstates[bufnr] = {
+ client_ranges = {},
+ row_level = {},
+ row_kinds = {},
+ row_text = {},
+ }
+
+ -- Event hooks from `buf_attach` can't be removed externally.
+ -- Hooks and `bufstate` share the same lifecycle;
+ -- they should self-destroy if `bufstate == nil`.
+ api.nvim_buf_attach(bufnr, false, {
+ -- `on_detach` also runs on buffer reload (`:e`).
+ -- Ensure `bufstate` and hooks are cleared to avoid duplication or leftover states.
+ on_detach = function()
+ bufstates[bufnr] = nil
+ api.nvim_clear_autocmds({ buffer = bufnr, group = augroup_setup })
+ end,
+ -- Reset `bufstate` and request folding ranges.
+ on_reload = function()
+ bufstates[bufnr] = {
+ client_ranges = {},
+ row_level = {},
+ row_kinds = {},
+ row_text = {},
+ }
+ request(bufnr)
+ end,
+ --- Sync changed rows with their previous foldlevels before applying new ones.
+ on_bytes = function(_, _, _, start_row, _, _, old_row, _, _, new_row, _, _)
+ if bufstates[bufnr] == nil then
+ return true
+ end
+ local row_level = bufstates[bufnr].row_level
+ if next(row_level) == nil then
+ return
+ end
+ local row = new_row - old_row
+ if row > 0 then
+ vim._list_insert(row_level, start_row, start_row + math.abs(row) - 1, { -1 })
+ -- If the previous row ends a fold,
+ -- Nvim treats the first row after consecutive `-1`s as a new fold start,
+ -- which is not the desired behavior.
+ local prev_level = row_level[start_row - 1]
+ if prev_level and prev_level[2] == '<' then
+ row_level[start_row] = { prev_level[1] - 1 }
+ end
+ elseif row < 0 then
+ vim._list_remove(row_level, start_row, start_row + math.abs(row) - 1)
+ end
+ end,
+ })
+ api.nvim_create_autocmd('LspDetach', {
+ group = augroup_setup,
+ buffer = bufnr,
+ callback = function(args)
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
+ ---@type integer
+ local client_id = args.data.client_id
+ bufstates[bufnr].client_ranges[client_id] = nil
+
+ ---@type vim.lsp.Client[]
+ local clients = vim
+ .iter(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange }))
+ ---@param client vim.lsp.Client
+ :filter(function(client)
+ return client.id ~= client_id
+ end)
+ :totable()
+ if #clients == 0 then
+ bufstates[bufnr] = {
+ client_ranges = {},
+ row_level = {},
+ row_kinds = {},
+ row_text = {},
+ }
+ end
+
+ foldupdate(bufnr)
+ end,
+ })
+ api.nvim_create_autocmd('LspAttach', {
+ group = augroup_setup,
+ buffer = bufnr,
+ callback = function(args)
+ local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+ if client:supports_method(vim.lsp.protocol.Methods.textDocument_foldingRange, bufnr) then
+ request(bufnr, client)
+ end
+ end,
+ })
+ api.nvim_create_autocmd('LspNotify', {
+ group = augroup_setup,
+ buffer = bufnr,
+ callback = function(args)
+ local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+ if
+ client:supports_method(ms.textDocument_foldingRange, bufnr)
+ and (
+ args.data.method == ms.textDocument_didChange
+ or args.data.method == ms.textDocument_didOpen
+ )
+ then
+ request(bufnr, client)
+ end
+ end,
+ })
+
+ request(bufnr)
+
+ return bufstates[bufnr]
+end
+
+---@param kind lsp.FoldingRangeKind
+---@param winid integer
+local function foldclose(kind, winid)
+ vim._with({ win = winid }, function()
+ local bufnr = api.nvim_win_get_buf(winid)
+ local row_kinds = bufstates[bufnr].row_kinds
+ -- Reverse traverse to ensure that the smallest ranges are closed first.
+ for row = api.nvim_buf_line_count(bufnr) - 1, 0, -1 do
+ local kinds = row_kinds[row]
+ if kinds and kinds[kind] then
+ vim.cmd(row + 1 .. 'foldclose')
+ end
+ end
+ end)
+end
+
+---@param kind lsp.FoldingRangeKind
+---@param winid? integer
+function M.foldclose(kind, winid)
+ vim.validate('kind', kind, 'string')
+ vim.validate('winid', winid, 'number', true)
+
+ winid = winid or api.nvim_get_current_win()
+ local bufnr = api.nvim_win_get_buf(winid)
+ local bufstate = bufstates[bufnr]
+ if not bufstate then
+ return
+ end
+
+ if bufstate.version == util.buf_versions[bufnr] then
+ foldclose(kind, winid)
+ return
+ end
+ -- Schedule `foldclose()` if the buffer is not up-to-date.
+
+ if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then
+ return
+ end
+ ---@type lsp.FoldingRangeParams
+ local params = { textDocument = util.make_text_document_params(bufnr) }
+ vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, function(...)
+ multi_handler(...)
+ foldclose(kind, winid)
+ end)
+end
+
+---@return string
+function M.foldtext()
+ local bufnr = api.nvim_get_current_buf()
+ local lnum = vim.v.foldstart
+ local row = lnum - 1
+ local bufstate = bufstates[bufnr]
+ if bufstate and bufstate.row_text[row] then
+ return bufstate.row_text[row]
+ end
+ return vim.fn.getline(lnum)
+end
+
+---@param lnum? integer
+---@return string level
+function M.foldexpr(lnum)
+ local bufnr = api.nvim_get_current_buf()
+ local bufstate = bufstates[bufnr] or setup(bufnr)
+ if not bufstate then
+ return '0'
+ end
+
+ local row = (lnum or vim.v.lnum) - 1
+ local level = bufstate.row_level[row]
+ return level and (level[2] or '') .. (level[1] or '0') or '0'
+end
+
+return M
diff --git a/runtime/lua/vim/lsp/_meta.lua b/runtime/lua/vim/lsp/_meta.lua
index bf693ccc57..589a49c003 100644
--- a/runtime/lua/vim/lsp/_meta.lua
+++ b/runtime/lua/vim/lsp/_meta.lua
@@ -1,8 +1,8 @@
---@meta
error('Cannot require a meta file')
----@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext): ...any
----@alias lsp.MultiHandler fun(results: table<integer,{err: lsp.ResponseError?, result: any}>, context: lsp.HandlerContext): ...any
+---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any
+---@alias lsp.MultiHandler fun(results: table<integer,{err: lsp.ResponseError?, result: any}>, context: lsp.HandlerContext, config?: table): ...any
---@class lsp.HandlerContext
---@field method string
diff --git a/runtime/lua/vim/lsp/_snippet_grammar.lua b/runtime/lua/vim/lsp/_snippet_grammar.lua
index 9318fefcbc..f06d6e9afd 100644
--- a/runtime/lua/vim/lsp/_snippet_grammar.lua
+++ b/runtime/lua/vim/lsp/_snippet_grammar.lua
@@ -127,6 +127,7 @@ local function node(type)
end
-- stylua: ignore
+--- @diagnostic disable-next-line: missing-fields
local G = P({
'snippet';
snippet = Ct(Cg(
diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua
index f75d43f373..554f0cb991 100644
--- a/runtime/lua/vim/lsp/_tagfunc.lua
+++ b/runtime/lua/vim/lsp/_tagfunc.lua
@@ -6,12 +6,12 @@ local ms = lsp.protocol.Methods
---@param name string
---@param range lsp.Range
---@param uri string
----@param offset_encoding string
+---@param position_encoding string
---@return {name: string, filename: string, cmd: string, kind?: string}
-local function mk_tag_item(name, range, uri, offset_encoding)
+local function mk_tag_item(name, range, uri, position_encoding)
local bufnr = vim.uri_to_bufnr(uri)
-- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position
- local byte = util._get_line_byte_from_position(bufnr, range.start, offset_encoding) + 1
+ local byte = util._get_line_byte_from_position(bufnr, range.start, position_encoding) + 1
return {
name = name,
filename = vim.uri_to_fname(uri),
@@ -32,9 +32,9 @@ local function query_definition(pattern)
--- @param range lsp.Range
--- @param uri string
- --- @param offset_encoding string
- local add = function(range, uri, offset_encoding)
- table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding))
+ --- @param position_encoding string
+ local add = function(range, uri, position_encoding)
+ table.insert(results, mk_tag_item(pattern, range, uri, position_encoding))
end
local remaining = #clients
@@ -59,7 +59,7 @@ local function query_definition(pattern)
remaining = remaining - 1
end
local params = util.make_position_params(win, client.offset_encoding)
- client.request(ms.textDocument_definition, params, on_response, bufnr)
+ client:request(ms.textDocument_definition, params, on_response, bufnr)
end
vim.wait(1000, function()
return remaining == 0
@@ -78,11 +78,11 @@ local function query_workspace_symbols(pattern)
local results = {}
for client_id, responses in pairs(assert(results_by_client)) do
local client = lsp.get_client_by_id(client_id)
- local offset_encoding = client and client.offset_encoding or 'utf-16'
+ local position_encoding = client and client.offset_encoding or 'utf-16'
local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]]
for _, symbol in pairs(symbols or {}) do
local loc = symbol.location
- local item = mk_tag_item(symbol.name, loc.range, loc.uri, offset_encoding)
+ local item = mk_tag_item(symbol.name, loc.range, loc.uri, position_encoding)
item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown'
table.insert(results, item)
end
diff --git a/runtime/lua/vim/lsp/_transport.lua b/runtime/lua/vim/lsp/_transport.lua
new file mode 100644
index 0000000000..19ff2a8ab0
--- /dev/null
+++ b/runtime/lua/vim/lsp/_transport.lua
@@ -0,0 +1,182 @@
+local uv = vim.uv
+local log = require('vim.lsp.log')
+
+local is_win = vim.fn.has('win32') == 1
+
+--- Checks whether a given path exists and is a directory.
+---@param filename string path to check
+---@return boolean
+local function is_dir(filename)
+ local stat = uv.fs_stat(filename)
+ return stat and stat.type == 'directory' or false
+end
+
+--- @class (private) vim.lsp.rpc.Transport
+--- @field write fun(self: vim.lsp.rpc.Transport, msg: string)
+--- @field is_closing fun(self: vim.lsp.rpc.Transport): boolean
+--- @field terminate fun(self: vim.lsp.rpc.Transport)
+
+--- @class (private,exact) vim.lsp.rpc.Transport.Run : vim.lsp.rpc.Transport
+--- @field new fun(): vim.lsp.rpc.Transport.Run
+--- @field sysobj? vim.SystemObj
+local TransportRun = {}
+
+--- @return vim.lsp.rpc.Transport.Run
+function TransportRun.new()
+ return setmetatable({}, { __index = TransportRun })
+end
+
+--- @param cmd string[] Command to start the LSP server.
+--- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams
+--- @param on_read fun(err: any, data: string)
+--- @param on_exit fun(code: integer, signal: integer)
+function TransportRun:run(cmd, extra_spawn_params, on_read, on_exit)
+ local function on_stderr(_, chunk)
+ if chunk then
+ log.error('rpc', cmd[1], 'stderr', chunk)
+ end
+ end
+
+ extra_spawn_params = extra_spawn_params or {}
+
+ if extra_spawn_params.cwd then
+ assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
+ end
+
+ local detached = not is_win
+ if extra_spawn_params.detached ~= nil then
+ detached = extra_spawn_params.detached
+ end
+
+ local ok, sysobj_or_err = pcall(vim.system, cmd, {
+ stdin = true,
+ stdout = on_read,
+ stderr = on_stderr,
+ cwd = extra_spawn_params.cwd,
+ env = extra_spawn_params.env,
+ detach = detached,
+ }, function(obj)
+ on_exit(obj.code, obj.signal)
+ end)
+
+ if not ok then
+ local err = sysobj_or_err --[[@as string]]
+ local sfx = err:match('ENOENT')
+ and '. The language server is either not installed, missing from PATH, or not executable.'
+ or string.format(' with error message: %s', err)
+
+ error(('Spawning language server with cmd: `%s` failed%s'):format(vim.inspect(cmd), sfx))
+ end
+
+ self.sysobj = sysobj_or_err --[[@as vim.SystemObj]]
+end
+
+function TransportRun:write(msg)
+ assert(self.sysobj):write(msg)
+end
+
+function TransportRun:is_closing()
+ return self.sysobj == nil or self.sysobj:is_closing()
+end
+
+function TransportRun:terminate()
+ assert(self.sysobj):kill(15)
+end
+
+--- @class (private,exact) vim.lsp.rpc.Transport.Connect : vim.lsp.rpc.Transport
+--- @field new fun(): vim.lsp.rpc.Transport.Connect
+--- @field handle? uv.uv_pipe_t|uv.uv_tcp_t
+--- Connect returns a PublicClient synchronously so the caller
+--- can immediately send messages before the connection is established
+--- -> Need to buffer them until that happens
+--- @field connected boolean
+--- @field closing boolean
+--- @field msgbuf vim.Ringbuf
+--- @field on_exit? fun(code: integer, signal: integer)
+local TransportConnect = {}
+
+--- @return vim.lsp.rpc.Transport.Connect
+function TransportConnect.new()
+ return setmetatable({
+ connected = false,
+ -- size should be enough because the client can't really do anything until initialization is done
+ -- which required a response from the server - implying the connection got established
+ msgbuf = vim.ringbuf(10),
+ closing = false,
+ }, { __index = TransportConnect })
+end
+
+--- @param host_or_path string
+--- @param port? integer
+--- @param on_read fun(err: any, data: string)
+--- @param on_exit? fun(code: integer, signal: integer)
+function TransportConnect:connect(host_or_path, port, on_read, on_exit)
+ self.on_exit = on_exit
+ self.handle = (
+ port and assert(uv.new_tcp(), 'Could not create new TCP socket')
+ or assert(uv.new_pipe(false), 'Pipe could not be opened.')
+ )
+
+ local function on_connect(err)
+ if err then
+ local address = not port and host_or_path or (host_or_path .. ':' .. port)
+ vim.schedule(function()
+ vim.notify(
+ string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)),
+ vim.log.levels.WARN
+ )
+ end)
+ return
+ end
+ self.handle:read_start(on_read)
+ self.connected = true
+ for msg in self.msgbuf do
+ self.handle:write(msg)
+ end
+ end
+
+ if not port then
+ self.handle:connect(host_or_path, on_connect)
+ return
+ end
+
+ --- @diagnostic disable-next-line:param-type-mismatch bad UV typing
+ local info = uv.getaddrinfo(host_or_path, nil)
+ local resolved_host = info and info[1] and info[1].addr or host_or_path
+ self.handle:connect(resolved_host, port, on_connect)
+end
+
+function TransportConnect:write(msg)
+ if self.connected then
+ local _, err = self.handle:write(msg)
+ if err and not self.closing then
+ log.error('Error on handle:write: %q', err)
+ end
+ return
+ end
+
+ self.msgbuf:push(msg)
+end
+
+function TransportConnect:is_closing()
+ return self.closing
+end
+
+function TransportConnect:terminate()
+ if self.closing then
+ return
+ end
+ self.closing = true
+ if self.handle then
+ self.handle:shutdown()
+ self.handle:close()
+ end
+ if self.on_exit then
+ self.on_exit(0, 0)
+ end
+end
+
+return {
+ TransportRun = TransportRun,
+ TransportConnect = TransportConnect,
+}
diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua
index c4cdb5aea8..4711b3cc9b 100644
--- a/runtime/lua/vim/lsp/_watchfiles.lua
+++ b/runtime/lua/vim/lsp/_watchfiles.lua
@@ -116,7 +116,7 @@ function M.register(reg, client_id)
local params = {
changes = change_queues[client_id],
}
- client.notify(ms.workspace_didChangeWatchedFiles, params)
+ client:notify(ms.workspace_didChangeWatchedFiles, params)
queue_timers[client_id] = nil
change_queues[client_id] = nil
change_cache[client_id] = nil
@@ -174,6 +174,7 @@ function M.cancel(client_id)
cancel()
end
end
+ cancels[client_id] = nil
end
return M
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 6383855a30..48aa809ebd 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -20,7 +20,7 @@ local function client_positional_params(params)
end
end
-local hover_ns = api.nvim_create_namespace('vim_lsp_hover_range')
+local hover_ns = api.nvim_create_namespace('nvim.lsp.hover_range')
--- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts
--- @field silent? boolean
@@ -232,7 +232,7 @@ local function get_locations(method, opts)
end
for _, client in ipairs(clients) do
local params = util.make_position_params(win, client.offset_encoding)
- client.request(method, params, function(_, result)
+ client:request(method, params, function(_, result)
on_response(_, result, client)
end)
end
@@ -252,13 +252,13 @@ end
--- vim.lsp.buf.definition({ on_list = on_list })
--- vim.lsp.buf.references(nil, { on_list = on_list })
--- ```
+--- @field on_list? fun(t: vim.lsp.LocationOpts.OnList)
---
---- If you prefer loclist instead of qflist:
+--- Whether to use the |location-list| or the |quickfix| list in the default handler.
--- ```lua
--- vim.lsp.buf.definition({ loclist = true })
---- vim.lsp.buf.references(nil, { loclist = true })
+--- vim.lsp.buf.references(nil, { loclist = false })
--- ```
---- @field on_list? fun(t: vim.lsp.LocationOpts.OnList)
--- @field loclist? boolean
--- @class vim.lsp.LocationOpts.OnList
@@ -324,12 +324,11 @@ local function process_signature_help_results(results)
return signatures
end
-local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
+local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help')
--- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts
--- @field silent? boolean
--- TODO(lewis6991): support multiple clients
--- Displays signature information about the symbol under the cursor in a
--- floating window.
--- @param config? vim.lsp.buf.signature_help.Opts
@@ -356,6 +355,7 @@ function M.signature_help(config)
local ft = vim.bo[ctx.bufnr].filetype
local total = #signatures
+ local can_cycle = total > 1 and config.focusable
local idx = 0
--- @param update_win? integer
@@ -371,7 +371,7 @@ function M.signature_help(config)
return
end
- local sfx = total > 1 and string.format(' (%d/%d) (<C-s> to cycle)', idx, total) or ''
+ local sfx = can_cycle and string.format(' (%d/%d) (<C-s> to cycle)', idx, total) or ''
local title = string.format('Signature Help: %s%s', client.name, sfx)
if config.border then
config.title = title
@@ -402,7 +402,7 @@ function M.signature_help(config)
local fbuf, fwin = show_signature()
- if total > 1 then
+ if can_cycle then
vim.keymap.set('n', '<C-s>', function()
show_signature(fwin)
end, {
@@ -423,7 +423,7 @@ end
---
---@see vim.lsp.protocol.CompletionTriggerKind
function M.completion(context)
- vim.depends('vim.lsp.buf.completion', 'vim.lsp.commpletion.trigger', '0.12')
+ vim.depends('vim.lsp.buf.completion', 'vim.lsp.completion.trigger', '0.12')
return lsp.buf_request(
0,
ms.textDocument_completion,
@@ -450,10 +450,10 @@ local function range_from_selection(bufnr, mode)
-- 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
+ end_col, start_col = start_col, end_col --- @type integer, integer
elseif end_row < start_row then
- start_row, end_row = end_row, start_row
- start_col, end_col = end_col, start_col
+ start_row, end_row = end_row, start_row --- @type integer, integer
+ start_col, end_col = end_col, start_col --- @type integer, integer
end
if mode == 'V' then
start_col = 1
@@ -487,7 +487,7 @@ end
--- ```lua
--- -- Never request typescript-language-server for formatting
--- vim.lsp.buf.format {
---- filter = function(client) return client.name ~= "tsserver" end
+--- filter = function(client) return client.name ~= "ts_ls" end
--- }
--- ```
--- @field filter? fun(client: vim.lsp.Client): boolean?
@@ -519,7 +519,7 @@ end
--- @param opts? vim.lsp.buf.format.Opts
function M.format(opts)
opts = opts or {}
- local bufnr = opts.bufnr or api.nvim_get_current_buf()
+ local bufnr = vim._resolve_bufnr(opts.bufnr)
local mode = api.nvim_get_mode().mode
local range = opts.range
-- Try to use visual selection if no range is given
@@ -553,27 +553,34 @@ function M.format(opts)
--- @param client vim.lsp.Client
--- @param params lsp.DocumentFormattingParams
- --- @return lsp.DocumentFormattingParams
+ --- @return lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams
local function set_range(client, params)
- local to_lsp_range = function(r) ---@return lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams
+ --- @param r {start:[integer,integer],end:[integer, integer]}
+ local function to_lsp_range(r)
return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range
end
+ local ret = params --[[@as lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams]]
if passed_multiple_ranges then
- params.ranges = vim.tbl_map(to_lsp_range, range)
+ ret = params --[[@as lsp.DocumentRangesFormattingParams]]
+ --- @cast range {start:[integer,integer],end:[integer, integer]}
+ ret.ranges = vim.tbl_map(to_lsp_range, range)
elseif range then
- params.range = to_lsp_range(range)
+ ret = params --[[@as lsp.DocumentRangeFormattingParams]]
+ ret.range = to_lsp_range(range)
end
- return params
+ return ret
end
if opts.async then
+ --- @param idx? integer
+ --- @param client? vim.lsp.Client
local function do_format(idx, client)
- if not client then
+ if not idx or not client then
return
end
local params = set_range(client, util.make_formatting_params(opts.formatting_options))
- client.request(method, params, function(...)
+ client:request(method, params, function(...)
local handler = client.handlers[method] or lsp.handlers[method]
handler(...)
do_format(next(clients, idx))
@@ -584,7 +591,7 @@ function M.format(opts)
local timeout_ms = opts.timeout_ms or 1000
for _, client in pairs(clients) do
local params = set_range(client, util.make_formatting_params(opts.formatting_options))
- local result, err = client.request_sync(method, params, timeout_ms, bufnr)
+ 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
@@ -615,7 +622,7 @@ end
---@param opts? vim.lsp.buf.rename.Opts Additional options:
function M.rename(new_name, opts)
opts = opts or {}
- local bufnr = opts.bufnr or api.nvim_get_current_buf()
+ local bufnr = vim._resolve_bufnr(opts.bufnr)
local clients = lsp.get_clients({
bufnr = bufnr,
name = opts.name,
@@ -636,38 +643,40 @@ function M.rename(new_name, opts)
local cword = vim.fn.expand('<cword>')
--- @param range lsp.Range
- --- @param offset_encoding string
- local function get_text_at_range(range, offset_encoding)
+ --- @param position_encoding string
+ local function get_text_at_range(range, position_encoding)
return api.nvim_buf_get_text(
bufnr,
range.start.line,
- util._get_line_byte_from_position(bufnr, range.start, offset_encoding),
+ util._get_line_byte_from_position(bufnr, range.start, position_encoding),
range['end'].line,
- util._get_line_byte_from_position(bufnr, range['end'], offset_encoding),
+ util._get_line_byte_from_position(bufnr, range['end'], position_encoding),
{}
)[1]
end
+ --- @param idx? integer
+ --- @param client? vim.lsp.Client
local function try_use_client(idx, client)
- if not client then
+ if not idx or not client then
return
end
--- @param name string
local function rename(name)
- local params = util.make_position_params(win, client.offset_encoding)
+ local params = util.make_position_params(win, client.offset_encoding) --[[@as lsp.RenameParams]]
params.newName = name
local handler = client.handlers[ms.textDocument_rename]
or lsp.handlers[ms.textDocument_rename]
- client.request(ms.textDocument_rename, params, function(...)
+ client:request(ms.textDocument_rename, params, function(...)
handler(...)
try_use_client(next(clients, idx))
end, bufnr)
end
- if client.supports_method(ms.textDocument_prepareRename) then
+ if client:supports_method(ms.textDocument_prepareRename) then
local params = util.make_position_params(win, client.offset_encoding)
- client.request(ms.textDocument_prepareRename, params, function(err, result)
+ client:request(ms.textDocument_prepareRename, params, function(err, result)
if err or result == nil then
if next(clients, idx) then
try_use_client(next(clients, idx))
@@ -706,7 +715,7 @@ function M.rename(new_name, opts)
end, bufnr)
else
assert(
- client.supports_method(ms.textDocument_rename),
+ client:supports_method(ms.textDocument_rename),
'Client must support textDocument/rename'
)
if new_name then
@@ -732,7 +741,7 @@ end
--- Lists all the references to the symbol under the cursor in the quickfix window.
---
----@param context (table|nil) Context for the request
+---@param context lsp.ReferenceContext? Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
---@param opts? vim.lsp.ListOpts
function M.references(context, opts)
@@ -781,7 +790,7 @@ function M.references(context, opts)
params.context = context or {
includeDeclaration = true,
}
- client.request(ms.textDocument_references, params, function(_, result)
+ client:request(ms.textDocument_references, params, function(_, result)
local items = util.locations_to_items(result or {}, client.offset_encoding)
vim.list_extend(all_items, items)
remaining = remaining - 1
@@ -792,9 +801,10 @@ function M.references(context, opts)
end
end
---- Lists all symbols in the current buffer in the quickfix window.
+--- Lists all symbols in the current buffer in the |location-list|.
--- @param opts? vim.lsp.ListOpts
function M.document_symbol(opts)
+ opts = vim.tbl_deep_extend('keep', opts or {}, { loclist = true })
local params = { textDocument = util.make_text_document_params() }
request_with_opts(ms.textDocument_documentSymbol, params, opts)
end
@@ -813,7 +823,7 @@ local function request_with_id(client_id, method, params, handler, bufnr)
)
return
end
- client.request(method, params, handler, bufnr)
+ client:request(method, params, handler, bufnr)
end
--- @param item lsp.TypeHierarchyItem|lsp.CallHierarchyItem
@@ -880,7 +890,7 @@ local function hierarchy(method)
for _, client in ipairs(clients) do
local params = util.make_position_params(win, client.offset_encoding)
--- @param result lsp.CallHierarchyItem[]|lsp.TypeHierarchyItem[]?
- client.request(prepare_method, params, function(err, result, ctx)
+ client:request(prepare_method, params, function(err, result, ctx)
if err then
vim.notify(err.message, vim.log.levels.WARN)
elseif result then
@@ -1131,8 +1141,8 @@ local function on_code_action_results(results, opts)
local action = choice.action
local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number')
- if not action.edit and client.supports_method(ms.codeAction_resolve) then
- client.request(ms.codeAction_resolve, action, function(err, resolved_action)
+ if not action.edit and client:supports_method(ms.codeAction_resolve) then
+ client:request(ms.codeAction_resolve, action, function(err, resolved_action)
if err then
if action.command then
apply_action(action, client, choice.ctx)
@@ -1224,6 +1234,7 @@ function M.code_action(opts)
for _, client in ipairs(clients) do
---@type lsp.CodeActionParams
local params
+
if opts.range then
assert(type(opts.range) == 'table', 'code_action range must be a table')
local start = assert(opts.range.start, 'range must have a `start` property')
@@ -1236,6 +1247,9 @@ function M.code_action(opts)
else
params = util.make_range_params(win, client.offset_encoding)
end
+
+ --- @cast params lsp.CodeActionParams
+
if context.diagnostics then
params.context = context
else
@@ -1253,7 +1267,7 @@ function M.code_action(opts)
})
end
- client.request(ms.textDocument_codeAction, params, on_result, bufnr)
+ client:request(ms.textDocument_codeAction, params, on_result, bufnr)
end
end
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 11ecb87507..253ccc48f4 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -75,17 +75,17 @@ local validate = vim.validate
---
--- Map with language server specific settings.
--- See the {settings} in |vim.lsp.Client|.
---- @field settings? table
+--- @field settings? lsp.LSPObject
---
--- 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
+--- Commands passed to `start()` 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.
--- @field commands? table<string,fun(command: lsp.Command, ctx: table)>
---
--- Values to pass in the initialization request as `initializationOptions`. See `initialize` in
--- the LSP spec.
---- @field init_options? table
+--- @field init_options? lsp.LSPObject
---
--- Name in log messages.
--- (default: client-id)
@@ -94,7 +94,8 @@ local validate = vim.validate
--- Language ID as string. Defaults to the buffer filetype.
--- @field get_language_id? fun(bufnr: integer, filetype: string): string
---
---- The encoding that the LSP server expects. Client does not verify this is correct.
+--- Called "position encoding" in LSP spec, the encoding that the LSP server expects.
+--- Client does not verify this is correct.
--- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32'
---
--- Callback invoked when the client operation throws an error. `code` is a number describing the error.
@@ -103,7 +104,7 @@ local validate = vim.validate
--- @field on_error? fun(code: integer, err: string)
---
--- Callback 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()|.
+--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|.
--- You can use this to modify parameters before they are sent.
--- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)
---
@@ -148,8 +149,10 @@ local validate = vim.validate
--- See |vim.lsp.rpc.start()|.
--- @field rpc vim.lsp.rpc.PublicClient
---
---- The encoding used for communicating with the server. You can modify this in
---- the `config`'s `on_init` method before text is sent to the server.
+--- Called "position encoding" in LSP spec,
+--- the encoding used for communicating with the server.
+--- You can modify this in the `config`'s `on_init` method
+--- before text is sent to the server.
--- @field offset_encoding string
---
--- The handlers used by the client as described in |lsp-handler|.
@@ -161,16 +164,20 @@ local validate = vim.validate
--- for an active request, or "cancel" for a cancel request. It will be
--- "complete" ephemerally while executing |LspRequest| autocmds when replies
--- are received from the server.
---- @field requests table<integer,{ type: string, bufnr: integer, method: string}>
+--- @field requests table<integer,{ type: string, bufnr: integer, method: string}?>
---
--- copy of the table that was passed by the user
---- to |vim.lsp.start_client()|.
+--- to |vim.lsp.start()|.
--- @field config vim.lsp.ClientConfig
---
--- Response from the server sent on `initialize` describing the server's
--- capabilities.
--- @field server_capabilities lsp.ServerCapabilities?
---
+--- Response from the server sent on `initialize` describing information about
+--- the server.
+--- @field server_info lsp.ServerInfo?
+---
--- A ring buffer (|vim.ringbuf()|) containing progress messages
--- sent by the server.
--- @field progress vim.lsp.Client.Progress
@@ -186,9 +193,6 @@ local validate = vim.validate
---
--- @field attached_buffers table<integer,true>
---
---- Buffers that should be attached to upon initialize()
---- @field package _buffers_to_attach table<integer,true>
----
--- @field private _log_prefix string
---
--- Track this so that we can escalate automatically if we've already tried a
@@ -207,7 +211,7 @@ local validate = vim.validate
--- Map with language server specific settings. These are returned to the
--- language server if requested via `workspace/configuration`. Keys are
--- case-sensitive.
---- @field settings table
+--- @field settings lsp.LSPObject
---
--- A table with flags for the client. The current (experimental) flags are:
--- @field flags vim.lsp.Client.Flags
@@ -219,70 +223,28 @@ local validate = vim.validate
--- @field private registrations table<string,lsp.Registration[]>
--- @field dynamic_capabilities lsp.DynamicCapabilities
---
---- Sends a request to the server.
---- This is a thin wrapper around {client.rpc.request} with some additional
---- checking.
---- If {handler} is not specified and if there's no respective global
---- handler, then an error will occur.
---- Returns: {status}, {client_id}?. {status} is a boolean indicating if
---- the notification was successful. If it is `false`, then it will always
---- be `false` (the client has shutdown).
---- If {status} is `true`, the function returns {request_id} as the second
---- result. You can use this with `client.cancel_request(request_id)` to cancel
---- the request.
---- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?
----
---- Sends a request to the server and synchronously waits for the response.
---- This is a wrapper around {client.request}
---- Returns: { err=err, result=result }, a dict, where `err` and `result`
---- come from the |lsp-handler|. On timeout, cancel or error, returns `(nil,
---- err)` where `err` is a string describing the failure reason. If the request
---- was unsuccessful returns `nil`.
---- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict
----
---- Sends a notification to an LSP server.
---- Returns: a boolean to indicate if the notification was successful. If
---- it is false, then it will always be false (the client has shutdown).
---- @field notify fun(method: string, params: table?): boolean
----
---- Cancels a request with a given request id.
---- Returns: same as `notify()`.
---- @field cancel_request fun(id: integer): boolean
----
---- Stops a client, optionally with force.
---- By default, it will just ask the server to shutdown without force.
---- If you request to stop a client which has previously been requested to
---- shutdown, it will automatically escalate and force shutdown.
---- @field stop fun(force?: boolean)
----
---- Runs the on_attach function from the client's config if it was defined.
---- Useful for buffer-local setup.
---- @field on_attach fun(bufnr: integer)
----
--- @field private _before_init_cb? vim.lsp.client.before_init_cb
--- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[]
--- @field private _on_init_cbs vim.lsp.client.on_init_cb[]
--- @field private _on_exit_cbs vim.lsp.client.on_exit_cb[]
--- @field private _on_error_cb? fun(code: integer, err: string)
----
---- Checks if a client supports a given method.
---- Always returns true for unknown off-spec methods.
---- {opts} is a optional `{bufnr?: integer}` table.
---- Some language server capabilities can be file specific.
---- @field supports_method fun(method: string, opts?: {bufnr: integer?}): boolean
----
---- Checks whether a client is stopped.
---- Returns: true if the client is fully stopped.
---- @field is_stopped fun(): boolean
local Client = {}
Client.__index = Client
---- @param cls table
---- @param meth any
---- @return function
-local function method_wrapper(cls, meth)
- return function(...)
- return meth(cls, ...)
+--- @param obj table<string,any>
+--- @param cls table<string,function>
+--- @param name string
+local function method_wrapper(obj, cls, name)
+ local meth = assert(cls[name])
+ obj[name] = function(...)
+ local arg = select(1, ...)
+ if arg and getmetatable(arg) == cls then
+ -- First argument is self, call meth directly
+ return meth(...)
+ end
+ vim.deprecate('client.' .. name, 'client:' .. name, '0.13')
+ -- First argument is not self, insert it
+ return meth(obj, ...)
end
end
@@ -304,9 +266,6 @@ local valid_encodings = {
['utf8'] = 'utf-8',
['utf16'] = 'utf-16',
['utf32'] = 'utf-32',
- UTF8 = 'utf-8',
- UTF16 = 'utf-16',
- UTF32 = 'utf-32',
}
--- Normalizes {encoding} to valid LSP encoding names.
@@ -315,12 +274,12 @@ local valid_encodings = {
local function validate_encoding(encoding)
validate('encoding', encoding, 'string', true)
if not encoding then
- return valid_encodings.UTF16
+ return valid_encodings.utf16
end
return valid_encodings[encoding:lower()]
or error(
string.format(
- "Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'",
+ "Invalid position encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'",
encoding
)
)
@@ -346,7 +305,7 @@ local function default_get_language_id(_bufnr, filetype)
return filetype
end
---- Validates a client configuration as given to |vim.lsp.start_client()|.
+--- Validates a client configuration as given to |vim.lsp.start()|.
--- @param config vim.lsp.ClientConfig
local function validate_config(config)
validate('config', config, 'table')
@@ -404,31 +363,6 @@ local function get_name(id, config)
return tostring(id)
end
---- @param workspace_folders string|lsp.WorkspaceFolder[]?
---- @return lsp.WorkspaceFolder[]?
-local function get_workspace_folders(workspace_folders)
- if type(workspace_folders) == 'table' then
- return workspace_folders
- elseif type(workspace_folders) == 'string' then
- return {
- {
- uri = vim.uri_from_fname(workspace_folders),
- name = workspace_folders,
- },
- }
- end
-end
-
---- @generic T
---- @param x elem_or_list<T>?
---- @return T[]
-local function ensure_list(x)
- if type(x) == 'table' then
- return x
- end
- return { x }
-end
-
--- @nodoc
--- @param config vim.lsp.ClientConfig
--- @return vim.lsp.Client?
@@ -455,13 +389,13 @@ function Client.create(config)
settings = config.settings or {},
flags = config.flags or {},
get_language_id = config.get_language_id or default_get_language_id,
- capabilities = config.capabilities or lsp.protocol.make_client_capabilities(),
- workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir),
+ capabilities = config.capabilities,
+ workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
root_dir = config.root_dir,
_before_init_cb = config.before_init,
- _on_init_cbs = ensure_list(config.on_init),
- _on_exit_cbs = ensure_list(config.on_exit),
- _on_attach_cbs = ensure_list(config.on_attach),
+ _on_init_cbs = vim._ensure_list(config.on_init),
+ _on_exit_cbs = vim._ensure_list(config.on_exit),
+ _on_attach_cbs = vim._ensure_list(config.on_attach),
_on_error_cb = config.on_error,
_trace = get_trace(config.trace),
@@ -477,6 +411,9 @@ function Client.create(config)
messages = { name = name, messages = {}, progress = {}, status = {} },
}
+ self.capabilities =
+ vim.tbl_deep_extend('force', lsp.protocol.make_client_capabilities(), self.capabilities or {})
+
--- @class lsp.DynamicCapabilities
--- @nodoc
self.dynamic_capabilities = {
@@ -499,24 +436,23 @@ function Client.create(config)
end,
}
- self.request = method_wrapper(self, Client._request)
- self.request_sync = method_wrapper(self, Client._request_sync)
- self.notify = method_wrapper(self, Client._notify)
- self.cancel_request = method_wrapper(self, Client._cancel_request)
- self.stop = method_wrapper(self, Client._stop)
- self.is_stopped = method_wrapper(self, Client._is_stopped)
- self.on_attach = method_wrapper(self, Client._on_attach)
- self.supports_method = method_wrapper(self, Client._supports_method)
-
--- @type table<string|integer, string> title of unfinished progress sequences by token
self.progress.pending = {}
--- @type vim.lsp.rpc.Dispatchers
local dispatchers = {
- notification = method_wrapper(self, Client._notification),
- server_request = method_wrapper(self, Client._server_request),
- on_error = method_wrapper(self, Client._on_error),
- on_exit = method_wrapper(self, Client._on_exit),
+ notification = function(...)
+ return self:_notification(...)
+ end,
+ server_request = function(...)
+ return self:_server_request(...)
+ end,
+ on_error = function(...)
+ return self:_on_error(...)
+ end,
+ on_exit = function(...)
+ return self:_on_exit(...)
+ end,
}
-- Start the RPC client.
@@ -533,6 +469,15 @@ function Client.create(config)
setmetatable(self, Client)
+ method_wrapper(self, Client, 'request')
+ method_wrapper(self, Client, 'request_sync')
+ method_wrapper(self, Client, 'notify')
+ method_wrapper(self, Client, 'cancel_request')
+ method_wrapper(self, Client, 'stop')
+ method_wrapper(self, Client, 'is_stopped')
+ method_wrapper(self, Client, 'on_attach')
+ method_wrapper(self, Client, 'supports_method')
+
return self
end
@@ -615,8 +560,10 @@ function Client:initialize()
self.offset_encoding = self.server_capabilities.positionEncoding
end
+ self.server_info = result.serverInfo
+
if next(self.settings) then
- self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings })
+ self:notify(ms.workspace_didChangeConfiguration, { settings = self.settings })
end
-- If server is being restarted, make sure to re-attach to any previously attached buffers.
@@ -628,7 +575,7 @@ function Client:initialize()
for buf in pairs(reattach_bufs) do
-- The buffer may have been detached in the on_init callback.
if self.attached_buffers[buf] then
- self:_on_attach(buf)
+ self:on_attach(buf)
end
end
@@ -645,24 +592,62 @@ end
--- Returns the default handler if the user hasn't set a custom one.
---
--- @param method (string) LSP method name
---- @return lsp.Handler|nil handler for the given method, if defined, or the default from |vim.lsp.handlers|
+--- @return lsp.Handler? handler for the given method, if defined, or the default from |vim.lsp.handlers|
function Client:_resolve_handler(method)
return self.handlers[method] or lsp.handlers[method]
end
---- Returns the buffer number for the given {bufnr}.
----
---- @param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer
---- @return integer bufnr
-local function resolve_bufnr(bufnr)
- validate('bufnr', bufnr, 'number', true)
- if bufnr == nil or bufnr == 0 then
- return api.nvim_get_current_buf()
+--- @private
+--- @param id integer
+--- @param req_type 'pending'|'complete'|'cancel'|
+--- @param bufnr? integer (only required for req_type='pending')
+--- @param method? string (only required for req_type='pending')
+function Client:_process_request(id, req_type, bufnr, method)
+ local pending = req_type == 'pending'
+
+ validate('id', id, 'number')
+ if pending then
+ validate('bufnr', bufnr, 'number')
+ validate('method', method, 'string')
+ end
+
+ local cur_request = self.requests[id]
+
+ if pending and cur_request then
+ log.error(
+ self._log_prefix,
+ ('Cannot create request with id %d as one already exists'):format(id)
+ )
+ return
+ elseif not pending and not cur_request then
+ log.error(
+ self._log_prefix,
+ ('Cannot find request with id %d whilst attempting to %s'):format(id, req_type)
+ )
+ return
+ end
+
+ if cur_request then
+ bufnr = cur_request.bufnr
+ method = cur_request.method
end
- return bufnr
+
+ assert(bufnr and method)
+
+ local request = { type = req_type, bufnr = bufnr, method = method }
+
+ -- Clear 'complete' requests
+ -- Note 'pending' and 'cancelled' requests are cleared when the server sends a response
+ -- which is processed via the notify_reply_callback argument to rpc.request.
+ self.requests[id] = req_type ~= 'complete' and request or nil
+
+ api.nvim_exec_autocmds('LspRequest', {
+ buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
+ modeline = false,
+ data = { client_id = self.id, request_id = id, request = request },
+ })
end
---- @private
--- Sends a request to the server.
---
--- This is a thin wrapper around {client.rpc.request} with some additional
@@ -671,15 +656,14 @@ end
--- @param method string LSP method name.
--- @param params? table LSP request params.
--- @param handler? lsp.Handler Response |lsp-handler| for this method.
---- @param bufnr integer Buffer handle (0 for current).
---- @return boolean status, integer? request_id {status} is a bool indicating
---- whether the request was successful. If it is `false`, then it will
---- always be `false` (the client has shutdown). If it was
---- successful, then it will return {request_id} as the
---- second result. You can use this with `client.cancel_request(request_id)`
+--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current.
+--- @return boolean status indicates whether the request was successful.
+--- If it is `false`, then it will always be `false` (the client has shutdown).
+--- @return integer? request_id Can be used with |Client:cancel_request()|.
+--- `nil` is request failed.
--- to cancel the-request.
--- @see |vim.lsp.buf_request_all()|
-function Client:_request(method, params, handler, bufnr)
+function Client:request(method, params, handler, bufnr)
if not handler then
handler = assert(
self:_resolve_handler(method),
@@ -688,37 +672,24 @@ function Client:_request(method, params, handler, bufnr)
end
-- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state
changetracking.flush(self, bufnr)
+ bufnr = vim._resolve_bufnr(bufnr)
local version = lsp.util.buf_versions[bufnr]
- bufnr = resolve_bufnr(bufnr)
log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr)
local success, request_id = self.rpc.request(method, params, function(err, result)
- local context = {
+ handler(err, result, {
method = method,
client_id = self.id,
bufnr = bufnr,
params = params,
version = version,
- }
- handler(err, result, context)
- end, function(request_id)
- local request = self.requests[request_id]
- request.type = 'complete'
- api.nvim_exec_autocmds('LspRequest', {
- buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
- modeline = false,
- data = { client_id = self.id, request_id = request_id, request = request },
})
- self.requests[request_id] = nil
+ end, function(request_id)
+ -- Called when the server sends a response to the request (including cancelled acknowledgment).
+ self:_process_request(request_id, 'complete')
end)
if success and request_id then
- local request = { type = 'pending', bufnr = bufnr, method = method }
- self.requests[request_id] = request
- api.nvim_exec_autocmds('LspRequest', {
- buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil,
- modeline = false,
- data = { client_id = self.id, request_id = request_id, request = request },
- })
+ self:_process_request(request_id, 'pending', bufnr, method)
end
return success, request_id
@@ -731,41 +702,39 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err
---
--- @param ... string List to write to the buffer
local function err_message(...)
- local message = table.concat(vim.iter({ ... }):flatten():totable())
+ local chunks = { { table.concat(vim.iter({ ... }):flatten():totable()) } }
if vim.in_fast_event() then
vim.schedule(function()
- api.nvim_err_writeln(message)
+ api.nvim_echo(chunks, true, { err = true })
api.nvim_command('redraw')
end)
else
- api.nvim_err_writeln(message)
+ api.nvim_echo(chunks, true, { err = true })
api.nvim_command('redraw')
end
end
---- @private
--- Sends a request to the server and synchronously waits for the response.
---
---- This is a wrapper around {client.request}
+--- This is a wrapper around |Client:request()|
---
---- @param method (string) LSP method name.
---- @param params (table) LSP request params.
---- @param timeout_ms (integer|nil) Maximum time in milliseconds to wait for
+--- @param method string LSP method name.
+--- @param params table LSP request params.
+--- @param timeout_ms integer? Maximum time in milliseconds to wait for
--- a result. Defaults to 1000
---- @param bufnr (integer) Buffer handle (0 for current).
---- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict, where
---- `err` and `result` come from the |lsp-handler|.
---- On timeout, cancel or error, returns `(nil, err)` where `err` is a
---- string describing the failure reason. If the request was unsuccessful
---- returns `nil`.
+--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current.
+--- @return {err: lsp.ResponseError?, result:any}? `result` and `err` from the |lsp-handler|.
+--- `nil` is the request was unsuccessful
+--- @return string? err On timeout, cancel or error, where `err` is a
+--- string describing the failure reason.
--- @see |vim.lsp.buf_request_sync()|
-function Client:_request_sync(method, params, timeout_ms, bufnr)
+function Client:request_sync(method, params, timeout_ms, bufnr)
local request_result = nil
local function _sync_handler(err, result)
request_result = { err = err, result = result }
end
- local success, request_id = self:_request(method, params, _sync_handler, bufnr)
+ local success, request_id = self:request(method, params, _sync_handler, bufnr)
if not success then
return nil
end
@@ -776,22 +745,20 @@ function Client:_request_sync(method, params, timeout_ms, bufnr)
if not wait_result then
if request_id then
- self:_cancel_request(request_id)
+ self:cancel_request(request_id)
end
return nil, wait_result_reason[reason]
end
return request_result
end
---- @package
--- Sends a notification to an LSP server.
---
--- @param method string LSP method name.
---- @param params table|nil LSP request params.
---- @return boolean status true if the notification was successful.
---- If it is false, then it will always be false
---- (the client has shutdown).
-function Client:_notify(method, params)
+--- @param params table? LSP request params.
+--- @return boolean status indicating if the notification was successful.
+--- If it is false, then the client has shutdown.
+function Client:notify(method, params)
if method ~= ms.textDocument_didChange then
changetracking.flush(self)
end
@@ -814,41 +781,32 @@ function Client:_notify(method, params)
return client_active
end
---- @private
--- Cancels a request with a given request id.
---
---- @param id (integer) id of request to cancel
---- @return boolean status true if notification was successful. false otherwise
---- @see |vim.lsp.client.notify()|
-function Client:_cancel_request(id)
- validate('id', id, 'number')
- local request = self.requests[id]
- if request and request.type == 'pending' then
- request.type = 'cancel'
- api.nvim_exec_autocmds('LspRequest', {
- buffer = api.nvim_buf_is_valid(request.bufnr) and request.bufnr or nil,
- modeline = false,
- data = { client_id = self.id, request_id = id, request = request },
- })
- end
+--- @param id integer id of request to cancel
+--- @return boolean status indicating if the notification was successful.
+--- @see |Client:notify()|
+function Client:cancel_request(id)
+ self:_process_request(id, 'cancel')
return self.rpc.notify(ms.dollar_cancelRequest, { id = id })
end
---- @private
--- Stops a client, optionally with force.
---
---- By default, it will just ask the - server to shutdown without force. If
+--- By default, it will just request the server to shutdown without force. If
--- you request to stop a client which has previously been requested to
--- shutdown, it will automatically escalate and force shutdown.
---
---- @param force boolean|nil
-function Client:_stop(force)
+--- @param force? boolean
+function Client:stop(force)
local rpc = self.rpc
if rpc.is_closing() then
return
end
+ vim.lsp._watchfiles.cancel(self.id)
+
if force or not self.initialized or self._graceful_shutdown_failed then
rpc.terminate()
return
@@ -863,7 +821,6 @@ function Client:_stop(force)
rpc.terminate()
self._graceful_shutdown_failed = true
end
- vim.lsp._watchfiles.cancel(self.id)
end)
end
@@ -945,20 +902,22 @@ end
--- @param bufnr? integer
--- @return lsp.Registration?
function Client:_get_registration(method, bufnr)
- bufnr = bufnr or vim.api.nvim_get_current_buf()
+ bufnr = vim._resolve_bufnr(bufnr)
for _, reg in ipairs(self.registrations[method] or {}) do
- if not reg.registerOptions or not reg.registerOptions.documentSelector then
+ local regoptions = reg.registerOptions --[[@as {documentSelector:lsp.TextDocumentFilter[]}]]
+ if not regoptions or not regoptions.documentSelector then
return reg
end
- local documentSelector = reg.registerOptions.documentSelector
+ local documentSelector = regoptions.documentSelector
local language = self:_get_language_id(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local fname = vim.uri_to_fname(uri)
for _, filter in ipairs(documentSelector) do
+ local flang, fscheme, fpat = filter.language, filter.scheme, filter.pattern
if
- not (filter.language and language ~= filter.language)
- and not (filter.scheme and not vim.startswith(uri, filter.scheme .. ':'))
- and not (filter.pattern and not vim.glob.to_lpeg(filter.pattern):match(fname))
+ not (flang and language ~= flang)
+ and not (fscheme and not vim.startswith(uri, fscheme .. ':'))
+ and not (type(fpat) == 'string' and not vim.glob.to_lpeg(fpat):match(fname))
then
return reg
end
@@ -966,12 +925,11 @@ function Client:_get_registration(method, bufnr)
end
end
---- @private
--- Checks whether a client is stopped.
---
--- @return boolean # true if client is stopped or in the process of being
--- stopped; false otherwise
-function Client:_is_stopped()
+function Client:is_stopped()
return self.rpc.is_closing()
end
@@ -983,7 +941,7 @@ end
--- @param handler? lsp.Handler only called if a server command
function Client:exec_cmd(command, context, handler)
context = vim.deepcopy(context or {}, true) --[[@as lsp.HandlerContext]]
- context.bufnr = context.bufnr or api.nvim_get_current_buf()
+ context.bufnr = vim._resolve_bufnr(context.bufnr)
context.client_id = self.id
local cmdname = command.command
local fn = self.commands[cmdname] or lsp.commands[cmdname]
@@ -1013,7 +971,7 @@ function Client:exec_cmd(command, context, handler)
command = cmdname,
arguments = command.arguments,
}
- self.request(ms.workspace_executeCommand, params, handler, context.bufnr)
+ self:request(ms.workspace_executeCommand, params, handler, context.bufnr)
end
--- Default handler for the 'textDocument/didOpen' LSP notification.
@@ -1021,14 +979,14 @@ end
--- @param bufnr integer Number of the buffer, or 0 for current
function Client:_text_document_did_open_handler(bufnr)
changetracking.init(self, bufnr)
- if not self.supports_method(ms.textDocument_didOpen) then
+ if not self:supports_method(ms.textDocument_didOpen) then
return
end
if not api.nvim_buf_is_loaded(bufnr) then
return
end
- self.notify(ms.textDocument_didOpen, {
+ self:notify(ms.textDocument_didOpen, {
textDocument = {
version = lsp.util.buf_versions[bufnr],
uri = vim.uri_from_bufnr(bufnr),
@@ -1049,8 +1007,9 @@ function Client:_text_document_did_open_handler(bufnr)
end
--- Runs the on_attach function from the client's config if it was defined.
+--- Useful for buffer-local setup.
--- @param bufnr integer Buffer number
-function Client:_on_attach(bufnr)
+function Client:on_attach(bufnr)
self:_text_document_did_open_handler(bufnr)
lsp._set_defaults(self, bufnr)
@@ -1085,10 +1044,18 @@ function Client:write_error(code, err)
err_message(self._log_prefix, ': Error ', client_error, ': ', vim.inspect(err))
end
---- @private
+--- Checks if a client supports a given method.
+--- Always returns true for unknown off-spec methods.
+---
+--- Note: Some language server capabilities can be file specific.
--- @param method string
---- @param opts? {bufnr: integer?}
-function Client:_supports_method(method, opts)
+--- @param bufnr? integer
+function Client:supports_method(method, bufnr)
+ -- Deprecated form
+ if type(bufnr) == 'table' then
+ --- @diagnostic disable-next-line:no-unknown
+ bufnr = bufnr.bufnr
+ end
local required_capability = lsp._request_name_to_capability[method]
-- if we don't know about the method, assume that the client supports it.
if not required_capability then
@@ -1101,12 +1068,12 @@ function Client:_supports_method(method, opts)
local rmethod = lsp._resolve_to_request[method]
if rmethod then
if self:_supports_registration(rmethod) then
- local reg = self:_get_registration(rmethod, opts and opts.bufnr)
+ local reg = self:_get_registration(rmethod, bufnr)
return vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or false
end
else
if self:_supports_registration(method) then
- return self:_get_registration(method, opts and opts.bufnr) ~= nil
+ return self:_get_registration(method, bufnr) ~= nil
end
end
return false
@@ -1205,9 +1172,9 @@ function Client:_add_workspace_folder(dir)
end
end
- local wf = assert(get_workspace_folders(dir))
+ local wf = assert(lsp._get_workspace_folders(dir))
- self:_notify(ms.workspace_didChangeWorkspaceFolders, {
+ self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = wf, removed = {} },
})
@@ -1220,9 +1187,9 @@ end
--- Remove a directory to the workspace folders.
--- @param dir string?
function Client:_remove_workspace_folder(dir)
- local wf = assert(get_workspace_folders(dir))
+ local wf = assert(lsp._get_workspace_folders(dir))
- self:_notify(ms.workspace_didChangeWorkspaceFolders, {
+ self:notify(ms.workspace_didChangeWorkspaceFolders, {
event = { added = {}, removed = wf },
})
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index fdbdda695a..e36d8fee27 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -21,7 +21,7 @@ local lens_cache_by_buf = setmetatable({}, {
---client_id -> namespace
local namespaces = setmetatable({}, {
__index = function(t, key)
- local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
+ local value = api.nvim_create_namespace('nvim.lsp.codelens:' .. key)
rawset(t, key, value)
return value
end,
@@ -30,7 +30,7 @@ local namespaces = setmetatable({}, {
---@private
M.__namespaces = namespaces
-local augroup = api.nvim_create_augroup('vim_lsp_codelens', {})
+local augroup = api.nvim_create_augroup('nvim.lsp.codelens', {})
api.nvim_create_autocmd('LspDetach', {
group = augroup,
@@ -104,16 +104,12 @@ function M.run()
end
end
-local function resolve_bufnr(bufnr)
- return bufnr == 0 and api.nvim_get_current_buf() or bufnr
-end
-
--- Clear the lenses
---
---@param client_id integer|nil filter by client_id. All clients if nil
---@param bufnr integer|nil filter by buffer. All buffers if nil, 0 for current buffer
function M.clear(client_id, bufnr)
- bufnr = bufnr and resolve_bufnr(bufnr)
+ bufnr = bufnr and vim._resolve_bufnr(bufnr)
local buffers = bufnr and { bufnr }
or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs())
for _, iter_bufnr in pairs(buffers) do
@@ -231,7 +227,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback)
countdown()
else
assert(client)
- client.request(ms.codeLens_resolve, lens, function(_, result)
+ client:request(ms.codeLens_resolve, lens, function(_, result)
if api.nvim_buf_is_loaded(bufnr) and result and result.command then
lens.command = result.command
-- Eager display to have some sort of incremental feedback
@@ -296,7 +292,7 @@ end
--- @param opts? vim.lsp.codelens.refresh.Opts Optional fields
function M.refresh(opts)
opts = opts or {}
- local bufnr = opts.bufnr and resolve_bufnr(opts.bufnr)
+ local bufnr = opts.bufnr and vim._resolve_bufnr(opts.bufnr)
local buffers = bufnr and { bufnr }
or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs())
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
index 92bc110a97..cf6d07745f 100644
--- a/runtime/lua/vim/lsp/completion.lua
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -127,8 +127,10 @@ end
--- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
---
--- @param item lsp.CompletionItem
+--- @param prefix string
+--- @param match fun(text: string, prefix: string):boolean
--- @return string
-local function get_completion_word(item)
+local function get_completion_word(item, prefix, match)
if item.insertTextFormat == protocol.InsertTextFormat.Snippet then
if item.textEdit then
-- Use label instead of text if text has different starting characters.
@@ -146,7 +148,12 @@ local function get_completion_word(item)
--
-- Typing `i` would remove the candidate because newText starts with `t`.
local text = parse_snippet(item.insertText or item.textEdit.newText)
- return #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label
+ local word = #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label
+ if item.filterText and not match(word, prefix) then
+ return item.filterText
+ else
+ return word
+ end
elseif item.insertText and item.insertText ~= '' then
return parse_snippet(item.insertText)
else
@@ -224,6 +231,9 @@ end
---@param prefix string
---@return boolean
local function match_item_by_value(value, prefix)
+ if prefix == '' then
+ return true
+ end
if vim.o.completeopt:find('fuzzy') ~= nil then
return next(vim.fn.matchfuzzy({ value }, prefix)) ~= nil
end
@@ -276,7 +286,7 @@ function M._lsp_to_complete_items(result, prefix, client_id)
local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert')
for _, item in ipairs(items) do
if matches(item) then
- local word = get_completion_word(item)
+ local word = get_completion_word(item, prefix, match_item_by_value)
local hl_group = ''
if
item.deprecated
@@ -404,7 +414,7 @@ local function request(clients, bufnr, win, callback)
for _, client in pairs(clients) do
local client_id = client.id
local params = lsp.util.make_position_params(win, client.offset_encoding)
- local ok, request_id = client.request(ms.textDocument_completion, params, function(err, result)
+ local ok, request_id = client:request(ms.textDocument_completion, params, function(err, result)
responses[client_id] = { err = err, result = result }
remaining_requests = remaining_requests - 1
if remaining_requests == 0 then
@@ -421,7 +431,7 @@ local function request(clients, bufnr, win, callback)
for client_id, request_id in pairs(request_ids) do
local client = lsp.get_client_by_id(client_id)
if client then
- client.cancel_request(request_id)
+ client:cancel_request(request_id)
end
end
end
@@ -460,7 +470,7 @@ local function trigger(bufnr, clients)
local server_start_boundary --- @type integer?
for client_id, response in pairs(responses) do
if response.err then
- vim.notify_once(response.err.message, vim.log.levels.warn)
+ vim.notify_once(response.err.message, vim.log.levels.WARN)
end
local result = response.result
@@ -550,7 +560,7 @@ local function on_complete_done()
return
end
- local offset_encoding = client.offset_encoding or 'utf-16'
+ local position_encoding = client.offset_encoding or 'utf-16'
local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider
local function clear_word()
@@ -576,13 +586,13 @@ local function on_complete_done()
if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then
clear_word()
- lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding)
+ lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, position_encoding)
apply_snippet_and_command()
elseif resolve_provider and type(completion_item) == 'table' then
local changedtick = vim.b[bufnr].changedtick
--- @param result lsp.CompletionItem
- client.request(ms.completionItem_resolve, completion_item, function(err, result)
+ client:request(ms.completionItem_resolve, completion_item, function(err, result)
if changedtick ~= vim.b[bufnr].changedtick then
return
end
@@ -591,7 +601,7 @@ local function on_complete_done()
if err then
vim.notify_once(err.message, vim.log.levels.WARN)
elseif result and result.additionalTextEdits then
- lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, offset_encoding)
+ lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding)
if result.command then
completion_item.command = result.command
end
@@ -605,6 +615,12 @@ local function on_complete_done()
end
end
+---@param bufnr integer
+---@return string
+local function get_augroup(bufnr)
+ return string.format('nvim.lsp.completion_%d', bufnr)
+end
+
--- @class vim.lsp.completion.BufferOpts
--- @field autotrigger? boolean Default: false When true, completion triggers automatically based on the server's `triggerCharacters`.
--- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|.
@@ -629,8 +645,7 @@ local function enable_completions(client_id, bufnr, opts)
})
-- Set up autocommands.
- local group =
- api.nvim_create_augroup(string.format('vim/lsp/completion-%d', bufnr), { clear = true })
+ local group = api.nvim_create_augroup(get_augroup(bufnr), { clear = true })
api.nvim_create_autocmd('CompleteDone', {
group = group,
buffer = bufnr,
@@ -698,7 +713,7 @@ local function disable_completions(client_id, bufnr)
handle.clients[client_id] = nil
if not next(handle.clients) then
buf_handles[bufnr] = nil
- api.nvim_del_augroup_by_name(string.format('vim/lsp/completion-%d', bufnr))
+ api.nvim_del_augroup_by_name(get_augroup(bufnr))
else
for char, clients in pairs(handle.triggers) do
--- @param c vim.lsp.Client
@@ -716,7 +731,7 @@ end
--- @param bufnr integer Buffer handle, or 0 for the current buffer
--- @param opts? vim.lsp.completion.BufferOpts
function M.enable(enable, client_id, bufnr, opts)
- bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr
+ bufnr = vim._resolve_bufnr(bufnr)
if enable then
enable_completions(client_id, bufnr, opts or {})
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index 8fd30c7668..fe24928a69 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -5,7 +5,7 @@ local api = vim.api
local M = {}
-local augroup = api.nvim_create_augroup('vim_lsp_diagnostic', {})
+local augroup = api.nvim_create_augroup('nvim.lsp.diagnostic', {})
local DEFAULT_CLIENT_ID = -1
@@ -20,7 +20,7 @@ end
---@return lsp.DiagnosticSeverity
local function severity_vim_to_lsp(severity)
if type(severity) == 'string' then
- severity = vim.diagnostic.severity[severity]
+ severity = vim.diagnostic.severity[severity] --- @type integer
end
return severity
end
@@ -77,7 +77,7 @@ end
local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
local buf_lines = get_buf_lines(bufnr)
local client = vim.lsp.get_client_by_id(client_id)
- local offset_encoding = client and client.offset_encoding or 'utf-16'
+ local position_encoding = client and client.offset_encoding or 'utf-16'
--- @param diagnostic lsp.Diagnostic
--- @return vim.Diagnostic
return vim.tbl_map(function(diagnostic)
@@ -89,15 +89,16 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)
string.format('Unsupported Markup message from LSP client %d', client_id),
vim.lsp.log_levels.ERROR
)
+ --- @diagnostic disable-next-line: undefined-field,no-unknown
message = diagnostic.message.value
end
local line = buf_lines and buf_lines[start.line + 1] or ''
--- @type vim.Diagnostic
return {
lnum = start.line,
- col = vim.str_byteindex(line, offset_encoding, start.character, false),
+ col = vim.str_byteindex(line, position_encoding, start.character, false),
end_lnum = _end.line,
- end_col = vim.str_byteindex(line, offset_encoding, _end.character, false),
+ end_col = vim.str_byteindex(line, position_encoding, _end.character, false),
severity = severity_lsp_to_vim(diagnostic.severity),
message = message,
source = diagnostic.source,
@@ -208,7 +209,7 @@ end
--- @param uri string
--- @param client_id? integer
---- @param diagnostics vim.Diagnostic[]
+--- @param diagnostics lsp.Diagnostic[]
--- @param is_pull boolean
local function handle_diagnostics(uri, client_id, diagnostics, is_pull)
local fname = vim.uri_to_fname(uri)
@@ -246,10 +247,18 @@ end
---
--- See |vim.diagnostic.config()| for configuration options.
---
----@param _ lsp.ResponseError?
+---@param error lsp.ResponseError?
---@param result lsp.DocumentDiagnosticReport
---@param ctx lsp.HandlerContext
-function M.on_diagnostic(_, result, ctx)
+function M.on_diagnostic(error, result, ctx)
+ if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then
+ if error.data == nil or error.data.retriggerRequest ~= false then
+ local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
+ client:request(ctx.method, ctx.params)
+ end
+ return
+ end
+
if result == nil or result.kind == 'unchanged' then
return
end
@@ -348,9 +357,7 @@ end
---@param bufnr (integer) Buffer handle, or 0 for current
---@private
function M._enable(bufnr)
- if bufnr == nil or bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
if not bufstates[bufnr] then
bufstates[bufnr] = { enabled = true }
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 5c28d88b38..b35140dfad 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -47,7 +47,7 @@ RSC[ms.dollar_progress] = function(_, params, ctx)
local value = params.value
if type(value) == 'table' then
- kind = value.kind
+ kind = value.kind --- @type string
-- Carry over title of `begin` messages to `report` and `end` messages
-- So that consumers always have it available, even if they consume a
-- subset of the full sequence
@@ -247,12 +247,12 @@ local function response_to_list(map_result, entity, title_fn)
local items = map_result(result, ctx.bufnr)
local list = { title = title, items = items, context = ctx }
- if config.loclist then
- vim.fn.setloclist(0, {}, ' ', list)
- vim.cmd.lopen()
- elseif config.on_list then
+ if config.on_list then
assert(vim.is_callable(config.on_list), 'on_list is not a function')
config.on_list(list)
+ elseif config.loclist then
+ vim.fn.setloclist(0, {}, ' ', list)
+ vim.cmd.lopen()
else
vim.fn.setqflist({}, ' ', list)
vim.cmd('botright copen')
@@ -382,7 +382,7 @@ end
--- @diagnostic disable-next-line: deprecated
RCS[ms.textDocument_hover] = M.hover
-local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help')
+local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help')
--- @deprecated remove in 0.13
--- |lsp-handler| for the method "textDocument/signatureHelp".
@@ -582,9 +582,8 @@ NSC['window/showMessage'] = function(_, params, ctx)
if message_type == protocol.MessageType.Error then
err_message('LSP[', client_name, '] ', message)
else
- --- @type string
- local message_type_name = protocol.MessageType[message_type]
- api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message))
+ message = ('LSP[%s][%s] %s\n'):format(client_name, protocol.MessageType[message_type], message)
+ api.nvim_echo({ { message } }, true, {})
end
return params
end
@@ -659,7 +658,8 @@ for k, fn in pairs(M) do
})
end
- if err then
+ -- ServerCancelled errors should be propagated to the request handler
+ if err and err.code ~= protocol.ErrorCodes.ServerCancelled then
-- LSP spec:
-- interface ResponseError:
-- code: integer;
diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua
index 0d314108fe..8af9f2f791 100644
--- a/runtime/lua/vim/lsp/health.lua
+++ b/runtime/lua/vim/lsp/health.lua
@@ -28,42 +28,48 @@ local function check_log()
report_fn(string.format('Log size: %d KB', log_size / 1000))
end
+--- @param f function
+--- @return string
+local function func_tostring(f)
+ local info = debug.getinfo(f, 'S')
+ return ('<function %s:%s>'):format(info.source, info.linedefined)
+end
+
local function check_active_clients()
vim.health.start('vim.lsp: Active Clients')
local clients = vim.lsp.get_clients()
if next(clients) then
for _, client in pairs(clients) do
+ local server_version = vim.tbl_get(client, 'server_info', 'version')
+ or '? (no serverInfo.version response)'
local cmd ---@type string
- if type(client.config.cmd) == 'table' then
- cmd = table.concat(client.config.cmd --[[@as table]], ' ')
- elseif type(client.config.cmd) == 'function' then
- cmd = tostring(client.config.cmd)
+ local ccmd = client.config.cmd
+ if type(ccmd) == 'table' then
+ cmd = vim.inspect(ccmd)
+ elseif type(ccmd) == 'function' then
+ cmd = func_tostring(ccmd)
end
local dirs_info ---@type string
if client.workspace_folders and #client.workspace_folders > 1 then
- dirs_info = string.format(
- ' Workspace folders:\n %s',
- vim
- .iter(client.workspace_folders)
- ---@param folder lsp.WorkspaceFolder
- :map(function(folder)
- return folder.name
- end)
- :join('\n ')
- )
+ local wfolders = {} --- @type string[]
+ for _, dir in ipairs(client.workspace_folders) do
+ wfolders[#wfolders + 1] = dir.name
+ end
+ dirs_info = ('- Workspace folders:\n %s'):format(table.concat(wfolders, '\n '))
else
dirs_info = string.format(
- ' Root directory: %s',
+ '- Root directory: %s',
client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~')
) or nil
end
report_info(table.concat({
string.format('%s (id: %d)', client.name, client.id),
+ string.format('- Version: %s', server_version),
dirs_info,
- string.format(' Command: %s', cmd),
- string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })),
+ string.format('- Command: %s', cmd),
+ string.format('- Settings: %s', vim.inspect(client.settings, { newline = '\n ' })),
string.format(
- ' Attached buffers: %s',
+ '- Attached buffers: %s',
vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ')
),
}, '\n'))
@@ -174,10 +180,45 @@ local function check_position_encodings()
end
end
+local function check_enabled_configs()
+ vim.health.start('vim.lsp: Enabled Configurations')
+
+ for name in vim.spairs(vim.lsp._enabled_configs) do
+ local config = vim.lsp.config[name]
+ local text = {} --- @type string[]
+ text[#text + 1] = ('%s:'):format(name)
+ for k, v in
+ vim.spairs(config --[[@as table<string,any>]])
+ do
+ local v_str --- @type string?
+ if k == 'name' then
+ v_str = nil
+ elseif k == 'filetypes' or k == 'root_markers' then
+ v_str = table.concat(v, ', ')
+ elseif type(v) == 'function' then
+ v_str = func_tostring(v)
+ else
+ v_str = vim.inspect(v, { newline = '\n ' })
+ end
+
+ if k == 'cmd' and type(v) == 'table' and vim.fn.executable(v[1]) == 0 then
+ report_warn(("'%s' is not executable. Configuration will not be used."):format(v[1]))
+ end
+
+ if v_str then
+ text[#text + 1] = ('- %s: %s'):format(k, v_str)
+ end
+ end
+ text[#text + 1] = ''
+ report_info(table.concat(text, '\n'))
+ end
+end
+
--- Performs a healthcheck for LSP
function M.check()
check_log()
check_active_clients()
+ check_enabled_configs()
check_watcher()
check_position_encodings()
end
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index f1ae9a8e9e..37e1202d1d 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -29,8 +29,8 @@ local bufstates = vim.defaulttable(function(_)
})
end)
-local namespace = api.nvim_create_namespace('vim_lsp_inlayhint')
-local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {})
+local namespace = api.nvim_create_namespace('nvim.lsp.inlayhint')
+local augroup = api.nvim_create_augroup('nvim.lsp.inlayhint', {})
--- |lsp-handler| for the method `textDocument/inlayHint`
--- Store hints for a specific buffer and client
@@ -122,12 +122,12 @@ end
--- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer
---
--- local client = vim.lsp.get_client_by_id(hint.client_id)
---- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0)
+--- local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0)
--- local resolved_hint = assert(resp and resp.result, resp.err)
--- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding)
---
--- location = resolved_hint.label[1].location
---- client.request('textDocument/hover', {
+--- client:request('textDocument/hover', {
--- textDocument = { uri = location.uri },
--- position = location.range.start,
--- })
@@ -149,8 +149,8 @@ function M.get(filter)
vim.list_extend(hints, M.get(vim.tbl_extend('keep', { bufnr = buf }, filter)))
end, vim.api.nvim_list_bufs())
return hints
- elseif bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
+ else
+ bufnr = vim._resolve_bufnr(bufnr)
end
local bufstate = bufstates[bufnr]
@@ -203,9 +203,7 @@ end
--- Clear inlay hints
---@param bufnr (integer) Buffer handle, or 0 for current
local function clear(bufnr)
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
local bufstate = bufstates[bufnr]
local client_lens = (bufstate or {}).client_hints or {}
local client_ids = vim.tbl_keys(client_lens) --- @type integer[]
@@ -221,9 +219,7 @@ end
--- Disable inlay hints for a buffer
---@param bufnr (integer) Buffer handle, or 0 for current
local function _disable(bufnr)
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
clear(bufnr)
bufstates[bufnr] = nil
bufstates[bufnr].enabled = false
@@ -242,9 +238,7 @@ end
--- Enable inlay hints for a buffer
---@param bufnr (integer) Buffer handle, or 0 for current
local function _enable(bufnr)
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
bufstates[bufnr] = nil
bufstates[bufnr].enabled = true
_refresh(bufnr)
@@ -371,13 +365,10 @@ function M.is_enabled(filter)
filter = filter or {}
local bufnr = filter.bufnr
- vim.validate('bufnr', bufnr, 'number', true)
if bufnr == nil then
return globalstate.enabled
- elseif bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
end
- return bufstates[bufnr].enabled
+ return bufstates[vim._resolve_bufnr(bufnr)].enabled
end
--- Optional filters |kwargs|, or `nil` for all.
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 7db48b0c06..fbfd0cd6b0 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -15,7 +15,6 @@ local sysname = vim.uv.os_uname().sysname
--- @class vim.lsp.protocol.constants
--- @nodoc
local constants = {
- --- @enum lsp.DiagnosticSeverity
DiagnosticSeverity = {
-- Reports an error.
Error = 1,
@@ -27,7 +26,6 @@ local constants = {
Hint = 4,
},
- --- @enum lsp.DiagnosticTag
DiagnosticTag = {
-- Unused or unnecessary code
Unnecessary = 1,
@@ -35,7 +33,6 @@ local constants = {
Deprecated = 2,
},
- ---@enum lsp.MessageType
MessageType = {
-- An error message.
Error = 1,
@@ -50,7 +47,6 @@ local constants = {
},
-- The file event type.
- ---@enum lsp.FileChangeType
FileChangeType = {
-- The file got created.
Created = 1,
@@ -149,7 +145,6 @@ 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.
@@ -174,6 +169,7 @@ local constants = {
-- Defined by the protocol.
RequestCancelled = -32800,
ContentModified = -32801,
+ ServerCancelled = -32802,
},
-- Describes the content type that a client supports in various
@@ -245,7 +241,6 @@ local constants = {
-- Defines whether the insert text in a completion item should be interpreted as
-- plain text or a snippet.
- --- @enum lsp.InsertTextFormat
InsertTextFormat = {
-- The primary text to be inserted is treated as a plain string.
PlainText = 1,
@@ -304,7 +299,6 @@ local constants = {
SourceOrganizeImports = 'source.organizeImports',
},
-- The reason why code actions were requested.
- ---@enum lsp.CodeActionTriggerKind
CodeActionTriggerKind = {
-- Code actions were explicitly requested by the user or by an extension.
Invoked = 1,
@@ -439,6 +433,13 @@ function protocol.make_client_capabilities()
properties = { 'command' },
},
},
+ foldingRange = {
+ dynamicRegistration = false,
+ lineFoldingOnly = true,
+ foldingRange = {
+ collapsedText = true,
+ },
+ },
formatting = {
dynamicRegistration = true,
},
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 6c8564845f..a0d1fe776b 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -1,18 +1,8 @@
-local uv = vim.uv
local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
+local lsp_transport = require('vim.lsp._transport')
local validate, schedule_wrap = vim.validate, vim.schedule_wrap
-local is_win = vim.fn.has('win32') == 1
-
---- Checks whether a given path exists and is a directory.
----@param filename string path to check
----@return boolean
-local function is_dir(filename)
- local stat = uv.fs_stat(filename)
- return stat and stat.type == 'directory' or false
-end
-
--- Embeds the given string into a table and correctly computes `Content-Length`.
---
---@param message string
@@ -242,8 +232,11 @@ local default_dispatchers = {
end,
}
----@private
-function M.create_read_loop(handle_body, on_no_chunk, on_error)
+--- @private
+--- @param handle_body fun(body: string)
+--- @param on_exit? fun()
+--- @param on_error fun(err: any)
+function M.create_read_loop(handle_body, on_exit, on_error)
local parse_chunk = coroutine.wrap(request_parser_loop) --[[@as fun(chunk: string?): vim.lsp.rpc.Headers?, string?]]
parse_chunk()
return function(err, chunk)
@@ -253,8 +246,8 @@ function M.create_read_loop(handle_body, on_no_chunk, on_error)
end
if not chunk then
- if on_no_chunk then
- on_no_chunk()
+ if on_exit then
+ on_exit()
end
return
end
@@ -262,7 +255,7 @@ function M.create_read_loop(handle_body, on_no_chunk, on_error)
while true do
local headers, body = parse_chunk(chunk)
if headers then
- handle_body(body)
+ handle_body(assert(body))
chunk = ''
else
break
@@ -282,14 +275,14 @@ local Client = {}
---@private
function Client:encode_and_send(payload)
log.debug('rpc.send', payload)
- if self.transport.is_closing() then
+ if self.transport:is_closing() then
return false
end
local jsonstr = assert(
vim.json.encode(payload),
string.format("Couldn't encode payload '%s'", vim.inspect(payload))
)
- self.transport.write(format_message_with_content_length(jsonstr))
+ self.transport:write(format_message_with_content_length(jsonstr))
return true
end
@@ -323,7 +316,7 @@ end
---@param method string The invoked LSP method
---@param params table? Parameters for the invoked LSP method
---@param callback fun(err?: lsp.ResponseError, result: any) Callback to invoke
----@param notify_reply_callback fun(message_id: integer)|nil Callback to invoke as soon as a request is no longer pending
+---@param notify_reply_callback? fun(message_id: integer) Callback to invoke as soon as a request is no longer pending
---@return boolean success `true` if request could be sent, `false` if not
---@return integer? message_id if request could be sent, `nil` if not
function Client:request(method, params, callback, notify_reply_callback)
@@ -337,21 +330,16 @@ function Client:request(method, params, callback, notify_reply_callback)
method = method,
params = params,
})
- local message_callbacks = self.message_callbacks
- local notify_reply_callbacks = self.notify_reply_callbacks
- if result then
- if message_callbacks then
- message_callbacks[message_id] = schedule_wrap(callback)
- else
- return false, nil
- end
- if notify_reply_callback and notify_reply_callbacks then
- notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
- end
- return result, message_id
- else
- return false, nil
+
+ if not result then
+ return false
+ end
+
+ self.message_callbacks[message_id] = schedule_wrap(callback)
+ if notify_reply_callback then
+ self.notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback)
end
+ return result, message_id
end
---@package
@@ -370,7 +358,7 @@ end
---@param ... any
---@return boolean status
---@return any head
----@return any|nil ...
+---@return any? ...
function Client:pcall_handler(errkind, status, head, ...)
if not status then
self:on_error(errkind, head, ...)
@@ -385,7 +373,7 @@ end
---@param ... any
---@return boolean status
---@return any head
----@return any|nil ...
+---@return any? ...
function Client:try_call(errkind, fn, ...)
return self:pcall_handler(errkind, pcall(fn, ...))
end
@@ -394,7 +382,8 @@ end
-- time and log them. This would require storing the timestamp. I could call
-- them with an error then, perhaps.
----@package
+--- @package
+--- @param body string
function Client:handle_body(body)
local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } })
if not ok then
@@ -406,7 +395,7 @@ function Client:handle_body(body)
if type(decoded) ~= 'table' then
self:on_error(M.client_errors.INVALID_SERVER_MESSAGE, decoded)
elseif type(decoded.method) == 'string' and decoded.id then
- local err --- @type lsp.ResponseError|nil
+ local err --- @type lsp.ResponseError?
-- Schedule here so that the users functions don't trigger an error and
-- we can still use the result.
vim.schedule(coroutine.wrap(function()
@@ -453,45 +442,36 @@ function Client:handle_body(body)
local result_id = assert(tonumber(decoded.id), 'response id must be a number')
-- Notify the user that a response was received for the request
- local notify_reply_callbacks = self.notify_reply_callbacks
- local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id]
+ local notify_reply_callback = self.notify_reply_callbacks[result_id]
if notify_reply_callback then
validate('notify_reply_callback', notify_reply_callback, 'function')
notify_reply_callback(result_id)
- notify_reply_callbacks[result_id] = nil
+ self.notify_reply_callbacks[result_id] = nil
end
- local message_callbacks = self.message_callbacks
-
-- Do not surface RequestCancelled to users, it is RPC-internal.
if decoded.error then
- local mute_error = false
+ assert(type(decoded.error) == 'table')
if decoded.error.code == protocol.ErrorCodes.RequestCancelled then
log.debug('Received cancellation ack', decoded)
- mute_error = true
- end
-
- if mute_error then
-- Clear any callback since this is cancelled now.
-- This is safe to do assuming that these conditions hold:
-- - The server will not send a result callback after this cancellation.
-- - If the server sent this cancellation ACK after sending the result, the user of this RPC
-- client will ignore the result themselves.
- if result_id and message_callbacks then
- message_callbacks[result_id] = nil
+ if result_id then
+ self.message_callbacks[result_id] = nil
end
return
end
end
- local callback = message_callbacks and message_callbacks[result_id]
+ local callback = self.message_callbacks[result_id]
if callback then
- message_callbacks[result_id] = nil
+ self.message_callbacks[result_id] = nil
validate('callback', callback, 'function')
if decoded.error then
- decoded.error = setmetatable(decoded.error, {
- __tostring = M.format_rpc_error,
- })
+ setmetatable(decoded.error, { __tostring = M.format_rpc_error })
end
self:try_call(
M.client_errors.SERVER_RESULT_CALLBACK_ERROR,
@@ -517,11 +497,6 @@ function Client:handle_body(body)
end
end
----@class (private) vim.lsp.rpc.Transport
----@field write fun(msg: string)
----@field is_closing fun(): boolean
----@field terminate fun()
-
---@param dispatchers vim.lsp.rpc.Dispatchers
---@param transport vim.lsp.rpc.Transport
---@return vim.lsp.rpc.Client
@@ -536,11 +511,20 @@ local function new_client(dispatchers, transport)
return setmetatable(state, { __index = Client })
end
----@class vim.lsp.rpc.PublicClient
----@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(message_id: integer)|nil):boolean,integer? see |vim.lsp.rpc.request()|
----@field notify fun(method: string, params: any):boolean see |vim.lsp.rpc.notify()|
----@field is_closing fun(): boolean
----@field terminate fun()
+--- Client RPC object
+--- @class vim.lsp.rpc.PublicClient
+---
+--- See [vim.lsp.rpc.request()]
+--- @field request fun(method: string, params: table?, callback: fun(err?: lsp.ResponseError, result: any), notify_reply_callback?: fun(message_id: integer)):boolean,integer?
+---
+--- See [vim.lsp.rpc.notify()]
+--- @field notify fun(method: string, params: any): boolean
+---
+--- Indicates if the RPC is closing.
+--- @field is_closing fun(): boolean
+---
+--- Terminates the RPC client.
+--- @field terminate fun()
---@param client vim.lsp.rpc.Client
---@return vim.lsp.rpc.PublicClient
@@ -551,20 +535,20 @@ local function public_client(client)
---@private
function result.is_closing()
- return client.transport.is_closing()
+ return client.transport:is_closing()
end
---@private
function result.terminate()
- client.transport.terminate()
+ client.transport:terminate()
end
--- Sends a request to the LSP server and runs {callback} upon response.
---
---@param method (string) The invoked LSP method
---@param params (table?) Parameters for the invoked LSP method
- ---@param callback fun(err: lsp.ResponseError|nil, result: any) Callback to invoke
- ---@param notify_reply_callback fun(message_id: integer)|nil Callback to invoke as soon as a request is no longer pending
+ ---@param callback fun(err: lsp.ResponseError?, result: any) Callback to invoke
+ ---@param notify_reply_callback? fun(message_id: integer) Callback to invoke as soon as a request is no longer pending
---@return boolean success `true` if request could be sent, `false` if not
---@return integer? message_id if request could be sent, `nil` if not
function result.request(method, params, callback, notify_reply_callback)
@@ -610,6 +594,21 @@ local function merge_dispatchers(dispatchers)
return merged
end
+--- @param client vim.lsp.rpc.Client
+--- @param on_exit? fun()
+local function create_client_read_loop(client, on_exit)
+ --- @param body string
+ local function handle_body(body)
+ client:handle_body(body)
+ end
+
+ local function on_error(err)
+ client:on_error(M.client_errors.READ_ERROR, err)
+ end
+
+ return M.create_read_loop(handle_body, on_exit, on_error)
+end
+
--- Create a LSP RPC client factory that connects to either:
---
--- - a named pipe (windows)
@@ -617,83 +616,26 @@ end
--- - a host and port via TCP
---
--- Return a function that can be passed to the `cmd` field for
---- |vim.lsp.start_client()| or |vim.lsp.start()|.
+--- |vim.lsp.start()|.
---
---@param host_or_path string host to connect to or path to a pipe/domain socket
---@param port integer? TCP port to connect to. If absent the first argument must be a pipe
---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient
function M.connect(host_or_path, port)
+ validate('host_or_path', host_or_path, 'string')
+ validate('port', port, 'number', true)
+
return function(dispatchers)
+ validate('dispatchers', dispatchers, 'table', true)
+
dispatchers = merge_dispatchers(dispatchers)
- local handle = (
- port == nil
- and assert(
- uv.new_pipe(false),
- string.format('Pipe with name %s could not be opened.', host_or_path)
- )
- or assert(uv.new_tcp(), 'Could not create new TCP socket')
- )
- local closing = false
- -- Connect returns a PublicClient synchronously so the caller
- -- can immediately send messages before the connection is established
- -- -> Need to buffer them until that happens
- local connected = false
- -- size should be enough because the client can't really do anything until initialization is done
- -- which required a response from the server - implying the connection got established
- local msgbuf = vim.ringbuf(10)
- local transport = {
- write = function(msg)
- if connected then
- local _, err = handle:write(msg)
- if err and not closing then
- log.error('Error on handle:write: %q', err)
- end
- else
- msgbuf:push(msg)
- end
- end,
- is_closing = function()
- return closing
- end,
- terminate = function()
- if not closing then
- closing = true
- handle:shutdown()
- handle:close()
- dispatchers.on_exit(0, 0)
- end
- end,
- }
+
+ local transport = lsp_transport.TransportConnect.new()
local client = new_client(dispatchers, transport)
- local function on_connect(err)
- if err then
- local address = port == nil and host_or_path or (host_or_path .. ':' .. port)
- vim.schedule(function()
- vim.notify(
- string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)),
- vim.log.levels.WARN
- )
- end)
- return
- end
- local handle_body = function(body)
- client:handle_body(body)
- end
- handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err)
- client:on_error(M.client_errors.READ_ERROR, read_err)
- end))
- connected = true
- for msg in msgbuf do
- handle:write(msg)
- end
- end
- if port == nil then
- handle:connect(host_or_path, on_connect)
- else
- local info = uv.getaddrinfo(host_or_path, nil)
- local resolved_host = info and info[1] and info[1].addr or host_or_path
- handle:connect(resolved_host, port, on_connect)
- end
+ local on_read = create_client_read_loop(client, function()
+ transport:terminate()
+ end)
+ transport:connect(host_or_path, port, on_read, dispatchers.on_exit)
return public_client(client)
end
@@ -713,83 +655,19 @@ end
--- @param cmd string[] Command to start the LSP server.
--- @param dispatchers? vim.lsp.rpc.Dispatchers
--- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams
---- @return vim.lsp.rpc.PublicClient : Client RPC object, with these methods:
---- - `notify()` |vim.lsp.rpc.notify()|
---- - `request()` |vim.lsp.rpc.request()|
---- - `is_closing()` returns a boolean indicating if the RPC is closing.
---- - `terminate()` terminates the RPC client.
+--- @return vim.lsp.rpc.PublicClient
function M.start(cmd, dispatchers, extra_spawn_params)
log.info('Starting RPC client', { cmd = cmd, extra = extra_spawn_params })
validate('cmd', cmd, 'table')
validate('dispatchers', dispatchers, 'table', true)
- extra_spawn_params = extra_spawn_params or {}
-
- if extra_spawn_params.cwd then
- assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory')
- end
-
dispatchers = merge_dispatchers(dispatchers)
- local sysobj ---@type vim.SystemObj
-
- local client = new_client(dispatchers, {
- write = function(msg)
- sysobj:write(msg)
- end,
- is_closing = function()
- return sysobj == nil or sysobj:is_closing()
- end,
- terminate = function()
- sysobj:kill(15)
- end,
- })
-
- local handle_body = function(body)
- client:handle_body(body)
- end
-
- local stdout_handler = M.create_read_loop(handle_body, nil, function(err)
- client:on_error(M.client_errors.READ_ERROR, err)
- end)
-
- local stderr_handler = function(_, chunk)
- if chunk then
- log.error('rpc', cmd[1], 'stderr', chunk)
- end
- end
-
- local detached = not is_win
- if extra_spawn_params.detached ~= nil then
- detached = extra_spawn_params.detached
- end
-
- local ok, sysobj_or_err = pcall(vim.system, cmd, {
- stdin = true,
- stdout = stdout_handler,
- stderr = stderr_handler,
- cwd = extra_spawn_params.cwd,
- env = extra_spawn_params.env,
- detach = detached,
- }, function(obj)
- dispatchers.on_exit(obj.code, obj.signal)
- end)
-
- if not ok then
- local err = sysobj_or_err --[[@as string]]
- local sfx --- @type string
- if string.match(err, 'ENOENT') then
- sfx = '. The language server is either not installed, missing from PATH, or not executable.'
- else
- sfx = string.format(' with error message: %s', err)
- end
- local msg =
- string.format('Spawning language server with cmd: `%s` failed%s', vim.inspect(cmd), sfx)
- error(msg)
- end
-
- sysobj = sysobj_or_err --[[@as vim.SystemObj]]
+ local transport = lsp_transport.TransportRun.new()
+ local client = new_client(dispatchers, transport)
+ local on_read = create_client_read_loop(client)
+ transport:run(cmd, extra_spawn_params, on_read, dispatchers.on_exit)
return public_client(client)
end
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index 215e5f41aa..dd8b654856 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -139,7 +139,7 @@ local function tokens_to_ranges(data, bufnr, client, request)
if token_type then
local modifiers = modifiers_from_number(data[i + 4], token_modifiers)
- local end_char = start_char + data[i + 2]
+ local end_char = start_char + data[i + 2] --- @type integer LuaLS bug
local buf_line = lines and lines[line + 1] or ''
local start_col = vim.str_byteindex(buf_line, encoding, start_char, false)
local end_col = vim.str_byteindex(buf_line, encoding, end_char, false)
@@ -166,7 +166,7 @@ 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.augroup = api.nvim_create_augroup('nvim.lsp.semantic_tokens:' .. bufnr, { clear = true })
self.client_state = {}
STHighlighter.active[bufnr] = self
@@ -225,7 +225,7 @@ 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),
+ namespace = api.nvim_create_namespace('nvim.lsp.semantic_tokens:' .. client_id),
active_request = {},
current_result = {},
}
@@ -273,7 +273,7 @@ function STHighlighter:send_request()
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)
+ client:cancel_request(active_request.request_id)
active_request = {}
state.active_request = active_request
end
@@ -288,7 +288,7 @@ function STHighlighter:send_request()
method = method .. '/delta'
params.previousResultId = current_result.result_id
end
- local success, request_id = client.request(method, params, function(err, response, ctx)
+ 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)
@@ -519,7 +519,7 @@ function STHighlighter:reset()
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)
+ client:cancel_request(state.active_request.request_id)
state.active_request = {}
end
end
@@ -547,7 +547,7 @@ function STHighlighter:mark_dirty(client_id)
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)
+ client:cancel_request(state.active_request.request_id)
state.active_request = {}
end
end
@@ -600,9 +600,7 @@ function M.start(bufnr, client_id, opts)
vim.validate('bufnr', bufnr, 'number')
vim.validate('client_id', client_id, 'number')
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
opts = opts or {}
assert(
@@ -655,9 +653,7 @@ function M.stop(bufnr, client_id)
vim.validate('bufnr', bufnr, 'number')
vim.validate('client_id', client_id, 'number')
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
local highlighter = STHighlighter.active[bufnr]
if not highlighter then
@@ -691,9 +687,7 @@ end
--- - modifiers (table) token modifiers as a set. E.g., { static = true, readonly = true }
--- - client_id (integer)
function M.get_at_pos(bufnr, row, col)
- if bufnr == nil or bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
local highlighter = STHighlighter.active[bufnr]
if not highlighter then
@@ -739,8 +733,7 @@ function M.force_refresh(bufnr)
vim.validate('bufnr', bufnr, 'number', true)
local buffers = bufnr == nil and vim.tbl_keys(STHighlighter.active)
- or bufnr == 0 and { api.nvim_get_current_buf() }
- or { bufnr }
+ or { vim._resolve_bufnr(bufnr) }
for _, buffer in ipairs(buffers) do
local highlighter = STHighlighter.active[buffer]
@@ -770,9 +763,7 @@ end
---@param hl_group (string) Highlight group name
---@param opts? vim.lsp.semantic_tokens.highlight_token.Opts Optional parameters:
function M.highlight_token(token, bufnr, client_id, hl_group, opts)
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
local highlighter = STHighlighter.active[bufnr]
if not highlighter then
return
@@ -814,7 +805,7 @@ function M._refresh(err, _, ctx)
return vim.NIL
end
-local namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens')
+local namespace = api.nvim_create_namespace('nvim.lsp.semantic_tokens')
api.nvim_set_decoration_provider(namespace, {
on_win = function(_, _, bufnr, topline, botline)
local highlighter = STHighlighter.active[bufnr]
diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua
index 3df45ebff0..621f63b25f 100644
--- a/runtime/lua/vim/lsp/sync.lua
+++ b/runtime/lua/vim/lsp/sync.lua
@@ -48,21 +48,21 @@ local str_utfindex = vim.str_utfindex
local str_utf_start = vim.str_utf_start
local str_utf_end = vim.str_utf_end
--- Given a line, byte idx, alignment, and offset_encoding convert to the aligned
+-- Given a line, byte idx, alignment, and position_encoding convert to the aligned
-- utf-8 index and either the utf-16, or utf-32 index.
---@param line string the line to index into
---@param byte integer the byte idx
----@param offset_encoding string utf-8|utf-16|utf-32|nil (default: utf-8)
+---@param position_encoding string utf-8|utf-16|utf-32|nil (default: utf-8)
---@return integer byte_idx of first change position
---@return integer char_idx of first change position
-local function align_end_position(line, byte, offset_encoding)
+local function align_end_position(line, byte, position_encoding)
local char --- @type integer
-- If on the first byte, or an empty string: the trivial case
if byte == 1 or #line == 0 then
char = byte
-- Called in the case of extending an empty line "" -> "a"
elseif byte == #line + 1 then
- char = str_utfindex(line, offset_encoding) + 1
+ char = str_utfindex(line, position_encoding) + 1
else
-- Modifying line, find the nearest utf codepoint
local offset = str_utf_start(line, byte)
@@ -73,9 +73,9 @@ local function align_end_position(line, byte, offset_encoding)
end
if byte <= #line then
--- Convert to 0 based for input, and from 0 based for output
- char = str_utfindex(line, offset_encoding, byte - 1) + 1
+ char = str_utfindex(line, position_encoding, byte - 1) + 1
else
- char = str_utfindex(line, offset_encoding) + 1
+ char = str_utfindex(line, position_encoding) + 1
end
-- Extending line, find the nearest utf codepoint for the last valid character
end
@@ -93,7 +93,7 @@ end
---@param firstline integer firstline from on_lines, adjusted to 1-index
---@param lastline integer lastline from on_lines, adjusted to 1-index
---@param new_lastline integer new_lastline from on_lines, adjusted to 1-index
----@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8)
+---@param position_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8)
---@return vim.lsp.sync.Range result table include line_idx, byte_idx, and char_idx of first change position
local function compute_start_range(
prev_lines,
@@ -101,7 +101,7 @@ local function compute_start_range(
firstline,
lastline,
new_lastline,
- offset_encoding
+ position_encoding
)
local char_idx --- @type integer?
local byte_idx --- @type integer?
@@ -115,7 +115,7 @@ local function compute_start_range(
if line then
line_idx = firstline - 1
byte_idx = #line + 1
- char_idx = str_utfindex(line, offset_encoding) + 1
+ char_idx = str_utfindex(line, position_encoding) + 1
else
line_idx = firstline
byte_idx = 1
@@ -152,11 +152,11 @@ local function compute_start_range(
char_idx = 1
elseif start_byte_idx == #prev_line + 1 then
byte_idx = start_byte_idx
- char_idx = str_utfindex(prev_line, offset_encoding) + 1
+ char_idx = str_utfindex(prev_line, position_encoding) + 1
else
byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx)
--- Convert to 0 based for input, and from 0 based for output
- char_idx = vim.str_utfindex(prev_line, offset_encoding, byte_idx - 1) + 1
+ char_idx = vim.str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1
end
-- Return the start difference (shared for new and prev lines)
@@ -174,7 +174,7 @@ end
---@param firstline integer
---@param lastline integer
---@param new_lastline integer
----@param offset_encoding string
+---@param position_encoding string
---@return vim.lsp.sync.Range prev_end_range
---@return vim.lsp.sync.Range curr_end_range
local function compute_end_range(
@@ -184,7 +184,7 @@ local function compute_end_range(
firstline,
lastline,
new_lastline,
- offset_encoding
+ position_encoding
)
-- A special case for the following `firstline == new_lastline` case where lines are deleted.
-- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol.
@@ -193,7 +193,7 @@ local function compute_end_range(
return {
line_idx = lastline - 1,
byte_idx = #prev_line + 1,
- char_idx = str_utfindex(prev_line, offset_encoding) + 1,
+ char_idx = str_utfindex(prev_line, position_encoding) + 1,
}, { line_idx = 1, byte_idx = 1, char_idx = 1 }
end
-- If firstline == new_lastline, the first change occurred on a line that was deleted.
@@ -259,7 +259,7 @@ local function compute_end_range(
prev_end_byte_idx = 1
end
local prev_byte_idx, prev_char_idx =
- align_end_position(prev_line, prev_end_byte_idx, offset_encoding)
+ align_end_position(prev_line, prev_end_byte_idx, position_encoding)
local prev_end_range =
{ line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx }
@@ -274,7 +274,7 @@ local function compute_end_range(
curr_end_byte_idx = 1
end
local curr_byte_idx, curr_char_idx =
- align_end_position(curr_line, curr_end_byte_idx, offset_encoding)
+ align_end_position(curr_line, curr_end_byte_idx, position_encoding)
curr_end_range =
{ line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx }
end
@@ -317,7 +317,7 @@ local function extract_text(lines, start_range, end_range, line_ending)
end
end
--- rangelength depends on the offset encoding
+-- rangelength depends on the position encoding
-- bytes for utf-8 (clangd with extension)
-- codepoints for utf-16
-- codeunits for utf-32
@@ -326,10 +326,10 @@ end
---@param lines string[]
---@param start_range vim.lsp.sync.Range
---@param end_range vim.lsp.sync.Range
----@param offset_encoding string
+---@param position_encoding string
---@param line_ending string
---@return integer
-local function compute_range_length(lines, start_range, end_range, offset_encoding, line_ending)
+local function compute_range_length(lines, start_range, end_range, position_encoding, line_ending)
local line_ending_length = #line_ending
-- Single line case
if start_range.line_idx == end_range.line_idx then
@@ -339,7 +339,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi
local start_line = lines[start_range.line_idx]
local range_length --- @type integer
if start_line and #start_line > 0 then
- range_length = str_utfindex(start_line, offset_encoding)
+ range_length = str_utfindex(start_line, position_encoding)
- start_range.char_idx
+ 1
+ line_ending_length
@@ -352,7 +352,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi
for idx = start_range.line_idx + 1, end_range.line_idx - 1 do
-- Length full line plus newline character
if #lines[idx] > 0 then
- range_length = range_length + str_utfindex(lines[idx], offset_encoding) + #line_ending
+ range_length = range_length + str_utfindex(lines[idx], position_encoding) + #line_ending
else
range_length = range_length + line_ending_length
end
@@ -372,7 +372,7 @@ end
---@param firstline integer line to begin search for first difference
---@param lastline integer line to begin search in old_lines for last difference
---@param new_lastline integer line to begin search in new_lines for last difference
----@param offset_encoding string encoding requested by language server
+---@param position_encoding string encoding requested by language server
---@param line_ending string
---@return lsp.TextDocumentContentChangeEvent : see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent
function M.compute_diff(
@@ -381,7 +381,7 @@ function M.compute_diff(
firstline,
lastline,
new_lastline,
- offset_encoding,
+ position_encoding,
line_ending
)
-- Find the start of changes between the previous and current buffer. Common between both.
@@ -393,7 +393,7 @@ function M.compute_diff(
firstline + 1,
lastline + 1,
new_lastline + 1,
- offset_encoding
+ position_encoding
)
-- Find the last position changed in the previous and current buffer.
-- prev_end_range is sent to the server as as the end of the changed range.
@@ -405,7 +405,7 @@ function M.compute_diff(
firstline + 1,
lastline + 1,
new_lastline + 1,
- offset_encoding
+ position_encoding
)
-- Grab the changed text of from start_range to curr_end_range in the current buffer.
@@ -414,7 +414,7 @@ function M.compute_diff(
-- Compute the range of the replaced text. Deprecated but still required for certain language servers
local range_length =
- compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending)
+ compute_range_length(prev_lines, start_range, prev_end_range, position_encoding, line_ending)
-- convert to 0 based indexing
local result = {
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 6eab0f3da4..e16a905c44 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -49,7 +49,8 @@ local function get_border_size(opts)
if not border_size[border] then
border_error(border)
end
- return unpack(border_size[border])
+ local r = border_size[border]
+ return r[1], r[2]
end
if 8 % #border ~= 0 then
@@ -192,9 +193,7 @@ local function get_lines(bufnr, rows)
rows = type(rows) == 'table' and rows or { rows }
-- This is needed for bufload and bufloaded
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
local function buf_lines()
local lines = {} --- @type table<integer,string>
@@ -277,9 +276,9 @@ end
--- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position
---@param position lsp.Position
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
---@return integer
-local function get_line_byte_from_position(bufnr, position, offset_encoding)
+local function get_line_byte_from_position(bufnr, position, position_encoding)
-- LSP's line and characters are 0-indexed
-- Vim's line and columns are 1-indexed
local col = position.character
@@ -287,7 +286,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding)
-- character
if col > 0 then
local line = get_line(bufnr, position.line) or ''
- return vim.str_byteindex(line, offset_encoding, col, false)
+ return vim.str_byteindex(line, position_encoding, col, false)
end
return col
end
@@ -295,12 +294,12 @@ end
--- Applies a list of text edits to a buffer.
---@param text_edits lsp.TextEdit[]
---@param bufnr integer Buffer id
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit
-function M.apply_text_edits(text_edits, bufnr, offset_encoding)
+function M.apply_text_edits(text_edits, bufnr, position_encoding)
validate('text_edits', text_edits, 'table', false)
validate('bufnr', bufnr, 'number', false)
- validate('offset_encoding', offset_encoding, 'string', false)
+ validate('position_encoding', position_encoding, 'string', false)
if not next(text_edits) then
return
@@ -359,9 +358,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
-- Convert from LSP style ranges to Neovim style ranges.
local start_row = text_edit.range.start.line
- local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding)
+ local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, position_encoding)
local end_row = text_edit.range['end'].line
- local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding)
+ local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], position_encoding)
local text = vim.split(text_edit.newText, '\n', { plain = true })
local max = api.nvim_buf_line_count(bufnr)
@@ -430,14 +429,14 @@ end
---
---@param text_document_edit lsp.TextDocumentEdit
---@param index? integer: Optional index of the edit, if from a list of edits (or nil, if not from a list)
----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'
+---@param position_encoding? 'utf-8'|'utf-16'|'utf-32'
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit
-function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
+function M.apply_text_document_edit(text_document_edit, index, position_encoding)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
- if offset_encoding == nil then
+ if position_encoding == nil then
vim.notify_once(
- 'apply_text_document_edit must be called with valid offset encoding',
+ 'apply_text_document_edit must be called with valid position encoding',
vim.log.levels.WARN
)
return
@@ -459,7 +458,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding)
return
end
- M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding)
+ M.apply_text_edits(text_document_edit.edits, bufnr, position_encoding)
end
local function path_components(path)
@@ -619,12 +618,12 @@ end
--- Applies a `WorkspaceEdit`.
---
---@param workspace_edit lsp.WorkspaceEdit
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' (required)
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32' (required)
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit
-function M.apply_workspace_edit(workspace_edit, offset_encoding)
- if offset_encoding == nil then
+function M.apply_workspace_edit(workspace_edit, position_encoding)
+ if position_encoding == nil then
vim.notify_once(
- 'apply_workspace_edit must be called with valid offset encoding',
+ 'apply_workspace_edit must be called with valid position encoding',
vim.log.levels.WARN
)
return
@@ -641,7 +640,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding)
elseif change.kind then --- @diagnostic disable-line:undefined-field
error(string.format('Unsupported change: %q', vim.inspect(change)))
else
- M.apply_text_document_edit(change, idx, offset_encoding)
+ M.apply_text_document_edit(change, idx, position_encoding)
end
end
return
@@ -654,7 +653,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding)
for uri, changes in pairs(all_changes) do
local bufnr = vim.uri_to_bufnr(uri)
- M.apply_text_edits(changes, bufnr, offset_encoding)
+ M.apply_text_edits(changes, bufnr, position_encoding)
end
end
@@ -877,15 +876,16 @@ function M.make_floating_popup_options(width, height, opts)
return {
anchor = anchor,
+ row = row + (opts.offset_y or 0),
col = col + (opts.offset_x or 0),
height = height,
focusable = opts.focusable,
- relative = opts.relative == 'mouse' and 'mouse' or 'cursor',
- row = row + (opts.offset_y or 0),
+ relative = (opts.relative == 'mouse' or opts.relative == 'editor') and opts.relative
+ or 'cursor',
style = 'minimal',
width = width,
border = opts.border or default_border,
- zindex = opts.zindex or 50,
+ zindex = opts.zindex or (api.nvim_win_get_config(0).zindex or 49) + 1,
title = title,
title_pos = title_pos,
}
@@ -904,17 +904,20 @@ end
--- Shows document and optionally jumps to the location.
---
---@param location lsp.Location|lsp.LocationLink
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'?
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'?
---@param opts? vim.lsp.util.show_document.Opts
---@return boolean `true` if succeeded
-function M.show_document(location, offset_encoding, opts)
+function M.show_document(location, position_encoding, opts)
-- location may be Location or LocationLink
local uri = location.uri or location.targetUri
if uri == nil then
return false
end
- if offset_encoding == nil then
- vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN)
+ if position_encoding == nil then
+ vim.notify_once(
+ 'show_document must be called with valid position encoding',
+ vim.log.levels.WARN
+ )
return false
end
local bufnr = vim.uri_to_bufnr(uri)
@@ -946,7 +949,7 @@ function M.show_document(location, offset_encoding, opts)
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)
+ local col = get_line_byte_from_position(bufnr, range.start, position_encoding)
api.nvim_win_set_cursor(win, { row + 1, col })
vim._with({ win = win }, function()
-- Open folds under the cursor
@@ -961,12 +964,12 @@ end
---
---@deprecated use `vim.lsp.util.show_document` with `{focus=true}` instead
---@param location lsp.Location|lsp.LocationLink
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'?
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'?
---@param reuse_win boolean? 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)
+function M.jump_to_location(location, position_encoding, reuse_win)
vim.deprecate('vim.lsp.util.jump_to_location', nil, '0.12')
- return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true })
+ return M.show_document(location, position_encoding, { reuse_win = reuse_win, focus = true })
end
--- Previews a location in a floating window
@@ -1355,7 +1358,7 @@ end
---@param bufnrs table list of buffers where the preview window will remain visible
---@see autocmd-events
local function close_preview_autocmd(events, winnr, bufnrs)
- local augroup = api.nvim_create_augroup('preview_window_' .. winnr, {
+ local augroup = api.nvim_create_augroup('nvim.preview_window_' .. winnr, {
clear = true,
})
@@ -1430,7 +1433,7 @@ function M._make_floating_popup_size(contents, opts)
if vim.tbl_isempty(line_widths) then
for _, line in ipairs(contents) do
local line_width = vim.fn.strdisplaywidth(line:gsub('%z', '\n'))
- height = height + math.ceil(line_width / wrap_at)
+ height = height + math.max(1, math.ceil(line_width / wrap_at))
end
else
for i = 1, #contents do
@@ -1493,7 +1496,7 @@ end
--- @field title_pos? 'left'|'center'|'right'
---
--- (default: `'cursor'`)
---- @field relative? 'mouse'|'cursor'
+--- @field relative? 'mouse'|'cursor'|'editor'
---
--- - "auto": place window based on which side of the cursor has more lines
--- - "above": place the window above the cursor unless there are not enough lines
@@ -1566,8 +1569,6 @@ function M.open_floating_preview(contents, syntax, opts)
if do_stylize then
local width = M._make_floating_popup_size(contents, opts)
contents = M._normalize_markdown(contents, { width = width })
- vim.bo[floating_bufnr].filetype = 'markdown'
- vim.treesitter.start(floating_bufnr)
else
-- Clean up input: trim empty lines
contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true })
@@ -1617,9 +1618,22 @@ function M.open_floating_preview(contents, syntax, opts)
api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr)
end
- if do_stylize then
- vim.wo[floating_winnr].conceallevel = 2
+ local augroup_name = ('nvim.closing_floating_preview_%d'):format(floating_winnr)
+ local ok =
+ pcall(api.nvim_get_autocmds, { group = augroup_name, pattern = tostring(floating_winnr) })
+ if not ok then
+ api.nvim_create_autocmd('WinClosed', {
+ group = api.nvim_create_augroup(augroup_name, {}),
+ pattern = tostring(floating_winnr),
+ callback = function()
+ if api.nvim_buf_is_valid(bufnr) then
+ vim.b[bufnr].lsp_floating_preview = nil
+ end
+ api.nvim_del_augroup_by_name(augroup_name)
+ end,
+ })
end
+
vim.wo[floating_winnr].foldenable = false -- Disable folding.
vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping.
vim.wo[floating_winnr].breakindent = true -- Slightly better list presentation.
@@ -1628,11 +1642,17 @@ function M.open_floating_preview(contents, syntax, opts)
vim.bo[floating_bufnr].modifiable = false
vim.bo[floating_bufnr].bufhidden = 'wipe'
+ if do_stylize then
+ vim.wo[floating_winnr].conceallevel = 2
+ vim.bo[floating_bufnr].filetype = 'markdown'
+ vim.treesitter.start(floating_bufnr)
+ end
+
return floating_bufnr, floating_winnr
end
do --[[ References ]]
- local reference_ns = api.nvim_create_namespace('vim_lsp_references')
+ local reference_ns = api.nvim_create_namespace('nvim.lsp.references')
--- Removes document highlights from a buffer.
---
@@ -1645,18 +1665,18 @@ do --[[ References ]]
---
---@param bufnr integer Buffer id
---@param references lsp.DocumentHighlight[] objects to highlight
- ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
+ ---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent
- function M.buf_highlight_references(bufnr, references, offset_encoding)
+ function M.buf_highlight_references(bufnr, references, position_encoding)
validate('bufnr', bufnr, 'number', true)
- validate('offset_encoding', offset_encoding, 'string', false)
+ validate('position_encoding', position_encoding, 'string', false)
for _, reference in ipairs(references) do
local range = reference.range
local start_line = range.start.line
local end_line = range['end'].line
- local start_idx = get_line_byte_from_position(bufnr, range.start, offset_encoding)
- local end_idx = get_line_byte_from_position(bufnr, range['end'], offset_encoding)
+ local start_idx = get_line_byte_from_position(bufnr, range.start, position_encoding)
+ local end_idx = get_line_byte_from_position(bufnr, range['end'], position_encoding)
local document_highlight_kind = {
[protocol.DocumentHighlightKind.Text] = 'LspReferenceText',
@@ -1690,16 +1710,16 @@ end)
--- |setloclist()|.
---
---@param locations lsp.Location[]|lsp.LocationLink[]
----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'
+---@param position_encoding? 'utf-8'|'utf-16'|'utf-32'
--- default to first client of buffer
---@return vim.quickfix.entry[] # See |setqflist()| for the format
-function M.locations_to_items(locations, offset_encoding)
- if offset_encoding == nil then
+function M.locations_to_items(locations, position_encoding)
+ if position_encoding == nil then
vim.notify_once(
- 'locations_to_items must be called with valid offset encoding',
+ 'locations_to_items must be called with valid position encoding',
vim.log.levels.WARN
)
- offset_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding
+ position_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding
end
local items = {} --- @type vim.quickfix.entry[]
@@ -1736,8 +1756,8 @@ function M.locations_to_items(locations, offset_encoding)
local end_row = end_pos.line
local line = lines[row] or ''
local end_line = lines[end_row] or ''
- local col = vim.str_byteindex(line, offset_encoding, pos.character, false)
- local end_col = vim.str_byteindex(end_line, offset_encoding, end_pos.character, false)
+ local col = vim.str_byteindex(line, position_encoding, pos.character, false)
+ local end_col = vim.str_byteindex(end_line, position_encoding, end_pos.character, false)
items[#items + 1] = {
filename = filename,
@@ -1848,19 +1868,18 @@ function M.try_trim_markdown_code_blocks(lines)
end
---@param window integer?: window handle or 0 for current, defaults to current
----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window`
-local function make_position_param(window, offset_encoding)
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
+local function make_position_param(window, position_encoding)
window = window or 0
local buf = api.nvim_win_get_buf(window)
local row, col = unpack(api.nvim_win_get_cursor(window))
- offset_encoding = offset_encoding or M._get_offset_encoding(buf)
row = row - 1
local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
if not line then
return { line = 0, character = 0 }
end
- col = vim.str_utfindex(line, offset_encoding, col, false)
+ col = vim.str_utfindex(line, position_encoding, col, false)
return { line = row, character = col }
end
@@ -1868,20 +1887,28 @@ end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
---
---@param window integer?: window handle or 0 for current, defaults to current
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window`
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
---@return lsp.TextDocumentPositionParams
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
-function M.make_position_params(window, offset_encoding)
+function M.make_position_params(window, position_encoding)
window = window or 0
local buf = api.nvim_win_get_buf(window)
- offset_encoding = offset_encoding or M._get_offset_encoding(buf)
+ if position_encoding == nil then
+ vim.notify_once(
+ 'position_encoding param is required in vim.lsp.util.make_position_params. Defaulting to position encoding of the first client.',
+ vim.log.levels.WARN
+ )
+ --- @diagnostic disable-next-line: deprecated
+ position_encoding = M._get_offset_encoding(buf)
+ end
return {
textDocument = M.make_text_document_params(buf),
- position = make_position_param(window, offset_encoding),
+ position = make_position_param(window, position_encoding),
}
end
--- Utility function for getting the encoding of the first LSP client on the given buffer.
+---@deprecated
---@param bufnr integer buffer handle or 0 for current, defaults to current
---@return string encoding first client if there is one, nil otherwise
function M._get_offset_encoding(bufnr)
@@ -1904,7 +1931,7 @@ function M._get_offset_encoding(bufnr)
offset_encoding = this_offset_encoding
elseif offset_encoding ~= this_offset_encoding then
vim.notify_once(
- 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet',
+ 'warning: multiple different client offset_encodings detected for buffer, vim.lsp.util._get_offset_encoding() uses the offset_encoding from the first client',
vim.log.levels.WARN
)
end
@@ -1919,13 +1946,19 @@ end
--- `textDocument/rangeFormatting`.
---
---@param window integer? window handle or 0 for current, defaults to current
----@param offset_encoding "utf-8"|"utf-16"|"utf-32"? defaults to `offset_encoding` of first client of buffer of `window`
----@return table { textDocument = { uri = `current_file_uri` }, range = { start =
----`current_position`, end = `current_position` } }
-function M.make_range_params(window, offset_encoding)
+---@param position_encoding "utf-8"|"utf-16"|"utf-32"
+---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }
+function M.make_range_params(window, position_encoding)
local buf = api.nvim_win_get_buf(window or 0)
- offset_encoding = offset_encoding or M._get_offset_encoding(buf)
- local position = make_position_param(window, offset_encoding)
+ if position_encoding == nil then
+ vim.notify_once(
+ 'position_encoding param is required in vim.lsp.util.make_range_params. Defaulting to position encoding of the first client.',
+ vim.log.levels.WARN
+ )
+ --- @diagnostic disable-next-line: deprecated
+ position_encoding = M._get_offset_encoding(buf)
+ end
+ local position = make_position_param(window, position_encoding)
return {
textDocument = M.make_text_document_params(buf),
range = { start = position, ['end'] = position },
@@ -1940,15 +1973,21 @@ end
---@param end_pos [integer,integer]? {row,col} mark-indexed position.
--- Defaults to the end of the last visual selection.
---@param bufnr integer? buffer handle or 0 for current, defaults to current
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of `bufnr`
----@return table { textDocument = { uri = `current_file_uri` }, range = { start =
----`start_position`, end = `end_position` } }
-function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
+---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }
+function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding)
validate('start_pos', start_pos, 'table', true)
validate('end_pos', end_pos, 'table', true)
- validate('offset_encoding', offset_encoding, 'string', true)
- bufnr = bufnr or api.nvim_get_current_buf()
- offset_encoding = offset_encoding or M._get_offset_encoding(bufnr)
+ validate('position_encoding', position_encoding, 'string', true)
+ bufnr = vim._resolve_bufnr(bufnr)
+ if position_encoding == nil then
+ vim.notify_once(
+ 'position_encoding param is required in vim.lsp.util.make_given_range_params. Defaulting to position encoding of the first client.',
+ vim.log.levels.WARN
+ )
+ --- @diagnostic disable-next-line: deprecated
+ position_encoding = M._get_offset_encoding(bufnr)
+ end
--- @type [integer, integer]
local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) }
--- @type [integer, integer]
@@ -1956,12 +1995,12 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding)
-- convert to 0-index
A[1] = A[1] - 1
B[1] = B[1] - 1
- -- account for offset_encoding.
+ -- account for position_encoding.
if A[2] > 0 then
- A[2] = M.character_offset(bufnr, A[1], A[2], offset_encoding)
+ A[2] = M.character_offset(bufnr, A[1], A[2], position_encoding)
end
if B[2] > 0 then
- B[2] = M.character_offset(bufnr, B[1], B[2], offset_encoding)
+ B[2] = M.character_offset(bufnr, B[1], B[2], position_encoding)
end
-- we need to offset the end character position otherwise we loose the last
-- character of the selection, as LSP end position is exclusive
@@ -2068,9 +2107,9 @@ end
---@param bufnr integer
---@param start_line integer
---@param end_line integer
----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'
+---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
---@return lsp.Range
-local function make_line_range_params(bufnr, start_line, end_line, offset_encoding)
+local function make_line_range_params(bufnr, start_line, end_line, position_encoding)
local last_line = api.nvim_buf_line_count(bufnr) - 1
---@type lsp.Position
@@ -2079,7 +2118,12 @@ local function make_line_range_params(bufnr, start_line, end_line, offset_encodi
if end_line == last_line and not vim.bo[bufnr].endofline then
end_pos = {
line = end_line,
- character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding),
+ character = M.character_offset(
+ bufnr,
+ end_line,
+ #get_line(bufnr, end_line),
+ position_encoding
+ ),
}
else
end_pos = { line = end_line + 1, character = 0 }
@@ -2103,10 +2147,7 @@ end
---@param opts? vim.lsp.util._refresh.Opts Options table
function M._refresh(method, opts)
opts = opts or {}
- local bufnr = opts.bufnr
- if bufnr == nil or bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ local bufnr = vim._resolve_bufnr(opts.bufnr)
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method, id = opts.client_id })
@@ -2122,7 +2163,12 @@ function M._refresh(method, opts)
local first = vim.fn.line('w0', window)
local last = vim.fn.line('w$', window)
for _, client in ipairs(clients) do
- client.request(method, {
+ for rid, req in pairs(client.requests) do
+ if req.method == method and req.type == 'pending' and req.bufnr == bufnr then
+ client:cancel_request(rid)
+ end
+ end
+ client:request(method, {
textDocument = textDocument,
range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),
}, nil, bufnr)
@@ -2131,7 +2177,7 @@ function M._refresh(method, opts)
end
else
for _, client in ipairs(clients) do
- client.request(method, {
+ client:request(method, {
textDocument = textDocument,
range = make_line_range_params(
bufnr,
diff --git a/runtime/lua/vim/provider/health.lua b/runtime/lua/vim/provider/health.lua
index 5ecb00f49b..fa01951b02 100644
--- a/runtime/lua/vim/provider/health.lua
+++ b/runtime/lua/vim/provider/health.lua
@@ -10,22 +10,20 @@ end
-- Attempts to construct a shell command from an args list.
-- Only for display, to help users debug a failed command.
+--- @param cmd string|string[]
local function shellify(cmd)
if type(cmd) ~= 'table' then
return cmd
end
- local escaped = {}
+ local escaped = {} --- @type string[]
for i, v in ipairs(cmd) do
- if v:match('[^A-Za-z_/.-]') then
- escaped[i] = vim.fn.shellescape(v)
- else
- escaped[i] = v
- end
+ escaped[i] = v:match('[^A-Za-z_/.-]') and vim.fn.shellescape(v) or v
end
return table.concat(escaped, ' ')
end
-- Handler for s:system() function.
+--- @param self {output: string, stderr: string, add_stderr_to_output: boolean}
local function system_handler(self, _, data, event)
if event == 'stderr' then
if self.add_stderr_to_output then
@@ -38,7 +36,7 @@ local function system_handler(self, _, data, event)
end
end
---- @param cmd table List of command arguments to execute
+--- @param cmd string|string[] List of command arguments to execute
--- @param args? table Optional arguments:
--- - stdin (string): Data to write to the job's stdin
--- - stderr (boolean): Append stderr to stdout
@@ -47,8 +45,8 @@ end
local function system(cmd, args)
args = args or {}
local stdin = args.stdin or ''
- local stderr = vim.F.if_nil(args.stderr, false)
- local ignore_error = vim.F.if_nil(args.ignore_error, false)
+ local stderr = args.stderr or false
+ local ignore_error = args.ignore_error or false
local shell_error_code = 0
local opts = {
@@ -530,13 +528,14 @@ local function version_info(python)
if rc ~= 0 or nvim_version == '' then
nvim_version = 'unable to find pynvim module version'
local base = vim.fs.basename(nvim_path)
- local metas = vim.fn.glob(base .. '-*/METADATA', true, 1)
- vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, 1))
- vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, 1))
+ local metas = vim.fn.glob(base .. '-*/METADATA', true, true)
+ vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, true))
+ vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, true))
metas = table.sort(metas, compare)
if metas and next(metas) ~= nil then
for line in io.lines(metas[1]) do
+ --- @cast line string
local version = line:match('^Version: (%S+)')
if version then
nvim_version = version
@@ -762,6 +761,7 @@ local function python()
-- subshells launched from Nvim.
local bin_dir = iswin and 'Scripts' or 'bin'
local venv_bins = vim.fn.glob(string.format('%s/%s/python*', virtual_env, bin_dir), true, true)
+ --- @param v string
venv_bins = vim.tbl_filter(function(v)
-- XXX: Remove irrelevant executables found in bin/.
return not v:match('python.*%-config')
@@ -809,6 +809,7 @@ local function python()
msg,
bin_dir,
table.concat(
+ --- @param v string
vim.tbl_map(function(v)
return vim.fs.basename(v)
end, venv_bins),
@@ -817,12 +818,15 @@ local function python()
)
end
local conj = '\nBut '
+ local msgs = {} --- @type string[]
for _, err in ipairs(errors) do
- msg = msg .. conj .. err
+ msgs[#msgs + 1] = msg
+ msgs[#msgs + 1] = conj
+ msgs[#msgs + 1] = err
conj = '\nAnd '
end
- msg = msg .. '\nSo invoking Python may lead to unexpected results.'
- health.warn(msg, vim.tbl_keys(hints))
+ msgs[#msgs + 1] = '\nSo invoking Python may lead to unexpected results.'
+ health.warn(table.concat(msgs), vim.tbl_keys(hints))
else
health.info(msg)
health.info(
diff --git a/runtime/lua/vim/re.lua b/runtime/lua/vim/re.lua
index 114f74eb80..e0a36703e3 100644
--- a/runtime/lua/vim/re.lua
+++ b/runtime/lua/vim/re.lua
@@ -1,3 +1,4 @@
+--- @diagnostic disable: no-unknown
--
-- Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license)
-- written by Roberto Ierusalimschy
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 4f2373b182..f19533f474 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -7,8 +7,7 @@
-- so this wouldn't be a separate case to consider)
---@nodoc
----@diagnostic disable-next-line: lowercase-global
-vim = vim or {}
+_G.vim = _G.vim or {}
---@generic T
---@param orig T
@@ -737,6 +736,51 @@ function vim.list_slice(list, start, finish)
return new_list
end
+--- Efficiently insert items into the middle of a list.
+---
+--- Calling table.insert() in a loop will re-index the tail of the table on
+--- every iteration, instead this function will re-index the table exactly
+--- once.
+---
+--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
+---
+---@param t any[]
+---@param first integer
+---@param last integer
+---@param v any
+function vim._list_insert(t, first, last, v)
+ local n = #t
+
+ -- Shift table forward
+ for i = n - first, 0, -1 do
+ t[last + 1 + i] = t[first + i]
+ end
+
+ -- Fill in new values
+ for i = first, last do
+ t[i] = v
+ end
+end
+
+--- Efficiently remove items from middle of a list.
+---
+--- Calling table.remove() in a loop will re-index the tail of the table on
+--- every iteration, instead this function will re-index the table exactly
+--- once.
+---
+--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
+---
+---@param t any[]
+---@param first integer
+---@param last integer
+function vim._list_remove(t, first, last)
+ local n = #t
+ for i = 0, n - first do
+ t[first + i] = t[last + 1 + i]
+ t[last + 1 + i] = nil
+ end
+end
+
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
---
---@see |lua-patterns|
@@ -914,7 +958,7 @@ do
--- function vim.startswith(s, prefix)
--- vim.validate('s', s, 'string')
--- vim.validate('prefix', prefix, 'string')
- --- ...
+ --- -- ...
--- end
--- ```
---
@@ -934,7 +978,7 @@ do
--- age={age, 'number'},
--- hobbies={hobbies, 'table'},
--- }
- --- ...
+ --- -- ...
--- end
--- ```
---
@@ -968,7 +1012,7 @@ do
--- best performance.
---
--- @param name string Argument name
- --- @param value string Argument value
+ --- @param value any Argument value
--- @param validator vim.validate.Validator
--- - (`string|string[]`): Any value that can be returned from |lua-type()| in addition to
--- `'callable'`: `'boolean'`, `'callable'`, `'function'`, `'nil'`, `'number'`, `'string'`, `'table'`,
@@ -1354,4 +1398,24 @@ function vim._with(context, f)
return vim._with_c(context, callback)
end
+--- @param bufnr? integer
+--- @return integer
+function vim._resolve_bufnr(bufnr)
+ if bufnr == nil or bufnr == 0 then
+ return vim.api.nvim_get_current_buf()
+ end
+ vim.validate('bufnr', bufnr, 'number')
+ return bufnr
+end
+
+--- @generic T
+--- @param x elem_or_list<T>?
+--- @return T[]
+function vim._ensure_list(x)
+ if type(x) == 'table' then
+ return x
+ end
+ return { x }
+end
+
return vim
diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua
index af7e3c6d33..bfd439181e 100644
--- a/runtime/lua/vim/snippet.lua
+++ b/runtime/lua/vim/snippet.lua
@@ -1,6 +1,6 @@
local G = vim.lsp._snippet_grammar
-local snippet_group = vim.api.nvim_create_augroup('vim/snippet', {})
-local snippet_ns = vim.api.nvim_create_namespace('vim/snippet')
+local snippet_group = vim.api.nvim_create_augroup('nvim.snippet', {})
+local snippet_ns = vim.api.nvim_create_namespace('nvim.snippet')
local hl_group = 'SnippetTabstop'
local jump_forward_key = '<tab>'
local jump_backward_key = '<s-tab>'
diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua
index d45c8021c6..f910ab3a1d 100644
--- a/runtime/lua/vim/text.lua
+++ b/runtime/lua/vim/text.lua
@@ -2,6 +2,18 @@
local M = {}
+local alphabet = '0123456789ABCDEF'
+local atoi = {} ---@type table<string, integer>
+local itoa = {} ---@type table<integer, string>
+do
+ for i = 1, #alphabet do
+ local char = alphabet:sub(i, i)
+ itoa[i - 1] = char
+ atoi[char] = i - 1
+ atoi[char:lower()] = i - 1
+ end
+end
+
--- Hex encode a string.
---
--- @param str string String to encode
@@ -9,7 +21,9 @@ local M = {}
function M.hexencode(str)
local enc = {} ---@type string[]
for i = 1, #str do
- enc[i] = string.format('%02X', str:byte(i, i + 1))
+ local byte = str:byte(i)
+ enc[2 * i - 1] = itoa[math.floor(byte / 16)]
+ enc[2 * i] = itoa[byte % 16]
end
return table.concat(enc)
end
@@ -26,8 +40,12 @@ function M.hexdecode(enc)
local str = {} ---@type string[]
for i = 1, #enc, 2 do
- local n = assert(tonumber(enc:sub(i, i + 1), 16))
- str[#str + 1] = string.char(n)
+ local u = atoi[enc:sub(i, i)]
+ local l = atoi[enc:sub(i + 1, i + 1)]
+ if not u or not l then
+ return nil, 'string must contain only hex characters'
+ end
+ str[(i + 1) / 2] = string.char(u * 16 + l)
end
return table.concat(str), nil
end
diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua
index dca89f413c..10638e10d8 100644
--- a/runtime/lua/vim/treesitter.lua
+++ b/runtime/lua/vim/treesitter.lua
@@ -32,9 +32,7 @@ M.minimum_language_version = vim._ts_get_minimum_language_version()
---
---@return vim.treesitter.LanguageTree object to use for parsing
function M._create_parser(bufnr, lang, opts)
- if bufnr == 0 then
- bufnr = vim.api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
vim.fn.bufload(bufnr)
@@ -63,8 +61,6 @@ function M._create_parser(bufnr, lang, opts)
{ on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true }
)
- self:parse()
-
return self
end
@@ -90,9 +86,7 @@ function M.get_parser(bufnr, lang, opts)
opts = opts or {}
local should_error = opts.error == nil or opts.error
- if bufnr == nil or bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
if not valid_lang(lang) then
lang = M.language.get_lang(vim.bo[bufnr].filetype)
@@ -155,7 +149,7 @@ end
--- Returns the node's range or an unpacked range table
---
----@param node_or_range (TSNode | table) Node or table of positions
+---@param node_or_range TSNode|Range4 Node or table of positions
---
---@return integer start_row
---@return integer start_col
@@ -163,7 +157,8 @@ end
---@return integer end_col
function M.get_node_range(node_or_range)
if type(node_or_range) == 'table' then
- return unpack(node_or_range)
+ --- @cast node_or_range -TSNode LuaLS bug
+ return M._range.unpack4(node_or_range)
else
return node_or_range:range(false)
end
@@ -244,23 +239,24 @@ function M.node_contains(node, range)
-- allow a table so nodes can be mocked
vim.validate('node', node, { 'userdata', 'table' })
vim.validate('range', range, M._range.validate, 'integer list with 4 or 6 elements')
- return M._range.contains({ node:range() }, range)
+ --- @diagnostic disable-next-line: missing-fields LuaLS bug
+ local nrange = { node:range() } --- @type Range4
+ return M._range.contains(nrange, range)
end
--- Returns a list of highlight captures at the given position
---
---- Each capture is represented by a table containing the capture name as a string as
---- well as a table of metadata (`priority`, `conceal`, ...; empty if none are defined).
+--- Each capture is represented by a table containing the capture name as a string, the capture's
+--- language, a table of metadata (`priority`, `conceal`, ...; empty if none are defined), and the
+--- id of the capture.
---
---@param bufnr integer Buffer number (0 for current buffer)
---@param row integer Position row
---@param col integer Position column
---
----@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[]
+---@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata, id: integer}[]
function M.get_captures_at_pos(bufnr, row, col)
- if bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ bufnr = vim._resolve_bufnr(bufnr)
local buf_highlighter = M.highlighter.active[bufnr]
if not buf_highlighter then
@@ -291,12 +287,15 @@ function M.get_captures_at_pos(bufnr, row, col)
local iter = q:query():iter_captures(root, buf_highlighter.bufnr, row, row + 1)
- for capture, node, metadata in iter do
+ for id, node, metadata in iter do
if M.is_in_node_range(node, row, col) then
---@diagnostic disable-next-line: invisible
- local c = q._query.captures[capture] -- name of the capture in the query
- if c ~= nil then
- table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() })
+ local capture = q._query.captures[id] -- name of the capture in the query
+ if capture ~= nil then
+ table.insert(
+ matches,
+ { capture = capture, metadata = metadata, lang = tree:lang(), id = id }
+ )
end
end
end
@@ -361,11 +360,7 @@ end
function M.get_node(opts)
opts = opts or {}
- local bufnr = opts.bufnr
-
- if not bufnr or bufnr == 0 then
- bufnr = api.nvim_get_current_buf()
- end
+ local bufnr = vim._resolve_bufnr(opts.bufnr)
local row, col --- @type integer, integer
if opts.pos then
@@ -403,6 +398,8 @@ end
--- 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`.
---
+--- Note: By default, the highlighter parses code asynchronously, using a segment time of 3ms.
+---
--- Example:
---
--- ```lua
@@ -414,10 +411,10 @@ end
--- })
--- ```
---
----@param bufnr (integer|nil) Buffer to be highlighted (default: current buffer)
----@param lang (string|nil) Language of the parser (default: from buffer filetype)
+---@param bufnr integer? Buffer to be highlighted (default: current buffer)
+---@param lang string? Language of the parser (default: from buffer filetype)
function M.start(bufnr, lang)
- bufnr = bufnr or api.nvim_get_current_buf()
+ bufnr = vim._resolve_bufnr(bufnr)
local parser = assert(M.get_parser(bufnr, lang, { error = false }))
M.highlighter.new(parser)
end
@@ -426,7 +423,7 @@ end
---
---@param bufnr (integer|nil) Buffer to stop highlighting (default: current buffer)
function M.stop(bufnr)
- bufnr = (bufnr and bufnr ~= 0) and bufnr or api.nvim_get_current_buf()
+ bufnr = vim._resolve_bufnr(bufnr)
if M.highlighter.active[bufnr] then
M.highlighter.active[bufnr]:destroy()
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 7237d2e7d4..38318347a7 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -19,76 +19,36 @@ local api = vim.api
---The range on which to evaluate foldexpr.
---When in insert mode, the evaluation is deferred to InsertLeave.
---@field foldupdate_range? Range2
+---
+---The treesitter parser associated with this buffer.
+---@field parser? vim.treesitter.LanguageTree
local FoldInfo = {}
FoldInfo.__index = FoldInfo
---@private
-function FoldInfo.new()
+---@param bufnr integer
+function FoldInfo.new(bufnr)
return setmetatable({
levels0 = {},
levels = {},
+ parser = ts.get_parser(bufnr, nil, { error = false }),
}, FoldInfo)
end
---- Efficiently remove items from middle of a list a list.
----
---- Calling table.remove() in a loop will re-index the tail of the table on
---- every iteration, instead this function will re-index the table exactly
---- once.
----
---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
----
----@param t any[]
----@param first integer
----@param last integer
-local function list_remove(t, first, last)
- local n = #t
- for i = 0, n - first do
- t[first + i] = t[last + 1 + i]
- t[last + 1 + i] = nil
- end
-end
-
---@package
---@param srow integer
---@param erow integer 0-indexed, exclusive
function FoldInfo:remove_range(srow, erow)
- list_remove(self.levels, srow + 1, erow)
- list_remove(self.levels0, srow + 1, erow)
-end
-
---- Efficiently insert items into the middle of a list.
----
---- Calling table.insert() in a loop will re-index the tail of the table on
---- every iteration, instead this function will re-index the table exactly
---- once.
----
---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
----
----@param t any[]
----@param first integer
----@param last integer
----@param v any
-local function list_insert(t, first, last, v)
- local n = #t
-
- -- Shift table forward
- for i = n - first, 0, -1 do
- t[last + 1 + i] = t[first + i]
- end
-
- -- Fill in new values
- for i = first, last do
- t[i] = v
- end
+ vim._list_remove(self.levels, srow + 1, erow)
+ vim._list_remove(self.levels0, srow + 1, erow)
end
---@package
---@param srow integer
---@param erow integer 0-indexed, exclusive
function FoldInfo:add_range(srow, erow)
- list_insert(self.levels, srow + 1, erow, -1)
- list_insert(self.levels0, srow + 1, erow, -1)
+ vim._list_insert(self.levels, srow + 1, erow, -1)
+ vim._list_insert(self.levels0, srow + 1, erow, -1)
end
---@param range Range2
@@ -109,111 +69,122 @@ end
---@param info TS.FoldInfo
---@param srow integer?
---@param erow integer? 0-indexed, exclusive
----@param parse_injections? boolean
-local function compute_folds_levels(bufnr, info, srow, erow, parse_injections)
+---@param callback function?
+local function compute_folds_levels(bufnr, info, srow, erow, callback)
srow = srow or 0
erow = erow or api.nvim_buf_line_count(bufnr)
- local parser = assert(ts.get_parser(bufnr, nil, { error = false }))
-
- parser:parse(parse_injections and { srow, erow } or nil)
-
- local enter_counts = {} ---@type table<integer, integer>
- local leave_counts = {} ---@type table<integer, integer>
- local prev_start = -1
- local prev_stop = -1
+ local parser = info.parser
+ if not parser then
+ return
+ end
- parser:for_each_tree(function(tree, ltree)
- local query = ts.query.get(ltree:lang(), 'folds')
- if not query then
+ parser:parse(nil, function(_, trees)
+ if not trees then
return
end
- -- Collect folds starting from srow - 1, because we should first subtract the folds that end at
- -- srow - 1 from the level of srow - 1 to get accurate level of srow.
- for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do
- for id, nodes in pairs(match) do
- if query.captures[id] == 'fold' then
- local range = ts.get_range(nodes[1], bufnr, metadata[id])
- local start, _, stop, stop_col = Range.unpack4(range)
-
- if #nodes > 1 then
- -- assumes nodes are ordered by range
- local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id])
- local _, _, end_stop, end_stop_col = Range.unpack4(end_range)
- stop = end_stop
- stop_col = end_stop_col
- end
+ local enter_counts = {} ---@type table<integer, integer>
+ local leave_counts = {} ---@type table<integer, integer>
+ local prev_start = -1
+ local prev_stop = -1
- if stop_col == 0 then
- stop = stop - 1
- end
+ parser:for_each_tree(function(tree, ltree)
+ local query = ts.query.get(ltree:lang(), 'folds')
+ if not query then
+ return
+ end
- local fold_length = stop - start + 1
-
- -- Fold only multiline nodes that are not exactly the same as previously met folds
- -- Checking against just the previously found fold is sufficient if nodes
- -- are returned in preorder or postorder when traversing tree
- if
- fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop)
- then
- enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1
- leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1
- prev_start = start
- prev_stop = stop
+ -- Collect folds starting from srow - 1, because we should first subtract the folds that end at
+ -- srow - 1 from the level of srow - 1 to get accurate level of srow.
+ for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do
+ for id, nodes in pairs(match) do
+ if query.captures[id] == 'fold' then
+ local range = ts.get_range(nodes[1], bufnr, metadata[id])
+ local start, _, stop, stop_col = Range.unpack4(range)
+
+ if #nodes > 1 then
+ -- assumes nodes are ordered by range
+ local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id])
+ local _, _, end_stop, end_stop_col = Range.unpack4(end_range)
+ stop = end_stop
+ stop_col = end_stop_col
+ end
+
+ if stop_col == 0 then
+ stop = stop - 1
+ end
+
+ local fold_length = stop - start + 1
+
+ -- Fold only multiline nodes that are not exactly the same as previously met folds
+ -- Checking against just the previously found fold is sufficient if nodes
+ -- are returned in preorder or postorder when traversing tree
+ if
+ fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop)
+ then
+ enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1
+ leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1
+ prev_start = start
+ prev_stop = stop
+ end
end
end
end
- end
- end)
+ end)
- local nestmax = vim.wo.foldnestmax
- local level0_prev = info.levels0[srow] or 0
- local leave_prev = leave_counts[srow] or 0
-
- -- We now have the list of fold opening and closing, fill the gaps and mark where fold start
- for lnum = srow + 1, erow do
- local enter_line = enter_counts[lnum] or 0
- local leave_line = leave_counts[lnum] or 0
- local level0 = level0_prev - leave_prev + enter_line
-
- -- Determine if it's the start/end of a fold
- -- NB: vim's fold-expr interface does not have a mechanism to indicate that
- -- two (or more) folds start at this line, so it cannot distinguish between
- -- ( \n ( \n )) \n (( \n ) \n )
- -- versus
- -- ( \n ( \n ) \n ( \n ) \n )
- -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and
- -- vim interprets as the second case.
- -- If it did have such a mechanism, (clamped - clamped_prev)
- -- would be the correct number of starts to pass on.
- local adjusted = level0 ---@type integer
- local prefix = ''
- if enter_line > 0 then
- prefix = '>'
- if leave_line > 0 then
- -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line
- -- so that f2 gets the correct level on this line. This may reduce the size of f1 below
- -- foldminlines, but we don't handle it for simplicity.
- adjusted = level0 - leave_line
- leave_line = 0
+ local nestmax = vim.wo.foldnestmax
+ local level0_prev = info.levels0[srow] or 0
+ local leave_prev = leave_counts[srow] or 0
+
+ -- We now have the list of fold opening and closing, fill the gaps and mark where fold start
+ for lnum = srow + 1, erow do
+ local enter_line = enter_counts[lnum] or 0
+ local leave_line = leave_counts[lnum] or 0
+ local level0 = level0_prev - leave_prev + enter_line
+
+ -- Determine if it's the start/end of a fold
+ -- NB: vim's fold-expr interface does not have a mechanism to indicate that
+ -- two (or more) folds start at this line, so it cannot distinguish between
+ -- ( \n ( \n )) \n (( \n ) \n )
+ -- versus
+ -- ( \n ( \n ) \n ( \n ) \n )
+ -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and
+ -- vim interprets as the second case.
+ -- If it did have such a mechanism, (clamped - clamped_prev)
+ -- would be the correct number of starts to pass on.
+ local adjusted = level0 ---@type integer
+ local prefix = ''
+ if enter_line > 0 then
+ prefix = '>'
+ if leave_line > 0 then
+ -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line
+ -- so that f2 gets the correct level on this line. This may reduce the size of f1 below
+ -- foldminlines, but we don't handle it for simplicity.
+ adjusted = level0 - leave_line
+ leave_line = 0
+ end
end
- end
- -- Clamp at foldnestmax.
- local clamped = adjusted
- if adjusted > nestmax then
- prefix = ''
- clamped = nestmax
- end
+ -- Clamp at foldnestmax.
+ local clamped = adjusted
+ if adjusted > nestmax then
+ prefix = ''
+ clamped = nestmax
+ end
- -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels().
- info.levels0[lnum] = adjusted
- info.levels[lnum] = prefix .. tostring(clamped)
+ -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels().
+ info.levels0[lnum] = adjusted
+ info.levels[lnum] = prefix .. tostring(clamped)
- leave_prev = leave_line
- level0_prev = adjusted
- end
+ leave_prev = leave_line
+ level0_prev = adjusted
+ end
+
+ if callback then
+ callback()
+ end
+ end)
end
local M = {}
@@ -221,7 +192,7 @@ local M = {}
---@type table<integer,TS.FoldInfo>
local foldinfos = {}
-local group = api.nvim_create_augroup('treesitter/fold', {})
+local group = api.nvim_create_augroup('nvim.treesitter.fold', {})
--- Update the folds in the windows that contain the buffer and use expr foldmethod (assuming that
--- the user doesn't use different foldexpr for the same buffer).
@@ -298,12 +269,19 @@ local function schedule_if_loaded(bufnr, fn)
end
---@param bufnr integer
----@param foldinfo TS.FoldInfo
---@param tree_changes Range4[]
-local function on_changedtree(bufnr, foldinfo, tree_changes)
+local function on_changedtree(bufnr, tree_changes)
schedule_if_loaded(bufnr, function()
+ -- Buffer reload clears `foldinfos[bufnr]`, which may still be nil when callback is invoked.
+ local foldinfo = foldinfos[bufnr]
+ if not foldinfo then
+ return
+ end
+
local srow_upd, erow_upd ---@type integer?, integer?
local max_erow = api.nvim_buf_line_count(bufnr)
+ -- TODO(ribru17): Replace this with a proper .all() awaiter once #19624 is resolved
+ local iterations = 0
for _, change in ipairs(tree_changes) do
local srow, _, erow, ecol = Range.unpack4(change)
-- If a parser doesn't have any ranges explicitly set, treesitter will
@@ -317,24 +295,31 @@ local function on_changedtree(bufnr, foldinfo, tree_changes)
end
-- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit.
srow = math.max(srow - vim.wo.foldminlines, 0)
- compute_folds_levels(bufnr, foldinfo, srow, erow)
srow_upd = srow_upd and math.min(srow_upd, srow) or srow
erow_upd = erow_upd and math.max(erow_upd, erow) or erow
- end
- if #tree_changes > 0 then
- foldinfo:foldupdate(bufnr, srow_upd, erow_upd)
+ compute_folds_levels(bufnr, foldinfo, srow, erow, function()
+ iterations = iterations + 1
+ if iterations == #tree_changes then
+ foldinfo:foldupdate(bufnr, srow_upd, erow_upd)
+ end
+ end)
end
end)
end
---@param bufnr integer
----@param foldinfo TS.FoldInfo
---@param start_row integer
---@param old_row integer
---@param old_col integer
---@param new_row integer
---@param new_col integer
-local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, new_row, new_col)
+local function on_bytes(bufnr, start_row, start_col, old_row, old_col, new_row, new_col)
+ -- Buffer reload clears `foldinfos[bufnr]`, which may still be nil when callback is invoked.
+ local foldinfo = foldinfos[bufnr]
+ if not foldinfo then
+ return
+ end
+
-- extend the end to fully include the range
local end_row_old = start_row + old_row + 1
local end_row_new = start_row + new_row + 1
@@ -373,15 +358,16 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col,
-- is invoked. For example, `J` with non-zero count triggers multiple on_bytes before executing
-- the scheduled callback. So we accumulate the edited ranges in `on_bytes_range`.
schedule_if_loaded(bufnr, function()
- if not foldinfo.on_bytes_range then
+ if not (foldinfo.on_bytes_range and foldinfos[bufnr]) then
return
end
local srow, erow = foldinfo.on_bytes_range[1], foldinfo.on_bytes_range[2]
foldinfo.on_bytes_range = nil
-- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit.
srow = math.max(srow - vim.wo.foldminlines, 0)
- compute_folds_levels(bufnr, foldinfo, srow, erow)
- foldinfo:foldupdate(bufnr, srow, erow)
+ compute_folds_levels(bufnr, foldinfo, srow, erow, function()
+ foldinfo:foldupdate(bufnr, srow, erow)
+ end)
end)
end
end
@@ -392,22 +378,30 @@ function M.foldexpr(lnum)
lnum = lnum or vim.v.lnum
local bufnr = api.nvim_get_current_buf()
- local parser = ts.get_parser(bufnr, nil, { error = false })
- if not parser then
- return '0'
- end
-
if not foldinfos[bufnr] then
- foldinfos[bufnr] = FoldInfo.new()
+ foldinfos[bufnr] = FoldInfo.new(bufnr)
+ api.nvim_create_autocmd({ 'BufUnload', 'VimEnter' }, {
+ buffer = bufnr,
+ once = true,
+ callback = function()
+ foldinfos[bufnr] = nil
+ end,
+ })
+
+ local parser = foldinfos[bufnr].parser
+ if not parser then
+ return '0'
+ end
+
compute_folds_levels(bufnr, foldinfos[bufnr])
parser:register_cbs({
on_changedtree = function(tree_changes)
- on_changedtree(bufnr, foldinfos[bufnr], tree_changes)
+ on_changedtree(bufnr, tree_changes)
end,
on_bytes = function(_, _, start_row, start_col, _, old_row, old_col, _, new_row, new_col, _)
- on_bytes(bufnr, foldinfos[bufnr], start_row, start_col, old_row, old_col, new_row, new_col)
+ on_bytes(bufnr, start_row, start_col, old_row, old_col, new_row, new_col)
end,
on_detach = function()
@@ -423,10 +417,17 @@ api.nvim_create_autocmd('OptionSet', {
pattern = { 'foldminlines', 'foldnestmax' },
desc = 'Refresh treesitter folds',
callback = function()
- for bufnr, _ in pairs(foldinfos) do
- foldinfos[bufnr] = FoldInfo.new()
- compute_folds_levels(bufnr, foldinfos[bufnr])
- foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr))
+ local buf = api.nvim_get_current_buf()
+ local bufs = vim.v.option_type == 'global' and vim.tbl_keys(foldinfos)
+ or foldinfos[buf] and { buf }
+ or {}
+ for _, bufnr in ipairs(bufs) do
+ foldinfos[bufnr] = FoldInfo.new(bufnr)
+ api.nvim_buf_call(bufnr, function()
+ compute_folds_levels(bufnr, foldinfos[bufnr], nil, nil, function()
+ foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr))
+ end)
+ end)
end
end,
})
diff --git a/runtime/lua/vim/treesitter/_meta/misc.lua b/runtime/lua/vim/treesitter/_meta/misc.lua
index 33701ef254..c532257f49 100644
--- a/runtime/lua/vim/treesitter/_meta/misc.lua
+++ b/runtime/lua/vim/treesitter/_meta/misc.lua
@@ -20,9 +20,15 @@ error('Cannot require a meta file')
---@class (exact) TSQueryInfo
---@field captures string[]
---@field patterns table<integer, (integer|string)[][]>
+---
+---@class TSLangInfo
+---@field fields string[]
+---@field symbols table<string,boolean>
+---@field _wasm boolean
+---@field _abi_version integer
--- @param lang string
---- @return table
+--- @return TSLangInfo
vim._ts_inspect_language = function(lang) end
---@return integer
diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua
index d982b6a505..552905c3f0 100644
--- a/runtime/lua/vim/treesitter/_meta/tsnode.lua
+++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua
@@ -68,12 +68,6 @@ function TSNode:named_child_count() end
--- @return TSNode?
function TSNode:named_child(index) end
---- Get the node's child that contains {descendant}.
---- @param descendant TSNode
---- @return TSNode?
---- @deprecated
-function TSNode:child_containing_descendant(descendant) end
-
--- Get the node's child that contains {descendant} (includes {descendant}).
---
--- For example, with the following node hierarchy:
@@ -109,17 +103,9 @@ function TSNode:end_() end
--- - end row
--- - end column
--- - end byte (if {include_bytes} is `true`)
---- @param include_bytes boolean?
-function TSNode:range(include_bytes) end
-
---- @nodoc
--- @param include_bytes false?
--- @return integer, integer, integer, integer
-function TSNode:range(include_bytes) end
-
---- @nodoc
---- @param include_bytes true
---- @return integer, integer, integer, integer, integer, integer
+--- @overload fun(self: TSNode, include_bytes: true): integer, integer, integer, integer, integer, integer
function TSNode:range(include_bytes) end
--- Get the node's type as a string.
diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua
index a825505378..3dfc6b0cfe 100644
--- a/runtime/lua/vim/treesitter/_query_linter.lua
+++ b/runtime/lua/vim/treesitter/_query_linter.lua
@@ -1,6 +1,6 @@
local api = vim.api
-local namespace = api.nvim_create_namespace('vim.treesitter.query_linter')
+local namespace = api.nvim_create_namespace('nvim.treesitter.query_linter')
local M = {}
@@ -138,7 +138,9 @@ local function lint_match(buf, match, query, lang_context, diagnostics)
-- perform language-independent checks only for first lang
if lang_context.is_first_lang and cap_id == 'error' then
local node_text = vim.treesitter.get_node_text(node, buf):gsub('\n', ' ')
- add_lint_for_node(diagnostics, { node:range() }, 'Syntax error: ' .. node_text)
+ ---@diagnostic disable-next-line: missing-fields LuaLS varargs bug
+ local range = { node:range() } --- @type Range4
+ add_lint_for_node(diagnostics, range, 'Syntax error: ' .. node_text)
end
-- other checks rely on Neovim parser introspection
diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua
index 26817cdba5..24dd8243db 100644
--- a/runtime/lua/vim/treesitter/dev.lua
+++ b/runtime/lua/vim/treesitter/dev.lua
@@ -119,7 +119,7 @@ function TSTreeView:new(bufnr, lang)
end
local t = {
- ns = api.nvim_create_namespace('treesitter/dev-inspect'),
+ ns = api.nvim_create_namespace('nvim.treesitter.dev_inspect'),
nodes = nodes,
named = named,
---@type vim.treesitter.dev.TSTreeViewOpts
@@ -135,15 +135,7 @@ function TSTreeView:new(bufnr, lang)
return t
end
-local decor_ns = api.nvim_create_namespace('ts.dev')
-
----@param range Range4
----@return string
-local function range_to_string(range)
- ---@type integer, integer, integer, integer
- local row, col, end_row, end_col = unpack(range)
- return string.format('[%d, %d] - [%d, %d]', row, col, end_row, end_col)
-end
+local decor_ns = api.nvim_create_namespace('nvim.treesitter.dev')
---@param w integer
---@return boolean closed Whether the window was closed.
@@ -227,14 +219,17 @@ function TSTreeView:draw(bufnr)
local lang_hl_marks = {} ---@type table[]
for i, item in self:iter() do
- local range_str = range_to_string({ item.node:range() })
+ local range_str = ('[%d, %d] - [%d, %d]'):format(item.node:range())
local lang_str = self.opts.lang and string.format(' %s', item.lang) or ''
local text ---@type string
if item.node:named() then
- text = string.format('(%s', item.node:type())
+ text = string.format('(%s%s', item.node:missing() and 'MISSING ' or '', item.node:type())
else
text = string.format('%q', item.node:type()):gsub('\n', 'n')
+ if item.node:missing() then
+ text = string.format('(MISSING %s)', text)
+ end
end
if item.field then
text = string.format('%s: %s', item.field, text)
@@ -442,7 +437,7 @@ function M.inspect_tree(opts)
end,
})
- local group = api.nvim_create_augroup('treesitter/dev', {})
+ local group = api.nvim_create_augroup('nvim.treesitter.dev', {})
api.nvim_create_autocmd('CursorMoved', {
group = group,
@@ -547,7 +542,7 @@ function M.inspect_tree(opts)
})
end
-local edit_ns = api.nvim_create_namespace('treesitter/dev-edit')
+local edit_ns = api.nvim_create_namespace('nvim.treesitter.dev_edit')
---@param query_win integer
---@param base_win integer
@@ -633,7 +628,7 @@ function M.edit_query(lang)
-- can infer the language later.
api.nvim_buf_set_name(query_buf, string.format('%s/query_editor.scm', lang))
- local group = api.nvim_create_augroup('treesitter/dev-edit', {})
+ local group = api.nvim_create_augroup('nvim.treesitter.dev_edit', {})
api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, {
group = group,
buffer = query_buf,
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 8ce8652f7d..6dd47811bd 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -2,7 +2,7 @@ local api = vim.api
local query = vim.treesitter.query
local Range = require('vim.treesitter._range')
-local ns = api.nvim_create_namespace('treesitter/highlighter')
+local ns = api.nvim_create_namespace('nvim.treesitter.highlighter')
---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch
@@ -69,6 +69,7 @@ end
---@field private _queries table<string,vim.treesitter.highlighter.Query>
---@field tree vim.treesitter.LanguageTree
---@field private redraw_count integer
+---@field parsing boolean true if we are parsing asynchronously
local TSHighlighter = {
active = {},
}
@@ -147,8 +148,6 @@ function TSHighlighter.new(tree, opts)
vim.opt_local.spelloptions:append('noplainbuffer')
end)
- self.tree:parse()
-
return self
end
@@ -161,7 +160,10 @@ function TSHighlighter:destroy()
vim.bo[self.bufnr].spelloptions = self.orig_spelloptions
vim.b[self.bufnr].ts_highlight = nil
if vim.g.syntax_on == 1 then
- api.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr })
+ api.nvim_exec_autocmds(
+ 'FileType',
+ { group = 'syntaxset', buffer = self.bufnr, modeline = false }
+ )
end
end
end
@@ -299,6 +301,8 @@ local function on_line_impl(self, buf, line, is_spell_nav)
state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1)
end
+ local captures = state.highlighter_query:query().captures
+
while line >= state.next_row do
local capture, node, metadata, match = state.iter(line)
@@ -311,7 +315,7 @@ local function on_line_impl(self, buf, line, is_spell_nav)
if capture then
local hl = state.highlighter_query:get_hl_from_capture(capture)
- local capture_name = state.highlighter_query:query().captures[capture]
+ local capture_name = captures[capture]
local spell, spell_pri_offset = get_spell(capture_name)
@@ -382,19 +386,23 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _)
end
---@private
----@param _win integer
---@param buf integer
---@param topline integer
---@param botline integer
-function TSHighlighter._on_win(_, _win, buf, topline, botline)
+function TSHighlighter._on_win(_, _, buf, topline, botline)
local self = TSHighlighter.active[buf]
- if not self then
+ if not self or self.parsing then
return false
end
- self.tree:parse({ topline, botline + 1 })
- self:prepare_highlight_states(topline, botline + 1)
+ self.parsing = self.tree:parse({ topline, botline + 1 }, function(_, trees)
+ if trees and self.parsing then
+ self.parsing = false
+ api.nvim__redraw({ buf = buf, valid = false, flush = false })
+ end
+ end) == nil
self.redraw_count = self.redraw_count + 1
- return true
+ self:prepare_highlight_states(topline, botline)
+ return #self._highlight_states > 0
end
api.nvim_set_decoration_provider(ns, {
diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua
index 446051dfd7..16d19bfc5a 100644
--- a/runtime/lua/vim/treesitter/language.lua
+++ b/runtime/lua/vim/treesitter/language.lua
@@ -133,8 +133,9 @@ function M.add(lang, opts)
path = paths[1]
end
- return loadparser(path, lang, symbol_name) or nil,
- string.format('Cannot load parser %s for language "%s"', path, lang)
+ local res = loadparser(path, lang, symbol_name)
+ return res,
+ res == nil and string.format('Cannot load parser %s for language "%s"', path, lang) or nil
end
--- @param x string|string[]
@@ -174,7 +175,7 @@ end
--- (`"`).
---
---@param lang string Language
----@return table
+---@return TSLangInfo
function M.inspect(lang)
M.add(lang)
return vim._ts_inspect_language(lang)
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 4b42164dc8..ea745c4deb 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -44,6 +44,8 @@ local query = require('vim.treesitter.query')
local language = require('vim.treesitter.language')
local Range = require('vim.treesitter._range')
+local default_parse_timeout_ms = 3
+
---@alias TSCallbackName
---| 'changedtree'
---| 'bytes'
@@ -58,6 +60,8 @@ local Range = require('vim.treesitter._range')
---| 'on_child_added'
---| 'on_child_removed'
+---@alias ParserThreadState { timeout: integer? }
+
--- @type table<TSCallbackNameOn,TSCallbackName>
local TSCallbackNames = {
on_changedtree = 'changedtree',
@@ -76,8 +80,13 @@ local TSCallbackNames = {
---@field private _injections_processed boolean
---@field private _opts table Options
---@field private _parser TSParser Parser for language
----@field private _has_regions boolean
+---Table of regions for which the tree is currently running an async parse
+---@field private _ranges_being_parsed table<string, boolean>
+---Table of callback queues, keyed by each region for which the callbacks should be run
+---@field private _cb_queues table<string, fun(err?: string, trees?: table<integer, TSTree>)[]>
---@field private _regions table<integer, Range6[]>?
+---The total number of regions. Since _regions can have holes, we cannot simply read this value from #_regions.
+---@field private _num_regions integer
---List of regions this tree should manage and parse. If nil then regions are
---taken from _trees. This is mostly a short-lived cache for included_regions()
---@field private _lang string Language name
@@ -85,7 +94,8 @@ local TSCallbackNames = {
---@field private _source (integer|string) Buffer or string to parse
---@field private _trees table<integer, TSTree> Reference to parsed tree (one for each language).
---Each key is the index of region, which is synced with _regions and _valid.
----@field private _valid boolean|table<integer,boolean> If the parsed tree is valid
+---@field private _valid_regions table<integer,true> Set of valid region IDs.
+---@field private _is_entirely_valid boolean Whether the entire tree (excluding children) is valid.
---@field private _logger? fun(logtype: string, msg: string)
---@field private _logfile? file*
local LanguageTree = {}
@@ -117,7 +127,7 @@ function LanguageTree.new(source, lang, opts)
local injections = opts.injections or {}
- --- @type vim.treesitter.LanguageTree
+ --- @class vim.treesitter.LanguageTree
local self = {
_source = source,
_lang = lang,
@@ -126,10 +136,13 @@ function LanguageTree.new(source, lang, opts)
_opts = opts,
_injection_query = injections[lang] and query.parse(lang, injections[lang])
or query.get(lang, 'injections'),
- _has_regions = false,
_injections_processed = false,
- _valid = false,
+ _valid_regions = {},
+ _num_regions = 1,
+ _is_entirely_valid = false,
_parser = vim._create_ts_parser(lang),
+ _ranges_being_parsed = {},
+ _cb_queues = {},
_callbacks = {},
_callbacks_rec = {},
}
@@ -182,7 +195,7 @@ end
---Measure execution time of a function
---@generic R1, R2, R3
----@param f fun(): R1, R2, R2
+---@param f fun(): R1, R2, R3
---@return number, R1, R2, R3
local function tcall(f, ...)
local start = vim.uv.hrtime()
@@ -190,6 +203,7 @@ local function tcall(f, ...)
local r = { f(...) }
--- @type number
local duration = (vim.uv.hrtime() - start) / 1000000
+ --- @diagnostic disable-next-line: redundant-return-value
return duration, unpack(r)
end
@@ -231,7 +245,9 @@ end
--- tree in treesitter. Doesn't clear filesystem cache. Called often, so needs to be fast.
---@param reload boolean|nil
function LanguageTree:invalidate(reload)
- self._valid = false
+ self._valid_regions = {}
+ self._is_entirely_valid = false
+ self._parser:reset()
-- buffer was reloaded, reparse all trees
if reload then
@@ -258,20 +274,51 @@ function LanguageTree:trees()
end
--- Gets the language of this tree node.
+--- @return string
function LanguageTree:lang()
return self._lang
end
+--- @param region Range6[]
+--- @param range? boolean|Range
+--- @return boolean
+local function intercepts_region(region, range)
+ if #region == 0 then
+ return true
+ end
+
+ if range == nil then
+ return false
+ end
+
+ if type(range) == 'boolean' then
+ return range
+ end
+
+ for _, r in ipairs(region) do
+ if Range.intercepts(r, range) then
+ return true
+ end
+ end
+
+ return false
+end
+
--- Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest
--- state of the source. If invalid, user should call |LanguageTree:parse()|.
----@param exclude_children boolean|nil whether to ignore the validity of children (default `false`)
+---@param exclude_children boolean? whether to ignore the validity of children (default `false`)
+---@param range Range? range to check for validity
---@return boolean
-function LanguageTree:is_valid(exclude_children)
- local valid = self._valid
+function LanguageTree:is_valid(exclude_children, range)
+ local valid_regions = self._valid_regions
- if type(valid) == 'table' then
- for i, _ in pairs(self:included_regions()) do
- if not valid[i] then
+ if not self._is_entirely_valid then
+ if not range then
+ return false
+ end
+ -- TODO: Efficiently search for possibly intersecting regions using a binary search
+ for i, region in pairs(self:included_regions()) do
+ if not valid_regions[i] and intercepts_region(region, range) then
return false
end
end
@@ -283,97 +330,81 @@ function LanguageTree:is_valid(exclude_children)
end
for _, child in pairs(self._children) do
- if not child:is_valid(exclude_children) then
+ if not child:is_valid(exclude_children, range) then
return false
end
end
end
- if type(valid) == 'boolean' then
- return valid
- end
-
- self._valid = true
return true
end
--- Returns a map of language to child tree.
+--- @return table<string,vim.treesitter.LanguageTree>
function LanguageTree:children()
return self._children
end
--- Returns the source content of the language tree (bufnr or string).
+--- @return integer|string
function LanguageTree:source()
return self._source
end
---- @param region Range6[]
---- @param range? boolean|Range
---- @return boolean
-local function intercepts_region(region, range)
- if #region == 0 then
- return true
- end
-
- if range == nil then
- return false
- end
-
- if type(range) == 'boolean' then
- return range
- end
-
- for _, r in ipairs(region) do
- if Range.intercepts(r, range) then
- return true
- end
- end
-
- return false
-end
-
--- @private
--- @param range boolean|Range?
+--- @param thread_state ParserThreadState
--- @return Range6[] changes
--- @return integer no_regions_parsed
--- @return number total_parse_time
-function LanguageTree:_parse_regions(range)
+--- @return boolean finished whether async parsing still needs time
+function LanguageTree:_parse_regions(range, thread_state)
local changes = {}
local no_regions_parsed = 0
local total_parse_time = 0
- if type(self._valid) ~= 'table' then
- self._valid = {}
- end
-
-- If there are no ranges, set to an empty list
-- so the included ranges in the parser are cleared.
for i, ranges in pairs(self:included_regions()) do
if
- not self._valid[i]
+ not self._valid_regions[i]
and (
intercepts_region(ranges, range)
or (self._trees[i] and intercepts_region(self._trees[i]:included_ranges(false), range))
)
then
self._parser:set_included_ranges(ranges)
+ self._parser:set_timeout(thread_state.timeout and thread_state.timeout * 1000 or 0) -- ms -> micros
+
local parse_time, tree, tree_changes =
tcall(self._parser.parse, self._parser, self._trees[i], self._source, true)
+ while true do
+ if tree then
+ break
+ end
+ coroutine.yield(changes, no_regions_parsed, total_parse_time, false)
- -- Pass ranges if this is an initial parse
- local cb_changes = self._trees[i] and tree_changes or tree:included_ranges(true)
+ parse_time, tree, tree_changes =
+ tcall(self._parser.parse, self._parser, self._trees[i], self._source, true)
+ end
- self:_do_callback('changedtree', cb_changes, tree)
+ self:_do_callback('changedtree', tree_changes, tree)
self._trees[i] = tree
vim.list_extend(changes, tree_changes)
total_parse_time = total_parse_time + parse_time
no_regions_parsed = no_regions_parsed + 1
- self._valid[i] = true
+ self._valid_regions[i] = true
+
+ -- _valid_regions can have holes, but that is okay because this equality is only true when it
+ -- has no holes (meaning all regions are valid)
+ if #self._valid_regions == self._num_regions then
+ self._is_entirely_valid = true
+ end
end
end
- return changes, no_regions_parsed, total_parse_time
+ return changes, no_regions_parsed, total_parse_time, true
end
--- @private
@@ -409,6 +440,98 @@ function LanguageTree:_add_injections()
return query_time
end
+--- @param range boolean|Range?
+--- @return string
+local function range_to_string(range)
+ return type(range) == 'table' and table.concat(range, ',') or tostring(range)
+end
+
+--- @private
+--- @param range boolean|Range?
+--- @param callback fun(err?: string, trees?: table<integer, TSTree>)
+function LanguageTree:_push_async_callback(range, callback)
+ local key = range_to_string(range)
+ self._cb_queues[key] = self._cb_queues[key] or {}
+ local queue = self._cb_queues[key]
+ queue[#queue + 1] = callback
+end
+
+--- @private
+--- @param range boolean|Range?
+--- @param err? string
+--- @param trees? table<integer, TSTree>
+function LanguageTree:_run_async_callbacks(range, err, trees)
+ local key = range_to_string(range)
+ for _, cb in ipairs(self._cb_queues[key]) do
+ cb(err, trees)
+ end
+ self._ranges_being_parsed[key] = nil
+ self._cb_queues[key] = nil
+end
+
+--- Run an asynchronous parse, calling {on_parse} when complete.
+---
+--- @private
+--- @param range boolean|Range?
+--- @param on_parse fun(err?: string, trees?: table<integer, TSTree>)
+--- @return table<integer, TSTree>? trees the list of parsed trees, if parsing completed synchronously
+function LanguageTree:_async_parse(range, on_parse)
+ self:_push_async_callback(range, on_parse)
+
+ -- If we are already running an async parse, just queue the callback.
+ local range_string = range_to_string(range)
+ if not self._ranges_being_parsed[range_string] then
+ self._ranges_being_parsed[range_string] = true
+ else
+ return
+ end
+
+ local source = self._source
+ local is_buffer_parser = type(source) == 'number'
+ local buf = is_buffer_parser and vim.b[source] or nil
+ local ct = is_buffer_parser and buf.changedtick or nil
+ local total_parse_time = 0
+ local redrawtime = vim.o.redrawtime
+
+ local thread_state = {} ---@type ParserThreadState
+
+ ---@type fun(): table<integer, TSTree>, boolean
+ local parse = coroutine.wrap(self._parse)
+
+ local function step()
+ if is_buffer_parser then
+ if
+ not vim.api.nvim_buf_is_valid(source --[[@as number]])
+ then
+ return nil
+ end
+
+ -- If buffer was changed in the middle of parsing, reset parse state
+ if buf.changedtick ~= ct then
+ ct = buf.changedtick
+ total_parse_time = 0
+ parse = coroutine.wrap(self._parse)
+ end
+ end
+
+ thread_state.timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil
+ local parse_time, trees, finished = tcall(parse, self, range, thread_state)
+ total_parse_time = total_parse_time + parse_time
+
+ if finished then
+ self:_run_async_callbacks(range, nil, trees)
+ return trees
+ elseif total_parse_time > redrawtime then
+ self:_run_async_callbacks(range, 'TIMEOUT', nil)
+ return nil
+ else
+ vim.schedule(step)
+ end
+ end
+
+ return step()
+end
+
--- Recursively parse all regions in the language tree using |treesitter-parsers|
--- for the corresponding languages and run injection queries on the parsed trees
--- to determine whether child trees should be created and parsed.
@@ -420,11 +543,33 @@ end
--- Set to `true` to run a complete parse of the source (Note: Can be slow!)
--- Set to `false|nil` to only parse regions with empty ranges (typically
--- only the root tree without injections).
---- @return table<integer, TSTree>
-function LanguageTree:parse(range)
- if self:is_valid() then
+--- @param on_parse fun(err?: string, trees?: table<integer, TSTree>)? Function invoked when parsing completes.
+--- When provided and `vim.g._ts_force_sync_parsing` is not set, parsing will run
+--- asynchronously. The first argument to the function is a string representing the error type,
+--- in case of a failure (currently only possible for timeouts). The second argument is the list
+--- of trees returned by the parse (upon success), or `nil` if the parse timed out (determined
+--- by 'redrawtime').
+---
+--- If parsing was still able to finish synchronously (within 3ms), `parse()` returns the list
+--- of trees. Otherwise, it returns `nil`.
+--- @return table<integer, TSTree>?
+function LanguageTree:parse(range, on_parse)
+ if on_parse then
+ return self:_async_parse(range, on_parse)
+ end
+ local trees, _ = self:_parse(range, {})
+ return trees
+end
+
+--- @private
+--- @param range boolean|Range|nil
+--- @param thread_state ParserThreadState
+--- @return table<integer, TSTree> trees
+--- @return boolean finished
+function LanguageTree:_parse(range, thread_state)
+ if self:is_valid(nil, type(range) == 'table' and range or nil) then
self:_log('valid')
- return self._trees
+ return self._trees, true
end
local changes --- @type Range6[]?
@@ -435,15 +580,27 @@ function LanguageTree:parse(range)
local total_parse_time = 0
-- At least 1 region is invalid
- if not self:is_valid(true) then
- changes, no_regions_parsed, total_parse_time = self:_parse_regions(range)
+ if not self:is_valid(true, type(range) == 'table' and range or nil) then
+ ---@type fun(self: vim.treesitter.LanguageTree, range: boolean|Range?, thread_state: ParserThreadState): Range6[], integer, number, boolean
+ local parse_regions = coroutine.wrap(self._parse_regions)
+ while true do
+ local is_finished
+ changes, no_regions_parsed, total_parse_time, is_finished =
+ parse_regions(self, range, thread_state)
+ thread_state.timeout = thread_state.timeout
+ and math.max(thread_state.timeout - total_parse_time, 0)
+ if is_finished then
+ break
+ end
+ coroutine.yield(self._trees, false)
+ end
-- Need to run injections when we parsed something
if no_regions_parsed > 0 then
self._injections_processed = false
end
end
- if not self._injections_processed and range ~= false and range ~= nil then
+ if not self._injections_processed and range then
query_time = self:_add_injections()
self._injections_processed = true
end
@@ -457,10 +614,24 @@ function LanguageTree:parse(range)
})
for _, child in pairs(self._children) do
- child:parse(range)
+ if thread_state.timeout == 0 then
+ coroutine.yield(self._trees, false)
+ end
+
+ ---@type fun(): table<integer, TSTree>, boolean
+ local parse = coroutine.wrap(child._parse)
+
+ while true do
+ local ctime, _, child_finished = tcall(parse, child, range, thread_state)
+ if child_finished then
+ thread_state.timeout = thread_state.timeout and math.max(thread_state.timeout - ctime, 0)
+ break
+ end
+ coroutine.yield(self._trees, child_finished)
+ end
end
- return self._trees
+ return self._trees, true
end
--- Invokes the callback for each |LanguageTree| recursively.
@@ -504,7 +675,8 @@ function LanguageTree:add_child(lang)
return self._children[lang]
end
---- @package
+---Returns the parent tree. `nil` for the root tree.
+---@return vim.treesitter.LanguageTree?
function LanguageTree:parent()
return self._parent
end
@@ -551,38 +723,34 @@ end
---region is valid or not.
---@param fn fun(index: integer, region: Range6[]): boolean
function LanguageTree:_iter_regions(fn)
- if not self._valid then
+ if vim.deep_equal(self._valid_regions, {}) then
return
end
- local was_valid = type(self._valid) ~= 'table'
-
- if was_valid then
- self:_log('was valid', self._valid)
- self._valid = {}
+ if self._is_entirely_valid then
+ self:_log('was valid')
end
local all_valid = true
for i, region in pairs(self:included_regions()) do
- if was_valid or self._valid[i] then
- self._valid[i] = fn(i, region)
- if not self._valid[i] then
+ if self._valid_regions[i] then
+ -- Setting this to nil rather than false allows us to determine if all regions were parsed
+ -- just by checking the length of _valid_regions.
+ self._valid_regions[i] = fn(i, region) and true or nil
+ if not self._valid_regions[i] then
self:_log(function()
return 'invalidating region', i, region_tostr(region)
end)
end
end
- if not self._valid[i] then
+ if not self._valid_regions[i] then
all_valid = false
end
end
- -- Compress the valid value to 'true' if there are no invalid regions
- if all_valid then
- self._valid = all_valid
- end
+ self._is_entirely_valid = all_valid
end
--- Sets the included regions that should be parsed by this |LanguageTree|.
@@ -602,14 +770,13 @@ end
---@private
---@param new_regions (Range4|Range6|TSNode)[][] List of regions this tree should manage and parse.
function LanguageTree:set_included_regions(new_regions)
- self._has_regions = true
-
-- Transform the tables from 4 element long to 6 element long (with byte offset)
for _, region in ipairs(new_regions) do
for i, range in ipairs(region) do
if type(range) == 'table' and #range == 4 then
region[i] = Range.add_bytes(self._source, range --[[@as Range4]])
elseif type(range) == 'userdata' then
+ --- @diagnostic disable-next-line: missing-fields LuaLS varargs bug
region[i] = { range:range(true) }
end
end
@@ -633,6 +800,7 @@ function LanguageTree:set_included_regions(new_regions)
end
self._regions = new_regions
+ self._num_regions = #new_regions
end
---Gets the set of included regions managed by this LanguageTree. This can be different from the
@@ -646,18 +814,8 @@ function LanguageTree:included_regions()
return self._regions
end
- if not self._has_regions then
- -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range)
- return { {} }
- end
-
- local regions = {} ---@type Range6[][]
- for i, _ in pairs(self._trees) do
- regions[i] = self._trees[i]:included_ranges(true)
- end
-
- self._regions = regions
- return regions
+ -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range)
+ return { {} }
end
---@param node TSNode
@@ -821,7 +979,7 @@ end
--- @private
--- @return table<string, Range6[][]>
function LanguageTree:_get_injections()
- if not self._injection_query then
+ if not self._injection_query or #self._injection_query.captures == 0 then
return {}
end
@@ -907,7 +1065,15 @@ function LanguageTree:_edit(
)
end
- self._regions = nil
+ self._parser:reset()
+
+ if self._regions then
+ local regions = {} ---@type table<integer, Range6[]>
+ for i, tree in pairs(self._trees) do
+ regions[i] = tree:included_ranges(true)
+ end
+ self._regions = regions
+ end
local changed_range = {
start_row,
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 1677e8d364..10fb82e533 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -1,17 +1,77 @@
+--- @brief This Lua |treesitter-query| interface allows you to create queries and use them to parse
+--- text. See |vim.treesitter.query.parse()| for a working example.
+
local api = vim.api
local language = require('vim.treesitter.language')
local memoize = vim.func._memoize
+local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
+local EXTENDS_FORMAT = '^;+%s*extends%s*$'
+
local M = {}
+local function is_directive(name)
+ return string.sub(name, -1) == '!'
+end
+
+---@nodoc
+---@class vim.treesitter.query.ProcessedPredicate
+---@field [1] string predicate name
+---@field [2] boolean should match
+---@field [3] (integer|string)[] the original predicate
+
+---@alias vim.treesitter.query.ProcessedDirective (integer|string)[]
+
+---@nodoc
+---@class vim.treesitter.query.ProcessedPattern {
+---@field predicates vim.treesitter.query.ProcessedPredicate[]
+---@field directives vim.treesitter.query.ProcessedDirective[]
+
+--- Splits the query patterns into predicates and directives.
+---@param patterns table<integer, (integer|string)[][]>
+---@return table<integer, vim.treesitter.query.ProcessedPattern>
+local function process_patterns(patterns)
+ ---@type table<integer, vim.treesitter.query.ProcessedPattern>
+ local processed_patterns = {}
+
+ for k, pattern_list in pairs(patterns) do
+ ---@type vim.treesitter.query.ProcessedPredicate[]
+ local predicates = {}
+ ---@type vim.treesitter.query.ProcessedDirective[]
+ local directives = {}
+
+ for _, pattern in ipairs(pattern_list) do
+ -- Note: tree-sitter strips the leading # from predicates for us.
+ local pred_name = pattern[1]
+ ---@cast pred_name string
+
+ if is_directive(pred_name) then
+ table.insert(directives, pattern)
+ else
+ local should_match = true
+ if pred_name:match('^not%-') then
+ pred_name = pred_name:sub(5)
+ should_match = false
+ end
+ table.insert(predicates, { pred_name, should_match, pattern })
+ end
+ end
+
+ processed_patterns[k] = { predicates = predicates, directives = directives }
+ end
+
+ return processed_patterns
+end
+
---@nodoc
---Parsed query, see |vim.treesitter.query.parse()|
---
---@class vim.treesitter.Query
----@field lang string name of the language for this parser
+---@field lang string parser language name
---@field captures string[] list of (unique) capture names defined in query
----@field info vim.treesitter.QueryInfo contains information used in the query (e.g. captures, predicates, directives)
+---@field info vim.treesitter.QueryInfo query context (e.g. captures, predicates, directives)
---@field query TSQuery userdata query object
+---@field private _processed_patterns table<integer, vim.treesitter.query.ProcessedPattern>
local Query = {}
Query.__index = Query
@@ -30,6 +90,7 @@ function Query.new(lang, ts_query)
patterns = query_info.patterns,
}
self.captures = self.info.captures
+ self._processed_patterns = process_patterns(self.info.patterns)
return self
end
@@ -109,9 +170,6 @@ function M.get_files(lang, query_name, is_included)
-- ;+ inherits: ({language},)*{language}
--
-- {language} ::= {lang} | ({lang})
- local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$'
- local EXTENDS_FORMAT = '^;+%s*extends%s*$'
-
for _, filename in ipairs(lang_files) do
local file, err = io.open(filename, 'r')
if not file then
@@ -184,8 +242,8 @@ local function read_query_files(filenames)
return table.concat(contents, '')
end
--- The explicitly set queries from |vim.treesitter.query.set()|
----@type table<string,table<string,vim.treesitter.Query>>
+-- The explicitly set query strings from |vim.treesitter.query.set()|
+---@type table<string,table<string,string>>
local explicit_queries = setmetatable({}, {
__index = function(t, k)
local lang_queries = {}
@@ -197,14 +255,27 @@ local explicit_queries = setmetatable({}, {
--- Sets the runtime query named {query_name} for {lang}
---
---- This allows users to override any runtime files and/or configuration
+--- This allows users to override or extend any runtime files and/or configuration
--- set by plugins.
---
+--- For example, you could enable spellchecking of `C` identifiers with the
+--- following code:
+--- ```lua
+--- vim.treesitter.query.set(
+--- 'c',
+--- 'highlights',
+--- [[;inherits c
+--- (identifier) @spell]])
+--- ]])
+--- ```
+---
---@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(lang, query_name, text)
- explicit_queries[lang][query_name] = M.parse(lang, text)
+ --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics
+ M.get:clear(lang, query_name)
+ explicit_queries[lang][query_name] = text
end
--- Returns the runtime query {query_name} for {lang}.
@@ -214,34 +285,82 @@ end
---
---@return vim.treesitter.Query? : Parsed query. `nil` if no query files are found.
M.get = memoize('concat-2', function(lang, query_name)
+ local query_string ---@type string
+
if explicit_queries[lang][query_name] then
- return explicit_queries[lang][query_name]
- end
+ local query_files = {}
+ local base_langs = {} ---@type string[]
- local query_files = M.get_files(lang, query_name)
- local query_string = read_query_files(query_files)
+ for line in explicit_queries[lang][query_name]:gmatch('([^\n]*)\n?') do
+ if not vim.startswith(line, ';') then
+ break
+ end
+
+ local lang_list = line:match(MODELINE_FORMAT)
+ if lang_list then
+ for _, incl_lang in ipairs(vim.split(lang_list, ',')) do
+ local is_optional = incl_lang:match('%(.*%)')
+
+ if is_optional then
+ add_included_lang(base_langs, lang, incl_lang:sub(2, #incl_lang - 1))
+ else
+ add_included_lang(base_langs, lang, incl_lang)
+ end
+ end
+ elseif line:match(EXTENDS_FORMAT) then
+ table.insert(base_langs, lang)
+ end
+ end
+
+ for _, base_lang in ipairs(base_langs) do
+ local base_files = M.get_files(base_lang, query_name, true)
+ vim.list_extend(query_files, base_files)
+ end
+
+ query_string = read_query_files(query_files) .. explicit_queries[lang][query_name]
+ else
+ local query_files = M.get_files(lang, query_name)
+ query_string = read_query_files(query_files)
+ end
if #query_string == 0 then
return nil
end
return M.parse(lang, query_string)
-end)
+end, false)
+
+api.nvim_create_autocmd('OptionSet', {
+ pattern = { 'runtimepath' },
+ group = api.nvim_create_augroup('nvim.treesitter.query_cache_reset', { clear = true }),
+ callback = function()
+ --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics
+ M.get:clear()
+ end,
+})
---- 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 the `iter_captures` and `iter_matches` methods.
+--- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used
+--- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|),
+--- or inspect the query via these fields:
+--- - `captures`: a list of unique capture names defined in the query (alias: `info.captures`).
+--- - `info.patterns`: information about predicates.
---
---- Exposes `info` and `captures` with additional context about {query}.
---- - `captures` contains the list of unique capture names defined in {query}.
---- - `info.captures` also points to `captures`.
---- - `info.patterns` contains information about predicates.
+--- Example:
+--- ```lua
+--- local query = vim.treesitter.query.parse('vimdoc', [[
+--- ; query
+--- ((h1) @str
+--- (#trim! @str 1 1 1 1))
+--- ]])
+--- local tree = vim.treesitter.get_parser():parse()[1]
+--- for id, node, metadata in query:iter_captures(tree:root(), 0) do
+--- -- Print the node name and source text.
+--- vim.print({node:type(), vim.treesitter.get_node_text(node, vim.api.nvim_get_current_buf())})
+--- end
+--- ```
---
---@param lang string Language to use for the query
----@param query string Query in s-expr syntax
+---@param query string Query text, in s-expr syntax
---
---@return vim.treesitter.Query : Parsed query
---
@@ -250,7 +369,7 @@ M.parse = memoize('concat-2', function(lang, query)
assert(language.add(lang))
local ts_query = vim._ts_parse_query(lang, query)
return Query.new(lang, ts_query)
-end)
+end, false)
--- Implementations of predicates that can optionally be prefixed with "any-".
---
@@ -572,13 +691,17 @@ local directive_handlers = {
metadata[id].text = text:gsub(pattern, replacement)
end,
- -- Trim blank lines from end of the node
- -- Example: (#trim! @fold)
- -- TODO(clason): generalize to arbitrary whitespace removal
+ -- Trim whitespace from both sides of the node
+ -- Example: (#trim! @fold 1 1 1 1)
['trim!'] = function(match, _, bufnr, pred, metadata)
local capture_id = pred[2]
assert(type(capture_id) == 'number')
+ local trim_start_lines = pred[3] == '1'
+ local trim_start_cols = pred[4] == '1'
+ local trim_end_lines = pred[5] == '1' or not pred[3] -- default true for backwards compatibility
+ local trim_end_cols = pred[6] == '1'
+
local nodes = match[capture_id]
if not nodes or #nodes == 0 then
return
@@ -588,20 +711,45 @@ local directive_handlers = {
local start_row, start_col, end_row, end_col = node:range()
- -- Don't trim if region ends in middle of a line
- if end_col ~= 0 then
- return
+ local node_text = vim.split(vim.treesitter.get_node_text(node, bufnr), '\n')
+ if end_col == 0 then
+ -- get_node_text() will ignore the last line if the node ends at column 0
+ node_text[#node_text + 1] = ''
end
- while end_row >= start_row do
- -- As we only care when end_col == 0, always inspect one line above end_row.
- local end_line = api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1]
+ local end_idx = #node_text
+ local start_idx = 1
- if end_line ~= '' then
- break
+ if trim_end_lines then
+ while end_idx > 0 and node_text[end_idx]:find('^%s*$') do
+ end_idx = end_idx - 1
+ end_row = end_row - 1
+ -- set the end position to the last column of the next line, or 0 if we just trimmed the
+ -- last line
+ end_col = end_idx > 0 and #node_text[end_idx] or 0
end
+ end
+ if trim_end_cols then
+ if end_idx == 0 then
+ end_row = start_row
+ end_col = start_col
+ else
+ local whitespace_start = node_text[end_idx]:find('(%s*)$')
+ end_col = (whitespace_start - 1) + (end_idx == 1 and start_col or 0)
+ end
+ end
- end_row = end_row - 1
+ if trim_start_lines then
+ while start_idx <= end_idx and node_text[start_idx]:find('^%s*$') do
+ start_idx = start_idx + 1
+ start_row = start_row + 1
+ start_col = 0
+ end
+ end
+ if trim_start_cols and node_text[start_idx] then
+ local _, whitespace_end = node_text[start_idx]:find('^(%s*)')
+ whitespace_end = whitespace_end or 0
+ start_col = (start_idx == 1 and start_col or 0) + whitespace_end
end
-- If this produces an invalid range, we just skip it.
@@ -711,84 +859,50 @@ function M.list_predicates()
return vim.tbl_keys(predicate_handlers)
end
-local function xor(x, y)
- return (x or y) and not (x and y)
-end
-
-local function is_directive(name)
- return string.sub(name, -1) == '!'
-end
-
---@private
----@param match TSQueryMatch
+---@param pattern_i integer
+---@param predicates vim.treesitter.query.ProcessedPredicate[]
+---@param captures table<integer, TSNode[]>
---@param source integer|string
-function Query:match_preds(match, source)
- local _, pattern = match:info()
- local preds = self.info.patterns[pattern]
-
- if not preds then
- return true
- end
-
- local captures = match:captures()
-
- for _, pred in pairs(preds) do
- -- Here we only want to return if a predicate DOES NOT match, and
- -- continue on the other case. This way unknown predicates will not be considered,
- -- which allows some testing and easier user extensibility (#12173).
- -- Also, tree-sitter strips the leading # from predicates for us.
- local is_not = false
-
- -- Skip over directives... they will get processed after all the predicates.
- if not is_directive(pred[1]) then
- local pred_name = pred[1]
- if pred_name:match('^not%-') then
- pred_name = pred_name:sub(5)
- is_not = true
- end
-
- local handler = predicate_handlers[pred_name]
-
- if not handler then
- error(string.format('No handler for %s', pred[1]))
- return false
- end
-
- local pred_matches = handler(captures, pattern, source, pred)
+---@return boolean whether the predicates match
+function Query:_match_predicates(predicates, pattern_i, captures, source)
+ for _, predicate in ipairs(predicates) do
+ local processed_name = predicate[1]
+ local should_match = predicate[2]
+ local orig_predicate = predicate[3]
+
+ local handler = predicate_handlers[processed_name]
+ if not handler then
+ error(string.format('No handler for %s', orig_predicate[1]))
+ return false
+ end
- if not xor(is_not, pred_matches) then
- return false
- end
+ local does_match = handler(captures, pattern_i, source, orig_predicate)
+ if does_match ~= should_match then
+ return false
end
end
return true
end
---@private
----@param match TSQueryMatch
+---@param pattern_i integer
+---@param directives vim.treesitter.query.ProcessedDirective[]
+---@param source integer|string
+---@param captures table<integer, TSNode[]>
---@return vim.treesitter.query.TSMetadata metadata
-function Query:apply_directives(match, source)
+function Query:_apply_directives(directives, pattern_i, captures, source)
---@type vim.treesitter.query.TSMetadata
local metadata = {}
- local _, pattern = match:info()
- local preds = self.info.patterns[pattern]
-
- if not preds then
- return metadata
- end
- local captures = match:captures()
-
- for _, pred in pairs(preds) do
- if is_directive(pred[1]) then
- local handler = directive_handlers[pred[1]]
-
- if not handler then
- error(string.format('No handler for %s', pred[1]))
- end
+ for _, directive in pairs(directives) do
+ local handler = directive_handlers[directive[1]]
- handler(captures, pattern, source, pred, metadata)
+ if not handler then
+ error(string.format('No handler for %s', directive[1]))
end
+
+ handler(captures, pattern_i, source, directive, metadata)
end
return metadata
@@ -812,26 +926,22 @@ local function value_or_node_range(start, stop, node)
return start, stop
end
---- @param match TSQueryMatch
---- @return integer
-local function match_id_hash(_, match)
- return (match:info())
-end
-
---- Iterate over all captures from all matches inside {node}
+--- Iterates over all captures from all matches in {node}.
---
---- {source} is needed if the query contains predicates; then the caller
+--- {source} is required 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} and {stop} 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 {stop} row values are used from the given node.
---
---- The iterator returns four values: a numeric id identifying the capture,
---- the captured node, metadata from any directives processing the match,
---- and the match itself.
---- The following example shows how to get captures by name:
+--- The iterator returns four values:
+--- 1. the numeric id identifying the capture
+--- 2. the captured node
+--- 3. metadata from any directives processing the match
+--- 4. the match itself
---
+--- Example: how to get captures by name:
--- ```lua
--- for id, node, metadata, match in query:iter_captures(tree:root(), bufnr, first, last) do
--- local name = query.captures[id] -- name of the capture in the query
@@ -847,8 +957,8 @@ end
---@param start? integer Starting line for the search. Defaults to `node:start()`.
---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`.
---
----@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch):
---- capture id, capture node, metadata, match
+---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree):
+--- capture id, capture node, metadata, match, tree
---
---@note Captures are only returned if the query pattern of a specific capture contained predicates.
function Query:iter_captures(node, source, start, stop)
@@ -858,10 +968,14 @@ function Query:iter_captures(node, source, start, stop)
start, stop = value_or_node_range(start, stop, node)
+ -- Copy the tree to ensure it is valid during the entire lifetime of the iterator
+ local tree = node:tree():copy()
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 })
- local apply_directives = memoize(match_id_hash, self.apply_directives, true)
- local match_preds = memoize(match_id_hash, self.match_preds, true)
+ -- For faster checks that a match is not in the cache.
+ local highest_cached_match_id = -1
+ ---@type table<integer, vim.treesitter.query.TSMetadata>
+ local match_cache = {}
local function iter(end_line)
local capture, captured_node, match = cursor:next_capture()
@@ -870,18 +984,39 @@ function Query:iter_captures(node, source, start, stop)
return
end
- if not match_preds(self, match, source) then
- local match_id = match:info()
- cursor:remove_match(match_id)
- if end_line and captured_node:range() > end_line then
- return nil, captured_node, nil, nil
- end
- return iter(end_line) -- tail call: try next match
+ local match_id, pattern_i = match:info()
+
+ --- @type vim.treesitter.query.TSMetadata
+ local metadata
+ if match_id <= highest_cached_match_id then
+ metadata = match_cache[match_id]
end
- local metadata = apply_directives(self, match, source)
+ if not metadata then
+ metadata = {}
+
+ local processed_pattern = self._processed_patterns[pattern_i]
+ if processed_pattern then
+ local captures = match:captures()
- return capture, captured_node, metadata, match
+ local predicates = processed_pattern.predicates
+ if not self:_match_predicates(predicates, pattern_i, captures, source) then
+ cursor:remove_match(match_id)
+ if end_line and captured_node:range() > end_line then
+ return nil, captured_node, nil, nil
+ end
+ return iter(end_line) -- tail call: try next match
+ end
+
+ local directives = processed_pattern.directives
+ metadata = self:_apply_directives(directives, pattern_i, captures, source)
+ end
+
+ highest_cached_match_id = math.max(highest_cached_match_id, match_id)
+ match_cache[match_id] = metadata
+ end
+
+ return capture, captured_node, metadata, match, tree
end
return iter
end
@@ -903,7 +1038,7 @@ end
--- -- `node` was captured by the `name` capture in the match
---
--- local node_data = metadata[id] -- Node level metadata
---- ... use the info here ...
+--- -- ... use the info here ...
--- end
--- end
--- end
@@ -922,7 +1057,7 @@ end
--- (last) node instead of the full list of matching nodes. This option is only for backward
--- compatibility and will be removed in a future release.
---
----@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata): pattern id, match, metadata
+---@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree): pattern id, match, metadata, tree
function Query:iter_matches(node, source, start, stop, opts)
opts = opts or {}
opts.match_limit = opts.match_limit or 256
@@ -933,6 +1068,8 @@ function Query:iter_matches(node, source, start, stop, opts)
start, stop = value_or_node_range(start, stop, node)
+ -- Copy the tree to ensure it is valid during the entire lifetime of the iterator
+ local tree = node:tree():copy()
local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts)
local function iter()
@@ -942,17 +1079,22 @@ function Query:iter_matches(node, source, start, stop, opts)
return
end
- local match_id, pattern = match:info()
+ local match_id, pattern_i = match:info()
+ local processed_pattern = self._processed_patterns[pattern_i]
+ local captures = match:captures()
- if not self:match_preds(match, source) then
- cursor:remove_match(match_id)
- return iter() -- tail call: try next match
+ --- @type vim.treesitter.query.TSMetadata
+ local metadata = {}
+ if processed_pattern then
+ local predicates = processed_pattern.predicates
+ if not self:_match_predicates(predicates, pattern_i, captures, source) then
+ cursor:remove_match(match_id)
+ return iter() -- tail call: try next match
+ end
+ local directives = processed_pattern.directives
+ metadata = self:_apply_directives(directives, pattern_i, captures, source)
end
- local metadata = self:apply_directives(match, source)
-
- local captures = match:captures()
-
if opts.all == false then
-- Convert the match table into the old buggy version for backward
-- compatibility. This is slow, but we only do it when the caller explicitly opted into it by
@@ -961,11 +1103,11 @@ function Query:iter_matches(node, source, start, stop, opts)
for k, v in pairs(captures or {}) do
old_match[k] = v[#v]
end
- return pattern, old_match, metadata
+ return pattern_i, old_match, metadata
end
-- TODO(lewis6991): create a new function that returns {match, metadata}
- return pattern, captures, metadata
+ return pattern_i, captures, metadata, tree
end
return iter
end
diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua
index b4e4098b91..8b6f1a61ee 100644
--- a/runtime/lua/vim/uri.lua
+++ b/runtime/lua/vim/uri.lua
@@ -15,7 +15,7 @@ local PATTERNS = {
rfc2396 = "^A-Za-z0-9%-_.!~*'()",
-- RFC 2732
-- https://tools.ietf.org/html/rfc2732
- rfc2732 = "^A-Za-z0-9%-_.!~*'()[]",
+ rfc2732 = "^A-Za-z0-9%-_.!~*'()%[%]",
-- RFC 3986
-- https://tools.ietf.org/html/rfc3986#section-2.2
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/",
@@ -60,9 +60,10 @@ end
---@param path string Path to file
---@return string URI
function M.uri_from_fname(path)
- local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string?
+ local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string?, string?
local is_windows = volume_path ~= nil
if is_windows then
+ assert(fname)
path = volume_path .. M.uri_encode(fname:gsub('\\', '/'))
else
path = M.uri_encode(path)
@@ -111,7 +112,7 @@ function M.uri_to_fname(uri)
uri = M.uri_decode(uri)
--TODO improve this.
if is_windows_file_uri(uri) then
- uri = uri:gsub('^file:/+', ''):gsub('/', '\\')
+ uri = uri:gsub('^file:/+', ''):gsub('/', '\\') --- @type string
else
uri = uri:gsub('^file:/+', '/') ---@type string
end
diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua
index d64ef98d2d..06c54ac033 100644
--- a/runtime/lua/vim/version.lua
+++ b/runtime/lua/vim/version.lua
@@ -227,8 +227,7 @@ end
---@field to? vim.Version
local VersionRange = {}
---- @private
----
+---@nodoc
---@param version string|vim.Version
function VersionRange:has(version)
if type(version) == 'string' then
diff --git a/runtime/lua/vim/vimhelp.lua b/runtime/lua/vim/vimhelp.lua
index 5579cc0174..a494d311b1 100644
--- a/runtime/lua/vim/vimhelp.lua
+++ b/runtime/lua/vim/vimhelp.lua
@@ -7,7 +7,7 @@ local M = {}
--- Note: {patterns} is assumed to be sorted by occurrence in the file.
--- @param patterns {start:string,stop:string,match:string}[]
function M.highlight_groups(patterns)
- local ns = vim.api.nvim_create_namespace('vimhelp')
+ local ns = vim.api.nvim_create_namespace('nvim.vimhelp')
vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
local save_cursor = vim.fn.getcurpos()
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 48a4bd2816..6866d46d51 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2024 Jul 12
+" Last Change: 2024 Dec 07
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" If there already is an option window, jump to that one.
@@ -626,8 +626,8 @@ call <SID>AddOption("terse", gettext("add 's' flag in 'shortmess' (don't show se
call <SID>BinOptionG("terse", &terse)
call <SID>AddOption("shortmess", gettext("list of flags to make messages shorter"))
call <SID>OptionG("shm", &shm)
-call <SID>AddOption("msghistory", gettext("how many messages are remembered"))
-call append("$", " \tset mhi=" . &mhi)
+call <SID>AddOption("messagesopt", gettext("options for outputting messages"))
+call <SID>OptionG("mopt", &mopt)
call <SID>AddOption("showcmd", gettext("show (partial) command keys in location given by 'showcmdloc'"))
let &sc = s:old_sc
call <SID>BinOptionG("sc", &sc)
diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim
index aa977488e5..8a220eeb9b 100644
--- a/runtime/pack/dist/opt/matchit/autoload/matchit.vim
+++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim
@@ -222,6 +222,7 @@ function matchit#Match_wrapper(word, forward, mode) range
let view = winsaveview()
call cursor(0, curcol + 1)
if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
+ \ || skip =~ 'v:lua.vim.treesitter' && !exists('b:ts_highlight')
let skip = "0"
else
execute "if " .. skip .. "| let skip = '0' | endif"
@@ -678,6 +679,7 @@ fun! matchit#MultiMatch(spflag, mode)
let middlepat = substitute(middlepat, ',', '\\|', 'g')
if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on"))
+ \ || skip =~ 'v:lua.vim.treesitter' && !exists('b:ts_highlight')
let skip = '0'
else
try
@@ -760,10 +762,16 @@ endfun
" S:foo becomes (current syntax item) !~ foo
" r:foo becomes (line before cursor) =~ foo
" R:foo becomes (line before cursor) !~ foo
+" t:foo becomes (current treesitter captures) =~ foo
+" T:foo becomes (current treesitter captures) !~ foo
fun! s:ParseSkip(str)
let skip = a:str
if skip[1] == ":"
- if skip[0] ==# "s"
+ if skip[0] ==# "t" || skip[0] ==# "s" && &syntax != 'on' && exists("b:ts_highlight")
+ let skip = "match(v:lua.vim.treesitter.get_captures_at_cursor(), '" .. strpart(skip,2) .. "') != -1"
+ elseif skip[0] ==# "T" || skip[0] ==# "S" && &syntax != 'on' && exists("b:ts_highlight")
+ let skip = "match(v:lua.vim.treesitter.get_captures_at_cursor(), '" .. strpart(skip,2) .. "') == -1"
+ elseif skip[0] ==# "s"
let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" ..
\ strpart(skip,2) .. "'"
elseif skip[0] ==# "S"
diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt
index 8d56df6ddc..58031d8a8a 100644
--- a/runtime/pack/dist/opt/matchit/doc/matchit.txt
+++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt
@@ -237,6 +237,8 @@ supported by matchit.vim:
S:foo becomes (current syntax item) !~ foo
r:foo becomes (line before cursor) =~ foo
R:foo becomes (line before cursor) !~ foo
+ t:foo becomes (current treesitter captures) =~ foo
+ T:foo becomes (current treesitter captures) !~ foo
(The "s" is meant to suggest "syntax", and the "r" is meant to suggest
"regular expression".)
diff --git a/runtime/pack/dist/opt/netrw/LICENSE.txt b/runtime/pack/dist/opt/netrw/LICENSE.txt
new file mode 100644
index 0000000000..702c2386ac
--- /dev/null
+++ b/runtime/pack/dist/opt/netrw/LICENSE.txt
@@ -0,0 +1,16 @@
+Unless otherwise stated, all files in this directory are distributed under the
+Zero-Clause BSD license.
+
+Zero-Clause BSD
+===============
+
+Permission to use, copy, modify, and/or distribute this software for
+any purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/runtime/pack/dist/opt/netrw/README.md b/runtime/pack/dist/opt/netrw/README.md
new file mode 100644
index 0000000000..ecd97f1e9a
--- /dev/null
+++ b/runtime/pack/dist/opt/netrw/README.md
@@ -0,0 +1,544 @@
+# Netrw.vim
+
+netrw.vim plugin from vim (upstream repository)
+
+The upstream maintained netrw plugin. The original has been created and
+maintained by Charles E Campbell and maintained by the vim project until
+v9.1.0988.
+
+Every major version a snapshot from here will be sent to the main [Vim][1]
+upstream for distribution with Vim.
+
+# License
+
+To see License informations see the LICENSE.txt file included in this
+repository.
+
+# Credits
+
+Below are stated the contribution made in the past to netrw.
+
+Changes made to `autoload/netrw.vim`:
+- 2023 Nov 21 by Vim Project: ignore wildignore when expanding $COMSPEC (v173a)
+- 2023 Nov 22 by Vim Project: fix handling of very long filename on longlist style (v173a)
+- 2024 Feb 19 by Vim Project: (announce adoption)
+- 2024 Feb 29 by Vim Project: handle symlinks in tree mode correctly
+- 2024 Apr 03 by Vim Project: detect filetypes for remote edited files
+- 2024 May 08 by Vim Project: cleanup legacy Win9X checks
+- 2024 May 09 by Vim Project: remove hard-coded private.ppk
+- 2024 May 10 by Vim Project: recursively delete directories by default
+- 2024 May 13 by Vim Project: prefer scp over pscp
+- 2024 Jun 04 by Vim Project: set bufhidden if buffer changed, nohidden is set and buffer shall be switched (#14915)
+- 2024 Jun 13 by Vim Project: glob() on Windows fails when a directory name contains [] (#14952)
+- 2024 Jun 23 by Vim Project: save ad restore registers when liststyle = WIDELIST (#15077, #15114)
+- 2024 Jul 22 by Vim Project: avoid endless recursion (#15318)
+- 2024 Jul 23 by Vim Project: escape filename before trying to delete it (#15330)
+- 2024 Jul 30 by Vim Project: handle mark-copy to same target directory (#12112)
+- 2024 Aug 02 by Vim Project: honor g:netrw_alt{o,v} for :{S,H,V}explore (#15417)
+- 2024 Aug 15 by Vim Project: style changes, prevent E121 (#15501)
+- 2024 Aug 22 by Vim Project: fix mf-selection highlight (#15551)
+- 2024 Aug 22 by Vim Project: adjust echo output of mx command (#15550)
+- 2024 Sep 15 by Vim Project: more strict confirmation dialog (#15680)
+- 2024 Sep 19 by Vim Project: mf-selection highlight uses wrong pattern (#15700)
+- 2024 Sep 21 by Vim Project: remove extraneous closing bracket (#15718)
+- 2024 Oct 21 by Vim Project: remove netrwFileHandlers (#15895)
+- 2024 Oct 27 by Vim Project: clean up gx mapping (#15721)
+- 2024 Oct 30 by Vim Project: fix filetype detection for remote files (#15961)
+- 2024 Oct 30 by Vim Project: fix x mapping on cygwin (#13687)
+- 2024 Oct 31 by Vim Project: add netrw#Launch() and netrw#Open() (#15962)
+- 2024 Oct 31 by Vim Project: fix E874 when browsing remote dir (#15964)
+- 2024 Nov 07 by Vim Project: use keeppatterns to prevent polluting the search history
+- 2024 Nov 07 by Vim Project: fix a few issues with netrw tree listing (#15996)
+- 2024 Nov 10 by Vim Project: directory symlink not resolved in tree view (#16020)
+- 2024 Nov 14 by Vim Project: small fixes to netrw#BrowseX (#16056)
+- 2024 Nov 23 by Vim Project: update decompress defaults (#16104)
+- 2024 Nov 23 by Vim Project: fix powershell escaping issues (#16094)
+- 2024 Dec 04 by Vim Project: do not detach for gvim (#16168)
+- 2024 Dec 08 by Vim Project: check the first arg of netrw_browsex_viewer for being executable (#16185)
+- 2024 Dec 12 by Vim Project: do not pollute the search history (#16206)
+- 2024 Dec 19 by Vim Project: change style (#16248)
+- 2024 Dec 20 by Vim Project: change style continued (#16266), fix escaping of # in :Open command (#16265)
+
+General changes made to netrw:
+
+```
+ v172: Sep 02, 2021 * (Bram Moolenaar) Changed "l:go" to "go"
+ * (Bram Moolenaar) no need for "b" in
+ netrw-safe guioptions
+ Nov 15, 2021 * removed netrw_localrm and netrw_localrmdir
+ references
+ Aug 18, 2022 * (Miguel Barro) improving compatibility with
+ powershell
+ v171: Oct 09, 2020 * included code in s:NetrwOptionsSafe()
+ to allow |'bh'| to be set to delete when
+ rather than hide when g:netrw_fastbrowse
+ was zero.
+ * Installed |g:netrw_clipboard| setting
+ * Installed option bypass for |'guioptions'|
+ a/A settings
+ * Changed popup_beval() to |popup_atcursor()|
+ in netrw#ErrorMsg (lacygoill). Apparently
+ popup_beval doesn't reliably close the
+ popup when the mouse is moved.
+ * VimEnter() now using win_execute to examine
+ buffers for an attempt to open a directory.
+ Avoids issues with popups/terminal from
+ command line. (lacygoill)
+ Jun 28, 2021 * (zeertzjq) provided a patch for use of
+ xmap,xno instead of vmap,vno in
+ netrwPlugin.vim. Avoids entanglement with
+ select mode.
+ Jul 14, 2021 * Fixed problem addressed by tst976; opening
+ a file using tree mode, going up a
+ directory, and opening a file there was
+ opening the file in the wrong directory.
+ Jul 28, 2021 * (Ingo Karkat) provided a patch fixing an
+ E488 error with netrwPlugin.vim
+ (occurred for vim versions < 8.02)
+ v170: Mar 11, 2020 * (reported by Reiner Herrmann) netrw+tree
+ would not hide with the ^\..* pattern
+ correctly.
+ * (Marcin Szamotulski) NetrwOptionRestore
+ 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()|)
+ Apr 30, 2020 * (reported by Manatsu Takahashi) while
+ using Lexplore, a modified file could
+ be overwritten. Sol'n: will not overwrite,
+ but will emit an |E37| (although one cannot
+ add an ! to override)
+ Jun 07, 2020 * (reported by Jo Totland) repeatedly invoking
+ :Lexplore and quitting it left unused
+ hidden buffers. Netrw will now set netrw
+ buffers created by :Lexplore to |'bh'|=wipe.
+ v169: Dec 20, 2019 * (reported by amkarthik) that netrw's x
+ (|netrw-x|) would throw an error when
+ attempting to open a local directory.
+ v168: Dec 12, 2019 * scp timeout error message not reported,
+ hopefully now fixed (Shane Xb Qian)
+ v167: Nov 29, 2019 * netrw does a save&restore on @* and @+.
+ That causes problems with the clipboard.
+ Now restores occurs only if @* or @+ have
+ been changed.
+ * netrw will change @* or @+ less often.
+ Never if I happen to have caught all the
+ operations that modify the unnamed
+ register (which also writes @*).
+ * Modified hiding behavior so that "s"
+ will not ignore hiding.
+ v166: Nov 06, 2019 * Removed a space from a nmap for "-"
+ * Numerous debugging statement changes
+ v163: Dec 05, 2017 * (Cristi Balan) reported that a setting ('sel')
+ was left changed
+ * (Holger Mitschke) reported a problem with
+ saving and restoring history. Fixed.
+ * Hopefully I fixed a nasty bug that caused a
+ file rename to wipe out a buffer that it
+ should not have wiped out.
+ * (Holger Mitschke) amended this help file
+ with additional |g:netrw_special_syntax|
+ items
+ * Prioritized wget over curl for
+ g:netrw_http_cmd
+ v162: Sep 19, 2016 * (haya14busa) pointed out two syntax errors
+ with a patch; these are now fixed.
+ Oct 26, 2016 * I started using mate-terminal and found that
+ x and gx (|netrw-x| and |netrw-gx|) were no
+ longer working. Fixed (using atril when
+ $DESKTOP_SESSION is "mate").
+ Nov 04, 2016 * (Martin Vuille) pointed out that @+ was
+ being restored with keepregstar rather than
+ keepregplus.
+ Nov 09, 2016 * Broke apart the command from the options,
+ mostly for Windows. Introduced new netrw
+ settings: |g:netrw_localcopycmdopt|
+ |g:netrw_localcopydircmdopt|
+ |g:netrw_localmkdiropt|
+ |g:netrw_localmovecmdopt|
+ Nov 21, 2016 * (mattn) provided a patch for preview; swapped
+ winwidth() with winheight()
+ 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
+ spaces inside the quotes also had to be
+ 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|)
+ when atop "Hiding" in the banner also caused
+ the active-banner hiding control to occur
+ Jan 03, 2017 * (Enno Nagel) reported that attempting to
+ apply netrw to a directory that was without
+ read permission caused a syntax error.
+ Jan 13, 2017 * (Ingo Karkat) provided a patch which makes
+ using netrw#Call() better. Now returns
+ value of internal routines return, for example.
+ Jan 13, 2017 * (Ingo Karkat) changed netrw#FileUrlRead to
+ use |:edit| instead of |:read|. I also
+ changed the routine name to netrw#FileUrlEdit.
+ Jan 16, 2017 * (Sayem) reported a problem where :Lexplore
+ could generate a new listing buffer and
+ window instead of toggling the netrw display.
+ Unfortunately, the directions for eliciting
+ the problem weren't complete, so I may or
+ may not have fixed that issue.
+ Feb 06, 2017 * Implemented cb and cB. Changed "c" to "cd".
+ (see |netrw-cb|, |netrw-cB|, and |netrw-cd|)
+ Mar 21, 2017 * previously, netrw would specify (safe) settings
+ even when the setting was already safe for
+ netrw. Netrw now attempts to leave such
+ already-netrw-safe settings alone.
+ (affects s:NetrwOptionRestore() and
+ s:NetrwSafeOptions(); also introduced
+ s:NetrwRestoreSetting())
+ Jun 26, 2017 * (Christian Brabandt) provided a patch to
+ allow curl to follow redirects (ie. -L
+ option)
+ Jun 26, 2017 * (Callum Howard) reported a problem with
+ :Lexpore not removing the Lexplore window
+ after a change-directory
+ Aug 30, 2017 * (Ingo Karkat) one cannot switch to the
+ previously edited file (e.g. with CTRL-^)
+ after editing a file:// URL. Patch to
+ have a "keepalt" included.
+ Oct 17, 2017 * (Adam Faryna) reported that gn (|netrw-gn|)
+ did not work on directories in the current
+ tree
+ v157: Apr 20, 2016 * (Nicola) had set up a "nmap <expr> ..." with
+ a function that returned a 0 while silently
+ invoking a shell command. The shell command
+ activated a ShellCmdPost event which in turn
+ called s:LocalBrowseRefresh(). That looks
+ over all netrw buffers for changes needing
+ refreshes. However, inside a |:map-<expr>|,
+ tab and window changes are disallowed. Fixed.
+ (affects netrw's s:LocalBrowseRefresh())
+ * g:netrw_localrmdir not used any more, but
+ the relevant patch that causes |delete()| to
+ take over was #1107 (not #1109).
+ * |expand()| is now used on |g:netrw_home|;
+ consequently, g:netrw_home may now use
+ environment variables
+ * s:NetrwLeftmouse and s:NetrwCLeftmouse will
+ return without doing anything if invoked
+ when inside a non-netrw window
+ Jun 15, 2016 * gx now calls netrw#GX() which returns
+ the word under the cursor. The new
+ wrinkle: if one is in a netrw buffer,
+ then netrw's s:NetrwGetWord().
+ Jun 22, 2016 * Netrw was executing all its associated
+ Filetype commands silently; I'm going
+ to try doing that "noisily" and see if
+ folks have a problem with that.
+ Aug 12, 2016 * Changed order of tool selection for
+ handling http://... viewing.
+ (Nikolay Aleksandrovich Pavlov)
+ Aug 21, 2016 * Included hiding/showing/all for tree
+ listings
+ * Fixed refresh (^L) for tree listings
+ v156: Feb 18, 2016 * Changed =~ to =~# where appropriate
+ Feb 23, 2016 * s:ComposePath(base,subdir) now uses
+ fnameescape() on the base portion
+ Mar 01, 2016 * (gt_macki) reported where :Explore would
+ make file unlisted. Fixed (tst943)
+ Apr 04, 2016 * (reported by John Little) netrw normally
+ suppresses browser messages, but sometimes
+ those "messages" are what is wanted.
+ See |g:netrw_suppress_gx_mesg|
+ Apr 06, 2016 * (reported by Carlos Pita) deleting a remote
+ file was giving an error message. Fixed.
+ Apr 08, 2016 * (Charles Cooper) had a problem with an
+ undefined b:netrw_curdir. He also provided
+ a fix.
+ Apr 20, 2016 * Changed s:NetrwGetBuffer(); now uses
+ dictionaries. Also fixed the "No Name"
+ buffer problem.
+ v155: Oct 29, 2015 * (Timur Fayzrakhmanov) reported that netrw's
+ mapping of ctrl-l was not allowing refresh of
+ other windows when it was done in a netrw
+ window.
+ Nov 05, 2015 * Improved s:TreeSqueezeDir() to use search()
+ instead of a loop
+ * NetrwBrowse() will return line to
+ w:netrw_bannercnt if cursor ended up in
+ banner
+ Nov 16, 2015 * Added a <Plug>NetrwTreeSqueeze (|netrw-s-cr|)
+ Nov 17, 2015 * Commented out imaps -- perhaps someone can
+ tell me how they're useful and should be
+ retained?
+ Nov 20, 2015 * Added |netrw-ma| and |netrw-mA| support
+ Nov 20, 2015 * gx (|netrw-gx|) on a URL downloaded the
+ file in addition to simply bringing up the
+ URL in a browser. Fixed.
+ Nov 23, 2015 * Added |g:netrw_sizestyle| support
+ Nov 27, 2015 * Inserted a lot of <c-u>s into various netrw
+ maps.
+ Jan 05, 2016 * |netrw-qL| implemented to mark files based
+ upon |location-list|s; similar to |netrw-qF|.
+ Jan 19, 2016 * using - call delete(directoryname,"d") -
+ instead of using g:netrw_localrmdir if
+ v7.4 + patch#1107 is available
+ Jan 28, 2016 * changed to using |winsaveview()| and
+ |winrestview()|
+ Jan 28, 2016 * s:NetrwTreePath() now does a save and
+ restore of view
+ Feb 08, 2016 * Fixed a tree-listing problem with remote
+ directories
+ v154: Feb 26, 2015 * (Yuri Kanivetsky) reported a situation where
+ a file was not treated properly as a file
+ due to g:netrw_keepdir == 1
+ Mar 25, 2015 * (requested by Ben Friz) one may now sort by
+ extension
+ Mar 28, 2015 * (requested by Matt Brooks) netrw has a lot
+ of buffer-local mappings; however, some
+ plugins (such as vim-surround) set up
+ conflicting mappings that cause vim to wait.
+ The "<nowait>" modifier has been included
+ with most of netrw's mappings to avoid that
+ delay.
+ Jun 26, 2015 * |netrw-gn| mapping implemented
+ * :Ntree NotADir resulted in having
+ the tree listing expand in the error messages
+ window. Fixed.
+ Jun 29, 2015 * Attempting to delete a file remotely caused
+ an error with "keepsol" mentioned; fixed.
+ Jul 08, 2015 * Several changes to keep the |:jumps| table
+ correct when working with
+ |g:netrw_fastbrowse| set to 2
+ * wide listing with accented characters fixed
+ (using %-S instead of %-s with a |printf()|
+ Jul 13, 2015 * (Daniel Hahler) CheckIfKde() could be true
+ but kfmclient not installed. Changed order
+ in netrw#BrowseX(): checks if kde and
+ kfmclient, then will use xdg-open on a unix
+ system (if xdg-open is executable)
+ Aug 11, 2015 * (McDonnell) tree listing mode wouldn't
+ select a file in a open subdirectory.
+ * (McDonnell) when multiple subdirectories
+ were concurrently open in tree listing
+ mode, a ctrl-L wouldn't refresh properly.
+ * The netrw:target menu showed duplicate
+ entries
+ Oct 13, 2015 * (mattn) provided an exception to handle
+ windows with shellslash set but no shell
+ Oct 23, 2015 * if g:netrw_usetab and <c-tab> now used
+ to control whether NetrwShrink is used
+ (see |netrw-c-tab|)
+ v153: May 13, 2014 * added another |g:netrw_ffkeep| usage {{{2
+ May 14, 2014 * changed s:PerformListing() so that it
+ always sets ft=netrw for netrw buffers
+ (ie. even when syntax highlighting is
+ off, not available, etc)
+ May 16, 2014 * introduced the |netrw-ctrl-r| functionality
+ May 17, 2014 * introduced the |netrw-:NetrwMB| functionality
+ * mb and mB (|netrw-mb|, |netrw-mB|) will
+ add/remove marked files from bookmark list
+ May 20, 2014 * (Enno Nagel) reported that :Lex <dirname>
+ wasn't working. Fixed.
+ May 26, 2014 * restored test to prevent leftmouse window
+ resizing from causing refresh.
+ (see s:NetrwLeftmouse())
+ * fixed problem where a refresh caused cursor
+ to go just under the banner instead of
+ staying put
+ May 28, 2014 * (László Bimba) provided a patch for opening
+ the |:Lexplore| window 100% high, optionally
+ on the right, and will work with remote
+ files.
+ May 29, 2014 * implemented :NetrwC (see |netrw-:NetrwC|)
+ Jun 01, 2014 * Removed some "silent"s from commands used
+ to implemented scp://... and pscp://...
+ directory listing. Permits request for
+ password to appear.
+ Jun 05, 2014 * (Enno Nagel) reported that user maps "/"
+ caused problems with "b" and "w", which
+ are mapped (for wide listings only) to
+ skip over files rather than just words.
+ Jun 10, 2014 * |g:netrw_gx| introduced to allow users to
+ override default "<cfile>" with the gx
+ (|netrw-gx|) map
+ Jun 11, 2014 * gx (|netrw-gx|), with |'autowrite'| set,
+ will write modified files. s:NetrwBrowseX()
+ will now save, turn off, and restore the
+ |'autowrite'| setting.
+ Jun 13, 2014 * added visual map for gx use
+ Jun 15, 2014 * (Enno Nagel) reported that with having hls
+ set and wide listing style in use, that the
+ b and w maps caused unwanted highlighting.
+ Jul 05, 2014 * |netrw-mv| and |netrw-mX| commands included
+ Jul 09, 2014 * |g:netrw_keepj| included, allowing optional
+ keepj
+ Jul 09, 2014 * fixing bugs due to previous update
+ Jul 21, 2014 * (Bruno Sutic) provided an updated
+ netrw_gitignore.vim
+ Jul 30, 2014 * (Yavuz Yetim) reported that editing two
+ remote files of the same name caused the
+ second instance to have a "temporary"
+ name. Fixed: now they use the same buffer.
+ Sep 18, 2014 * (Yasuhiro Matsumoto) provided a patch which
+ allows scp and windows local paths to work.
+ Oct 07, 2014 * gx (see |netrw-gx|) when atop a directory,
+ will now do |gf| instead
+ Nov 06, 2014 * For cygwin: cygstart will be available for
+ netrw#BrowseX() to use if its executable.
+ Nov 07, 2014 * Began support for file://... urls. Will use
+ |g:netrw_file_cmd| (typically elinks or links)
+ Dec 02, 2014 * began work on having mc (|netrw-mc|) copy
+ directories. Works for linux machines,
+ cygwin+vim, but not for windows+gvim.
+ Dec 02, 2014 * in tree mode, netrw was not opening
+ directories via symbolic links.
+ Dec 02, 2014 * added resolved link information to
+ thin and tree modes
+ Dec 30, 2014 * (issue#231) |:ls| was not showing
+ remote-file buffers reliably. Fixed.
+ v152: Apr 08, 2014 * uses the |'noswapfile'| option (requires {{{2
+ vim 7.4 with patch 213)
+ * (Enno Nagel) turn |'rnu'| off in netrw
+ buffers.
+ * (Quinn Strahl) suggested that netrw
+ allow regular window splitting to occur,
+ thereby allowing |'equalalways'| to take
+ effect.
+ * (qingtian zhao) normally, netrw will
+ save and restore the |'fileformat'|;
+ however, sometimes that isn't wanted
+ Apr 14, 2014 * whenever netrw marks a buffer as ro,
+ it will also mark it as nomod.
+ Apr 16, 2014 * sftp protocol now supported by
+ netrw#Obtain(); this means that one
+ may use "mc" to copy a remote file
+ to a local file using sftp, and that
+ the |netrw-O| command can obtain remote
+ files via sftp.
+ * added [count]C support (see |netrw-C|)
+ Apr 18, 2014 * when |g:netrw_chgwin| is one more than
+ the last window, then vertically split
+ the last window and use it as the
+ chgwin window.
+ May 09, 2014 * SavePosn was "saving filename under cursor"
+ from a non-netrw window when using :Rex.
+ v151: Jan 22, 2014 * extended :Rexplore to return to buffer {{{2
+ prior to Explore or editing a directory
+ * (Ken Takata) netrw gave error when
+ clipboard was disabled. Sol'n: Placed
+ several if has("clipboard") tests in.
+ * Fixed ftp://X@Y@Z// problem; X@Y now
+ part of user id, and only Z is part of
+ hostname.
+ * (A Loumiotis) reported that completion
+ using a directory name containing spaces
+ did not work. Fixed with a retry in
+ netrw#Explore() which removes the
+ backslashes vim inserted.
+ Feb 26, 2014 * :Rexplore now records the current file
+ using w:netrw_rexfile when returning via
+ |:Rexplore|
+ Mar 08, 2014 * (David Kotchan) provided some patches
+ allowing netrw to work properly with
+ windows shares.
+ * Multiple one-liner help messages available
+ by pressing <cr> while atop the "Quick
+ Help" line
+ * worked on ShellCmdPost, FocusGained event
+ handling.
+ * |:Lexplore| path: will be used to update
+ a left-side netrw browsing directory.
+ Mar 12, 2014 * |netrw-s-cr|: use <s-cr> to close
+ tree directory implemented
+ Mar 13, 2014 * (Tony Mechylynck) reported that using
+ the browser with ftp on a directory,
+ and selecting a gzipped txt file, that
+ an E19 occurred (which was issued by
+ gzip.vim). Fixed.
+ Mar 14, 2014 * Implemented :MF and :MT (see |netrw-:MF|
+ and |netrw-:MT|, respectively)
+ Mar 17, 2014 * |:Ntree| [dir] wasn't working properly; fixed
+ Mar 18, 2014 * Changed all uses of set to setl
+ Mar 18, 2014 * Commented the netrw_btkeep line in
+ s:NetrwOptionSave(); the effect is that
+ netrw buffers will remain as |'bt'|=nofile.
+ This should prevent swapfiles being created
+ for netrw buffers.
+ Mar 20, 2014 * Changed all uses of lcd to use s:NetrwLcd()
+ instead. Consistent error handling results
+ and it also handles Window's shares
+ * Fixed |netrw-d| command when applied with ftp
+ * https: support included for netrw#NetRead()
+ v150: Jul 12, 2013 * removed a "keepalt" to allow ":e #" to {{{2
+ return to the netrw directory listing
+ Jul 13, 2013 * (Jonas Diemer) suggested changing
+ a <cWORD> to <cfile>.
+ Jul 21, 2013 * (Yuri Kanivetsky) reported that netrw's
+ use of mkdir did not produce directories
+ following the user's umask.
+ Aug 27, 2013 * introduced |g:netrw_altfile| option
+ Sep 05, 2013 * s:Strlen() now uses |strdisplaywidth()|
+ when available, by default
+ Sep 12, 2013 * (Selyano Baldo) reported that netrw wasn't
+ opening some directories properly from the
+ command line.
+ Nov 09, 2013 * |:Lexplore| introduced
+ * (Ondrej Platek) reported an issue with
+ netrw's trees (P15). Fixed.
+ * (Jorge Solis) reported that "t" in
+ tree mode caused netrw to forget its
+ line position.
+ Dec 05, 2013 * Added <s-leftmouse> file marking
+ (see |netrw-mf|)
+ Dec 05, 2013 * (Yasuhiro Matsumoto) Explore should use
+ strlen() instead s:Strlen() when handling
+ multibyte chars with strpart()
+ (ie. strpart() is byte oriented, not
+ display-width oriented).
+ Dec 09, 2013 * (Ken Takata) Provided a patch; File sizes
+ and a portion of timestamps were wrongly
+ highlighted with the directory color when
+ setting `:let g:netrw_liststyle=1` on Windows.
+ * (Paul Domaskis) noted that sometimes
+ cursorline was activating in non-netrw
+ windows. All but one setting of cursorline
+ was done via setl; there was one that was
+ overlooked. Fixed.
+ Dec 24, 2013 * (esquifit) asked that netrw allow the
+ /cygdrive prefix be a user-alterable
+ parameter.
+ Jan 02, 2014 * Fixed a problem with netrw-based ballon
+ evaluation (ie. netrw#NetrwBaloonHelp()
+ not having been loaded error messages)
+ Jan 03, 2014 * Fixed a problem with tree listings
+ * New command installed: |:Ntree|
+ Jan 06, 2014 * (Ivan Brennan) reported a problem with
+ |netrw-P|. Fixed.
+ Jan 06, 2014 * Fixed a problem with |netrw-P| when the
+ modified file was to be abandoned.
+ Jan 15, 2014 * (Matteo Cavalleri) reported that when the
+ banner is suppressed and tree listing is
+ used, a blank line was left at the top of
+ the display. Fixed.
+ Jan 20, 2014 * (Gideon Go) reported that, in tree listing
+ style, with a previous window open, that
+ the wrong directory was being used to open
+ a file. Fixed. (P21)
+ v149: Apr 18, 2013 * in wide listing format, now have maps for {{{2
+ w and b to move to next/previous file
+ Apr 26, 2013 * one may now copy files in the same
+ directory; netrw will issue requests for
+ what names the files should be copied under
+ Apr 29, 2013 * Trying Benzinger's problem again. Seems
+ that commenting out the BufEnter and
+ installing VimEnter (only) works. Weird
+ problem! (tree listing, vim -O Dir1 Dir2)
+ May 01, 2013 * :Explore ftp://... wasn't working. Fixed.
+ May 02, 2013 * introduced |g:netrw_bannerbackslash| as
+ requested by Paul Domaskis.
+ Jul 03, 2013 * Explore now avoids splitting when a buffer
+ will be hidden.
+ v148: Apr 16, 2013 * changed Netrw's Style menu to allow direct {{{2
+ choice of listing style, hiding style, and
+ sorting style
+```
+
+[1]: https://github.com/vim/vim
diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim
new file mode 100644
index 0000000000..ae794954ce
--- /dev/null
+++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim
@@ -0,0 +1,11927 @@
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Former Maintainer: Charles E Campbell
+" Upstream: <https://github.com/saccarosium/netrw.vim>
+" Copyright: Copyright (C) 2016 Charles E. Campbell {{{1
+" Permission is hereby granted to use and distribute this code,
+" with or without modifications, provided that this copyright
+" notice is copied with it. Like anything else that's free,
+" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided
+" *as is* and come with no warranty of any kind, either
+" expressed or implied. By using this plugin, you agree that
+" in no event will the copyright holder be liable for any damages
+" resulting from the use of this software.
+"
+" Note: the code here was started in 1999 under a much earlier version of vim. The directory browsing
+" code was written using vim v6, which did not have Lists (Lists were first offered with vim-v7).
+
+" Load Once: {{{1
+if &cp || exists("g:loaded_netrw")
+ finish
+endif
+
+" Check that vim has patches that netrw requires.
+" Patches needed for v7.4: 1557, and 213.
+" (netrw will benefit from vim's having patch#656, too)
+let s:needspatches=[1557,213]
+if exists("s:needspatches")
+ for ptch in s:needspatches
+ if v:version < 704 || (v:version == 704 && !has("patch".ptch))
+ if !exists("s:needpatch{ptch}")
+ unsilent echomsg "***sorry*** this version of netrw requires vim v7.4 with patch#".ptch
+ endif
+ let s:needpatch{ptch}= 1
+ finish
+ endif
+ endfor
+endif
+
+let g:loaded_netrw = "v175"
+
+let s:keepcpo= &cpo
+setl cpo&vim
+"DechoFuncName 1
+"DechoRemOn
+"call Decho("doing autoload/netrw.vim version ".g:loaded_netrw,'~'.expand("<slnum>"))
+
+" ======================
+" Netrw Variables: {{{1
+" ======================
+
+" ---------------------------------------------------------------------
+" netrw#ErrorMsg: {{{2
+" 0=note = s:NOTE
+" 1=warning = s:WARNING
+" 2=error = s:ERROR
+" Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number)
+" netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number)
+" (this function can optionally take a list of messages)
+" Dec 2, 2019 : max errnum currently is 106
+fun! netrw#ErrorMsg(level,msg,errnum)
+ " call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow)
+
+ if a:level < g:netrw_errorlvl
+ " call Dret("netrw#ErrorMsg : suppressing level=".a:level." since g:netrw_errorlvl=".g:netrw_errorlvl)
+ return
+ endif
+
+ if a:level == 1
+ let level= "**warning** (netrw) "
+ elseif a:level == 2
+ let level= "**error** (netrw) "
+ else
+ let level= "**note** (netrw) "
+ endif
+ " call Decho("level=".level,'~'.expand("<slnum>"))
+
+ if g:netrw_use_errorwindow == 2 && exists("*popup_atcursor")
+ " use popup window
+ if type(a:msg) == 3
+ let msg = [level]+a:msg
+ else
+ let msg= level.a:msg
+ endif
+ let s:popuperr_id = popup_atcursor(msg,{})
+ let s:popuperr_text= ""
+ elseif g:netrw_use_errorwindow
+ " (default) netrw creates a one-line window to show error/warning
+ " messages (reliably displayed)
+
+ " record current window number
+ let s:winBeforeErr= winnr()
+ " call Decho("s:winBeforeErr=".s:winBeforeErr,'~'.expand("<slnum>"))
+
+ " getting messages out reliably is just plain difficult!
+ " This attempt splits the current window, creating a one line window.
+ if bufexists("NetrwMessage") && bufwinnr("NetrwMessage") > 0
+ " call Decho("write to NetrwMessage buffer",'~'.expand("<slnum>"))
+ exe bufwinnr("NetrwMessage")."wincmd w"
+ " call Decho("setl ma noro",'~'.expand("<slnum>"))
+ setl ma noro
+ if type(a:msg) == 3
+ for msg in a:msg
+ NetrwKeepj call setline(line("$")+1,level.msg)
+ endfor
+ else
+ NetrwKeepj call setline(line("$")+1,level.a:msg)
+ endif
+ NetrwKeepj $
+ else
+ " call Decho("create a NetrwMessage buffer window",'~'.expand("<slnum>"))
+ bo 1split
+ sil! call s:NetrwEnew()
+ sil! NetrwKeepj call s:NetrwOptionsSafe(1)
+ setl bt=nofile
+ NetrwKeepj file NetrwMessage
+ " call Decho("setl ma noro",'~'.expand("<slnum>"))
+ setl ma noro
+ if type(a:msg) == 3
+ for msg in a:msg
+ NetrwKeepj call setline(line("$")+1,level.msg)
+ endfor
+ else
+ NetrwKeepj call setline(line("$"),level.a:msg)
+ endif
+ NetrwKeepj $
+ endif
+ " call Decho("wrote msg<".level.a:msg."> to NetrwMessage win#".winnr(),'~'.expand("<slnum>"))
+ if &fo !~ '[ta]'
+ syn clear
+ syn match netrwMesgNote "^\*\*note\*\*"
+ syn match netrwMesgWarning "^\*\*warning\*\*"
+ syn match netrwMesgError "^\*\*error\*\*"
+ hi link netrwMesgWarning WarningMsg
+ hi link netrwMesgError Error
+ endif
+ " call Decho("setl noma ro bh=wipe",'~'.expand("<slnum>"))
+ setl ro nomod noma bh=wipe
+
+ else
+ " (optional) netrw will show messages using echomsg. Even if the
+ " message doesn't appear, at least it'll be recallable via :messages
+ " redraw!
+ if a:level == s:WARNING
+ echohl WarningMsg
+ elseif a:level == s:ERROR
+ echohl Error
+ endif
+
+ if type(a:msg) == 3
+ for msg in a:msg
+ unsilent echomsg level.msg
+ endfor
+ else
+ unsilent echomsg level.a:msg
+ endif
+
+ " call Decho("echomsg ***netrw*** ".a:msg,'~'.expand("<slnum>"))
+ echohl None
+ endif
+
+ " call Dret("netrw#ErrorMsg")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwInit: initializes variables if they haven't been defined {{{2
+" Loosely, varname = value.
+fun s:NetrwInit(varname,value)
+ " call Decho("varname<".a:varname."> value=".a:value,'~'.expand("<slnum>"))
+ if !exists(a:varname)
+ if type(a:value) == 0
+ exe "let ".a:varname."=".a:value
+ elseif type(a:value) == 1 && a:value =~ '^[{[]'
+ exe "let ".a:varname."=".a:value
+ elseif type(a:value) == 1
+ exe "let ".a:varname."="."'".a:value."'"
+ else
+ exe "let ".a:varname."=".a:value
+ endif
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" Netrw Constants: {{{2
+call s:NetrwInit("g:netrw_dirhistcnt",0)
+if !exists("s:LONGLIST")
+ call s:NetrwInit("s:THINLIST",0)
+ call s:NetrwInit("s:LONGLIST",1)
+ call s:NetrwInit("s:WIDELIST",2)
+ call s:NetrwInit("s:TREELIST",3)
+ call s:NetrwInit("s:MAXLIST" ,4)
+endif
+
+let s:NOTE = 0
+let s:WARNING = 1
+let s:ERROR = 2
+call s:NetrwInit("g:netrw_errorlvl", s:NOTE)
+
+" ---------------------------------------------------------------------
+" Default option values: {{{2
+let g:netrw_localcopycmdopt = ""
+let g:netrw_localcopydircmdopt = ""
+let g:netrw_localmkdiropt = ""
+let g:netrw_localmovecmdopt = ""
+
+" ---------------------------------------------------------------------
+" Default values for netrw's global protocol variables {{{2
+if !exists("g:netrw_use_errorwindow")
+ let g:netrw_use_errorwindow = 0
+endif
+
+if !exists("g:netrw_dav_cmd")
+ if executable("cadaver")
+ let g:netrw_dav_cmd = "cadaver"
+ elseif executable("curl")
+ let g:netrw_dav_cmd = "curl"
+ else
+ let g:netrw_dav_cmd = ""
+ endif
+endif
+if !exists("g:netrw_fetch_cmd")
+ if executable("fetch")
+ let g:netrw_fetch_cmd = "fetch -o"
+ else
+ let g:netrw_fetch_cmd = ""
+ endif
+endif
+if !exists("g:netrw_file_cmd")
+ if executable("elinks")
+ call s:NetrwInit("g:netrw_file_cmd","elinks")
+ elseif executable("links")
+ call s:NetrwInit("g:netrw_file_cmd","links")
+ endif
+endif
+if !exists("g:netrw_ftp_cmd")
+ let g:netrw_ftp_cmd = "ftp"
+endif
+let s:netrw_ftp_cmd= g:netrw_ftp_cmd
+if !exists("g:netrw_ftp_options")
+ let g:netrw_ftp_options= "-i -n"
+endif
+if !exists("g:netrw_http_cmd")
+ if executable("wget")
+ let g:netrw_http_cmd = "wget"
+ call s:NetrwInit("g:netrw_http_xcmd","-q -O")
+ elseif executable("curl")
+ let g:netrw_http_cmd = "curl"
+ call s:NetrwInit("g:netrw_http_xcmd","-L -o")
+ elseif executable("elinks")
+ let g:netrw_http_cmd = "elinks"
+ call s:NetrwInit("g:netrw_http_xcmd","-source >")
+ elseif executable("fetch")
+ let g:netrw_http_cmd = "fetch"
+ call s:NetrwInit("g:netrw_http_xcmd","-o")
+ elseif executable("links")
+ let g:netrw_http_cmd = "links"
+ call s:NetrwInit("g:netrw_http_xcmd","-http.extra-header ".shellescape("Accept-Encoding: identity", 1)." -source >")
+ else
+ let g:netrw_http_cmd = ""
+ endif
+endif
+call s:NetrwInit("g:netrw_http_put_cmd","curl -T")
+call s:NetrwInit("g:netrw_keepj","keepj")
+call s:NetrwInit("g:netrw_rcp_cmd" , "rcp")
+call s:NetrwInit("g:netrw_rsync_cmd", "rsync")
+call s:NetrwInit("g:netrw_rsync_sep", "/")
+if !exists("g:netrw_scp_cmd")
+ if executable("scp")
+ call s:NetrwInit("g:netrw_scp_cmd" , "scp -q")
+ elseif executable("pscp")
+ call s:NetrwInit("g:netrw_scp_cmd", 'pscp -q')
+ else
+ call s:NetrwInit("g:netrw_scp_cmd" , "scp -q")
+ endif
+endif
+call s:NetrwInit("g:netrw_sftp_cmd" , "sftp")
+call s:NetrwInit("g:netrw_ssh_cmd" , "ssh")
+
+if has("win32")
+ \ && exists("g:netrw_use_nt_rcp")
+ \ && g:netrw_use_nt_rcp
+ \ && executable( $SystemRoot .'/system32/rcp.exe')
+ let s:netrw_has_nt_rcp = 1
+ let s:netrw_rcpmode = '-b'
+else
+ let s:netrw_has_nt_rcp = 0
+ let s:netrw_rcpmode = ''
+endif
+
+" ---------------------------------------------------------------------
+" Default values for netrw's global variables {{{2
+" Cygwin Detection ------- {{{3
+if !exists("g:netrw_cygwin")
+ if has("win32unix") && &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$'
+ let g:netrw_cygwin= 1
+ else
+ let g:netrw_cygwin= 0
+ endif
+endif
+" Default values - a-c ---------- {{{3
+call s:NetrwInit("g:netrw_alto" , &sb)
+call s:NetrwInit("g:netrw_altv" , &spr)
+call s:NetrwInit("g:netrw_banner" , 1)
+call s:NetrwInit("g:netrw_browse_split", 0)
+call s:NetrwInit("g:netrw_bufsettings" , "noma nomod nonu nobl nowrap ro nornu")
+call s:NetrwInit("g:netrw_chgwin" , -1)
+call s:NetrwInit("g:netrw_clipboard" , 1)
+call s:NetrwInit("g:netrw_compress" , "gzip")
+call s:NetrwInit("g:netrw_ctags" , "ctags")
+if exists("g:netrw_cursorline") && !exists("g:netrw_cursor")
+ call netrw#ErrorMsg(s:NOTE,'g:netrw_cursorline is deprecated; use g:netrw_cursor instead',77)
+ let g:netrw_cursor= g:netrw_cursorline
+endif
+call s:NetrwInit("g:netrw_cursor" , 2)
+let s:netrw_usercul = &cursorline
+let s:netrw_usercuc = &cursorcolumn
+"call Decho("(netrw) COMBAK: cuc=".&l:cuc." cul=".&l:cul." initialization of s:netrw_cu[cl]")
+call s:NetrwInit("g:netrw_cygdrive","/cygdrive")
+" Default values - d-g ---------- {{{3
+call s:NetrwInit("s:didstarstar",0)
+call s:NetrwInit("g:netrw_dirhistcnt" , 0)
+let s:xz_opt = has('unix') ? "XZ_OPT=-T0" :
+ \ (has("win32") && &shell =~? '\vcmd(\.exe)?$' ?
+ \ "setx XZ_OPT=-T0 &&" : "")
+call s:NetrwInit("g:netrw_decompress ", "{"
+ \ .."'.lz4': 'lz4 -d',"
+ \ .."'.lzo': 'lzop -d',"
+ \ .."'.lz': 'lzip -dk',"
+ \ .."'.7z': '7za x',"
+ \ .."'.001': '7za x',"
+ \ .."'.zip': 'unzip',"
+ \ .."'.bz': 'bunzip2 -k',"
+ \ .."'.bz2': 'bunzip2 -k',"
+ \ .."'.gz': 'gunzip -k',"
+ \ .."'.lzma': 'unlzma -T0 -k',"
+ \ .."'.xz': 'unxz -T0 -k',"
+ \ .."'.zst': 'zstd -T0 -d',"
+ \ .."'.Z': 'uncompress -k',"
+ \ .."'.tar': 'tar -xvf',"
+ \ .."'.tar.bz': 'tar -xvjf',"
+ \ .."'.tar.bz2': 'tar -xvjf',"
+ \ .."'.tbz': 'tar -xvjf',"
+ \ .."'.tbz2': 'tar -xvjf',"
+ \ .."'.tar.gz': 'tar -xvzf',"
+ \ .."'.tgz': 'tar -xvzf',"
+ \ .."'.tar.lzma': '"..s:xz_opt.." tar -xvf --lzma',"
+ \ .."'.tlz': '"..s:xz_opt.." tar -xvf --lzma',"
+ \ .."'.tar.xz': '"..s:xz_opt.." tar -xvfJ',"
+ \ .."'.txz': '"..s:xz_opt.." tar -xvfJ',"
+ \ .."'.tar.zst': '"..s:xz_opt.." tar -xvf --use-compress-program=unzstd',"
+ \ .."'.tzst': '"..s:xz_opt.." tar -xvf --use-compress-program=unzstd',"
+ \ .."'.rar': '"..(executable("unrar")?"unrar x -ad":"rar x -ad").."'"
+ \ .."}")
+unlet s:xz_opt
+call s:NetrwInit("g:netrw_dirhistmax" , 10)
+call s:NetrwInit("g:netrw_fastbrowse" , 1)
+call s:NetrwInit("g:netrw_ftp_browse_reject", '^total\s\+\d\+$\|^Trying\s\+\d\+.*$\|^KERBEROS_V\d rejected\|^Security extensions not\|No such file\|: connect to address [0-9a-fA-F:]*: No route to host$')
+if !exists("g:netrw_ftp_list_cmd")
+ if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)
+ let g:netrw_ftp_list_cmd = "ls -lF"
+ let g:netrw_ftp_timelist_cmd = "ls -tlF"
+ let g:netrw_ftp_sizelist_cmd = "ls -slF"
+ else
+ let g:netrw_ftp_list_cmd = "dir"
+ let g:netrw_ftp_timelist_cmd = "dir"
+ let g:netrw_ftp_sizelist_cmd = "dir"
+ endif
+endif
+call s:NetrwInit("g:netrw_ftpmode",'binary')
+" Default values - h-lh ---------- {{{3
+call s:NetrwInit("g:netrw_hide",1)
+if !exists("g:netrw_ignorenetrc")
+ if &shell =~ '\c\<\%(cmd\|4nt\)\.exe$'
+ let g:netrw_ignorenetrc= 1
+ else
+ let g:netrw_ignorenetrc= 0
+ endif
+endif
+call s:NetrwInit("g:netrw_keepdir",1)
+if !exists("g:netrw_list_cmd")
+ if g:netrw_scp_cmd =~ '^pscp' && executable("pscp")
+ if exists("g:netrw_list_cmd_options")
+ let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME: ".g:netrw_list_cmd_options
+ else
+ let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME:"
+ endif
+ elseif executable(g:netrw_ssh_cmd)
+ " provide a scp-based default listing command
+ if exists("g:netrw_list_cmd_options")
+ let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa ".g:netrw_list_cmd_options
+ else
+ let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa"
+ endif
+ else
+ " call Decho(g:netrw_ssh_cmd." is not executable",'~'.expand("<slnum>"))
+ let g:netrw_list_cmd= ""
+ endif
+endif
+call s:NetrwInit("g:netrw_list_hide","")
+" Default values - lh-lz ---------- {{{3
+if exists("g:netrw_local_copycmd")
+ let g:netrw_localcopycmd= g:netrw_local_copycmd
+ call netrw#ErrorMsg(s:NOTE,"g:netrw_local_copycmd is deprecated in favor of g:netrw_localcopycmd",84)
+endif
+if !exists("g:netrw_localcmdshell")
+ let g:netrw_localcmdshell= ""
+endif
+if !exists("g:netrw_localcopycmd")
+ if has("win32")
+ if g:netrw_cygwin
+ let g:netrw_localcopycmd= "cp"
+ else
+ let g:netrw_localcopycmd = expand("$COMSPEC", v:true)
+ let g:netrw_localcopycmdopt= " /c copy"
+ endif
+ elseif has("unix") || has("macunix")
+ let g:netrw_localcopycmd= "cp"
+ else
+ let g:netrw_localcopycmd= ""
+ endif
+endif
+if !exists("g:netrw_localcopydircmd")
+ if has("win32")
+ if g:netrw_cygwin
+ let g:netrw_localcopydircmd = "cp"
+ let g:netrw_localcopydircmdopt= " -R"
+ else
+ let g:netrw_localcopydircmd = expand("$COMSPEC", v:true)
+ let g:netrw_localcopydircmdopt= " /c xcopy /e /c /h /i /k"
+ endif
+ elseif has("unix")
+ let g:netrw_localcopydircmd = "cp"
+ let g:netrw_localcopydircmdopt= " -R"
+ elseif has("macunix")
+ let g:netrw_localcopydircmd = "cp"
+ let g:netrw_localcopydircmdopt= " -R"
+ else
+ let g:netrw_localcopydircmd= ""
+ endif
+endif
+if exists("g:netrw_local_mkdir")
+ let g:netrw_localmkdir= g:netrw_local_mkdir
+ call netrw#ErrorMsg(s:NOTE,"g:netrw_local_mkdir is deprecated in favor of g:netrw_localmkdir",87)
+endif
+if has("win32")
+ if g:netrw_cygwin
+ call s:NetrwInit("g:netrw_localmkdir","mkdir")
+ else
+ let g:netrw_localmkdir = expand("$COMSPEC", v:true)
+ let g:netrw_localmkdiropt= " /c mkdir"
+ endif
+else
+ call s:NetrwInit("g:netrw_localmkdir","mkdir")
+endif
+call s:NetrwInit("g:netrw_remote_mkdir","mkdir")
+if exists("g:netrw_local_movecmd")
+ let g:netrw_localmovecmd= g:netrw_local_movecmd
+ call netrw#ErrorMsg(s:NOTE,"g:netrw_local_movecmd is deprecated in favor of g:netrw_localmovecmd",88)
+endif
+if !exists("g:netrw_localmovecmd")
+ if has("win32")
+ if g:netrw_cygwin
+ let g:netrw_localmovecmd= "mv"
+ else
+ let g:netrw_localmovecmd = expand("$COMSPEC", v:true)
+ let g:netrw_localmovecmdopt= " /c move"
+ endif
+ elseif has("unix") || has("macunix")
+ let g:netrw_localmovecmd= "mv"
+ else
+ let g:netrw_localmovecmd= ""
+ endif
+endif
+" following serves as an example for how to insert a version&patch specific test
+"if v:version < 704 || (v:version == 704 && !has("patch1107"))
+"endif
+call s:NetrwInit("g:netrw_liststyle" , s:THINLIST)
+" sanity checks
+if g:netrw_liststyle < 0 || g:netrw_liststyle >= s:MAXLIST
+ let g:netrw_liststyle= s:THINLIST
+endif
+if g:netrw_liststyle == s:LONGLIST && g:netrw_scp_cmd !~ '^pscp'
+ let g:netrw_list_cmd= g:netrw_list_cmd." -l"
+endif
+" Default values - m-r ---------- {{{3
+call s:NetrwInit("g:netrw_markfileesc" , '*./[\~')
+call s:NetrwInit("g:netrw_maxfilenamelen", 32)
+call s:NetrwInit("g:netrw_menu" , 1)
+call s:NetrwInit("g:netrw_mkdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mkdir")
+call s:NetrwInit("g:netrw_mousemaps" , (exists("+mouse") && &mouse =~# '[anh]'))
+call s:NetrwInit("g:netrw_retmap" , 0)
+if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)
+ call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME")
+elseif has("win32")
+ call s:NetrwInit("g:netrw_chgperm" , "cacls FILENAME /e /p PERM")
+else
+ call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME")
+endif
+call s:NetrwInit("g:netrw_preview" , 0)
+call s:NetrwInit("g:netrw_scpport" , "-P")
+call s:NetrwInit("g:netrw_servername" , "NETRWSERVER")
+call s:NetrwInit("g:netrw_sshport" , "-p")
+call s:NetrwInit("g:netrw_rename_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mv")
+call s:NetrwInit("g:netrw_rm_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm")
+call s:NetrwInit("g:netrw_rmdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rmdir")
+call s:NetrwInit("g:netrw_rmf_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm -f ")
+" Default values - q-s ---------- {{{3
+call s:NetrwInit("g:netrw_quickhelp",0)
+let s:QuickHelp= ["-:go up dir D:delete R:rename s:sort-by x:special",
+ \ "(create new) %:file d:directory",
+ \ "(windows split&open) o:horz v:vert p:preview",
+ \ "i:style qf:file info O:obtain r:reverse",
+ \ "(marks) mf:mark file mt:set target mm:move mc:copy",
+ \ "(bookmarks) mb:make mB:delete qb:list gb:go to",
+ \ "(history) qb:list u:go up U:go down",
+ \ "(targets) mt:target Tb:use bookmark Th:use history"]
+" g:netrw_sepchr: picking a character that doesn't appear in filenames that can be used to separate priority from filename
+call s:NetrwInit("g:netrw_sepchr" , (&enc == "euc-jp")? "\<Char-0x01>" : "\<Char-0xff>")
+if !exists("g:netrw_keepj") || g:netrw_keepj == "keepj"
+ call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil keepj " : "keepj ")
+else
+ call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil " : " ")
+endif
+call s:NetrwInit("g:netrw_sort_by" , "name") " alternatives: date , size
+call s:NetrwInit("g:netrw_sort_options" , "")
+call s:NetrwInit("g:netrw_sort_direction", "normal") " alternative: reverse (z y x ...)
+if !exists("g:netrw_sort_sequence")
+ if has("unix")
+ let g:netrw_sort_sequence= '[\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$'
+ else
+ let g:netrw_sort_sequence= '[\/]$,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$'
+ endif
+endif
+call s:NetrwInit("g:netrw_special_syntax" , 0)
+call s:NetrwInit("g:netrw_ssh_browse_reject", '^total\s\+\d\+$')
+call s:NetrwInit("g:netrw_use_noswf" , 1)
+call s:NetrwInit("g:netrw_sizestyle" ,"b")
+" Default values - t-w ---------- {{{3
+call s:NetrwInit("g:netrw_timefmt","%c")
+if !exists("g:netrw_xstrlen")
+ if exists("g:Align_xstrlen")
+ let g:netrw_xstrlen= g:Align_xstrlen
+ elseif exists("g:drawit_xstrlen")
+ let g:netrw_xstrlen= g:drawit_xstrlen
+ elseif &enc == "latin1" || !has("multi_byte")
+ let g:netrw_xstrlen= 0
+ else
+ let g:netrw_xstrlen= 1
+ endif
+endif
+call s:NetrwInit("g:NetrwTopLvlMenu","Netrw.")
+call s:NetrwInit("g:netrw_winsize",50)
+call s:NetrwInit("g:netrw_wiw",1)
+if g:netrw_winsize > 100|let g:netrw_winsize= 100|endif
+" ---------------------------------------------------------------------
+" Default values for netrw's script variables: {{{2
+call s:NetrwInit("g:netrw_fname_escape",' ?&;%')
+if has("win32")
+ call s:NetrwInit("g:netrw_glob_escape",'*?`{[]$')
+else
+ call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\')
+endif
+call s:NetrwInit("g:netrw_menu_escape",'.&? \')
+call s:NetrwInit("g:netrw_tmpfile_escape",' &;')
+call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\<C-V>\"")
+if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4')
+ let s:treedepthstring= "│ "
+else
+ let s:treedepthstring= "| "
+endif
+call s:NetrwInit("s:netrw_posn",'{}')
+
+" BufEnter event ignored by decho when following variable is true
+" Has a side effect that doau BufReadPost doesn't work, so
+" files read by network transfer aren't appropriately highlighted.
+"let g:decho_bufenter = 1 "Decho
+
+" ======================
+" Netrw Initialization: {{{1
+" ======================
+if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on")
+ " call Decho("installed beval events",'~'.expand("<slnum>"))
+ let &l:bexpr = "netrw#BalloonHelp()"
+ " call Decho("&l:bexpr<".&l:bexpr."> buf#".bufnr())
+ au FileType netrw setl beval
+ au WinLeave * if &ft == "netrw" && exists("s:initbeval")|let &beval= s:initbeval|endif
+ au VimEnter * let s:initbeval= &beval
+ "else " Decho
+ " if v:version < 700 | call Decho("did not install beval events: v:version=".v:version." < 700","~".expand("<slnum>")) | endif
+ " if !has("balloon_eval") | call Decho("did not install beval events: does not have balloon_eval","~".expand("<slnum>")) | endif
+ " if exists("s:initbeval") | call Decho("did not install beval events: s:initbeval exists","~".expand("<slnum>")) | endif
+ " if exists("g:netrw_nobeval") | call Decho("did not install beval events: g:netrw_nobeval exists","~".expand("<slnum>")) | endif
+ " if !has("syntax") | call Decho("did not install beval events: does not have syntax highlighting","~".expand("<slnum>")) | endif
+ " if exists("g:syntax_on") | call Decho("did not install beval events: g:syntax_on exists","~".expand("<slnum>")) | endif
+endif
+au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif
+
+if g:netrw_keepj =~# "keepj"
+ com! -nargs=* NetrwKeepj keepj <args>
+else
+ let g:netrw_keepj= ""
+ com! -nargs=* NetrwKeepj <args>
+endif
+
+" ==============================
+" Netrw Utility Functions: {{{1
+" ==============================
+
+" ---------------------------------------------------------------------
+" netrw#BalloonHelp: {{{2
+if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_on") && !exists("g:netrw_nobeval")
+ " call Decho("loading netrw#BalloonHelp()",'~'.expand("<slnum>"))
+ fun! netrw#BalloonHelp()
+ if &ft != "netrw"
+ return ""
+ endif
+ if exists("s:popuperr_id") && popup_getpos(s:popuperr_id) != {}
+ " popup error window is still showing
+ " s:pouperr_id and s:popuperr_text are set up in netrw#ErrorMsg()
+ if exists("s:popuperr_text") && s:popuperr_text != "" && v:beval_text != s:popuperr_text
+ " text under mouse hasn't changed; only close window when it changes
+ call popup_close(s:popuperr_id)
+ unlet s:popuperr_text
+ else
+ let s:popuperr_text= v:beval_text
+ endif
+ let mesg= ""
+ elseif !exists("w:netrw_bannercnt") || v:beval_lnum >= w:netrw_bannercnt || (exists("g:netrw_nobeval") && g:netrw_nobeval)
+ let mesg= ""
+ elseif v:beval_text == "Netrw" || v:beval_text == "Directory" || v:beval_text == "Listing"
+ let mesg = "i: thin-long-wide-tree gh: quick hide/unhide of dot-files qf: quick file info %:open new file"
+ elseif getline(v:beval_lnum) =~ '^"\s*/'
+ let mesg = "<cr>: edit/enter o: edit/enter in horiz window t: edit/enter in new tab v:edit/enter in vert window"
+ elseif v:beval_text == "Sorted" || v:beval_text == "by"
+ let mesg = 's: sort by name, time, file size, extension r: reverse sorting order mt: mark target'
+ elseif v:beval_text == "Sort" || v:beval_text == "sequence"
+ let mesg = "S: edit sorting sequence"
+ elseif v:beval_text == "Hiding" || v:beval_text == "Showing"
+ let mesg = "a: hiding-showing-all ctrl-h: editing hiding list mh: hide/show by suffix"
+ elseif v:beval_text == "Quick" || v:beval_text == "Help"
+ let mesg = "Help: press <F1>"
+ elseif v:beval_text == "Copy/Move" || v:beval_text == "Tgt"
+ let mesg = "mt: mark target mc: copy marked file to target mm: move marked file to target"
+ else
+ let mesg= ""
+ endif
+ return mesg
+ endfun
+ "else " Decho
+ " if v:version < 700 |call Decho("did not load netrw#BalloonHelp(): vim version ".v:version." < 700 -","~".expand("<slnum>"))|endif
+ " if !has("balloon_eval") |call Decho("did not load netrw#BalloonHelp(): does not have balloon eval","~".expand("<slnum>")) |endif
+ " if !has("syntax") |call Decho("did not load netrw#BalloonHelp(): syntax disabled","~".expand("<slnum>")) |endif
+ " if !exists("g:syntax_on") |call Decho("did not load netrw#BalloonHelp(): g:syntax_on n/a","~".expand("<slnum>")) |endif
+ " if exists("g:netrw_nobeval") |call Decho("did not load netrw#BalloonHelp(): g:netrw_nobeval exists","~".expand("<slnum>")) |endif
+endif
+
+" ------------------------------------------------------------------------
+" netrw#Explore: launch the local browser in the directory of the current file {{{2
+" indx: == -1: Nexplore
+" == -2: Pexplore
+" == +: this is overloaded:
+" * If Nexplore/Pexplore is in use, then this refers to the
+" indx'th item in the w:netrw_explore_list[] of items which
+" matched the */pattern **/pattern *//pattern **//pattern
+" * If Hexplore or Vexplore, then this will override
+" g:netrw_winsize to specify the qty of rows or columns the
+" newly split window should have.
+" dosplit==0: the window will be split iff the current file has been modified and hidden not set
+" dosplit==1: the window will be split before running the local browser
+" style == 0: Explore style == 1: Explore!
+" == 2: Hexplore style == 3: Hexplore!
+" == 4: Vexplore style == 5: Vexplore!
+" == 6: Texplore
+fun! netrw#Explore(indx,dosplit,style,...)
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ endif
+
+ " record current file for Rexplore's benefit
+ if &ft != "netrw"
+ let w:netrw_rexfile= expand("%:p")
+ endif
+
+ " record current directory
+ let curdir = simplify(b:netrw_curdir)
+ let curfiledir = substitute(expand("%:p"),'^\(.*[/\\]\)[^/\\]*$','\1','e')
+ if !exists("g:netrw_cygwin") && has("win32")
+ let curdir= substitute(curdir,'\','/','g')
+ endif
+
+ " using completion, directories with spaces in their names (thanks, Bill Gates, for a truly dumb idea)
+ " will end up with backslashes here. Solution: strip off backslashes that precede white space and
+ " try Explore again.
+ if a:0 > 0
+ if a:1 =~ "\\\s" && !filereadable(s:NetrwFile(a:1)) && !isdirectory(s:NetrwFile(a:1))
+ let a1 = substitute(a:1, '\\\(\s\)', '\1', 'g')
+ if a1 != a:1
+ call netrw#Explore(a:indx, a:dosplit, a:style, a1)
+ return
+ endif
+ endif
+ endif
+
+ " save registers
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ " call Decho("(netrw#Explore) save @* and @+",'~'.expand("<slnum>"))
+ sil! let keepregstar = @*
+ sil! let keepregplus = @+
+ endif
+ sil! let keepregslash= @/
+
+ " if dosplit
+ " -or- file has been modified AND file not hidden when abandoned
+ " -or- Texplore used
+ if a:dosplit || (&modified && &hidden == 0 && &bufhidden != "hide") || a:style == 6
+ call s:SaveWinVars()
+ let winsz= g:netrw_winsize
+ if a:indx > 0
+ let winsz= a:indx
+ endif
+
+ if a:style == 0 " Explore, Sexplore
+ let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz
+ if winsz == 0|let winsz= ""|endif
+ exe "noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s"
+
+ elseif a:style == 1 " Explore!, Sexplore!
+ let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz
+ if winsz == 0|let winsz= ""|endif
+ exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v"
+
+ elseif a:style == 2 " Hexplore
+ let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz
+ if winsz == 0|let winsz= ""|endif
+ exe "keepalt noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s"
+
+ elseif a:style == 3 " Hexplore!
+ let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz
+ if winsz == 0|let winsz= ""|endif
+ exe "keepalt noswapfile ".(!g:netrw_alto ? "below " : "above ").winsz."wincmd s"
+
+ elseif a:style == 4 " Vexplore
+ let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz
+ if winsz == 0|let winsz= ""|endif
+ exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v"
+
+ elseif a:style == 5 " Vexplore!
+ let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz
+ if winsz == 0|let winsz= ""|endif
+ exe "keepalt noswapfile ".(!g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v"
+
+ elseif a:style == 6 " Texplore
+ call s:SaveBufVars()
+ exe "keepalt tabnew ".fnameescape(curdir)
+ call s:RestoreBufVars()
+ endif
+ call s:RestoreWinVars()
+ endif
+ NetrwKeepj norm! 0
+
+ if a:0 > 0
+ if a:1 =~ '^\~' && (has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin))
+ let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),''))
+ elseif a:1 == '.'
+ let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd())
+ if dirname !~ '/$'
+ let dirname= dirname."/"
+ endif
+ elseif a:1 =~ '\$'
+ let dirname= simplify(expand(a:1))
+ elseif a:1 !~ '^\*\{1,2}/' && a:1 !~ '^\a\{3,}://'
+ let dirname= simplify(a:1)
+ else
+ let dirname= a:1
+ endif
+ else
+ " clear explore
+ call s:NetrwClearExplore()
+ return
+ endif
+
+ if dirname =~ '\.\./\=$'
+ let dirname= simplify(fnamemodify(dirname,':p:h'))
+ elseif dirname =~ '\.\.' || dirname == '.'
+ let dirname= simplify(fnamemodify(dirname,':p'))
+ endif
+
+ if dirname =~ '^\*//'
+ " starpat=1: Explore *//pattern (current directory only search for files containing pattern)
+ let pattern= substitute(dirname,'^\*//\(.*\)$','\1','')
+ let starpat= 1
+ if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
+
+ elseif dirname =~ '^\*\*//'
+ " starpat=2: Explore **//pattern (recursive descent search for files containing pattern)
+ let pattern= substitute(dirname,'^\*\*//','','')
+ let starpat= 2
+
+ elseif dirname =~ '/\*\*/'
+ " handle .../**/.../filepat
+ let prefixdir= substitute(dirname,'^\(.\{-}\)\*\*.*$','\1','')
+ if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && has("win32"))
+ let b:netrw_curdir = prefixdir
+ else
+ let b:netrw_curdir= getcwd().'/'.prefixdir
+ endif
+ let dirname= substitute(dirname,'^.\{-}\(\*\*/.*\)$','\1','')
+ let starpat= 4
+
+ elseif dirname =~ '^\*/'
+ " case starpat=3: Explore */filepat (search in current directory for filenames matching filepat)
+ let starpat= 3
+
+ elseif dirname=~ '^\*\*/'
+ " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat)
+ let starpat= 4
+
+ else
+ let starpat= 0
+ endif
+
+ if starpat == 0 && a:indx >= 0
+ " [Explore Hexplore Vexplore Sexplore] [dirname]
+ if dirname == ""
+ let dirname= curfiledir
+ endif
+ if dirname =~# '^scp://' || dirname =~ '^ftp://'
+ call netrw#Nread(2,dirname)
+ else
+ if dirname == ""
+ let dirname= getcwd()
+ elseif has("win32") && !g:netrw_cygwin
+ " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo',
+ " depending on whether backslashes have been converted to forward slashes by earlier code).
+ if dirname !~ '^[a-zA-Z]:' && dirname !~ '^\\\\\w\+' && dirname !~ '^//\w\+'
+ let dirname= b:netrw_curdir."/".dirname
+ endif
+ elseif dirname !~ '^/'
+ let dirname= b:netrw_curdir."/".dirname
+ endif
+ call netrw#LocalBrowseCheck(dirname)
+ endif
+ if exists("w:netrw_bannercnt")
+ " done to handle P08-Ingelrest. :Explore will _Always_ go to the line just after the banner.
+ " If one wants to return the same place in the netrw window, use :Rex instead.
+ exe w:netrw_bannercnt
+ endif
+
+
+ " starpat=1: Explore *//pattern (current directory only search for files containing pattern)
+ " starpat=2: Explore **//pattern (recursive descent search for files containing pattern)
+ " starpat=3: Explore */filepat (search in current directory for filenames matching filepat)
+ " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat)
+ elseif a:indx <= 0
+ " Nexplore, Pexplore, Explore: handle starpat
+ if !mapcheck("<s-up>","n") && !mapcheck("<s-down>","n") && exists("b:netrw_curdir")
+ let s:didstarstar= 1
+ nnoremap <buffer> <silent> <s-up> :Pexplore<cr>
+ nnoremap <buffer> <silent> <s-down> :Nexplore<cr>
+ endif
+
+ if has("path_extra")
+ if !exists("w:netrw_explore_indx")
+ let w:netrw_explore_indx= 0
+ endif
+
+ let indx = a:indx
+
+ if indx == -1
+ " Nexplore
+ if !exists("w:netrw_explore_list") " sanity check
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40)
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ sil! let @/ = keepregslash
+ return
+ endif
+ let indx= w:netrw_explore_indx
+ if indx < 0 | let indx= 0 | endif
+ if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif
+ let curfile= w:netrw_explore_list[indx]
+ while indx < w:netrw_explore_listlen && curfile == w:netrw_explore_list[indx]
+ let indx= indx + 1
+ endwhile
+ if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif
+
+ elseif indx == -2
+ " Pexplore
+ if !exists("w:netrw_explore_list") " sanity check
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41)
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ sil! let @/ = keepregslash
+ return
+ endif
+ let indx= w:netrw_explore_indx
+ if indx < 0 | let indx= 0 | endif
+ if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif
+ let curfile= w:netrw_explore_list[indx]
+ while indx >= 0 && curfile == w:netrw_explore_list[indx]
+ let indx= indx - 1
+ endwhile
+ if indx < 0 | let indx= 0 | endif
+
+ else
+ " Explore -- initialize
+ " build list of files to Explore with Nexplore/Pexplore
+ NetrwKeepj keepalt call s:NetrwClearExplore()
+ let w:netrw_explore_indx= 0
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ endif
+
+ " switch on starpat to build the w:netrw_explore_list of files
+ if starpat == 1
+ " starpat=1: Explore *//pattern (current directory only search for files containing pattern)
+ try
+ exe "NetrwKeepj noautocmd vimgrep /".pattern."/gj ".fnameescape(b:netrw_curdir)."/*"
+ catch /^Vim\%((\a\+)\)\=:E480/
+ keepalt call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pattern.">",76)
+ return
+ endtry
+ let w:netrw_explore_list = s:NetrwExploreListUniq(map(getqflist(),'bufname(v:val.bufnr)'))
+ if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
+
+ elseif starpat == 2
+ " starpat=2: Explore **//pattern (recursive descent search for files containing pattern)
+ try
+ exe "sil NetrwKeepj noautocmd keepalt vimgrep /".pattern."/gj "."**/*"
+ catch /^Vim\%((\a\+)\)\=:E480/
+ keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45)
+ if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ sil! let @/ = keepregslash
+ return
+ endtry
+ let s:netrw_curdir = b:netrw_curdir
+ let w:netrw_explore_list = getqflist()
+ let w:netrw_explore_list = s:NetrwExploreListUniq(map(w:netrw_explore_list,'s:netrw_curdir."/".bufname(v:val.bufnr)'))
+ if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
+
+ elseif starpat == 3
+ " starpat=3: Explore */filepat (search in current directory for filenames matching filepat)
+ let filepat= substitute(dirname,'^\*/','','')
+ let filepat= substitute(filepat,'^[%#<]','\\&','')
+ let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".filepat),'\n'))
+ if &hls | let keepregslash= s:ExplorePatHls(filepat) | endif
+
+ elseif starpat == 4
+ " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat)
+ let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".dirname),'\n'))
+ if &hls | let keepregslash= s:ExplorePatHls(dirname) | endif
+ endif " switch on starpat to build w:netrw_explore_list
+
+ let w:netrw_explore_listlen = len(w:netrw_explore_list)
+
+ if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/')
+ keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42)
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ sil! let @/ = keepregslash
+ return
+ endif
+ endif " if indx ... endif
+
+ " NetrwStatusLine support - for exploring support
+ let w:netrw_explore_indx= indx
+
+ " wrap the indx around, but issue a note
+ if indx >= w:netrw_explore_listlen || indx < 0
+ let indx = (indx < 0)? ( w:netrw_explore_listlen - 1 ) : 0
+ let w:netrw_explore_indx= indx
+ keepalt NetrwKeepj call netrw#ErrorMsg(s:NOTE,"no more files match Explore pattern",43)
+ endif
+
+ exe "let dirfile= w:netrw_explore_list[".indx."]"
+ let newdir= substitute(dirfile,'/[^/]*$','','e')
+
+ call netrw#LocalBrowseCheck(newdir)
+ if !exists("w:netrw_liststyle")
+ let w:netrw_liststyle= g:netrw_liststyle
+ endif
+ if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:LONGLIST
+ keepalt NetrwKeepj call search('^'.substitute(dirfile,"^.*/","","").'\>',"W")
+ else
+ keepalt NetrwKeepj call search('\<'.substitute(dirfile,"^.*/","","").'\>',"w")
+ endif
+ let w:netrw_explore_mtchcnt = indx + 1
+ let w:netrw_explore_bufnr = bufnr("%")
+ let w:netrw_explore_line = line(".")
+ keepalt NetrwKeepj call s:SetupNetrwStatusLine('%f %h%m%r%=%9*%{NetrwStatusLine()}')
+
+ else
+ if !exists("g:netrw_quiet")
+ keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44)
+ endif
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ sil! let @/ = keepregslash
+ return
+ endif
+
+ else
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && dirname =~ '/'
+ sil! unlet w:netrw_treedict
+ sil! unlet w:netrw_treetop
+ endif
+ let newdir= dirname
+ if !exists("b:netrw_curdir")
+ NetrwKeepj call netrw#LocalBrowseCheck(getcwd())
+ else
+ NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,newdir,0))
+ endif
+ endif
+
+ " visual display of **/ **// */ Exploration files
+ if exists("w:netrw_explore_indx") && exists("b:netrw_curdir")
+ if !exists("s:explore_prvdir") || s:explore_prvdir != b:netrw_curdir
+ " only update match list when current directory isn't the same as before
+ let s:explore_prvdir = b:netrw_curdir
+ let s:explore_match = ""
+ let dirlen = strlen(b:netrw_curdir)
+ if b:netrw_curdir !~ '/$'
+ let dirlen= dirlen + 1
+ endif
+ let prvfname= ""
+ for fname in w:netrw_explore_list
+ if fname =~ '^'.b:netrw_curdir
+ if s:explore_match == ""
+ let s:explore_match= '\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>'
+ else
+ let s:explore_match= s:explore_match.'\|\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>'
+ endif
+ elseif fname !~ '^/' && fname != prvfname
+ if s:explore_match == ""
+ let s:explore_match= '\<'.escape(fname,g:netrw_markfileesc).'\>'
+ else
+ let s:explore_match= s:explore_match.'\|\<'.escape(fname,g:netrw_markfileesc).'\>'
+ endif
+ endif
+ let prvfname= fname
+ endfor
+ if has("syntax") && exists("g:syntax_on") && g:syntax_on
+ exe "2match netrwMarkFile /".s:explore_match."/"
+ endif
+ endif
+ echo "<s-up>==Pexplore <s-down>==Nexplore"
+ else
+ 2match none
+ if exists("s:explore_match") | unlet s:explore_match | endif
+ if exists("s:explore_prvdir") | unlet s:explore_prvdir | endif
+ endif
+
+ " since Explore may be used to initialize netrw's browser,
+ " there's no danger of a late FocusGained event on initialization.
+ " Consequently, set s:netrw_events to 2.
+ let s:netrw_events= 2
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ sil! let @/ = keepregslash
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Lexplore: toggle Explorer window, keeping it on the left of the current tab {{{2
+" Uses g:netrw_chgwin : specifies the window where Lexplore files are to be opened
+" t:netrw_lexposn : winsaveview() output (used on Lexplore window)
+" t:netrw_lexbufnr: the buffer number of the Lexplore buffer (internal to this function)
+" s:lexplore_win : window number of Lexplore window (serves to indicate which window is a Lexplore window)
+" w:lexplore_buf : buffer number of Lexplore window (serves to indicate which window is a Lexplore window)
+fun! netrw#Lexplore(count,rightside,...)
+ " call Dfunc("netrw#Lexplore(count=".a:count." rightside=".a:rightside.",...) a:0=".a:0." ft=".&ft)
+ let curwin= winnr()
+
+ if a:0 > 0 && a:1 != ""
+ " if a netrw window is already on the left-side of the tab
+ " and a directory has been specified, explore with that
+ " directory.
+ let a1 = expand(a:1)
+ exe "1wincmd w"
+ if &ft == "netrw"
+ exe "Explore ".fnameescape(a1)
+ exe curwin."wincmd w"
+ let s:lexplore_win= curwin
+ let w:lexplore_buf= bufnr("%")
+ if exists("t:netrw_lexposn")
+ unlet t:netrw_lexposn
+ endif
+ return
+ endif
+ exe curwin."wincmd w"
+ else
+ let a1= ""
+ endif
+
+ if exists("t:netrw_lexbufnr")
+ " check if t:netrw_lexbufnr refers to a netrw window
+ let lexwinnr = bufwinnr(t:netrw_lexbufnr)
+ else
+ let lexwinnr= 0
+ endif
+
+ if lexwinnr > 0
+ " close down netrw explorer window
+ exe lexwinnr."wincmd w"
+ let g:netrw_winsize = -winwidth(0)
+ let t:netrw_lexposn = winsaveview()
+ close
+ if lexwinnr < curwin
+ let curwin= curwin - 1
+ endif
+ if lexwinnr != curwin
+ exe curwin."wincmd w"
+ endif
+ unlet t:netrw_lexbufnr
+
+ else
+ " open netrw explorer window
+ exe "1wincmd w"
+ let keep_altv = g:netrw_altv
+ let g:netrw_altv = 0
+ if a:count != 0
+ let netrw_winsize = g:netrw_winsize
+ let g:netrw_winsize = a:count
+ endif
+ let curfile= expand("%")
+ exe (a:rightside? "botright" : "topleft")." vertical ".((g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize) . " new"
+ if a:0 > 0 && a1 != ""
+ call netrw#Explore(0,0,0,a1)
+ exe "Explore ".fnameescape(a1)
+ elseif curfile =~ '^\a\{3,}://'
+ call netrw#Explore(0,0,0,substitute(curfile,'[^/\\]*$','',''))
+ else
+ call netrw#Explore(0,0,0,".")
+ endif
+ if a:count != 0
+ let g:netrw_winsize = netrw_winsize
+ endif
+ setlocal winfixwidth
+ let g:netrw_altv = keep_altv
+ let t:netrw_lexbufnr = bufnr("%")
+ " done to prevent build-up of hidden buffers due to quitting and re-invocation of :Lexplore.
+ " Since the intended use of :Lexplore is to have an always-present explorer window, the extra
+ " effort to prevent mis-use of :Lex is warranted.
+ set bh=wipe
+ if exists("t:netrw_lexposn")
+ call winrestview(t:netrw_lexposn)
+ unlet t:netrw_lexposn
+ endif
+ endif
+
+ " set up default window for editing via <cr>
+ if exists("g:netrw_chgwin") && g:netrw_chgwin == -1
+ if a:rightside
+ let g:netrw_chgwin= 1
+ else
+ let g:netrw_chgwin= 2
+ endif
+ endif
+
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Clean: remove netrw {{{2
+" supports :NetrwClean -- remove netrw from first directory on runtimepath
+" :NetrwClean! -- remove netrw from all directories on runtimepath
+fun! netrw#Clean(sys)
+ " call Dfunc("netrw#Clean(sys=".a:sys.")")
+
+ if a:sys
+ let choice= confirm("Remove personal and system copies of netrw?","&Yes\n&No")
+ else
+ let choice= confirm("Remove personal copy of netrw?","&Yes\n&No")
+ endif
+ " call Decho("choice=".choice,'~'.expand("<slnum>"))
+ let diddel= 0
+ let diddir= ""
+
+ if choice == 1
+ for dir in split(&rtp,',')
+ if filereadable(dir."/plugin/netrwPlugin.vim")
+ " call Decho("removing netrw-related files from ".dir,'~'.expand("<slnum>"))
+ if s:NetrwDelete(dir."/plugin/netrwPlugin.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/plugin/netrwPlugin.vim",55) |endif
+ if s:NetrwDelete(dir."/autoload/netrwFileHandlers.vim")|call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwFileHandlers.vim",55)|endif
+ if s:NetrwDelete(dir."/autoload/netrwSettings.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwSettings.vim",55) |endif
+ if s:NetrwDelete(dir."/autoload/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrw.vim",55) |endif
+ if s:NetrwDelete(dir."/syntax/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrw.vim",55) |endif
+ if s:NetrwDelete(dir."/syntax/netrwlist.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrwlist.vim",55) |endif
+ let diddir= dir
+ let diddel= diddel + 1
+ if !a:sys|break|endif
+ endif
+ endfor
+ endif
+
+ echohl WarningMsg
+ if diddel == 0
+ echomsg "netrw is either not installed or not removable"
+ elseif diddel == 1
+ echomsg "removed one copy of netrw from <".diddir.">"
+ else
+ echomsg "removed ".diddel." copies of netrw"
+ endif
+ echohl None
+
+ " call Dret("netrw#Clean")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#MakeTgt: make a target out of the directory name provided {{{2
+fun! netrw#MakeTgt(dname)
+ " call Dfunc("netrw#MakeTgt(dname<".a:dname.">)")
+ " simplify the target (eg. /abc/def/../ghi -> /abc/ghi)
+ let svpos = winsaveview()
+ " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ let s:netrwmftgt_islocal= (a:dname !~ '^\a\{3,}://')
+ " call Decho("s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>"))
+ if s:netrwmftgt_islocal
+ let netrwmftgt= simplify(a:dname)
+ else
+ let netrwmftgt= a:dname
+ endif
+ if exists("s:netrwmftgt") && netrwmftgt == s:netrwmftgt
+ " re-selected target, so just clear it
+ unlet s:netrwmftgt s:netrwmftgt_islocal
+ else
+ let s:netrwmftgt= netrwmftgt
+ endif
+ if g:netrw_fastbrowse <= 1
+ call s:NetrwRefresh((b:netrw_curdir !~ '\a\{3,}://'),b:netrw_curdir)
+ endif
+ " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))"
+ call winrestview(svpos)
+ " call Dret("netrw#MakeTgt")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Obtain: {{{2
+" netrw#Obtain(islocal,fname[,tgtdirectory])
+" islocal=0 obtain from remote source
+" =1 obtain from local source
+" fname : a filename or a list of filenames
+" tgtdir : optional place where files are to go (not present, uses getcwd())
+fun! netrw#Obtain(islocal,fname,...)
+ " call Dfunc("netrw#Obtain(islocal=".a:islocal." fname<".((type(a:fname) == 1)? a:fname : string(a:fname)).">) a:0=".a:0)
+ " NetrwStatusLine support - for obtaining support
+
+ if type(a:fname) == 1
+ let fnamelist= [ a:fname ]
+ elseif type(a:fname) == 3
+ let fnamelist= a:fname
+ else
+ call netrw#ErrorMsg(s:ERROR,"attempting to use NetrwObtain on something not a filename or a list",62)
+ " call Dret("netrw#Obtain")
+ return
+ endif
+ " call Decho("fnamelist<".string(fnamelist).">",'~'.expand("<slnum>"))
+ if a:0 > 0
+ let tgtdir= a:1
+ else
+ let tgtdir= getcwd()
+ endif
+ " call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>"))
+
+ if exists("b:netrw_islocal") && b:netrw_islocal
+ " obtain a file from local b:netrw_curdir to (local) tgtdir
+ " call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>"))
+ if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir
+ let topath= s:ComposePath(tgtdir,"")
+ if has("win32")
+ " transfer files one at time
+ " call Decho("transfer files one at a time",'~'.expand("<slnum>"))
+ for fname in fnamelist
+ " call Decho("system(".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath).")",'~'.expand("<slnum>"))
+ call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".s:ShellEscape(fname)." ".s:ShellEscape(topath))
+ if v:shell_error != 0
+ call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80)
+ " call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath))
+ return
+ endif
+ endfor
+ else
+ " transfer files with one command
+ " call Decho("transfer files with one command",'~'.expand("<slnum>"))
+ let filelist= join(map(deepcopy(fnamelist),"s:ShellEscape(v:val)"))
+ " call Decho("system(".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath).")",'~'.expand("<slnum>"))
+ call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".filelist." ".s:ShellEscape(topath))
+ if v:shell_error != 0
+ call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80)
+ " call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath))
+ return
+ endif
+ endif
+ elseif !exists("b:netrw_curdir")
+ call netrw#ErrorMsg(s:ERROR,"local browsing directory doesn't exist!",36)
+ else
+ call netrw#ErrorMsg(s:WARNING,"local browsing directory and current directory are identical",37)
+ endif
+
+ else
+ " obtain files from remote b:netrw_curdir to local tgtdir
+ " call Decho("obtain a file from remote ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>"))
+ if type(a:fname) == 1
+ call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname)
+ endif
+ call s:NetrwMethod(b:netrw_curdir)
+
+ if b:netrw_method == 4
+ " obtain file using scp
+ " call Decho("obtain via scp (method#4)",'~'.expand("<slnum>"))
+ if exists("g:netrw_port") && g:netrw_port != ""
+ let useport= " ".g:netrw_scpport." ".g:netrw_port
+ else
+ let useport= ""
+ endif
+ if b:netrw_fname =~ '/'
+ let path= substitute(b:netrw_fname,'^\(.*/\).\{-}$','\1','')
+ else
+ let path= ""
+ endif
+ let filelist= join(map(deepcopy(fnamelist),'escape(s:ShellEscape(g:netrw_machine.":".path.v:val,1)," ")'))
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".filelist." ".s:ShellEscape(tgtdir,1))
+
+ elseif b:netrw_method == 2
+ " obtain file using ftp + .netrc
+ " call Decho("obtain via ftp+.netrc (method #2)",'~'.expand("<slnum>"))
+ call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars()
+ let tmpbufnr= bufnr("%")
+ setl ff=unix
+ if exists("g:netrw_ftpmode") && g:netrw_ftpmode != ""
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("b:netrw_fname") && b:netrw_fname != ""
+ call setline(line("$")+1,'cd "'.b:netrw_fname.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+ for fname in fnamelist
+ call setline(line("$")+1,'get "'.fname.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endfor
+ if exists("g:netrw_port") && g:netrw_port != ""
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
+ else
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
+ endif
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
+ let debugkeep= &debug
+ setl debug=msg
+ call netrw#ErrorMsg(s:ERROR,getline(1),4)
+ let &debug= debugkeep
+ endif
+
+ elseif b:netrw_method == 3
+ " obtain with ftp + machine, id, passwd, and fname (ie. no .netrc)
+ " call Decho("obtain via ftp+mipf (method #3)",'~'.expand("<slnum>"))
+ call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars()
+ let tmpbufnr= bufnr("%")
+ setl ff=unix
+
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_uid") && g:netrw_uid != ""
+ if exists("g:netrw_ftp") && g:netrw_ftp == 1
+ NetrwKeepj put =g:netrw_uid
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ if exists("s:netrw_passwd") && s:netrw_passwd != ""
+ NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
+ endif
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ elseif exists("s:netrw_passwd")
+ NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+ endif
+
+ if exists("g:netrw_ftpmode") && g:netrw_ftpmode != ""
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("b:netrw_fname") && b:netrw_fname != ""
+ NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+ for fname in fnamelist
+ NetrwKeepj call setline(line("$")+1,'get "'.fname.'"')
+ endfor
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+
+ " perform ftp:
+ " -i : turns off interactive prompting from ftp
+ " -n unix : DON'T use <.netrc>, even though it exists
+ " -n win32: quit being obnoxious about password
+ " Note: using "_dd to delete to the black hole register; avoids messing up @@
+ NetrwKeepj norm! 1G"_dd
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ if getline(1) !~ "^$"
+ " call Decho("error<".getline(1).">",'~'.expand("<slnum>"))
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),5)
+ endif
+ endif
+
+ elseif b:netrw_method == 9
+ " obtain file using sftp
+ " call Decho("obtain via sftp (method #9)",'~'.expand("<slnum>"))
+ if a:fname =~ '/'
+ let localfile= substitute(a:fname,'^.*/','','')
+ else
+ let localfile= a:fname
+ endif
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1).s:ShellEscape(localfile)." ".s:ShellEscape(tgtdir))
+
+ elseif !exists("b:netrw_method") || b:netrw_method < 0
+ " probably a badly formed url; protocol not recognized
+ " call Dret("netrw#Obtain : unsupported method")
+ return
+
+ else
+ " protocol recognized but not supported for Obtain (yet?)
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"current protocol not supported for obtaining file",97)
+ endif
+ " call Dret("netrw#Obtain : current protocol not supported for obtaining file")
+ return
+ endif
+
+ " restore status line
+ if type(a:fname) == 1 && exists("s:netrw_users_stl")
+ NetrwKeepj call s:SetupNetrwStatusLine(s:netrw_users_stl)
+ endif
+
+ endif
+
+ " cleanup
+ if exists("tmpbufnr")
+ if bufnr("%") != tmpbufnr
+ exe tmpbufnr."bw!"
+ else
+ q!
+ endif
+ endif
+
+ " call Dret("netrw#Obtain")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Nread: save position, call netrw#NetRead(), and restore position {{{2
+fun! netrw#Nread(mode,fname)
+ " call Dfunc("netrw#Nread(mode=".a:mode." fname<".a:fname.">)")
+ let svpos= winsaveview()
+ " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ call netrw#NetRead(a:mode,a:fname)
+ " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ call winrestview(svpos)
+
+ if exists("w:netrw_liststyle") && w:netrw_liststyle != s:TREELIST
+ if exists("w:netrw_bannercnt")
+ " start with cursor just after the banner
+ exe w:netrw_bannercnt
+ endif
+ endif
+ " call Dret("netrw#Nread")
+endfun
+
+" ------------------------------------------------------------------------
+" s:NetrwOptionsSave: save options prior to setting to "netrw-buffer-standard" form {{{2
+" Options get restored by s:NetrwOptionsRestore()
+"
+" Option handling:
+" * save user's options (s:NetrwOptionsSave)
+" * set netrw-safe options (s:NetrwOptionsSafe)
+" - change an option only when user option != safe option (s:netrwSetSafeSetting)
+" * restore user's options (s:netrwOPtionsRestore)
+" - restore a user option when != safe option (s:NetrwRestoreSetting)
+" vt: (variable type) normally its either "w:" or "s:"
+fun! s:NetrwOptionsSave(vt)
+ " call Dfunc("s:NetrwOptionsSave(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%")).">"." winnr($)=".winnr("$")." mod=".&mod." ma=".&ma)
+ " call Decho(a:vt."netrw_optionsave".(exists("{a:vt}netrw_optionsave")? ("=".{a:vt}netrw_optionsave) : " doesn't exist"),'~'.expand("<slnum>"))
+ " 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." hid=".&hid,'~'.expand("<slnum>"))
+ " call Decho("(s:NetrwOptionsSave) lines=".&lines)
+
+ if !exists("{a:vt}netrw_optionsave")
+ let {a:vt}netrw_optionsave= 1
+ else
+ " call Dret("s:NetrwOptionsSave : options already saved")
+ return
+ endif
+ " call Decho("prior to save: fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." diff=".&l:diff,'~'.expand("<slnum>"))
+
+ " Save current settings and current directory
+ " call Decho("saving current settings and current directory",'~'.expand("<slnum>"))
+ let s:yykeep = @@
+ if exists("&l:acd")|let {a:vt}netrw_acdkeep = &l:acd|endif
+ let {a:vt}netrw_aikeep = &l:ai
+ let {a:vt}netrw_awkeep = &l:aw
+ let {a:vt}netrw_bhkeep = &l:bh
+ let {a:vt}netrw_blkeep = &l:bl
+ let {a:vt}netrw_btkeep = &l:bt
+ let {a:vt}netrw_bombkeep = &l:bomb
+ let {a:vt}netrw_cedit = &cedit
+ let {a:vt}netrw_cikeep = &l:ci
+ let {a:vt}netrw_cinkeep = &l:cin
+ let {a:vt}netrw_cinokeep = &l:cino
+ let {a:vt}netrw_comkeep = &l:com
+ let {a:vt}netrw_cpokeep = &l:cpo
+ let {a:vt}netrw_cuckeep = &l:cuc
+ let {a:vt}netrw_culkeep = &l:cul
+ " call Decho("(s:NetrwOptionsSave) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
+ let {a:vt}netrw_diffkeep = &l:diff
+ let {a:vt}netrw_fenkeep = &l:fen
+ if !exists("g:netrw_ffkeep") || g:netrw_ffkeep
+ let {a:vt}netrw_ffkeep = &l:ff
+ endif
+ let {a:vt}netrw_fokeep = &l:fo " formatoptions
+ let {a:vt}netrw_gdkeep = &l:gd " gdefault
+ let {a:vt}netrw_gokeep = &go " guioptions
+ let {a:vt}netrw_hidkeep = &l:hidden
+ let {a:vt}netrw_imkeep = &l:im
+ let {a:vt}netrw_iskkeep = &l:isk
+ let {a:vt}netrw_lines = &lines
+ let {a:vt}netrw_lskeep = &l:ls
+ let {a:vt}netrw_makeep = &l:ma
+ let {a:vt}netrw_magickeep = &l:magic
+ let {a:vt}netrw_modkeep = &l:mod
+ let {a:vt}netrw_nukeep = &l:nu
+ let {a:vt}netrw_rnukeep = &l:rnu
+ let {a:vt}netrw_repkeep = &l:report
+ let {a:vt}netrw_rokeep = &l:ro
+ let {a:vt}netrw_selkeep = &l:sel
+ let {a:vt}netrw_spellkeep = &l:spell
+ if !g:netrw_use_noswf
+ let {a:vt}netrw_swfkeep = &l:swf
+ endif
+ let {a:vt}netrw_tskeep = &l:ts
+ let {a:vt}netrw_twkeep = &l:tw " textwidth
+ let {a:vt}netrw_wigkeep = &l:wig " wildignore
+ let {a:vt}netrw_wrapkeep = &l:wrap
+ let {a:vt}netrw_writekeep = &l:write
+
+ " save a few selected netrw-related variables
+ " call Decho("saving a few selected netrw-related variables",'~'.expand("<slnum>"))
+ if g:netrw_keepdir
+ let {a:vt}netrw_dirkeep = getcwd()
+ " call Decho("saving to ".a:vt."netrw_dirkeep<".{a:vt}netrw_dirkeep.">",'~'.expand("<slnum>"))
+ endif
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ sil! let {a:vt}netrw_starkeep = @*
+ sil! let {a:vt}netrw_pluskeep = @+
+ endif
+ sil! let {a:vt}netrw_slashkeep= @/
+
+ " call Decho("(s:NetrwOptionsSave) 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>"))
+ " call Dret("s:NetrwOptionsSave : tab#".tabpagenr()." win#".winnr())
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwOptionsSafe: sets options to help netrw do its job {{{2
+" Use s:NetrwSaveOptions() to save user settings
+" Use s:NetrwOptionsRestore() to restore user settings
+fun! s:NetrwOptionsSafe(islocal)
+ " call Dfunc("s:NetrwOptionsSafe(islocal=".a:islocal.") win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%"))."> winnr($)=".winnr("$"))
+ " call Decho("win#".winnr()."'s ft=".&ft,'~'.expand("<slnum>"))
+ " 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,'~'.expand("<slnum>"))
+ if exists("+acd") | call s:NetrwSetSafeSetting("&l:acd",0)|endif
+ call s:NetrwSetSafeSetting("&l:ai",0)
+ call s:NetrwSetSafeSetting("&l:aw",0)
+ call s:NetrwSetSafeSetting("&l:bl",0)
+ call s:NetrwSetSafeSetting("&l:bomb",0)
+ if a:islocal
+ call s:NetrwSetSafeSetting("&l:bt","nofile")
+ else
+ call s:NetrwSetSafeSetting("&l:bt","acwrite")
+ endif
+ call s:NetrwSetSafeSetting("&l:ci",0)
+ call s:NetrwSetSafeSetting("&l:cin",0)
+ if g:netrw_fastbrowse > a:islocal
+ call s:NetrwSetSafeSetting("&l:bh","hide")
+ else
+ call s:NetrwSetSafeSetting("&l:bh","delete")
+ endif
+ call s:NetrwSetSafeSetting("&l:cino","")
+ call s:NetrwSetSafeSetting("&l:com","")
+ if &cpo =~ 'a' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'a','','g')) | endif
+ if &cpo =~ 'A' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'A','','g')) | endif
+ setl fo=nroql2
+ if &go =~ 'a' | set go-=a | endif
+ if &go =~ 'A' | set go-=A | endif
+ if &go =~ 'P' | set go-=P | endif
+ call s:NetrwSetSafeSetting("&l:hid",0)
+ call s:NetrwSetSafeSetting("&l:im",0)
+ setl isk+=@ isk+=* isk+=/
+ call s:NetrwSetSafeSetting("&l:magic",1)
+ if g:netrw_use_noswf
+ call s:NetrwSetSafeSetting("swf",0)
+ endif
+ call s:NetrwSetSafeSetting("&l:report",10000)
+ call s:NetrwSetSafeSetting("&l:sel","inclusive")
+ call s:NetrwSetSafeSetting("&l:spell",0)
+ call s:NetrwSetSafeSetting("&l:tw",0)
+ call s:NetrwSetSafeSetting("&l:wig","")
+ setl cedit&
+
+ " set up cuc and cul based on g:netrw_cursor and listing style
+ " COMBAK -- cuc cul related
+ call s:NetrwCursor(0)
+
+ " allow the user to override safe options
+ " call Decho("ft<".&ft."> ei=".&ei,'~'.expand("<slnum>"))
+ if &ft == "netrw"
+ " call Decho("do any netrw FileType autocmds (doau FileType netrw)",'~'.expand("<slnum>"))
+ keepalt NetrwKeepj doau FileType netrw
+ endif
+
+ " call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." bh=".&l:bh." bt<".&bt.">",'~'.expand("<slnum>"))
+ " 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,'~'.expand("<slnum>"))
+ " call Dret("s:NetrwOptionsSafe")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwOptionsRestore: restore options (based on prior s:NetrwOptionsSave) {{{2
+fun! s:NetrwOptionsRestore(vt)
+ if !exists("{a:vt}netrw_optionsave")
+ " filereadable() returns zero for remote files (e.g. scp://user@localhost//etc/fstab)
+ " Note: @ may not be in 'isfname', so '^\w\+://\f\+/' may not match
+ if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+'
+ filetype detect
+ else
+ setl ft=netrw
+ endif
+ return
+ endif
+ unlet {a:vt}netrw_optionsave
+
+ if exists("+acd")
+ if exists("{a:vt}netrw_acdkeep")
+ let curdir = getcwd()
+ let &l:acd = {a:vt}netrw_acdkeep
+ unlet {a:vt}netrw_acdkeep
+ if &l:acd
+ call s:NetrwLcd(curdir)
+ endif
+ endif
+ endif
+ call s:NetrwRestoreSetting(a:vt."netrw_aikeep","&l:ai")
+ call s:NetrwRestoreSetting(a:vt."netrw_awkeep","&l:aw")
+ call s:NetrwRestoreSetting(a:vt."netrw_blkeep","&l:bl")
+ call s:NetrwRestoreSetting(a:vt."netrw_btkeep","&l:bt")
+ call s:NetrwRestoreSetting(a:vt."netrw_bombkeep","&l:bomb")
+ call s:NetrwRestoreSetting(a:vt."netrw_cedit","&cedit")
+ call s:NetrwRestoreSetting(a:vt."netrw_cikeep","&l:ci")
+ call s:NetrwRestoreSetting(a:vt."netrw_cinkeep","&l:cin")
+ call s:NetrwRestoreSetting(a:vt."netrw_cinokeep","&l:cino")
+ call s:NetrwRestoreSetting(a:vt."netrw_comkeep","&l:com")
+ call s:NetrwRestoreSetting(a:vt."netrw_cpokeep","&l:cpo")
+ call s:NetrwRestoreSetting(a:vt."netrw_diffkeep","&l:diff")
+ call s:NetrwRestoreSetting(a:vt."netrw_fenkeep","&l:fen")
+ if exists("g:netrw_ffkeep") && g:netrw_ffkeep
+ call s:NetrwRestoreSetting(a:vt."netrw_ffkeep")","&l:ff")
+ endif
+ call s:NetrwRestoreSetting(a:vt."netrw_fokeep" ,"&l:fo")
+ call s:NetrwRestoreSetting(a:vt."netrw_gdkeep" ,"&l:gd")
+ call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&go")
+ call s:NetrwRestoreSetting(a:vt."netrw_hidkeep" ,"&l:hidden")
+ call s:NetrwRestoreSetting(a:vt."netrw_imkeep" ,"&l:im")
+ call s:NetrwRestoreSetting(a:vt."netrw_iskkeep" ,"&l:isk")
+ call s:NetrwRestoreSetting(a:vt."netrw_lines" ,"&lines")
+ call s:NetrwRestoreSetting(a:vt."netrw_lskeep" ,"&l:ls")
+ call s:NetrwRestoreSetting(a:vt."netrw_makeep" ,"&l:ma")
+ call s:NetrwRestoreSetting(a:vt."netrw_magickeep","&l:magic")
+ call s:NetrwRestoreSetting(a:vt."netrw_modkeep" ,"&l:mod")
+ call s:NetrwRestoreSetting(a:vt."netrw_nukeep" ,"&l:nu")
+ call s:NetrwRestoreSetting(a:vt."netrw_rnukeep" ,"&l:rnu")
+ call s:NetrwRestoreSetting(a:vt."netrw_repkeep" ,"&l:report")
+ call s:NetrwRestoreSetting(a:vt."netrw_rokeep" ,"&l:ro")
+ call s:NetrwRestoreSetting(a:vt."netrw_selkeep" ,"&l:sel")
+ call s:NetrwRestoreSetting(a:vt."netrw_spellkeep","&l:spell")
+ call s:NetrwRestoreSetting(a:vt."netrw_twkeep" ,"&l:tw")
+ call s:NetrwRestoreSetting(a:vt."netrw_wigkeep" ,"&l:wig")
+ call s:NetrwRestoreSetting(a:vt."netrw_wrapkeep" ,"&l:wrap")
+ call s:NetrwRestoreSetting(a:vt."netrw_writekeep","&l:write")
+ call s:NetrwRestoreSetting("s:yykeep","@@")
+ " former problem: start with liststyle=0; press <i> : result, following line resets l:ts.
+ " Fixed; in s:PerformListing, when w:netrw_liststyle is s:LONGLIST, will use a printf to pad filename with spaces
+ " rather than by appending a tab which previously was using "&ts" to set the desired spacing. (Sep 28, 2018)
+ call s:NetrwRestoreSetting(a:vt."netrw_tskeep","&l:ts")
+
+ if exists("{a:vt}netrw_swfkeep")
+ if &directory == ""
+ " user hasn't specified a swapfile directory;
+ " netrw will temporarily set the swapfile directory
+ " to the current directory as returned by getcwd().
+ let &l:directory= getcwd()
+ sil! let &l:swf = {a:vt}netrw_swfkeep
+ setl directory=
+ unlet {a:vt}netrw_swfkeep
+ elseif &l:swf != {a:vt}netrw_swfkeep
+ if !g:netrw_use_noswf
+ " following line causes a Press ENTER in windows -- can't seem to work around it!!!
+ sil! let &l:swf= {a:vt}netrw_swfkeep
+ endif
+ unlet {a:vt}netrw_swfkeep
+ endif
+ endif
+ if exists("{a:vt}netrw_dirkeep") && isdirectory(s:NetrwFile({a:vt}netrw_dirkeep)) && g:netrw_keepdir
+ let dirkeep = substitute({a:vt}netrw_dirkeep,'\\','/','g')
+ if exists("{a:vt}netrw_dirkeep")
+ call s:NetrwLcd(dirkeep)
+ unlet {a:vt}netrw_dirkeep
+ endif
+ endif
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ call s:NetrwRestoreSetting(a:vt."netrw_starkeep","@*")
+ call s:NetrwRestoreSetting(a:vt."netrw_pluskeep","@+")
+ endif
+ call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/")
+
+ " Moved the filetype detect here from NetrwGetFile() because remote files
+ " were having their filetype detect-generated settings overwritten by
+ " NetrwOptionRestore.
+ if &ft != "netrw"
+ filetype detect
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSetSafeSetting: sets an option to a safe setting {{{2
+" but only when the options' value and the safe setting differ
+" Doing this means that netrw will not come up as having changed a
+" setting last when it really didn't actually change it.
+"
+" Called from s:NetrwOptionsSafe
+" ex. call s:NetrwSetSafeSetting("&l:sel","inclusive")
+fun! s:NetrwSetSafeSetting(setting,safesetting)
+ " call Dfunc("s:NetrwSetSafeSetting(setting<".a:setting."> safesetting<".a:safesetting.">)")
+
+ if a:setting =~ '^&'
+ " call Decho("fyi: a:setting starts with &")
+ exe "let settingval= ".a:setting
+ " call Decho("fyi: settingval<".settingval.">")
+
+ if settingval != a:safesetting
+ " call Decho("set setting<".a:setting."> to option value<".a:safesetting.">")
+ if type(a:safesetting) == 0
+ exe "let ".a:setting."=".a:safesetting
+ elseif type(a:safesetting) == 1
+ exe "let ".a:setting."= '".a:safesetting."'"
+ else
+ call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:setting." with a safesetting of type#".type(a:safesetting),105)
+ endif
+ endif
+ endif
+
+ " call Dret("s:NetrwSetSafeSetting")
+endfun
+
+" ------------------------------------------------------------------------
+" s:NetrwRestoreSetting: restores specified setting using associated keepvar, {{{2
+" but only if the setting value differs from the associated keepvar.
+" Doing this means that netrw will not come up as having changed a
+" setting last when it really didn't actually change it.
+"
+" Used by s:NetrwOptionsRestore() to restore each netrw-sensitive setting
+" keepvars are set up by s:NetrwOptionsSave
+fun! s:NetrwRestoreSetting(keepvar,setting)
+ """ call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)")
+
+ " typically called from s:NetrwOptionsRestore
+ " call s:NetrwRestoreSettings(keep-option-variable-name,'associated-option')
+ " ex. call s:NetrwRestoreSetting(a:vt."netrw_selkeep","&l:sel")
+ " Restores option (but only if different) from a:keepvar
+ if exists(a:keepvar)
+ exe "let keepvarval= ".a:keepvar
+ exe "let setting= ".a:setting
+
+ "" call Decho("fyi: a:keepvar<".a:keepvar."> exists")
+ "" call Decho("fyi: keepvarval=".keepvarval)
+ "" call Decho("fyi: a:setting<".a:setting."> setting<".setting.">")
+
+ if setting != keepvarval
+ "" call Decho("restore setting<".a:setting."> (currently=".setting.") to keepvarval<".keepvarval.">")
+ if type(a:setting) == 0
+ exe "let ".a:setting."= ".keepvarval
+ elseif type(a:setting) == 1
+ exe "let ".a:setting."= '".substitute(keepvarval,"'","''","g")."'"
+ else
+ call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:keepvar." with a setting of type#".type(a:setting),105)
+ endif
+ endif
+
+ exe "unlet ".a:keepvar
+ endif
+
+ "" call Dret("s:NetrwRestoreSetting")
+endfun
+
+" ---------------------------------------------------------------------
+" NetrwStatusLine: {{{2
+fun! NetrwStatusLine()
+
+ " vvv NetrwStatusLine() debugging vvv
+ " let g:stlmsg=""
+ " if !exists("w:netrw_explore_bufnr")
+ " let g:stlmsg="!X<explore_bufnr>"
+ " elseif w:netrw_explore_bufnr != bufnr("%")
+ " let g:stlmsg="explore_bufnr!=".bufnr("%")
+ " endif
+ " if !exists("w:netrw_explore_line")
+ " let g:stlmsg=" !X<explore_line>"
+ " elseif w:netrw_explore_line != line(".")
+ " let g:stlmsg=" explore_line!={line(.)<".line(".").">"
+ " endif
+ " if !exists("w:netrw_explore_list")
+ " let g:stlmsg=" !X<explore_list>"
+ " endif
+ " ^^^ NetrwStatusLine() debugging ^^^
+
+ if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list")
+ " restore user's status line
+ let &l:stl = s:netrw_users_stl
+ let &laststatus = s:netrw_users_ls
+ if exists("w:netrw_explore_bufnr")|unlet w:netrw_explore_bufnr|endif
+ if exists("w:netrw_explore_line") |unlet w:netrw_explore_line |endif
+ return ""
+ else
+ return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen
+ endif
+endfun
+
+" ===============================
+" Netrw Transfer Functions: {{{1
+" ===============================
+
+" ------------------------------------------------------------------------
+" netrw#NetRead: responsible for reading a file over the net {{{2
+" mode: =0 read remote file and insert before current line
+" =1 read remote file and insert after current line
+" =2 replace with remote file
+" =3 obtain file, but leave in temporary format
+fun! netrw#NetRead(mode,...)
+ " call Dfunc("netrw#NetRead(mode=".a:mode.",...) a:0=".a:0." ".g:loaded_netrw.((a:0 > 0)? " a:1<".a:1.">" : ""))
+
+ " NetRead: save options {{{3
+ call s:NetrwOptionsSave("w:")
+ call s:NetrwOptionsSafe(0)
+ call s:RestoreCursorline()
+ " NetrwSafeOptions sets a buffer up for a netrw listing, which includes buflisting off.
+ " However, this setting is not wanted for a remote editing session. The buffer should be "nofile", still.
+ setl bl
+ " call Decho("buf#".bufnr("%")."<".bufname("%")."> bl=".&bl." bt=".&bt." bh=".&bh,'~'.expand("<slnum>"))
+
+ " NetRead: interpret mode into a readcmd {{{3
+ if a:mode == 0 " read remote file before current line
+ let readcmd = "0r"
+ elseif a:mode == 1 " read file after current line
+ let readcmd = "r"
+ elseif a:mode == 2 " replace with remote file
+ let readcmd = "%r"
+ elseif a:mode == 3 " skip read of file (leave as temporary)
+ let readcmd = "t"
+ else
+ exe a:mode
+ let readcmd = "r"
+ endif
+ let ichoice = (a:0 == 0)? 0 : 1
+ " call Decho("readcmd<".readcmd."> ichoice=".ichoice,'~'.expand("<slnum>"))
+
+ " NetRead: get temporary filename {{{3
+ let tmpfile= s:GetTempfile("")
+ if tmpfile == ""
+ " call Dret("netrw#NetRead : unable to get a tempfile!")
+ return
+ endif
+
+ while ichoice <= a:0
+
+ " attempt to repeat with previous host-file-etc
+ if exists("b:netrw_lastfile") && a:0 == 0
+ " call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>"))
+ let choice = b:netrw_lastfile
+ let ichoice= ichoice + 1
+
+ else
+ exe "let choice= a:" . ichoice
+ " call Decho("no lastfile: choice<" . choice . ">",'~'.expand("<slnum>"))
+
+ if match(choice,"?") == 0
+ " give help
+ echomsg 'NetRead Usage:'
+ echomsg ':Nread machine:path uses rcp'
+ echomsg ':Nread "machine path" uses ftp with <.netrc>'
+ echomsg ':Nread "machine id password path" uses ftp'
+ echomsg ':Nread dav://machine[:port]/path uses cadaver'
+ echomsg ':Nread fetch://machine/path uses fetch'
+ echomsg ':Nread ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>'
+ echomsg ':Nread http://[user@]machine/path uses http wget'
+ echomsg ':Nread file:///path uses elinks'
+ echomsg ':Nread https://[user@]machine/path uses http wget'
+ echomsg ':Nread rcp://[user@]machine/path uses rcp'
+ echomsg ':Nread rsync://machine[:port]/path uses rsync'
+ echomsg ':Nread scp://[user@]machine[[:#]port]/path uses scp'
+ echomsg ':Nread sftp://[user@]machine[[:#]port]/path uses sftp'
+ sleep 4
+ break
+
+ elseif match(choice,'^"') != -1
+ " Reconstruct Choice if choice starts with '"'
+ " call Decho("reconstructing choice",'~'.expand("<slnum>"))
+ if match(choice,'"$') != -1
+ " case "..."
+ let choice= strpart(choice,1,strlen(choice)-2)
+ else
+ " case "... ... ..."
+ let choice = strpart(choice,1,strlen(choice)-1)
+ let wholechoice = ""
+
+ while match(choice,'"$') == -1
+ let wholechoice = wholechoice . " " . choice
+ let ichoice = ichoice + 1
+ if ichoice > a:0
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",3)
+ endif
+ " call Dret("netrw#NetRead :2 getcwd<".getcwd().">")
+ return
+ endif
+ let choice= a:{ichoice}
+ endwhile
+ let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1)
+ endif
+ endif
+ endif
+
+ " call Decho("choice<" . choice . ">",'~'.expand("<slnum>"))
+ let ichoice= ichoice + 1
+
+ " NetRead: Determine method of read (ftp, rcp, etc) {{{3
+ call s:NetrwMethod(choice)
+ if !exists("b:netrw_method") || b:netrw_method < 0
+ " call Dret("netrw#NetRead : unsupported method")
+ return
+ endif
+ let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix
+
+ " Check whether or not NetrwBrowse() should be handling this request
+ " call Decho("checking if NetrwBrowse() should handle choice<".choice."> with netrw_list_cmd<".g:netrw_list_cmd.">",'~'.expand("<slnum>"))
+ if choice =~ "^.*[\/]$" && b:netrw_method != 5 && choice !~ '^https\=://'
+ " call Decho("yes, choice matches '^.*[\/]$'",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwBrowse(0,choice)
+ " call Dret("netrw#NetRead :3 getcwd<".getcwd().">")
+ return
+ endif
+
+ " ============
+ " NetRead: Perform Protocol-Based Read {{{3
+ " ===========================
+ if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1
+ echo "(netrw) Processing your read request..."
+ endif
+
+ ".........................................
+ " NetRead: (rcp) NetRead Method #1 {{{3
+ if b:netrw_method == 1 " read with rcp
+ " call Decho("read via rcp (method #1)",'~'.expand("<slnum>"))
+ " ER: nothing done with g:netrw_uid yet?
+ " ER: on Win2K" rcp machine[.user]:file tmpfile
+ " ER: when machine contains '.' adding .user is required (use $USERNAME)
+ " ER: the tmpfile is full path: rcp sees C:\... as host C
+ if s:netrw_has_nt_rcp == 1
+ if exists("g:netrw_uid") && ( g:netrw_uid != "" )
+ let uid_machine = g:netrw_machine .'.'. g:netrw_uid
+ else
+ " Any way needed it machine contains a '.'
+ let uid_machine = g:netrw_machine .'.'. $USERNAME
+ endif
+ else
+ if exists("g:netrw_uid") && ( g:netrw_uid != "" )
+ let uid_machine = g:netrw_uid .'@'. g:netrw_machine
+ else
+ let uid_machine = g:netrw_machine
+ endif
+ endif
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1))
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (ftp + <.netrc>) NetRead Method #2 {{{3
+ elseif b:netrw_method == 2 " read with ftp + <.netrc>
+ " call Decho("read via ftp+.netrc (method #2)",'~'.expand("<slnum>"))
+ let netrw_fname= b:netrw_fname
+ NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars()
+ let filtbuf= bufnr("%")
+ setl ff=unix
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>"))
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>"))
+ endif
+ call setline(line("$")+1,'get "'.netrw_fname.'" '.tmpfile)
+ " call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>"))
+ if exists("g:netrw_port") && g:netrw_port != ""
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
+ else
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
+ endif
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
+ let debugkeep = &debug
+ setl debug=msg
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),4)
+ let &debug = debugkeep
+ endif
+ call s:SaveBufVars()
+ keepj bd!
+ if bufname("%") == "" && getline("$") == "" && line('$') == 1
+ " needed when one sources a file in a nolbl setting window via ftp
+ q!
+ endif
+ call s:RestoreBufVars()
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (ftp + machine,id,passwd,filename) NetRead Method #3 {{{3
+ elseif b:netrw_method == 3 " read with ftp + machine, id, passwd, and fname
+ " Construct execution string (four lines) which will be passed through filter
+ " call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>"))
+ let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape)
+ NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars()
+ let filtbuf= bufnr("%")
+ setl ff=unix
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_uid") && g:netrw_uid != ""
+ if exists("g:netrw_ftp") && g:netrw_ftp == 1
+ NetrwKeepj put =g:netrw_uid
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ if exists("s:netrw_passwd")
+ NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
+ endif
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ elseif exists("s:netrw_passwd")
+ NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ endif
+
+ if exists("g:netrw_ftpmode") && g:netrw_ftpmode != ""
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ NetrwKeepj put ='get \"'.netrw_fname.'\" '.tmpfile
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+
+ " perform ftp:
+ " -i : turns off interactive prompting from ftp
+ " -n unix : DON'T use <.netrc>, even though it exists
+ " -n win32: quit being obnoxious about password
+ NetrwKeepj norm! 1G"_dd
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ if getline(1) !~ "^$"
+ " call Decho("error<".getline(1).">",'~'.expand("<slnum>"))
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,getline(1),5)
+ endif
+ endif
+ call s:SaveBufVars()|keepj bd!|call s:RestoreBufVars()
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (scp) NetRead Method #4 {{{3
+ elseif b:netrw_method == 4 " read with scp
+ " call Decho("read via scp (method #4)",'~'.expand("<slnum>"))
+ if exists("g:netrw_port") && g:netrw_port != ""
+ let useport= " ".g:netrw_scpport." ".g:netrw_port
+ else
+ let useport= ""
+ endif
+ " 'C' in 'C:\path\to\file' is handled as hostname on windows.
+ " This is workaround to avoid mis-handle windows local-path:
+ if g:netrw_scp_cmd =~ '^scp' && has("win32")
+ let tmpfile_get = substitute(tr(tmpfile, '\', '/'), '^\(\a\):[/\\]\(.*\)$', '/\1/\2', '')
+ else
+ let tmpfile_get = tmpfile
+ endif
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".escape(s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1),' ')." ".s:ShellEscape(tmpfile_get,1))
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (http) NetRead Method #5 (wget) {{{3
+ elseif b:netrw_method == 5
+ " call Decho("read via http (method #5)",'~'.expand("<slnum>"))
+ if g:netrw_http_cmd == ""
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,"neither the wget nor the fetch command is available",6)
+ endif
+ " call Dret("netrw#NetRead :4 getcwd<".getcwd().">")
+ return
+ endif
+
+ if match(b:netrw_fname,"#") == -1 || exists("g:netrw_http_xcmd")
+ " using g:netrw_http_cmd (usually elinks, links, curl, wget, or fetch)
+ " call Decho('using '.g:netrw_http_cmd.' (# not in b:netrw_fname<'.b:netrw_fname.">)",'~'.expand("<slnum>"))
+ if exists("g:netrw_http_xcmd")
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".s:ShellEscape(tmpfile,1))
+ else
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1))
+ endif
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+
+ else
+ " wget/curl/fetch plus a jump to an in-page marker (ie. http://abc/def.html#aMarker)
+ " call Decho("wget/curl plus jump (# in b:netrw_fname<".b:netrw_fname.">)",'~'.expand("<slnum>"))
+ let netrw_html= substitute(b:netrw_fname,"#.*$","","")
+ let netrw_tag = substitute(b:netrw_fname,"^.*#","","")
+ " call Decho("netrw_html<".netrw_html.">",'~'.expand("<slnum>"))
+ " call Decho("netrw_tag <".netrw_tag.">",'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.netrw_html,1))
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ " call Decho('<\s*a\s*name=\s*"'.netrw_tag.'"/','~'.expand("<slnum>"))
+ exe 'NetrwKeepj norm! 1G/<\s*a\s*name=\s*"'.netrw_tag.'"/'."\<CR>"
+ endif
+ let b:netrw_lastfile = choice
+ " call Decho("setl ro",'~'.expand("<slnum>"))
+ setl ro nomod
+
+ ".........................................
+ " NetRead: (dav) NetRead Method #6 {{{3
+ elseif b:netrw_method == 6
+ " call Decho("read via cadaver (method #6)",'~'.expand("<slnum>"))
+
+ if !executable(g:netrw_dav_cmd)
+ call netrw#ErrorMsg(s:ERROR,g:netrw_dav_cmd." is not executable",73)
+ " call Dret("netrw#NetRead : ".g:netrw_dav_cmd." not executable")
+ return
+ endif
+ if g:netrw_dav_cmd =~ "curl"
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".s:ShellEscape("dav://".g:netrw_machine.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1))
+ else
+ " Construct execution string (four lines) which will be passed through filter
+ let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape)
+ new
+ setl ff=unix
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ endif
+ if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != ""
+ NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd
+ endif
+ NetrwKeepj put ='get '.netrw_fname.' '.tmpfile
+ NetrwKeepj put ='quit'
+
+ " perform cadaver operation:
+ NetrwKeepj norm! 1G"_dd
+ call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd)
+ keepj bd!
+ endif
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (rsync) NetRead Method #7 {{{3
+ elseif b:netrw_method == 7
+ " call Decho("read via rsync (method #7)",'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1))
+ let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (fetch) NetRead Method #8 {{{3
+ " fetch://[user@]host[:http]/path
+ elseif b:netrw_method == 8
+ " call Decho("read via fetch (method #8)",'~'.expand("<slnum>"))
+ if g:netrw_fetch_cmd == ""
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"fetch command not available",7)
+ endif
+ " call Dret("NetRead")
+ return
+ endif
+ if exists("g:netrw_option") && g:netrw_option =~ ":https\="
+ let netrw_option= "http"
+ else
+ let netrw_option= "ftp"
+ endif
+ " call Decho("read via fetch for ".netrw_option,'~'.expand("<slnum>"))
+
+ if exists("g:netrw_uid") && g:netrw_uid != "" && exists("s:netrw_passwd") && s:netrw_passwd != ""
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1))
+ else
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1))
+ endif
+
+ let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+ " call Decho("setl ro",'~'.expand("<slnum>"))
+ setl ro nomod
+
+ ".........................................
+ " NetRead: (sftp) NetRead Method #9 {{{3
+ elseif b:netrw_method == 9
+ " call Decho("read via sftp (method #9)",'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile)
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: (file) NetRead Method #10 {{{3
+ elseif b:netrw_method == 10 && exists("g:netrw_file_cmd")
+ " " call Decho("read via ".b:netrw_file_cmd." (method #10)",'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_file_cmd." ".s:ShellEscape(b:netrw_fname,1)." ".tmpfile)
+ let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method)
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetRead: Complain {{{3
+ else
+ call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",8)
+ endif
+ endwhile
+
+ " NetRead: cleanup {{{3
+ if exists("b:netrw_method")
+ " call Decho("cleanup b:netrw_method and b:netrw_fname",'~'.expand("<slnum>"))
+ unlet b:netrw_method
+ unlet b:netrw_fname
+ endif
+ if s:FileReadable(tmpfile) && tmpfile !~ '.tar.bz2$' && tmpfile !~ '.tar.gz$' && tmpfile !~ '.zip' && tmpfile !~ '.tar' && readcmd != 't' && tmpfile !~ '.tar.xz$' && tmpfile !~ '.txz'
+ " call Decho("cleanup by deleting tmpfile<".tmpfile.">",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwDelete(tmpfile)
+ endif
+ NetrwKeepj call s:NetrwOptionsRestore("w:")
+
+ " call Dret("netrw#NetRead :5 getcwd<".getcwd().">")
+endfun
+
+" ------------------------------------------------------------------------
+" netrw#NetWrite: responsible for writing a file over the net {{{2
+fun! netrw#NetWrite(...) range
+ " call Dfunc("netrw#NetWrite(a:0=".a:0.") ".g:loaded_netrw)
+
+ " NetWrite: option handling {{{3
+ let mod= 0
+ call s:NetrwOptionsSave("w:")
+ call s:NetrwOptionsSafe(0)
+
+ " NetWrite: Get Temporary Filename {{{3
+ let tmpfile= s:GetTempfile("")
+ if tmpfile == ""
+ " call Dret("netrw#NetWrite : unable to get a tempfile!")
+ return
+ endif
+
+ if a:0 == 0
+ let ichoice = 0
+ else
+ let ichoice = 1
+ endif
+
+ let curbufname= expand("%")
+ " call Decho("curbufname<".curbufname.">",'~'.expand("<slnum>"))
+ if &binary
+ " For binary writes, always write entire file.
+ " (line numbers don't really make sense for that).
+ " Also supports the writing of tar and zip files.
+ " call Decho("(write entire file) sil exe w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>"))
+ exe "sil NetrwKeepj w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile)
+ elseif g:netrw_cygwin
+ " write (selected portion of) file to temporary
+ let cygtmpfile= substitute(tmpfile,g:netrw_cygdrive.'/\(.\)','\1:','')
+ " call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile),'~'.expand("<slnum>"))
+ exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile)
+ else
+ " write (selected portion of) file to temporary
+ " call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>"))
+ exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile)
+ endif
+
+ if curbufname == ""
+ " when the file is [No Name], and one attempts to Nwrite it, the buffer takes
+ " on the temporary file's name. Deletion of the temporary file during
+ " cleanup then causes an error message.
+ 0file!
+ endif
+
+ " NetWrite: while choice loop: {{{3
+ while ichoice <= a:0
+
+ " Process arguments: {{{4
+ " attempt to repeat with previous host-file-etc
+ if exists("b:netrw_lastfile") && a:0 == 0
+ " call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>"))
+ let choice = b:netrw_lastfile
+ let ichoice= ichoice + 1
+ else
+ exe "let choice= a:" . ichoice
+
+ " Reconstruct Choice when choice starts with '"'
+ if match(choice,"?") == 0
+ echomsg 'NetWrite Usage:"'
+ echomsg ':Nwrite machine:path uses rcp'
+ echomsg ':Nwrite "machine path" uses ftp with <.netrc>'
+ echomsg ':Nwrite "machine id password path" uses ftp'
+ echomsg ':Nwrite dav://[user@]machine/path uses cadaver'
+ echomsg ':Nwrite fetch://[user@]machine/path uses fetch'
+ echomsg ':Nwrite ftp://machine[#port]/path uses ftp (autodetects <.netrc>)'
+ echomsg ':Nwrite rcp://machine/path uses rcp'
+ echomsg ':Nwrite rsync://[user@]machine/path uses rsync'
+ echomsg ':Nwrite scp://[user@]machine[[:#]port]/path uses scp'
+ echomsg ':Nwrite sftp://[user@]machine/path uses sftp'
+ sleep 4
+ break
+
+ elseif match(choice,"^\"") != -1
+ if match(choice,"\"$") != -1
+ " case "..."
+ let choice=strpart(choice,1,strlen(choice)-2)
+ else
+ " case "... ... ..."
+ let choice = strpart(choice,1,strlen(choice)-1)
+ let wholechoice = ""
+
+ while match(choice,"\"$") == -1
+ let wholechoice= wholechoice . " " . choice
+ let ichoice = ichoice + 1
+ if choice > a:0
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",13)
+ endif
+ " call Dret("netrw#NetWrite")
+ return
+ endif
+ let choice= a:{ichoice}
+ endwhile
+ let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1)
+ endif
+ endif
+ endif
+ let ichoice= ichoice + 1
+ " call Decho("choice<" . choice . "> ichoice=".ichoice,'~'.expand("<slnum>"))
+
+ " Determine method of write (ftp, rcp, etc) {{{4
+ NetrwKeepj call s:NetrwMethod(choice)
+ if !exists("b:netrw_method") || b:netrw_method < 0
+ " call Dfunc("netrw#NetWrite : unsupported method")
+ return
+ endif
+
+ " =============
+ " NetWrite: Perform Protocol-Based Write {{{3
+ " ============================
+ if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1
+ echo "(netrw) Processing your write request..."
+ " call Decho("Processing your write request...",'~'.expand("<slnum>"))
+ endif
+
+ ".........................................
+ " NetWrite: (rcp) NetWrite Method #1 {{{3
+ if b:netrw_method == 1
+ " call Decho("write via rcp (method #1)",'~'.expand("<slnum>"))
+ if s:netrw_has_nt_rcp == 1
+ if exists("g:netrw_uid") && ( g:netrw_uid != "" )
+ let uid_machine = g:netrw_machine .'.'. g:netrw_uid
+ else
+ let uid_machine = g:netrw_machine .'.'. $USERNAME
+ endif
+ else
+ if exists("g:netrw_uid") && ( g:netrw_uid != "" )
+ let uid_machine = g:netrw_uid .'@'. g:netrw_machine
+ else
+ let uid_machine = g:netrw_machine
+ endif
+ endif
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1))
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetWrite: (ftp + <.netrc>) NetWrite Method #2 {{{3
+ elseif b:netrw_method == 2
+ " call Decho("write via ftp+.netrc (method #2)",'~'.expand("<slnum>"))
+ let netrw_fname = b:netrw_fname
+
+ " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
+ let bhkeep = &l:bh
+ let curbuf = bufnr("%")
+ setl bh=hide
+ keepj keepalt enew
+
+ " call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
+ setl ff=unix
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline("$"),'~'.expand("<slnum>"))
+ endif
+ NetrwKeepj call setline(line("$")+1,'put "'.tmpfile.'" "'.netrw_fname.'"')
+ " call Decho("filter input: ".getline("$"),'~'.expand("<slnum>"))
+ if exists("g:netrw_port") && g:netrw_port != ""
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
+ else
+ " call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
+ endif
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ if getline(1) !~ "^$"
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),14)
+ endif
+ let mod=1
+ endif
+
+ " remove enew buffer (quietly)
+ let filtbuf= bufnr("%")
+ exe curbuf."b!"
+ let &l:bh = bhkeep
+ exe filtbuf."bw!"
+
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetWrite: (ftp + machine, id, passwd, filename) NetWrite Method #3 {{{3
+ elseif b:netrw_method == 3
+ " Construct execution string (three or more lines) which will be passed through filter
+ " call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>"))
+ let netrw_fname = b:netrw_fname
+ let bhkeep = &l:bh
+
+ " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
+ let curbuf = bufnr("%")
+ setl bh=hide
+ keepj keepalt enew
+ setl ff=unix
+
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ if exists("g:netrw_uid") && g:netrw_uid != ""
+ if exists("g:netrw_ftp") && g:netrw_ftp == 1
+ NetrwKeepj put =g:netrw_uid
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ if exists("s:netrw_passwd") && s:netrw_passwd != ""
+ NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
+ endif
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ elseif exists("s:netrw_passwd") && s:netrw_passwd != ""
+ NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ endif
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline("$"),'~'.expand("<slnum>"))
+ endif
+ NetrwKeepj put ='put \"'.tmpfile.'\" \"'.netrw_fname.'\"'
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ " save choice/id/password for future use
+ let b:netrw_lastfile = choice
+
+ " perform ftp:
+ " -i : turns off interactive prompting from ftp
+ " -n unix : DON'T use <.netrc>, even though it exists
+ " -n win32: quit being obnoxious about password
+ NetrwKeepj norm! 1G"_dd
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ if getline(1) !~ "^$"
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,getline(1),15)
+ endif
+ let mod=1
+ endif
+
+ " remove enew buffer (quietly)
+ let filtbuf= bufnr("%")
+ exe curbuf."b!"
+ let &l:bh= bhkeep
+ exe filtbuf."bw!"
+
+ ".........................................
+ " NetWrite: (scp) NetWrite Method #4 {{{3
+ elseif b:netrw_method == 4
+ " call Decho("write via scp (method #4)",'~'.expand("<slnum>"))
+ if exists("g:netrw_port") && g:netrw_port != ""
+ let useport= " ".g:netrw_scpport." ".fnameescape(g:netrw_port)
+ else
+ let useport= ""
+ endif
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1))
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetWrite: (http) NetWrite Method #5 {{{3
+ elseif b:netrw_method == 5
+ " call Decho("write via http (method #5)",'~'.expand("<slnum>"))
+ let curl= substitute(g:netrw_http_put_cmd,'\s\+.*$',"","")
+ if executable(curl)
+ let url= g:netrw_choice
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(url,1) )
+ elseif !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd.">",16)
+ endif
+
+ ".........................................
+ " NetWrite: (dav) NetWrite Method #6 (cadaver) {{{3
+ elseif b:netrw_method == 6
+ " call Decho("write via cadaver (method #6)",'~'.expand("<slnum>"))
+
+ " Construct execution string (four lines) which will be passed through filter
+ let netrw_fname = escape(b:netrw_fname,g:netrw_fname_escape)
+ let bhkeep = &l:bh
+
+ " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
+ let curbuf = bufnr("%")
+ setl bh=hide
+ keepj keepalt enew
+
+ setl ff=unix
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ endif
+ if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != ""
+ NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd
+ endif
+ NetrwKeepj put ='put '.tmpfile.' '.netrw_fname
+
+ " perform cadaver operation:
+ NetrwKeepj norm! 1G"_dd
+ call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd)
+
+ " remove enew buffer (quietly)
+ let filtbuf= bufnr("%")
+ exe curbuf."b!"
+ let &l:bh = bhkeep
+ exe filtbuf."bw!"
+
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetWrite: (rsync) NetWrite Method #7 {{{3
+ elseif b:netrw_method == 7
+ " call Decho("write via rsync (method #7)",'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1))
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetWrite: (sftp) NetWrite Method #9 {{{3
+ elseif b:netrw_method == 9
+ " call Decho("write via sftp (method #9)",'~'.expand("<slnum>"))
+ let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape)
+ if exists("g:netrw_uid") && ( g:netrw_uid != "" )
+ let uid_machine = g:netrw_uid .'@'. g:netrw_machine
+ else
+ let uid_machine = g:netrw_machine
+ endif
+
+ " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead
+ let bhkeep = &l:bh
+ let curbuf = bufnr("%")
+ setl bh=hide
+ keepj keepalt enew
+
+ setl ff=unix
+ call setline(1,'put "'.escape(tmpfile,'\').'" '.netrw_fname)
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ let sftpcmd= substitute(g:netrw_sftp_cmd,"%TEMPFILE%",escape(tmpfile,'\'),"g")
+ call s:NetrwExe(s:netrw_silentxfer."%!".sftpcmd.' '.s:ShellEscape(uid_machine,1))
+ let filtbuf= bufnr("%")
+ exe curbuf."b!"
+ let &l:bh = bhkeep
+ exe filtbuf."bw!"
+ let b:netrw_lastfile = choice
+
+ ".........................................
+ " NetWrite: Complain {{{3
+ else
+ call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",17)
+ let leavemod= 1
+ endif
+ endwhile
+
+ " NetWrite: Cleanup: {{{3
+ " call Decho("cleanup",'~'.expand("<slnum>"))
+ if s:FileReadable(tmpfile)
+ " call Decho("tmpfile<".tmpfile."> readable, will now delete it",'~'.expand("<slnum>"))
+ call s:NetrwDelete(tmpfile)
+ endif
+ call s:NetrwOptionsRestore("w:")
+
+ if a:firstline == 1 && a:lastline == line("$")
+ " restore modifiability; usually equivalent to set nomod
+ let &l:mod= mod
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ elseif !exists("leavemod")
+ " indicate that the buffer has not been modified since last written
+ " call Decho("set nomod",'~'.expand("<slnum>"))
+ setl nomod
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ endif
+
+ " call Dret("netrw#NetWrite")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#NetSource: source a remotely hosted vim script {{{2
+" uses NetRead to get a copy of the file into a temporarily file,
+" then sources that file,
+" then removes that file.
+fun! netrw#NetSource(...)
+ " call Dfunc("netrw#NetSource() a:0=".a:0)
+ if a:0 > 0 && a:1 == '?'
+ " give help
+ echomsg 'NetSource Usage:'
+ echomsg ':Nsource dav://machine[:port]/path uses cadaver'
+ echomsg ':Nsource fetch://machine/path uses fetch'
+ echomsg ':Nsource ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>'
+ echomsg ':Nsource http[s]://[user@]machine/path uses http wget'
+ echomsg ':Nsource rcp://[user@]machine/path uses rcp'
+ echomsg ':Nsource rsync://machine[:port]/path uses rsync'
+ echomsg ':Nsource scp://[user@]machine[[:#]port]/path uses scp'
+ echomsg ':Nsource sftp://[user@]machine[[:#]port]/path uses sftp'
+ sleep 4
+ else
+ let i= 1
+ while i <= a:0
+ call netrw#NetRead(3,a:{i})
+ " call Decho("s:netread_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>"))
+ if s:FileReadable(s:netrw_tmpfile)
+ " call Decho("exe so ".fnameescape(s:netrw_tmpfile),'~'.expand("<slnum>"))
+ exe "so ".fnameescape(s:netrw_tmpfile)
+ " call Decho("delete(".s:netrw_tmpfile.")",'~'.expand("<slnum>"))
+ if delete(s:netrw_tmpfile)
+ call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".s:netrw_tmpfile.">!",103)
+ endif
+ unlet s:netrw_tmpfile
+ else
+ call netrw#ErrorMsg(s:ERROR,"unable to source <".a:{i}.">!",48)
+ endif
+ let i= i + 1
+ endwhile
+ endif
+ " call Dret("netrw#NetSource")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#SetTreetop: resets the tree top to the current directory/specified directory {{{2
+" (implements the :Ntree command)
+fun! netrw#SetTreetop(iscmd,...)
+
+ " iscmd==0: netrw#SetTreetop called using gn mapping
+ " iscmd==1: netrw#SetTreetop called using :Ntree from the command line
+ " clear out the current tree
+ if exists("w:netrw_treetop")
+ let inittreetop= w:netrw_treetop
+ unlet w:netrw_treetop
+ endif
+ if exists("w:netrw_treedict")
+ unlet w:netrw_treedict
+ endif
+
+ if (a:iscmd == 0 || a:1 == "") && exists("inittreetop")
+ let treedir = s:NetrwTreePath(inittreetop)
+ else
+ if isdirectory(s:NetrwFile(a:1))
+ let treedir = a:1
+ let s:netrw_treetop = treedir
+ elseif exists("b:netrw_curdir") && (isdirectory(s:NetrwFile(b:netrw_curdir."/".a:1)) || a:1 =~ '^\a\{3,}://')
+ let treedir = b:netrw_curdir."/".a:1
+ let s:netrw_treetop = treedir
+ else
+ " normally the cursor is left in the message window.
+ " However, here this results in the directory being listed in the message window, which is not wanted.
+ let netrwbuf= bufnr("%")
+ call netrw#ErrorMsg(s:ERROR,"sorry, ".a:1." doesn't seem to be a directory!",95)
+ exe bufwinnr(netrwbuf)."wincmd w"
+ let treedir = "."
+ let s:netrw_treetop = getcwd()
+ endif
+ endif
+
+ " determine if treedir is remote or local
+ let islocal= expand("%") !~ '^\a\{3,}://'
+
+ " browse the resulting directory
+ if islocal
+ call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(islocal,treedir,0))
+ else
+ call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,treedir,0))
+ endif
+
+endfun
+
+" ===========================================
+" s:NetrwGetFile: Function to read temporary file "tfile" with command "readcmd". {{{2
+" readcmd == %r : replace buffer with newly read file
+" == 0r : read file at top of buffer
+" == r : read file after current line
+" == t : leave file in temporary form (ie. don't read into buffer)
+fun! s:NetrwGetFile(readcmd, tfile, method)
+ " call Dfunc("NetrwGetFile(readcmd<".a:readcmd.">,tfile<".a:tfile."> method<".a:method.">)")
+
+ " readcmd=='t': simply do nothing
+ if a:readcmd == 't'
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ " call Dret("NetrwGetFile : skip read of tfile<".a:tfile.">")
+ return
+ endif
+
+ " get name of remote filename (ie. url and all)
+ let rfile= bufname("%")
+ " call Decho("rfile<".rfile.">",'~'.expand("<slnum>"))
+
+ if exists("*NetReadFixup")
+ " for the use of NetReadFixup (not otherwise used internally)
+ let line2= line("$")
+ endif
+
+ if a:readcmd[0] == '%'
+ " get file into buffer
+ " call Decho("get file into buffer",'~'.expand("<slnum>"))
+
+ " rename the current buffer to the temp file (ie. tfile)
+ if g:netrw_cygwin
+ let tfile= substitute(a:tfile,g:netrw_cygdrive.'/\(.\)','\1:','')
+ else
+ let tfile= a:tfile
+ endif
+ call s:NetrwBufRename(tfile)
+
+ " edit temporary file (ie. read the temporary file in)
+ if rfile =~ '\.zip$'
+ " call Decho("handling remote zip file with zip#Browse(tfile<".tfile.">)",'~'.expand("<slnum>"))
+ call zip#Browse(tfile)
+ elseif rfile =~ '\.tar$'
+ " call Decho("handling remote tar file with tar#Browse(tfile<".tfile.">)",'~'.expand("<slnum>"))
+ call tar#Browse(tfile)
+ elseif rfile =~ '\.tar\.gz$'
+ " call Decho("handling remote gzip-compressed tar file",'~'.expand("<slnum>"))
+ call tar#Browse(tfile)
+ elseif rfile =~ '\.tar\.bz2$'
+ " call Decho("handling remote bz2-compressed tar file",'~'.expand("<slnum>"))
+ call tar#Browse(tfile)
+ elseif rfile =~ '\.tar\.xz$'
+ " call Decho("handling remote xz-compressed tar file",'~'.expand("<slnum>"))
+ call tar#Browse(tfile)
+ elseif rfile =~ '\.txz$'
+ " call Decho("handling remote xz-compressed tar file (.txz)",'~'.expand("<slnum>"))
+ call tar#Browse(tfile)
+ else
+ " call Decho("edit temporary file",'~'.expand("<slnum>"))
+ NetrwKeepj e!
+ endif
+
+ " rename buffer back to remote filename
+ call s:NetrwBufRename(rfile)
+
+ " Jan 19, 2022: COMBAK -- bram problem with https://github.com/vim/vim/pull/9554.diff filetype
+ " Detect filetype of local version of remote file.
+ " Note that isk must not include a "/" for scripts.vim
+ " to process this detection correctly.
+ " call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>"))
+ " call Decho("..did_filetype()=".did_filetype())
+ " setl ft=
+ " call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">")
+ let iskkeep= &isk
+ setl isk-=/
+ filetype detect
+ " call Decho("..local filetype<".&ft."> for buf#".bufnr()."<".bufname().">")
+ let &l:isk= iskkeep
+ " call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)")
+ let line1 = 1
+ let line2 = line("$")
+
+ elseif !&ma
+ " attempting to read a file after the current line in the file, but the buffer is not modifiable
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"attempt to read<".a:tfile."> into a non-modifiable buffer!",94)
+ " call Dret("NetrwGetFile : attempt to read<".a:tfile."> into a non-modifiable buffer!")
+ return
+
+ elseif s:FileReadable(a:tfile)
+ " read file after current line
+ " call Decho("read file<".a:tfile."> after current line",'~'.expand("<slnum>"))
+ let curline = line(".")
+ let lastline= line("$")
+ " call Decho("exe<".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)."> line#".curline,'~'.expand("<slnum>"))
+ exe "NetrwKeepj ".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)
+ let line1= curline + 1
+ let line2= line("$") - lastline + 1
+
+ else
+ " not readable
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ " call Decho("tfile<".a:tfile."> not readable",'~'.expand("<slnum>"))
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"file <".a:tfile."> not readable",9)
+ " call Dret("NetrwGetFile : tfile<".a:tfile."> not readable")
+ return
+ endif
+
+ " User-provided (ie. optional) fix-it-up command
+ if exists("*NetReadFixup")
+ " call Decho("calling NetReadFixup(method<".a:method."> line1=".line1." line2=".line2.")",'~'.expand("<slnum>"))
+ NetrwKeepj call NetReadFixup(a:method, line1, line2)
+ " else " Decho
+ " call Decho("NetReadFixup() not called, doesn't exist (line1=".line1." line2=".line2.")",'~'.expand("<slnum>"))
+ endif
+
+ if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
+ " update the Buffers menu
+ NetrwKeepj call s:UpdateBuffersMenu()
+ endif
+
+ " call Decho("readcmd<".a:readcmd."> cmdarg<".v:cmdarg."> tfile<".a:tfile."> readable=".s:FileReadable(a:tfile),'~'.expand("<slnum>"))
+
+ " make sure file is being displayed
+ " redraw!
+
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ " call Dret("NetrwGetFile")
+endfun
+
+" ------------------------------------------------------------------------
+" s:NetrwMethod: determine method of transfer {{{2
+" Input:
+" choice = url [protocol:]//[userid@]hostname[:port]/[path-to-file]
+" Output:
+" b:netrw_method= 1: rcp
+" 2: ftp + <.netrc>
+" 3: ftp + machine, id, password, and [path]filename
+" 4: scp
+" 5: http[s] (wget)
+" 6: dav
+" 7: rsync
+" 8: fetch
+" 9: sftp
+" 10: file
+" g:netrw_machine= hostname
+" b:netrw_fname = filename
+" g:netrw_port = optional port number (for ftp)
+" g:netrw_choice = copy of input url (choice)
+fun! s:NetrwMethod(choice)
+ " call Dfunc("s:NetrwMethod(a:choice<".a:choice.">)")
+
+ " sanity check: choice should have at least three slashes in it
+ if strlen(substitute(a:choice,'[^/]','','g')) < 3
+ call netrw#ErrorMsg(s:ERROR,"not a netrw-style url; netrw uses protocol://[user@]hostname[:port]/[path])",78)
+ let b:netrw_method = -1
+ " call Dret("s:NetrwMethod : incorrect url format<".a:choice.">")
+ return
+ endif
+
+ " record current g:netrw_machine, if any
+ " curmachine used if protocol == ftp and no .netrc
+ if exists("g:netrw_machine")
+ let curmachine= g:netrw_machine
+ " call Decho("curmachine<".curmachine.">",'~'.expand("<slnum>"))
+ else
+ let curmachine= "N O T A HOST"
+ endif
+ if exists("g:netrw_port")
+ let netrw_port= g:netrw_port
+ endif
+
+ " insure that netrw_ftp_cmd starts off every method determination
+ " with the current g:netrw_ftp_cmd
+ let s:netrw_ftp_cmd= g:netrw_ftp_cmd
+
+ " initialization
+ let b:netrw_method = 0
+ let g:netrw_machine = ""
+ let b:netrw_fname = ""
+ let g:netrw_port = ""
+ let g:netrw_choice = a:choice
+
+ " Patterns:
+ " mipf : a:machine a:id password filename Use ftp
+ " mf : a:machine filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd
+ " ftpurm : ftp://[user@]host[[#:]port]/filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd
+ " rcpurm : rcp://[user@]host/filename Use rcp
+ " rcphf : [user@]host:filename Use rcp
+ " scpurm : scp://[user@]host[[#:]port]/filename Use scp
+ " httpurm : http[s]://[user@]host/filename Use wget
+ " davurm : dav[s]://host[:port]/path Use cadaver/curl
+ " rsyncurm : rsync://host[:port]/path Use rsync
+ " fetchurm : fetch://[user@]host[:http]/filename Use fetch (defaults to ftp, override for http)
+ " sftpurm : sftp://[user@]host/filename Use scp
+ " fileurm : file://[user@]host/filename Use elinks or links
+ let mipf = '^\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)$'
+ let mf = '^\(\S\+\)\s\+\(\S\+\)$'
+ let ftpurm = '^ftp://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\([#:]\d\+\)\=/\(.*\)$'
+ let rcpurm = '^rcp://\%(\([^/]*\)@\)\=\([^/]\{-}\)/\(.*\)$'
+ let rcphf = '^\(\(\h\w*\)@\)\=\(\h\w*\):\([^@]\+\)$'
+ let scpurm = '^scp://\([^/#:]\+\)\%([#:]\(\d\+\)\)\=/\(.*\)$'
+ let httpurm = '^https\=://\([^/]\{-}\)\(/.*\)\=$'
+ let davurm = '^davs\=://\([^/]\+\)/\(.*/\)\([-_.~[:alnum:]]\+\)$'
+ let rsyncurm = '^rsync://\([^/]\{-}\)/\(.*\)\=$'
+ let fetchurm = '^fetch://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\(:http\)\=/\(.*\)$'
+ let sftpurm = '^sftp://\([^/]\{-}\)/\(.*\)\=$'
+ let fileurm = '^file\=://\(.*\)$'
+
+ " call Decho("determine method:",'~'.expand("<slnum>"))
+ " Determine Method
+ " Method#1: rcp://user@hostname/...path-to-file {{{3
+ if match(a:choice,rcpurm) == 0
+ " call Decho("rcp://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 1
+ let userid = substitute(a:choice,rcpurm,'\1',"")
+ let g:netrw_machine = substitute(a:choice,rcpurm,'\2',"")
+ let b:netrw_fname = substitute(a:choice,rcpurm,'\3',"")
+ if userid != ""
+ let g:netrw_uid= userid
+ endif
+
+ " Method#4: scp://user@hostname/...path-to-file {{{3
+ elseif match(a:choice,scpurm) == 0
+ " call Decho("scp://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 4
+ let g:netrw_machine = substitute(a:choice,scpurm,'\1',"")
+ let g:netrw_port = substitute(a:choice,scpurm,'\2',"")
+ let b:netrw_fname = substitute(a:choice,scpurm,'\3',"")
+
+ " Method#5: http[s]://user@hostname/...path-to-file {{{3
+ elseif match(a:choice,httpurm) == 0
+ " call Decho("http[s]://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 5
+ let g:netrw_machine= substitute(a:choice,httpurm,'\1',"")
+ let b:netrw_fname = substitute(a:choice,httpurm,'\2',"")
+ let b:netrw_http = (a:choice =~ '^https:')? "https" : "http"
+
+ " Method#6: dav://hostname[:port]/..path-to-file.. {{{3
+ elseif match(a:choice,davurm) == 0
+ " call Decho("dav://...",'~'.expand("<slnum>"))
+ let b:netrw_method= 6
+ if a:choice =~ 'davs:'
+ let g:netrw_machine= 'https://'.substitute(a:choice,davurm,'\1/\2',"")
+ else
+ let g:netrw_machine= 'http://'.substitute(a:choice,davurm,'\1/\2',"")
+ endif
+ let b:netrw_fname = substitute(a:choice,davurm,'\3',"")
+
+ " Method#7: rsync://user@hostname/...path-to-file {{{3
+ elseif match(a:choice,rsyncurm) == 0
+ " call Decho("rsync://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 7
+ let g:netrw_machine= substitute(a:choice,rsyncurm,'\1',"")
+ let b:netrw_fname = substitute(a:choice,rsyncurm,'\2',"")
+
+ " Methods 2,3: ftp://[user@]hostname[[:#]port]/...path-to-file {{{3
+ elseif match(a:choice,ftpurm) == 0
+ " call Decho("ftp://...",'~'.expand("<slnum>"))
+ let userid = substitute(a:choice,ftpurm,'\2',"")
+ let g:netrw_machine= substitute(a:choice,ftpurm,'\3',"")
+ let g:netrw_port = substitute(a:choice,ftpurm,'\4',"")
+ let b:netrw_fname = substitute(a:choice,ftpurm,'\5',"")
+ " call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>"))
+ if userid != ""
+ let g:netrw_uid= userid
+ endif
+
+ if curmachine != g:netrw_machine
+ if exists("s:netrw_hup[".g:netrw_machine."]")
+ call NetUserPass("ftp:".g:netrw_machine)
+ elseif exists("s:netrw_passwd")
+ " if there's a change in hostname, require password re-entry
+ unlet s:netrw_passwd
+ endif
+ if exists("netrw_port")
+ unlet netrw_port
+ endif
+ endif
+
+ if exists("g:netrw_uid") && exists("s:netrw_passwd")
+ let b:netrw_method = 3
+ else
+ let host= substitute(g:netrw_machine,'\..*$','','')
+ if exists("s:netrw_hup[host]")
+ call NetUserPass("ftp:".host)
+
+ elseif has("win32") && s:netrw_ftp_cmd =~# '-[sS]:'
+ " call Decho("has -s: : s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>"))
+ " call Decho(" g:netrw_ftp_cmd<".g:netrw_ftp_cmd.">",'~'.expand("<slnum>"))
+ if g:netrw_ftp_cmd =~# '-[sS]:\S*MACHINE\>'
+ let s:netrw_ftp_cmd= substitute(g:netrw_ftp_cmd,'\<MACHINE\>',g:netrw_machine,'')
+ " call Decho("s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>"))
+ endif
+ let b:netrw_method= 2
+ elseif s:FileReadable(expand("$HOME/.netrc")) && !g:netrw_ignorenetrc
+ " call Decho("using <".expand("$HOME/.netrc")."> (readable)",'~'.expand("<slnum>"))
+ let b:netrw_method= 2
+ else
+ if !exists("g:netrw_uid") || g:netrw_uid == ""
+ call NetUserPass()
+ elseif !exists("s:netrw_passwd") || s:netrw_passwd == ""
+ call NetUserPass(g:netrw_uid)
+ " else just use current g:netrw_uid and s:netrw_passwd
+ endif
+ let b:netrw_method= 3
+ endif
+ endif
+
+ " Method#8: fetch {{{3
+ elseif match(a:choice,fetchurm) == 0
+ " call Decho("fetch://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 8
+ let g:netrw_userid = substitute(a:choice,fetchurm,'\2',"")
+ let g:netrw_machine= substitute(a:choice,fetchurm,'\3',"")
+ let b:netrw_option = substitute(a:choice,fetchurm,'\4',"")
+ let b:netrw_fname = substitute(a:choice,fetchurm,'\5',"")
+
+ " Method#3: Issue an ftp : "machine id password [path/]filename" {{{3
+ elseif match(a:choice,mipf) == 0
+ " call Decho("(ftp) host id pass file",'~'.expand("<slnum>"))
+ let b:netrw_method = 3
+ let g:netrw_machine = substitute(a:choice,mipf,'\1',"")
+ let g:netrw_uid = substitute(a:choice,mipf,'\2',"")
+ let s:netrw_passwd = substitute(a:choice,mipf,'\3',"")
+ let b:netrw_fname = substitute(a:choice,mipf,'\4',"")
+ call NetUserPass(g:netrw_machine,g:netrw_uid,s:netrw_passwd)
+
+ " Method#3: Issue an ftp: "hostname [path/]filename" {{{3
+ elseif match(a:choice,mf) == 0
+ " call Decho("(ftp) host file",'~'.expand("<slnum>"))
+ if exists("g:netrw_uid") && exists("s:netrw_passwd")
+ let b:netrw_method = 3
+ let g:netrw_machine = substitute(a:choice,mf,'\1',"")
+ let b:netrw_fname = substitute(a:choice,mf,'\2',"")
+
+ elseif s:FileReadable(expand("$HOME/.netrc"))
+ let b:netrw_method = 2
+ let g:netrw_machine = substitute(a:choice,mf,'\1',"")
+ let b:netrw_fname = substitute(a:choice,mf,'\2',"")
+ endif
+
+ " Method#9: sftp://user@hostname/...path-to-file {{{3
+ elseif match(a:choice,sftpurm) == 0
+ " call Decho("sftp://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 9
+ let g:netrw_machine= substitute(a:choice,sftpurm,'\1',"")
+ let b:netrw_fname = substitute(a:choice,sftpurm,'\2',"")
+
+ " Method#1: Issue an rcp: hostname:filename" (this one should be last) {{{3
+ elseif match(a:choice,rcphf) == 0
+ " call Decho("(rcp) [user@]host:file) rcphf<".rcphf.">",'~'.expand("<slnum>"))
+ let b:netrw_method = 1
+ let userid = substitute(a:choice,rcphf,'\2',"")
+ let g:netrw_machine = substitute(a:choice,rcphf,'\3',"")
+ let b:netrw_fname = substitute(a:choice,rcphf,'\4',"")
+ " call Decho('\1<'.substitute(a:choice,rcphf,'\1',"").">",'~'.expand("<slnum>"))
+ " call Decho('\2<'.substitute(a:choice,rcphf,'\2',"").">",'~'.expand("<slnum>"))
+ " call Decho('\3<'.substitute(a:choice,rcphf,'\3',"").">",'~'.expand("<slnum>"))
+ " call Decho('\4<'.substitute(a:choice,rcphf,'\4',"").">",'~'.expand("<slnum>"))
+ if userid != ""
+ let g:netrw_uid= userid
+ endif
+
+ " Method#10: file://user@hostname/...path-to-file {{{3
+ elseif match(a:choice,fileurm) == 0 && exists("g:netrw_file_cmd")
+ " call Decho("http[s]://...",'~'.expand("<slnum>"))
+ let b:netrw_method = 10
+ let b:netrw_fname = substitute(a:choice,fileurm,'\1',"")
+ " call Decho('\1<'.substitute(a:choice,fileurm,'\1',"").">",'~'.expand("<slnum>"))
+
+ " Cannot Determine Method {{{3
+ else
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:WARNING,"cannot determine method (format: protocol://[user@]hostname[:port]/[path])",45)
+ endif
+ let b:netrw_method = -1
+ endif
+ "}}}3
+
+ if g:netrw_port != ""
+ " remove any leading [:#] from port number
+ let g:netrw_port = substitute(g:netrw_port,'[#:]\+','','')
+ elseif exists("netrw_port")
+ " retain port number as implicit for subsequent ftp operations
+ let g:netrw_port= netrw_port
+ endif
+
+ " call Decho("a:choice <".a:choice.">",'~'.expand("<slnum>"))
+ " call Decho("b:netrw_method <".b:netrw_method.">",'~'.expand("<slnum>"))
+ " call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>"))
+ " call Decho("g:netrw_port <".g:netrw_port.">",'~'.expand("<slnum>"))
+ " if exists("g:netrw_uid") "Decho
+ " call Decho("g:netrw_uid <".g:netrw_uid.">",'~'.expand("<slnum>"))
+ " endif "Decho
+ " if exists("s:netrw_passwd") "Decho
+ " call Decho("s:netrw_passwd <".s:netrw_passwd.">",'~'.expand("<slnum>"))
+ " endif "Decho
+ " call Decho("b:netrw_fname <".b:netrw_fname.">",'~'.expand("<slnum>"))
+ " call Dret("s:NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port)
+endfun
+
+" ---------------------------------------------------------------------
+" NetUserPass: set username and password for subsequent ftp transfer {{{2
+" Usage: :call NetUserPass() -- will prompt for userid and password
+" :call NetUserPass("uid") -- will prompt for password
+" :call NetUserPass("uid","password") -- sets global userid and password
+" :call NetUserPass("ftp:host") -- looks up userid and password using hup dictionary
+" :call NetUserPass("host","uid","password") -- sets hup dictionary with host, userid, password
+fun! NetUserPass(...)
+
+ " call Dfunc("NetUserPass() a:0=".a:0)
+
+ if !exists('s:netrw_hup')
+ let s:netrw_hup= {}
+ endif
+
+ if a:0 == 0
+ " case: no input arguments
+
+ " change host and username if not previously entered; get new password
+ if !exists("g:netrw_machine")
+ let g:netrw_machine= input('Enter hostname: ')
+ endif
+ if !exists("g:netrw_uid") || g:netrw_uid == ""
+ " get username (user-id) via prompt
+ let g:netrw_uid= input('Enter username: ')
+ endif
+ " get password via prompting
+ let s:netrw_passwd= inputsecret("Enter Password: ")
+
+ " set up hup database
+ let host = substitute(g:netrw_machine,'\..*$','','')
+ if !exists('s:netrw_hup[host]')
+ let s:netrw_hup[host]= {}
+ endif
+ let s:netrw_hup[host].uid = g:netrw_uid
+ let s:netrw_hup[host].passwd = s:netrw_passwd
+
+ elseif a:0 == 1
+ " case: one input argument
+
+ if a:1 =~ '^ftp:'
+ " get host from ftp:... url
+ " access userid and password from hup (host-user-passwd) dictionary
+ " call Decho("case a:0=1: a:1<".a:1."> (get host from ftp:... url)",'~'.expand("<slnum>"))
+ let host = substitute(a:1,'^ftp:','','')
+ let host = substitute(host,'\..*','','')
+ if exists("s:netrw_hup[host]")
+ let g:netrw_uid = s:netrw_hup[host].uid
+ let s:netrw_passwd = s:netrw_hup[host].passwd
+ " call Decho("get s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>"))
+ " call Decho("get s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>"))
+ else
+ let g:netrw_uid = input("Enter UserId: ")
+ let s:netrw_passwd = inputsecret("Enter Password: ")
+ endif
+
+ else
+ " case: one input argument, not an url. Using it as a new user-id.
+ " call Decho("case a:0=1: a:1<".a:1."> (get host from input argument, not an url)",'~'.expand("<slnum>"))
+ if exists("g:netrw_machine")
+ if g:netrw_machine =~ '[0-9.]\+'
+ let host= g:netrw_machine
+ else
+ let host= substitute(g:netrw_machine,'\..*$','','')
+ endif
+ else
+ let g:netrw_machine= input('Enter hostname: ')
+ endif
+ let g:netrw_uid = a:1
+ " call Decho("set g:netrw_uid= <".g:netrw_uid.">",'~'.expand("<slnum>"))
+ if exists("g:netrw_passwd")
+ " ask for password if one not previously entered
+ let s:netrw_passwd= g:netrw_passwd
+ else
+ let s:netrw_passwd = inputsecret("Enter Password: ")
+ endif
+ endif
+
+ " call Decho("host<".host.">",'~'.expand("<slnum>"))
+ if exists("host")
+ if !exists('s:netrw_hup[host]')
+ let s:netrw_hup[host]= {}
+ endif
+ let s:netrw_hup[host].uid = g:netrw_uid
+ let s:netrw_hup[host].passwd = s:netrw_passwd
+ endif
+
+ elseif a:0 == 2
+ let g:netrw_uid = a:1
+ let s:netrw_passwd = a:2
+
+ elseif a:0 == 3
+ " enter hostname, user-id, and password into the hup dictionary
+ let host = substitute(a:1,'^\a\+:','','')
+ let host = substitute(host,'\..*$','','')
+ if !exists('s:netrw_hup[host]')
+ let s:netrw_hup[host]= {}
+ endif
+ let s:netrw_hup[host].uid = a:2
+ let s:netrw_hup[host].passwd = a:3
+ let g:netrw_uid = s:netrw_hup[host].uid
+ let s:netrw_passwd = s:netrw_hup[host].passwd
+ " call Decho("set s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>"))
+ " call Decho("set s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>"))
+ endif
+
+ " call Dret("NetUserPass : uid<".g:netrw_uid."> passwd<".s:netrw_passwd.">")
+endfun
+
+" =================================
+" Shared Browsing Support: {{{1
+" =================================
+
+" ---------------------------------------------------------------------
+" s:ExplorePatHls: converts an Explore pattern into a regular expression search pattern {{{2
+fun! s:ExplorePatHls(pattern)
+ " call Dfunc("s:ExplorePatHls(pattern<".a:pattern.">)")
+ let repat= substitute(a:pattern,'^**/\{1,2}','','')
+ " call Decho("repat<".repat.">",'~'.expand("<slnum>"))
+ let repat= escape(repat,'][.\')
+ " call Decho("repat<".repat.">",'~'.expand("<slnum>"))
+ let repat= '\<'.substitute(repat,'\*','\\(\\S\\+ \\)*\\S\\+','g').'\>'
+ " call Dret("s:ExplorePatHls repat<".repat.">")
+ return repat
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBookHistHandler: {{{2
+" 0: (user: <mb>) bookmark current directory
+" 1: (user: <gb>) change to the bookmarked directory
+" 2: (user: <qb>) list bookmarks
+" 3: (browsing) records current directory history
+" 4: (user: <u>) go up (previous) directory, using history
+" 5: (user: <U>) go down (next) directory, using history
+" 6: (user: <mB>) delete bookmark
+fun! s:NetrwBookHistHandler(chg,curdir)
+ " call Dfunc("s:NetrwBookHistHandler(chg=".a:chg." curdir<".a:curdir.">) cnt=".v:count." histcnt=".g:netrw_dirhistcnt." histmax=".g:netrw_dirhistmax)
+ if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0
+ " " call Dret("s:NetrwBookHistHandler - suppressed due to g:netrw_dirhistmax")
+ return
+ endif
+
+ let ykeep = @@
+ let curbufnr = bufnr("%")
+
+ if a:chg == 0
+ " bookmark the current directory
+ " call Decho("(user: <b>) bookmark the current directory",'~'.expand("<slnum>"))
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+ call s:NetrwBookmark(0)
+ echo "bookmarked marked files"
+ else
+ call s:MakeBookmark(a:curdir)
+ echo "bookmarked the current directory"
+ endif
+
+ try
+ call s:NetrwBookHistSave()
+ catch
+ endtry
+
+ elseif a:chg == 1
+ " change to the bookmarked directory
+ " call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>"))
+ if exists("g:netrw_bookmarklist[v:count-1]")
+ " call Decho("(user: <".v:count."gb>) bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>"))
+ exe "NetrwKeepj e ".fnameescape(g:netrw_bookmarklist[v:count-1])
+ else
+ echomsg "Sorry, bookmark#".v:count." doesn't exist!"
+ endif
+
+ elseif a:chg == 2
+ " redraw!
+ let didwork= 0
+ " list user's bookmarks
+ " call Decho("(user: <q>) list user's bookmarks",'~'.expand("<slnum>"))
+ if exists("g:netrw_bookmarklist")
+ " call Decho('list '.len(g:netrw_bookmarklist).' bookmarks','~'.expand("<slnum>"))
+ let cnt= 1
+ for bmd in g:netrw_bookmarklist
+ " call Decho("Netrw Bookmark#".cnt.": ".g:netrw_bookmarklist[cnt-1],'~'.expand("<slnum>"))
+ echo printf("Netrw Bookmark#%-2d: %s",cnt,g:netrw_bookmarklist[cnt-1])
+ let didwork = 1
+ let cnt = cnt + 1
+ endfor
+ endif
+
+ " list directory history
+ " Note: history is saved only when PerformListing is done;
+ " ie. when netrw can re-use a netrw buffer, the current directory is not saved in the history.
+ let cnt = g:netrw_dirhistcnt
+ let first = 1
+ let histcnt = 0
+ if g:netrw_dirhistmax > 0
+ while ( first || cnt != g:netrw_dirhistcnt )
+ " call Decho("first=".first." cnt=".cnt." dirhistcnt=".g:netrw_dirhistcnt,'~'.expand("<slnum>"))
+ if exists("g:netrw_dirhist_{cnt}")
+ " call Decho("Netrw History#".histcnt.": ".g:netrw_dirhist_{cnt},'~'.expand("<slnum>"))
+ echo printf("Netrw History#%-2d: %s",histcnt,g:netrw_dirhist_{cnt})
+ let didwork= 1
+ endif
+ let histcnt = histcnt + 1
+ let first = 0
+ let cnt = ( cnt - 1 ) % g:netrw_dirhistmax
+ if cnt < 0
+ let cnt= cnt + g:netrw_dirhistmax
+ endif
+ endwhile
+ else
+ let g:netrw_dirhistcnt= 0
+ endif
+ if didwork
+ call inputsave()|call input("Press <cr> to continue")|call inputrestore()
+ endif
+
+ elseif a:chg == 3
+ " saves most recently visited directories (when they differ)
+ " call Decho("(browsing) record curdir history",'~'.expand("<slnum>"))
+ if !exists("g:netrw_dirhistcnt") || !exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") || g:netrw_dirhist_{g:netrw_dirhistcnt} != a:curdir
+ if g:netrw_dirhistmax > 0
+ let g:netrw_dirhistcnt = ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax
+ let g:netrw_dirhist_{g:netrw_dirhistcnt} = a:curdir
+ endif
+ " call Decho("save dirhist#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>"))
+ endif
+
+ elseif a:chg == 4
+ " u: change to the previous directory stored on the history list
+ " call Decho("(user: <u>) chg to prev dir from history",'~'.expand("<slnum>"))
+ if g:netrw_dirhistmax > 0
+ let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - v:count1 ) % g:netrw_dirhistmax
+ if g:netrw_dirhistcnt < 0
+ let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax
+ endif
+ else
+ let g:netrw_dirhistcnt= 0
+ endif
+ if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}")
+ " call Decho("changedir u#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>"))
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
+ setl ma noro
+ " call Decho("setl ma noro",'~'.expand("<slnum>"))
+ sil! NetrwKeepj %d _
+ setl nomod
+ " call Decho("setl nomod",'~'.expand("<slnum>"))
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ endif
+ " call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>"))
+ exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt})
+ else
+ if g:netrw_dirhistmax > 0
+ let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + v:count1 ) % g:netrw_dirhistmax
+ else
+ let g:netrw_dirhistcnt= 0
+ endif
+ echo "Sorry, no predecessor directory exists yet"
+ endif
+
+ elseif a:chg == 5
+ " U: change to the subsequent directory stored on the history list
+ " call Decho("(user: <U>) chg to next dir from history",'~'.expand("<slnum>"))
+ if g:netrw_dirhistmax > 0
+ let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax
+ if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}")
+ " call Decho("changedir U#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>"))
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
+ " call Decho("setl ma noro",'~'.expand("<slnum>"))
+ setl ma noro
+ sil! NetrwKeepj %d _
+ " call Decho("removed all lines from buffer (%d)",'~'.expand("<slnum>"))
+ " call Decho("setl nomod",'~'.expand("<slnum>"))
+ setl nomod
+ " call Decho("(set nomod) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ endif
+ " call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>"))
+ exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt})
+ else
+ let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - 1 ) % g:netrw_dirhistmax
+ if g:netrw_dirhistcnt < 0
+ let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax
+ endif
+ echo "Sorry, no successor directory exists yet"
+ endif
+ else
+ let g:netrw_dirhistcnt= 0
+ echo "Sorry, no successor directory exists yet (g:netrw_dirhistmax is ".g:netrw_dirhistmax.")"
+ endif
+
+ elseif a:chg == 6
+ " call Decho("(user: <mB>) delete bookmark'd directory",'~'.expand("<slnum>"))
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+ call s:NetrwBookmark(1)
+ echo "removed marked files from bookmarks"
+ else
+ " delete the v:count'th bookmark
+ let iremove = v:count
+ let dremove = g:netrw_bookmarklist[iremove - 1]
+ " call Decho("delete bookmark#".iremove."<".g:netrw_bookmarklist[iremove - 1].">",'~'.expand("<slnum>"))
+ call s:MergeBookmarks()
+ " call Decho("remove g:netrw_bookmarklist[".(iremove-1)."]<".g:netrw_bookmarklist[(iremove-1)].">",'~'.expand("<slnum>"))
+ NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1)
+ echo "removed ".dremove." from g:netrw_bookmarklist"
+ " call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>"))
+ endif
+ " call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>"))
+
+ try
+ call s:NetrwBookHistSave()
+ catch
+ endtry
+ endif
+ call s:NetrwBookmarkMenu()
+ call s:NetrwTgtMenu()
+ let @@= ykeep
+ " call Dret("s:NetrwBookHistHandler")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBookHistRead: this function reads bookmarks and history {{{2
+" Will source the history file (.netrwhist) only if the g:netrw_disthistmax is > 0.
+" Sister function: s:NetrwBookHistSave()
+fun! s:NetrwBookHistRead()
+ " call Dfunc("s:NetrwBookHistRead()")
+ if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0
+ " call Dret("s:NetrwBookHistRead - nothing read (suppressed due to dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a").")")
+ return
+ endif
+ let ykeep= @@
+
+ " read bookmarks
+ if !exists("s:netrw_initbookhist")
+ let home = s:NetrwHome()
+ let savefile= home."/.netrwbook"
+ if filereadable(s:NetrwFile(savefile))
+ " call Decho("sourcing .netrwbook",'~'.expand("<slnum>"))
+ exe "keepalt NetrwKeepj so ".savefile
+ endif
+
+ " read history
+ if g:netrw_dirhistmax > 0
+ let savefile= home."/.netrwhist"
+ if filereadable(s:NetrwFile(savefile))
+ " call Decho("sourcing .netrwhist",'~'.expand("<slnum>"))
+ exe "keepalt NetrwKeepj so ".savefile
+ endif
+ let s:netrw_initbookhist= 1
+ au VimLeave * call s:NetrwBookHistSave()
+ endif
+ endif
+
+ let @@= ykeep
+ " call Decho("dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a"),'~'.expand("<slnum>"))
+ " call Decho("dirhistcnt=".(exists("g:netrw_dirhistcnt")? g:netrw_dirhistcnt : "n/a"),'~'.expand("<slnum>"))
+ " call Dret("s:NetrwBookHistRead")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBookHistSave: this function saves bookmarks and history to files {{{2
+" Sister function: s:NetrwBookHistRead()
+" I used to do this via viminfo but that appears to
+" be unreliable for long-term storage
+" If g:netrw_dirhistmax is <= 0, no history or bookmarks
+" will be saved.
+" (s:NetrwBookHistHandler(3,...) used to record history)
+fun! s:NetrwBookHistSave()
+ " call Dfunc("s:NetrwBookHistSave() dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt)
+ if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0
+ " call Dret("s:NetrwBookHistSave : nothing saved (dirhistmax=".g:netrw_dirhistmax.")")
+ return
+ endif
+
+ let savefile= s:NetrwHome()."/.netrwhist"
+ " call Decho("savefile<".savefile.">",'~'.expand("<slnum>"))
+ 1split
+
+ " setting up a new buffer which will become .netrwhist
+ call s:NetrwEnew()
+ " call Decho("case g:netrw_use_noswf=".g:netrw_use_noswf.(exists("+acd")? " +acd" : " -acd"),'~'.expand("<slnum>"))
+ if g:netrw_use_noswf
+ setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000 noswf
+ else
+ setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000
+ endif
+ setl nocin noai noci magic nospell nohid wig= noaw
+ setl ma noro write
+ if exists("+acd") | setl noacd | endif
+ sil! NetrwKeepj keepalt %d _
+
+ " rename enew'd file: .netrwhist -- no attempt to merge
+ " record dirhistmax and current dirhistcnt
+ " save history
+ " call Decho("saving history: dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt." lastline=".line("$"),'~'.expand("<slnum>"))
+ sil! keepalt file .netrwhist
+ call setline(1,"let g:netrw_dirhistmax =".g:netrw_dirhistmax)
+ call setline(2,"let g:netrw_dirhistcnt =".g:netrw_dirhistcnt)
+ if g:netrw_dirhistmax > 0
+ let lastline = line("$")
+ let cnt = g:netrw_dirhistcnt
+ let first = 1
+ while ( first || cnt != g:netrw_dirhistcnt )
+ let lastline= lastline + 1
+ if exists("g:netrw_dirhist_{cnt}")
+ call setline(lastline,'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'")
+ " call Decho("..".lastline.'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'",'~'.expand("<slnum>"))
+ endif
+ let first = 0
+ let cnt = ( cnt - 1 ) % g:netrw_dirhistmax
+ if cnt < 0
+ let cnt= cnt + g:netrw_dirhistmax
+ endif
+ endwhile
+ exe "sil! w! ".savefile
+ " call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>"))
+ endif
+
+ " save bookmarks
+ sil NetrwKeepj %d _
+ if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != []
+ " call Decho("saving bookmarks",'~'.expand("<slnum>"))
+ " merge and write .netrwbook
+ let savefile= s:NetrwHome()."/.netrwbook"
+
+ if filereadable(s:NetrwFile(savefile))
+ let booklist= deepcopy(g:netrw_bookmarklist)
+ exe "sil NetrwKeepj keepalt so ".savefile
+ for bdm in booklist
+ if index(g:netrw_bookmarklist,bdm) == -1
+ call add(g:netrw_bookmarklist,bdm)
+ endif
+ endfor
+ call sort(g:netrw_bookmarklist)
+ endif
+
+ " construct and save .netrwbook
+ call setline(1,"let g:netrw_bookmarklist= ".string(g:netrw_bookmarklist))
+ exe "sil! w! ".savefile
+ " call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>"))
+ endif
+
+ " cleanup -- remove buffer used to construct history
+ let bgone= bufnr("%")
+ q!
+ exe "keepalt ".bgone."bwipe!"
+
+ " call Dret("s:NetrwBookHistSave")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBrowse: This function uses the command in g:netrw_list_cmd to provide a {{{2
+" list of the contents of a local or remote directory. It is assumed that the
+" g:netrw_list_cmd has a string, USEPORT HOSTNAME, that needs to be substituted
+" with the requested remote hostname first.
+" Often called via: Explore/e dirname/etc -> netrw#LocalBrowseCheck() -> s:NetrwBrowse()
+fun! s:NetrwBrowse(islocal,dirname)
+ if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif
+
+ " save alternate-file's filename if w:netrw_rexlocal doesn't exist
+ " This is useful when one edits a local file, then :e ., then :Rex
+ if a:islocal && !exists("w:netrw_rexfile") && bufname("#") != ""
+ let w:netrw_rexfile= bufname("#")
+ endif
+
+ " s:NetrwBrowse : initialize history {{{3
+ if !exists("s:netrw_initbookhist")
+ NetrwKeepj call s:NetrwBookHistRead()
+ endif
+
+ " s:NetrwBrowse : simplify the dirname (especially for ".."s in dirnames) {{{3
+ if a:dirname !~ '^\a\{3,}://'
+ let dirname= simplify(a:dirname)
+ else
+ let dirname= a:dirname
+ endif
+
+ " repoint t:netrw_lexbufnr if appropriate
+ if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr
+ let repointlexbufnr= 1
+ endif
+
+ " s:NetrwBrowse : sanity checks: {{{3
+ if exists("s:netrw_skipbrowse")
+ unlet s:netrw_skipbrowse
+ return
+ endif
+ if !exists("*shellescape")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing shellescape()",69)
+ return
+ endif
+ if !exists("*fnameescape")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing fnameescape()",70)
+ return
+ endif
+
+ " s:NetrwBrowse : save options: {{{3
+ call s:NetrwOptionsSave("w:")
+
+ " s:NetrwBrowse : re-instate any marked files {{{3
+ if has("syntax") && exists("g:syntax_on") && g:syntax_on
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+ exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/"
+ endif
+ endif
+
+ if a:islocal && exists("w:netrw_acdkeep") && w:netrw_acdkeep
+ " s:NetrwBrowse : set up "safe" options for local directory/file {{{3
+ if s:NetrwLcd(dirname)
+ return
+ endif
+
+ elseif !a:islocal && dirname !~ '[\/]$' && dirname !~ '^"'
+ " s:NetrwBrowse : remote regular file handler {{{3
+ if bufname(dirname) != ""
+ exe "NetrwKeepj b ".bufname(dirname)
+ else
+ " attempt transfer of remote regular file
+
+ " remove any filetype indicator from end of dirname, except for the
+ " "this is a directory" indicator (/).
+ " There shouldn't be one of those here, anyway.
+ let path= substitute(dirname,'[*=@|]\r\=$','','e')
+ call s:RemotePathAnalysis(dirname)
+
+ " s:NetrwBrowse : remote-read the requested file into current buffer {{{3
+ call s:NetrwEnew(dirname)
+ call s:NetrwOptionsSafe(a:islocal)
+ setl ma noro
+ let b:netrw_curdir = dirname
+ let url = s:method."://".((s:user == "")? "" : s:user."@").s:machine.(s:port ? ":".s:port : "")."/".s:path
+ call s:NetrwBufRename(url)
+ exe "sil! NetrwKeepj keepalt doau BufReadPre ".fnameescape(s:fname)
+ sil call netrw#NetRead(2,url)
+ " netrw.vim and tar.vim have already handled decompression of the tarball; avoiding gzip.vim error
+ if s:path =~ '.bz2'
+ exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.bz2$','',''))
+ elseif s:path =~ '.gz'
+ exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.gz$','',''))
+ elseif s:path =~ '.gz'
+ exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.txz$','',''))
+ else
+ exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(s:fname)
+ endif
+ endif
+
+ " s:NetrwBrowse : save certain window-oriented variables into buffer-oriented variables {{{3
+ call s:SetBufWinVars()
+ call s:NetrwOptionsRestore("w:")
+ setl ma nomod noro
+ return
+ endif
+
+ " use buffer-oriented WinVars if buffer variables exist but associated window variables don't {{{3
+ call s:UseBufWinVars()
+
+ " set up some variables {{{3
+ let b:netrw_browser_active = 1
+ let dirname = dirname
+ let s:last_sort_by = g:netrw_sort_by
+
+ " set up menu {{{3
+ NetrwKeepj call s:NetrwMenu(1)
+
+ " get/set-up buffer {{{3
+ let svpos = winsaveview()
+
+ " NetrwGetBuffer might change buffers but s:rexposn_X was set for the
+ " previous buffer
+ let prevbufnr = bufnr('%')
+ let reusing= s:NetrwGetBuffer(a:islocal,dirname)
+ if exists("s:rexposn_".prevbufnr)
+ let s:rexposn_{bufnr('%')} = s:rexposn_{prevbufnr}
+ endif
+
+ " maintain markfile highlighting
+ if has("syntax") && exists("g:syntax_on") && g:syntax_on
+ if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != ""
+ exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/"
+ else
+ 2match none
+ endif
+ endif
+ if reusing && line("$") > 1
+ call s:NetrwOptionsRestore("w:")
+ setl noma nomod nowrap
+ return
+ endif
+
+ " set b:netrw_curdir to the new directory name {{{3
+ let b:netrw_curdir= dirname
+ if b:netrw_curdir =~ '[/\\]$'
+ let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e')
+ endif
+ if b:netrw_curdir =~ '\a:$' && has("win32")
+ let b:netrw_curdir= b:netrw_curdir."/"
+ endif
+ if b:netrw_curdir == ''
+ if has("amiga")
+ " On the Amiga, the empty string connotes the current directory
+ let b:netrw_curdir= getcwd()
+ else
+ " under unix, when the root directory is encountered, the result
+ " from the preceding substitute is an empty string.
+ let b:netrw_curdir= '/'
+ endif
+ endif
+ if !a:islocal && b:netrw_curdir !~ '/$'
+ let b:netrw_curdir= b:netrw_curdir.'/'
+ endif
+
+ " ------------
+ " (local only) {{{3
+ " ------------
+ if a:islocal
+ " Set up ShellCmdPost handling. Append current buffer to browselist
+ call s:LocalFastBrowser()
+
+ " handle g:netrw_keepdir: set vim's current directory to netrw's notion of the current directory {{{3
+ if !g:netrw_keepdir
+ if !exists("&l:acd") || !&l:acd
+ if s:NetrwLcd(b:netrw_curdir)
+ return
+ endif
+ endif
+ endif
+
+ " --------------------------------
+ " remote handling: {{{3
+ " --------------------------------
+ else
+
+ " analyze dirname and g:netrw_list_cmd {{{3
+ if dirname =~# "^NetrwTreeListing\>"
+ let dirname= b:netrw_curdir
+ elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
+ let dirname= substitute(b:netrw_curdir,'\\','/','g')
+ if dirname !~ '/$'
+ let dirname= dirname.'/'
+ endif
+ let b:netrw_curdir = dirname
+ else
+ let dirname = substitute(dirname,'\\','/','g')
+ endif
+
+ let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/]\+\)/\(.*\)$'
+ if dirname !~ dirpat
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw doesn't understand your dirname<".dirname.">",20)
+ endif
+ NetrwKeepj call s:NetrwOptionsRestore("w:")
+ setl noma nomod nowrap
+ return
+ endif
+ let b:netrw_curdir= dirname
+ endif " (additional remote handling)
+
+ " -------------------------------
+ " Perform Directory Listing: {{{3
+ " -------------------------------
+ NetrwKeepj call s:NetrwMaps(a:islocal)
+ NetrwKeepj call s:NetrwCommands(a:islocal)
+ NetrwKeepj call s:PerformListing(a:islocal)
+
+ " restore option(s)
+ call s:NetrwOptionsRestore("w:")
+
+ " If there is a rexposn: restore position with rexposn
+ " Otherwise : set rexposn
+ if exists("s:rexposn_".bufnr("%"))
+ NetrwKeepj call winrestview(s:rexposn_{bufnr('%')})
+ if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt
+ NetrwKeepj exe w:netrw_bannercnt
+ endif
+ else
+ NetrwKeepj call s:SetRexDir(a:islocal,b:netrw_curdir)
+ endif
+ if v:version >= 700 && has("balloon_eval") && &beval == 0 && &l:bexpr == "" && !exists("g:netrw_nobeval")
+ let &l:bexpr= "netrw#BalloonHelp()"
+ setl beval
+ endif
+
+ " repoint t:netrw_lexbufnr if appropriate
+ if exists("repointlexbufnr")
+ let t:netrw_lexbufnr= bufnr("%")
+ endif
+
+ " restore position
+ if reusing
+ call winrestview(svpos)
+ endif
+
+ " The s:LocalBrowseRefresh() function is called by an autocmd
+ " installed by s:LocalFastBrowser() when g:netrw_fastbrowse <= 1 (ie. slow or medium speed).
+ " However, s:NetrwBrowse() causes the FocusGained event to fire the first time.
+ return
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwFile: because of g:netrw_keepdir, isdirectory(), type(), etc may or {{{2
+" may not apply correctly; ie. netrw's idea of the current directory may
+" differ from vim's. This function insures that netrw's idea of the current
+" directory is used.
+" Returns a path to the file specified by a:fname
+fun! s:NetrwFile(fname)
+ " "" call Dfunc("s:NetrwFile(fname<".a:fname.">) win#".winnr())
+ " "" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a'),'~'.expand("<slnum>"))
+ " "" call Decho("g:netrw_cygwin =".(exists("g:netrw_cygwin")? g:netrw_cygwin : 'n/a'),'~'.expand("<slnum>"))
+ " "" call Decho("g:netrw_liststyle=".(exists("g:netrw_liststyle")? g:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
+ " "" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
+
+ " clean up any leading treedepthstring
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ let fname= substitute(a:fname,'^'.s:treedepthstring.'\+','','')
+ " "" call Decho("clean up any leading treedepthstring: fname<".fname.">",'~'.expand("<slnum>"))
+ else
+ let fname= a:fname
+ endif
+
+ if g:netrw_keepdir
+ " vim's idea of the current directory possibly may differ from netrw's
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ endif
+
+ if !exists("g:netrw_cygwin") && has("win32")
+ if fname =~ '^\' || fname =~ '^\a:\'
+ " windows, but full path given
+ let ret= fname
+ " "" call Decho("windows+full path: isdirectory(".fname.")",'~'.expand("<slnum>"))
+ else
+ " windows, relative path given
+ let ret= s:ComposePath(b:netrw_curdir,fname)
+ " "" call Decho("windows+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>"))
+ endif
+
+ elseif fname =~ '^/'
+ " not windows, full path given
+ let ret= fname
+ " "" call Decho("unix+full path: isdirectory(".fname.")",'~'.expand("<slnum>"))
+ else
+ " not windows, relative path given
+ let ret= s:ComposePath(b:netrw_curdir,fname)
+ " "" call Decho("unix+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>"))
+ endif
+ else
+ " vim and netrw agree on the current directory
+ let ret= fname
+ " "" call Decho("vim and netrw agree on current directory (g:netrw_keepdir=".g:netrw_keepdir.")",'~'.expand("<slnum>"))
+ " "" call Decho("vim directory: ".getcwd(),'~'.expand("<slnum>"))
+ " "" call Decho("netrw directory: ".(exists("b:netrw_curdir")? b:netrw_curdir : 'n/a'),'~'.expand("<slnum>"))
+ endif
+
+ " "" call Dret("s:NetrwFile ".ret)
+ return ret
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwFileInfo: supports qf (query for file information) {{{2
+fun! s:NetrwFileInfo(islocal,fname)
+ " call Dfunc("s:NetrwFileInfo(islocal=".a:islocal." fname<".a:fname.">) b:netrw_curdir<".b:netrw_curdir.">")
+ let ykeep= @@
+ if a:islocal
+ let lsopt= "-lsad"
+ if g:netrw_sizestyle =~# 'H'
+ let lsopt= "-lsadh"
+ elseif g:netrw_sizestyle =~# 'h'
+ let lsopt= "-lsadh --si"
+ endif
+ " call Decho("(s:NetrwFileInfo) lsopt<".lsopt.">")
+ if (has("unix") || has("macunix")) && executable("/bin/ls")
+
+ if getline(".") == "../"
+ echo system("/bin/ls ".lsopt." ".s:ShellEscape(".."))
+ " call Decho("#1: echo system(/bin/ls -lsad ".s:ShellEscape(..).")",'~'.expand("<slnum>"))
+
+ elseif w:netrw_liststyle == s:TREELIST && getline(".") !~ '^'.s:treedepthstring
+ echo system("/bin/ls ".lsopt." ".s:ShellEscape(b:netrw_curdir))
+ " call Decho("#2: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir).")",'~'.expand("<slnum>"))
+
+ elseif exists("b:netrw_curdir")
+ echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,a:fname)))
+ " call Decho("#3: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir.a:fname).")",'~'.expand("<slnum>"))
+
+ else
+ " call Decho('using ls '.a:fname." using cwd<".getcwd().">",'~'.expand("<slnum>"))
+ echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:NetrwFile(a:fname)))
+ " call Decho("#5: echo system(/bin/ls -lsad ".s:ShellEscape(a:fname).")",'~'.expand("<slnum>"))
+ endif
+ else
+ " use vim functions to return information about file below cursor
+ " call Decho("using vim functions to query for file info",'~'.expand("<slnum>"))
+ if !isdirectory(s:NetrwFile(a:fname)) && !filereadable(s:NetrwFile(a:fname)) && a:fname =~ '[*@/]'
+ let fname= substitute(a:fname,".$","","")
+ else
+ let fname= a:fname
+ endif
+ let t = getftime(s:NetrwFile(fname))
+ let sz = getfsize(s:NetrwFile(fname))
+ if g:netrw_sizestyle =~# "[hH]"
+ let sz= s:NetrwHumanReadable(sz)
+ endif
+ echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname)))
+ " call Decho("fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname)),'~'.expand("<slnum>"))
+ endif
+ else
+ echo "sorry, \"qf\" not supported yet for remote files"
+ endif
+ let @@= ykeep
+ " call Dret("s:NetrwFileInfo")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwFullPath: returns the full path to a directory and/or file {{{2
+fun! s:NetrwFullPath(filename)
+ " " call Dfunc("s:NetrwFullPath(filename<".a:filename.">)")
+ let filename= a:filename
+ if filename !~ '^/'
+ let filename= resolve(getcwd().'/'.filename)
+ endif
+ if filename != "/" && filename =~ '/$'
+ let filename= substitute(filename,'/$','','')
+ endif
+ " " call Dret("s:NetrwFullPath <".filename.">")
+ return filename
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwGetBuffer: [get a new|find an old netrw] buffer for a netrw listing {{{2
+" returns 0=cleared buffer
+" 1=re-used buffer (buffer not cleared)
+" Nov 09, 2020: tst952 shows that when user does :set hidden that NetrwGetBuffer will come up with a [No Name] buffer (hid fix)
+fun! s:NetrwGetBuffer(islocal,dirname)
+ " call Dfunc("s:NetrwGetBuffer(islocal=".a:islocal." dirname<".a:dirname.">) liststyle=".g:netrw_liststyle)
+ " 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." hid=".&hid,'~'.expand("<slnum>"))
+ " call Decho("netrwbuf dictionary=".(exists("s:netrwbuf")? string(s:netrwbuf) : 'n/a'),'~'.expand("<slnum>"))
+ " call Dredir("ls!","s:NetrwGetBuffer")
+ let dirname= a:dirname
+
+ " re-use buffer if possible {{{3
+ " call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>"))
+ if !exists("s:netrwbuf")
+ " call Decho(" s:netrwbuf initialized to {}",'~'.expand("<slnum>"))
+ let s:netrwbuf= {}
+ endif
+ " call Decho(" s:netrwbuf =".string(s:netrwbuf),'~'.expand("<slnum>"))
+ " call Decho(" w:netrw_liststyle =".(exists("w:netrw_liststyle")? w:netrw_liststyle : "n/a"),'~'.expand("<slnum>"))
+
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ let bufnum = -1
+
+ if !empty(s:netrwbuf) && has_key(s:netrwbuf,s:NetrwFullPath(dirname))
+ if has_key(s:netrwbuf,"NetrwTreeListing")
+ let bufnum= s:netrwbuf["NetrwTreeListing"]
+ else
+ let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)]
+ endif
+ " call Decho(" NetrwTreeListing: bufnum#".bufnum,'~'.expand("<slnum>"))
+ if !bufexists(bufnum)
+ call remove(s:netrwbuf,"NetrwTreeListing")
+ let bufnum= -1
+ endif
+ elseif bufnr("NetrwTreeListing") != -1
+ let bufnum= bufnr("NetrwTreeListing")
+ " call Decho(" NetrwTreeListing".": bufnum#".bufnum,'~'.expand("<slnum>"))
+ else
+ " call Decho(" did not find a NetrwTreeListing buffer",'~'.expand("<slnum>"))
+ let bufnum= -1
+ endif
+
+ elseif has_key(s:netrwbuf,s:NetrwFullPath(dirname))
+ let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)]
+ " call Decho(" lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."]=".bufnum,'~'.expand("<slnum>"))
+ if !bufexists(bufnum)
+ call remove(s:netrwbuf,s:NetrwFullPath(dirname))
+ let bufnum= -1
+ endif
+
+ else
+ " call Decho(" lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."] not a key",'~'.expand("<slnum>"))
+ let bufnum= -1
+ endif
+ " call Decho(" bufnum#".bufnum,'~'.expand("<slnum>"))
+
+ " highjack the current buffer
+ " IF the buffer already has the desired name
+ " AND it is empty
+ let curbuf = bufname("%")
+ if curbuf == '.'
+ let curbuf = getcwd()
+ endif
+ " call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)")
+ " call Decho("deciding if netrw may highjack the current buffer#".bufnr("%")."<".curbuf.">",'~'.expand("<slnum>"))
+ " call Decho("..dirname<".dirname."> IF dirname == bufname",'~'.expand("<slnum>"))
+ " call Decho("..curbuf<".curbuf.">",'~'.expand("<slnum>"))
+ " call Decho("..line($)=".line("$")." AND this is 1",'~'.expand("<slnum>"))
+ " call Decho("..getline(%)<".getline("%")."> AND this line is empty",'~'.expand("<slnum>"))
+ if dirname == curbuf && line("$") == 1 && getline("%") == ""
+ " call Dret("s:NetrwGetBuffer 0<cleared buffer> : highjacking buffer#".bufnr("%"))
+ return 0
+ else " DEBUG
+ " call Decho("..did NOT highjack buffer",'~'.expand("<slnum>"))
+ endif
+ " Aug 14, 2021: was thinking about looking for a [No Name] buffer here and using it, but that might cause problems
+
+ " get enew buffer and name it -or- re-use buffer {{{3
+ if bufnum < 0 " get enew buffer and name it
+ " call Decho("--get enew buffer and name it (bufnum#".bufnum."<0 OR bufexists(".bufnum.")=".bufexists(bufnum)."==0)",'~'.expand("<slnum>"))
+ call s:NetrwEnew(dirname)
+ " call Decho(" got enew buffer#".bufnr("%")." (altbuf<".expand("#").">)",'~'.expand("<slnum>"))
+ " name the buffer
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ " Got enew buffer; transform into a NetrwTreeListing
+ " call Decho("--transform enew buffer#".bufnr("%")." into a NetrwTreeListing --",'~'.expand("<slnum>"))
+ let w:netrw_treebufnr = bufnr("%")
+ call s:NetrwBufRename("NetrwTreeListing")
+ if g:netrw_use_noswf
+ setl nobl bt=nofile noswf
+ else
+ setl nobl bt=nofile
+ endif
+ nnoremap <silent> <buffer> [[ :sil call <SID>TreeListMove('[[')<cr>
+ nnoremap <silent> <buffer> ]] :sil call <SID>TreeListMove(']]')<cr>
+ nnoremap <silent> <buffer> [] :sil call <SID>TreeListMove('[]')<cr>
+ nnoremap <silent> <buffer> ][ :sil call <SID>TreeListMove('][')<cr>
+ " call Decho(" tree listing bufnr=".w:netrw_treebufnr,'~'.expand("<slnum>"))
+ else
+ call s:NetrwBufRename(dirname)
+ " enter the new buffer into the s:netrwbuf dictionary
+ let s:netrwbuf[s:NetrwFullPath(dirname)]= bufnr("%")
+ " call Decho("update netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."]=".bufnr("%"),'~'.expand("<slnum>"))
+ " call Decho("netrwbuf dictionary=".string(s:netrwbuf),'~'.expand("<slnum>"))
+ endif
+ " call Decho(" named enew buffer#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
+
+ else " Re-use the buffer
+ " call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>"))
+ " ignore all events
+ let eikeep= &ei
+ setl ei=all
+
+ if &ft == "netrw"
+ " call Decho("buffer type is netrw; not using keepalt with b ".bufnum)
+ exe "sil! NetrwKeepj noswapfile b ".bufnum
+ " call Dredir("ls!","one")
+ else
+ " call Decho("buffer type is not netrw; using keepalt with b ".bufnum)
+ call s:NetrwEditBuf(bufnum)
+ " call Dredir("ls!","two")
+ endif
+ " call Decho(" line($)=".line("$"),'~'.expand("<slnum>"))
+ if bufname("%") == '.'
+ call s:NetrwBufRename(getcwd())
+ endif
+
+ " restore ei
+ let &ei= eikeep
+
+ if line("$") <= 1 && getline(1) == ""
+ " empty buffer
+ NetrwKeepj call s:NetrwListSettings(a:islocal)
+ " 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,'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Dret("s:NetrwGetBuffer 0<buffer empty> : re-using buffer#".bufnr("%").", but its empty, so refresh it")
+ return 0
+
+ elseif g:netrw_fastbrowse == 0 || (a:islocal && g:netrw_fastbrowse == 1)
+ " call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse." a:islocal=".a:islocal.": clear buffer",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwListSettings(a:islocal)
+ sil NetrwKeepj %d _
+ " 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,'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Dret("s:NetrwGetBuffer 0<cleared buffer> : re-using buffer#".bufnr("%").", but refreshing due to g:netrw_fastbrowse=".g:netrw_fastbrowse)
+ return 0
+
+ elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ " call Decho("--re-use tree listing--",'~'.expand("<slnum>"))
+ " call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
+ setl ma
+ sil NetrwKeepj %d _
+ NetrwKeepj call s:NetrwListSettings(a:islocal)
+ " 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,'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Dret("s:NetrwGetBuffer 0<cleared buffer> : re-using buffer#".bufnr("%").", but treelist mode always needs a refresh")
+ return 0
+
+ else
+ " 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,'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Dret("s:NetrwGetBuffer 1<buffer not cleared>")
+ return 1
+ endif
+ endif
+
+ " do netrw settings: make this buffer not-a-file, modifiable, not line-numbered, etc {{{3
+ " fastbrowse Local Remote Hiding a buffer implies it may be re-used (fast)
+ " slow 0 D D Deleting a buffer implies it will not be re-used (slow)
+ " med 1 D H
+ " fast 2 H H
+ " call Decho("--do netrw settings: make this buffer#".bufnr("%")." not-a-file, modifiable, not line-numbered, etc--",'~'.expand("<slnum>"))
+ let fname= expand("%")
+ NetrwKeepj call s:NetrwListSettings(a:islocal)
+ call s:NetrwBufRename(fname)
+
+ " delete all lines from buffer {{{3
+ " call Decho("--delete all lines from buffer--",'~'.expand("<slnum>"))
+ " call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
+ sil! keepalt NetrwKeepj %d _
+
+ " 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,'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Dret("s:NetrwGetBuffer 0<cleared buffer>")
+ return 0
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwGetcwd: get the current directory. {{{2
+" Change backslashes to forward slashes, if any.
+" If doesc is true, escape certain troublesome characters
+fun! s:NetrwGetcwd(doesc)
+ " call Dfunc("NetrwGetcwd(doesc=".a:doesc.")")
+ let curdir= substitute(getcwd(),'\\','/','ge')
+ if curdir !~ '[\/]$'
+ let curdir= curdir.'/'
+ endif
+ if a:doesc
+ let curdir= fnameescape(curdir)
+ endif
+ " call Dret("NetrwGetcwd <".curdir.">")
+ return curdir
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwGetWord: it gets the directory/file named under the cursor {{{2
+fun! s:NetrwGetWord()
+ " call Dfunc("s:NetrwGetWord() liststyle=".s:ShowStyle()." virtcol=".virtcol("."))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ let keepsol= &l:sol
+ setl nosol
+
+ call s:UseBufWinVars()
+
+ " insure that w:netrw_liststyle is set up
+ if !exists("w:netrw_liststyle")
+ if exists("g:netrw_liststyle")
+ let w:netrw_liststyle= g:netrw_liststyle
+ else
+ let w:netrw_liststyle= s:THINLIST
+ endif
+ " call Decho("w:netrw_liststyle=".w:netrw_liststyle,'~'.expand("<slnum>"))
+ endif
+
+ if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt
+ " Active Banner support
+ " call Decho("active banner handling",'~'.expand("<slnum>"))
+ NetrwKeepj norm! 0
+ let dirname= "./"
+ let curline= getline('.')
+
+ if curline =~# '"\s*Sorted by\s'
+ NetrwKeepj norm! "_s
+ let s:netrw_skipbrowse= 1
+ echo 'Pressing "s" also works'
+
+ elseif curline =~# '"\s*Sort sequence:'
+ let s:netrw_skipbrowse= 1
+ echo 'Press "S" to edit sorting sequence'
+
+ elseif curline =~# '"\s*Quick Help:'
+ NetrwKeepj norm! ?
+ let s:netrw_skipbrowse= 1
+
+ elseif curline =~# '"\s*\%(Hiding\|Showing\):'
+ NetrwKeepj norm! a
+ let s:netrw_skipbrowse= 1
+ echo 'Pressing "a" also works'
+
+ elseif line("$") > w:netrw_bannercnt
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt
+ endif
+
+ elseif w:netrw_liststyle == s:THINLIST
+ " call Decho("thin column handling",'~'.expand("<slnum>"))
+ NetrwKeepj norm! 0
+ let dirname= substitute(getline('.'),'\t -->.*$','','')
+
+ elseif w:netrw_liststyle == s:LONGLIST
+ " call Decho("long column handling",'~'.expand("<slnum>"))
+ NetrwKeepj norm! 0
+ let dirname= substitute(getline('.'),'^\(\%(\S\+ \)*\S\+\).\{-}$','\1','e')
+
+ elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ " call Decho("treelist handling",'~'.expand("<slnum>"))
+ let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e')
+ let dirname= substitute(dirname,'\t -->.*$','','')
+
+ else
+ " call Decho("obtain word from wide listing",'~'.expand("<slnum>"))
+ let dirname= getline('.')
+
+ if !exists("b:netrw_cpf")
+ let b:netrw_cpf= 0
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif'
+ call histdel("/",-1)
+ " "call Decho("computed cpf=".b:netrw_cpf,'~'.expand("<slnum>"))
+ endif
+
+ " call Decho("buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
+ let filestart = (virtcol(".")/b:netrw_cpf)*b:netrw_cpf
+ " call Decho("filestart= ([virtcol=".virtcol(".")."]/[b:netrw_cpf=".b:netrw_cpf."])*b:netrw_cpf=".filestart." bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>"))
+ " call Decho("1: dirname<".dirname.">",'~'.expand("<slnum>"))
+ if filestart == 0
+ NetrwKeepj norm! 0ma
+ else
+ call cursor(line("."),filestart+1)
+ NetrwKeepj norm! ma
+ endif
+
+ let dict={}
+ " save the unnamed register and register 0-9 and a
+ let dict.a=[getreg('a'), getregtype('a')]
+ for i in range(0, 9)
+ let dict[i] = [getreg(i), getregtype(i)]
+ endfor
+ let dict.unnamed = [getreg(''), getregtype('')]
+
+ let eofname= filestart + b:netrw_cpf + 1
+ if eofname <= col("$")
+ call cursor(line("."),filestart+b:netrw_cpf+1)
+ NetrwKeepj norm! "ay`a
+ else
+ NetrwKeepj norm! "ay$
+ endif
+
+ let dirname = @a
+ call s:RestoreRegister(dict)
+
+ " call Decho("2: dirname<".dirname.">",'~'.expand("<slnum>"))
+ let dirname= substitute(dirname,'\s\+$','','e')
+ " call Decho("3: dirname<".dirname.">",'~'.expand("<slnum>"))
+ endif
+
+ " symlinks are indicated by a trailing "@". Remove it before further processing.
+ let dirname= substitute(dirname,"@$","","")
+
+ " executables are indicated by a trailing "*". Remove it before further processing.
+ let dirname= substitute(dirname,"\*$","","")
+
+ let &l:sol= keepsol
+
+ " call Dret("s:NetrwGetWord <".dirname.">")
+ return dirname
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwListSettings: make standard settings for making a netrw listing {{{2
+" g:netrw_bufsettings will be used after the listing is produced.
+" Called by s:NetrwGetBuffer()
+fun! s:NetrwListSettings(islocal)
+ " call Dfunc("s:NetrwListSettings(islocal=".a:islocal.")")
+ " 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,'~'.expand("<slnum>"))
+ let fname= bufname("%")
+ " " call Decho("setl bt=nofile nobl ma nonu nowrap noro nornu",'~'.expand("<slnum>"))
+ " nobl noma nomod nonu noma nowrap ro nornu (std g:netrw_bufsettings)
+ setl bt=nofile nobl ma nonu nowrap noro nornu
+ call s:NetrwBufRename(fname)
+ if g:netrw_use_noswf
+ setl noswf
+ endif
+ " call Dredir("ls!","s:NetrwListSettings")
+ " call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>"))
+ exe "setl ts=".(g:netrw_maxfilenamelen+1)
+ setl isk+=.,~,-
+ if g:netrw_fastbrowse > a:islocal
+ setl bh=hide
+ else
+ setl bh=delete
+ 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,'~'.expand("<slnum>"))
+ " call Dret("s:NetrwListSettings")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwListStyle: change list style (thin - long - wide - tree) {{{2
+" islocal=0: remote browsing
+" =1: local browsing
+fun! s:NetrwListStyle(islocal)
+ let ykeep = @@
+ let fname = s:NetrwGetWord()
+ if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif
+ let svpos = winsaveview()
+ let w:netrw_liststyle = (w:netrw_liststyle + 1) % s:MAXLIST
+
+ " repoint t:netrw_lexbufnr if appropriate
+ if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr
+ let repointlexbufnr= 1
+ endif
+
+ if w:netrw_liststyle == s:THINLIST
+ " use one column listing
+ let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
+
+ elseif w:netrw_liststyle == s:LONGLIST
+ " use long list
+ let g:netrw_list_cmd = g:netrw_list_cmd." -l"
+
+ elseif w:netrw_liststyle == s:WIDELIST
+ " give wide list
+ let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
+
+ elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
+
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"bad value for g:netrw_liststyle (=".w:netrw_liststyle.")",46)
+ let g:netrw_liststyle = s:THINLIST
+ let w:netrw_liststyle = g:netrw_liststyle
+ let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge')
+ endif
+ setl ma noro
+
+ " clear buffer - this will cause NetrwBrowse/LocalBrowseCheck to do a refresh
+ sil! NetrwKeepj %d _
+ " following prevents tree listing buffer from being marked "modified"
+ setl nomod
+
+ " refresh the listing
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call s:NetrwCursor(0)
+
+ " repoint t:netrw_lexbufnr if appropriate
+ if exists("repointlexbufnr")
+ let t:netrw_lexbufnr= bufnr("%")
+ endif
+
+ " restore position; keep cursor on the filename
+ " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ NetrwKeepj call winrestview(svpos)
+ let @@= ykeep
+
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBannerCtrl: toggles the display of the banner {{{2
+fun! s:NetrwBannerCtrl(islocal)
+ let ykeep= @@
+ " toggle the banner (enable/suppress)
+ let g:netrw_banner= !g:netrw_banner
+
+ " refresh the listing
+ let svpos= winsaveview()
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+
+ " keep cursor on the filename
+ if g:netrw_banner && exists("w:netrw_bannercnt") && line(".") >= w:netrw_bannercnt
+ let fname= s:NetrwGetWord()
+ sil NetrwKeepj $
+ let result= search('\%(^\%(|\+\s\)\=\|\s\{2,}\)\zs'.escape(fname,'.\[]*$^').'\%(\s\{2,}\|$\)','bc')
+ " " call Decho("search result=".result." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'N/A'),'~'.expand("<slnum>"))
+ if result <= 0 && exists("w:netrw_bannercnt")
+ exe "NetrwKeepj ".w:netrw_bannercnt
+ endif
+ endif
+ let @@= ykeep
+ " call Dret("s:NetrwBannerCtrl : g:netrw_banner=".g:netrw_banner)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBookmark: supports :NetrwMB[!] [file]s {{{2
+"
+" No bang: enters files/directories into Netrw's bookmark system
+" No argument and in netrw buffer:
+" if there are marked files: bookmark marked files
+" otherwise : bookmark file/directory under cursor
+" No argument and not in netrw buffer: bookmarks current open file
+" Has arguments: globs them individually and bookmarks them
+"
+" With bang: deletes files/directories from Netrw's bookmark system
+fun! s:NetrwBookmark(del,...)
+ if a:0 == 0
+ if &ft == "netrw"
+ let curbufnr = bufnr("%")
+
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+ " for every filename in the marked list
+ let svpos = winsaveview()
+ let islocal= expand("%") !~ '^\a\{3,}://'
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif
+ endfor
+ let curdir = exists("b:netrw_curdir")? b:netrw_curdir : getcwd()
+ call s:NetrwUnmarkList(curbufnr,curdir)
+ NetrwKeepj call s:NetrwRefresh(islocal,s:NetrwBrowseChgDir(islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ else
+ let fname= s:NetrwGetWord()
+ if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif
+ endif
+
+ else
+ " bookmark currently open file
+ let fname= expand("%")
+ if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif
+ endif
+
+ else
+ " bookmark specified files
+ " attempts to infer if working remote or local
+ " by deciding if the current file begins with an url
+ " Globbing cannot be done remotely.
+ let islocal= expand("%") !~ '^\a\{3,}://'
+ let i = 1
+ while i <= a:0
+ if islocal
+ if v:version > 704 || (v:version == 704 && has("patch656"))
+ let mbfiles= glob(fnameescape(a:{i}),0,1,1)
+ else
+ let mbfiles= glob(fnameescape(a:{i}),0,1)
+ endif
+ else
+ let mbfiles= [a:{i}]
+ endif
+ for mbfile in mbfiles
+ if a:del|call s:DeleteBookmark(mbfile)|else|call s:MakeBookmark(mbfile)|endif
+ endfor
+ let i= i + 1
+ endwhile
+ endif
+
+ " update the menu
+ call s:NetrwBookmarkMenu()
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBookmarkMenu: Uses menu priorities {{{2
+" .2.[cnt] for bookmarks, and
+" .3.[cnt] for history
+" (see s:NetrwMenu())
+fun! s:NetrwBookmarkMenu()
+ if !exists("s:netrw_menucnt")
+ return
+ endif
+ " call Dfunc("NetrwBookmarkMenu() histcnt=".g:netrw_dirhistcnt." menucnt=".s:netrw_menucnt)
+
+ " the following test assures that gvim is running, has menus available, and has menus enabled.
+ if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
+ if exists("g:NetrwTopLvlMenu")
+ " call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>"))
+ exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks'
+ exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete'
+ endif
+ if !exists("s:netrw_initbookhist")
+ call s:NetrwBookHistRead()
+ endif
+
+ " show bookmarked places
+ if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0
+ let cnt= 1
+ for bmd in g:netrw_bookmarklist
+ " call Decho('sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmark.'.bmd.' :e '.bmd,'~'.expand("<slnum>"))
+ let bmd= escape(bmd,g:netrw_menu_escape)
+
+ " show bookmarks for goto menu
+ exe 'sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks.'.bmd.' :e '.bmd."\<cr>"
+
+ " show bookmarks for deletion menu
+ exe 'sil! menu '.g:NetrwMenuPriority.".8.2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete.'.bmd.' '.cnt."mB"
+ let cnt= cnt + 1
+ endfor
+
+ endif
+
+ " show directory browsing history
+ if g:netrw_dirhistmax > 0
+ let cnt = g:netrw_dirhistcnt
+ let first = 1
+ let histcnt = 0
+ while ( first || cnt != g:netrw_dirhistcnt )
+ let histcnt = histcnt + 1
+ let priority = g:netrw_dirhistcnt + histcnt
+ if exists("g:netrw_dirhist_{cnt}")
+ let histdir= escape(g:netrw_dirhist_{cnt},g:netrw_menu_escape)
+ " call Decho('sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir,'~'.expand("<slnum>"))
+ exe 'sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir."\<cr>"
+ endif
+ let first = 0
+ let cnt = ( cnt - 1 ) % g:netrw_dirhistmax
+ if cnt < 0
+ let cnt= cnt + g:netrw_dirhistmax
+ endif
+ endwhile
+ endif
+
+ endif
+ " call Dret("NetrwBookmarkMenu")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBrowseChgDir: constructs a new directory based on the current {{{2
+" directory and a new directory name. Also, if the
+" "new directory name" is actually a file,
+" NetrwBrowseChgDir() edits the file.
+" cursor=0: newdir is relative to b:netrw_curdir
+" =1: newdir is relative to the path to the word under the cursor in
+" tree view
+fun! s:NetrwBrowseChgDir(islocal,newdir,cursor,...)
+ let ykeep= @@
+ if !exists("b:netrw_curdir")
+ " Don't try to change-directory: this can happen, for example, when netrw#ErrorMsg has been called
+ " and the current window is the NetrwMessage window.
+ let @@= ykeep
+ return
+ endif
+
+ " NetrwBrowseChgDir; save options and initialize {{{3
+ call s:SavePosn(s:netrw_posn)
+ NetrwKeepj call s:NetrwOptionsSave("s:")
+ NetrwKeepj call s:NetrwOptionsSafe(a:islocal)
+
+ let newdir = a:newdir
+ if a:cursor && exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop")
+ " dirname is the path to the word under the cursor
+ let dirname = s:NetrwTreePath(w:netrw_treetop)
+ " newdir resolves to a directory and points to a directory in dirname
+ " /tmp/test/folder_symlink/ -> /tmp/test/original_folder/
+ if a:islocal && fnamemodify(dirname, ':t') == newdir && isdirectory(resolve(dirname)) && resolve(dirname) == resolve(newdir)
+ let dirname = fnamemodify(resolve(dirname), ':p:h:h')
+ let newdir = fnamemodify(resolve(newdir), ':t')
+ endif
+ " Remove trailing "/"
+ let dirname = substitute(dirname, "/$", "", "")
+
+ " If the word under the cursor is a directory (except for ../), NetrwTreePath
+ " returns the full path, including the word under the cursor, remove it
+ if newdir =~ "/$" && newdir != "../"
+ let dirname = fnamemodify(dirname, ":h")
+ endif
+ else
+ let dirname = b:netrw_curdir
+ endif
+ if has("win32")
+ let dirname = substitute(dirname,'\\','/','ge')
+ endif
+ let dolockout = 0
+ let dorestore = 1
+
+ " ignore <cr>s when done in the banner
+ if g:netrw_banner
+ if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt
+ if getline(".") =~# 'Quick Help'
+ let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp)
+ setl ma noro nowrap
+ NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp])
+ setl noma nomod nowrap
+ NetrwKeepj call s:NetrwOptionsRestore("s:")
+ endif
+ endif
+ endif
+
+ " set up o/s-dependent directory recognition pattern
+ if has("amiga")
+ let dirpat= '[\/:]$'
+ else
+ let dirpat= '[\/]$'
+ endif
+
+ if dirname !~ dirpat
+ " apparently vim is "recognizing" that it is in a directory and
+ " is removing the trailing "/". Bad idea, so let's put it back.
+ let dirname= dirname.'/'
+ endif
+
+ if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir))))
+ " ------------------------------
+ " NetrwBrowseChgDir: edit a file {{{3
+ " ------------------------------
+
+ " save position for benefit of Rexplore
+ let s:rexposn_{bufnr("%")}= winsaveview()
+
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") && newdir !~ '^\(/\|\a:\)'
+ if dirname =~ '/$'
+ let dirname= dirname.newdir
+ else
+ let dirname= dirname."/".newdir
+ endif
+ elseif newdir =~ '^\(/\|\a:\)'
+ let dirname= newdir
+ else
+ let dirname= s:ComposePath(dirname,newdir)
+ endif
+ " this lets netrw#BrowseX avoid the edit
+ if a:0 < 1
+ NetrwKeepj call s:NetrwOptionsRestore("s:")
+ let curdir= b:netrw_curdir
+ if !exists("s:didsplit")
+ if type(g:netrw_browse_split) == 3
+ " open file in server
+ " Note that g:netrw_browse_split is a List: [servername,tabnr,winnr]
+ call s:NetrwServerEdit(a:islocal,dirname)
+ return
+
+ elseif g:netrw_browse_split == 1
+ " horizontally splitting the window first
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
+ exe "keepalt ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
+ if !&ea
+ keepalt wincmd _
+ endif
+ call s:SetRexDir(a:islocal,curdir)
+
+ elseif g:netrw_browse_split == 2
+ " vertically splitting the window first
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
+ exe "keepalt ".(g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s"
+ if !&ea
+ keepalt wincmd |
+ endif
+ call s:SetRexDir(a:islocal,curdir)
+
+ elseif g:netrw_browse_split == 3
+ " open file in new tab
+ keepalt tabnew
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ endif
+ call s:SetRexDir(a:islocal,curdir)
+
+ elseif g:netrw_browse_split == 4
+ " act like "P" (ie. open previous window)
+ if s:NetrwPrevWinOpen(2) == 3
+ let @@= ykeep
+ return
+ endif
+ call s:SetRexDir(a:islocal,curdir)
+
+ else
+ " handling a file, didn't split, so remove menu
+ call s:NetrwMenu(0)
+ " optional change to window
+ if g:netrw_chgwin >= 1
+ if winnr("$")+1 == g:netrw_chgwin
+ " if g:netrw_chgwin is set to one more than the last window, then
+ " vertically split the last window to make that window available.
+ let curwin= winnr()
+ exe "NetrwKeepj keepalt ".winnr("$")."wincmd w"
+ vs
+ exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd ".curwin
+ endif
+ exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w"
+ endif
+ call s:SetRexDir(a:islocal,curdir)
+ endif
+
+ endif
+
+ " the point where netrw actually edits the (local) file
+ " if its local only: LocalBrowseCheck() doesn't edit a file, but NetrwBrowse() will
+ " use keepalt to support :e # to return to a directory listing
+ if !&mod
+ " if e the new file would fail due to &mod, then don't change any of the flags
+ let dolockout= 1
+ endif
+ if a:islocal
+ " some like c-^ to return to the last edited file
+ " others like c-^ to return to the netrw buffer
+ " Apr 30, 2020: used to have e! here. That can cause loss of a modified file,
+ " so emit error E37 instead.
+ call s:NetrwEditFile("e","",dirname)
+ call s:NetrwCursor(1)
+ if &hidden || &bufhidden == "hide"
+ " file came from vim's hidden storage. Don't "restore" options with it.
+ let dorestore= 0
+ endif
+ else
+ endif
+
+ " handle g:Netrw_funcref -- call external-to-netrw functions
+ " This code will handle g:Netrw_funcref as an individual function reference
+ " or as a list of function references. It will ignore anything that's not
+ " a function reference. See :help Funcref for information about function references.
+ if exists("g:Netrw_funcref")
+ if type(g:Netrw_funcref) == 2
+ NetrwKeepj call g:Netrw_funcref()
+ elseif type(g:Netrw_funcref) == 3
+ for Fncref in g:Netrw_funcref
+ if type(Fncref) == 2
+ NetrwKeepj call Fncref()
+ endif
+ endfor
+ endif
+ endif
+ endif
+
+ elseif newdir =~ '^/'
+ " ----------------------------------------------------
+ " NetrwBrowseChgDir: just go to the new directory spec {{{3
+ " ----------------------------------------------------
+ let dirname = newdir
+ NetrwKeepj call s:SetRexDir(a:islocal,dirname)
+ NetrwKeepj call s:NetrwOptionsRestore("s:")
+ norm! m`
+
+ elseif newdir == './'
+ " ---------------------------------------------
+ " NetrwBrowseChgDir: refresh the directory list {{{3
+ " ---------------------------------------------
+ NetrwKeepj call s:SetRexDir(a:islocal,dirname)
+ norm! m`
+
+ elseif newdir == '../'
+ " --------------------------------------
+ " NetrwBrowseChgDir: go up one directory {{{3
+ " --------------------------------------
+
+ if w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
+ " force a refresh
+ setl noro ma
+ NetrwKeepj %d _
+ endif
+
+ if has("amiga")
+ " amiga
+ if a:islocal
+ let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+$\)','\1','')
+ let dirname= substitute(dirname,'/$','','')
+ else
+ let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+/$\)','\1','')
+ endif
+
+ elseif !g:netrw_cygwin && has("win32")
+ " windows
+ if a:islocal
+ let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','')
+ if dirname == ""
+ let dirname= '/'
+ endif
+ else
+ let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','')
+ endif
+ if dirname =~ '^\a:$'
+ let dirname= dirname.'/'
+ endif
+
+ else
+ " unix or cygwin
+ if a:islocal
+ let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','')
+ if dirname == ""
+ let dirname= '/'
+ endif
+ else
+ let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','')
+ endif
+ endif
+ NetrwKeepj call s:SetRexDir(a:islocal,dirname)
+ norm! m`
+
+ elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
+ " --------------------------------------
+ " NetrwBrowseChgDir: Handle Tree Listing {{{3
+ " --------------------------------------
+ " force a refresh (for TREELIST, NetrwTreeDir() will force the refresh)
+ setl noro ma
+ if !(exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir"))
+ NetrwKeepj %d _
+ endif
+ let treedir = s:NetrwTreeDir(a:islocal)
+ let s:treecurpos = winsaveview()
+ let haskey = 0
+
+ " search treedict for tree dir as-is
+ if has_key(w:netrw_treedict,treedir)
+ let haskey= 1
+ else
+ endif
+
+ " search treedict for treedir with a [/@] appended
+ if !haskey && treedir !~ '[/@]$'
+ if has_key(w:netrw_treedict,treedir."/")
+ let treedir= treedir."/"
+ let haskey = 1
+ else
+ endif
+ endif
+
+ " search treedict for treedir with any trailing / elided
+ if !haskey && treedir =~ '/$'
+ let treedir= substitute(treedir,'/$','','')
+ if has_key(w:netrw_treedict,treedir)
+ let haskey = 1
+ else
+ endif
+ endif
+
+ if haskey
+ " close tree listing for selected subdirectory
+ call remove(w:netrw_treedict,treedir)
+ let dirname= w:netrw_treetop
+ else
+ " go down one directory
+ let dirname= substitute(treedir,'/*$','/','')
+ endif
+ NetrwKeepj call s:SetRexDir(a:islocal,dirname)
+ let s:treeforceredraw = 1
+
+ else
+ " ----------------------------------------
+ " NetrwBrowseChgDir: Go down one directory {{{3
+ " ----------------------------------------
+ let dirname = s:ComposePath(dirname,newdir)
+ NetrwKeepj call s:SetRexDir(a:islocal,dirname)
+ norm! m`
+ endif
+
+ " --------------------------------------
+ " NetrwBrowseChgDir: Restore and Cleanup {{{3
+ " --------------------------------------
+ if dorestore
+ " dorestore is zero'd when a local file was hidden or bufhidden;
+ " in such a case, we want to keep whatever settings it may have.
+ NetrwKeepj call s:NetrwOptionsRestore("s:")
+ endif
+ if dolockout && dorestore
+ if filewritable(dirname)
+ setl ma noro nomod
+ else
+ setl ma ro nomod
+ endif
+ endif
+ call s:RestorePosn(s:netrw_posn)
+ let @@= ykeep
+
+ return dirname
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBrowseUpDir: implements the "-" mappings {{{2
+" for thin, long, and wide: cursor placed just after banner
+" for tree, keeps cursor on current filename
+fun! s:NetrwBrowseUpDir(islocal)
+ if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt-1
+ " this test needed because occasionally this function seems to be incorrectly called
+ " when multiple leftmouse clicks are taken when atop the one line help in the banner.
+ " I'm allowing the very bottom line to permit a "-" exit so that one may escape empty
+ " directories.
+ return
+ endif
+
+ norm! 0
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
+ let curline= getline(".")
+ let swwline= winline() - 1
+ if exists("w:netrw_treetop")
+ let b:netrw_curdir= w:netrw_treetop
+ elseif exists("b:netrw_curdir")
+ let w:netrw_treetop= b:netrw_curdir
+ else
+ let w:netrw_treetop= getcwd()
+ let b:netrw_curdir = w:netrw_treetop
+ endif
+ let curfile = getline(".")
+ let curpath = s:NetrwTreePath(w:netrw_treetop)
+ if a:islocal
+ call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0))
+ else
+ call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0))
+ endif
+ if w:netrw_treetop == '/'
+ keepj call search('^\M'.curfile,"w")
+ elseif curfile == '../'
+ keepj call search('^\M'.curfile,"wb")
+ else
+ while 1
+ keepj call search('^\M'.s:treedepthstring.curfile,"wb")
+ let treepath= s:NetrwTreePath(w:netrw_treetop)
+ if treepath == curpath
+ break
+ endif
+ endwhile
+ endif
+
+ else
+ call s:SavePosn(s:netrw_posn)
+ if exists("b:netrw_curdir")
+ let curdir= b:netrw_curdir
+ else
+ let curdir= expand(getcwd())
+ endif
+ if a:islocal
+ call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0))
+ else
+ call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0))
+ endif
+ call s:RestorePosn(s:netrw_posn)
+ let curdir= substitute(curdir,'^.*[\/]','','')
+ let curdir= '\<'. escape(curdir, '~'). '/'
+ call search(curdir,'wc')
+ endif
+endfun
+
+func s:redir()
+ " set up redirection (avoids browser messages)
+ " by default if not set, g:netrw_suppress_gx_mesg is true
+ if get(g:, 'netrw_suppress_gx_mesg', 1)
+ if &srr =~# "%s"
+ return printf(&srr, has("win32") ? "nul" : "/dev/null")
+ else
+ return &srr .. (has("win32") ? "nul" : "/dev/null")
+ endif
+ endif
+ return ''
+endfunc
+
+if has('unix')
+ if has('win32unix')
+ " Cygwin provides cygstart
+ if executable('cygstart')
+ fun! netrw#Launch(args)
+ exe 'silent ! cygstart --hide' a:args s:redir() | redraw!
+ endfun
+ elseif !empty($MSYSTEM) && executable('start')
+ " MSYS2/Git Bash comes by default without cygstart; see
+ " https://www.msys2.org/wiki/How-does-MSYS2-differ-from-Cygwin
+ " Instead it provides /usr/bin/start script running `cmd.exe //c start`
+ " Adding "" //b` sets void title, hides cmd window and blocks path conversion
+ " of /b to \b\ " by MSYS2; see https://www.msys2.org/docs/filesystem-paths/
+ fun! netrw#Launch(args)
+ exe 'silent !start "" //b' a:args s:redir() | redraw!
+ endfun
+ else
+ " imitate /usr/bin/start script for other environments and hope for the best
+ fun! netrw#Launch(args)
+ exe 'silent !cmd //c start "" //b' a:args s:redir() | redraw!
+ endfun
+ endif
+ elseif exists('$WSL_DISTRO_NAME') " use cmd.exe to start GUI apps in WSL
+ fun! netrw#Launch(args)
+ let args = a:args
+ exe 'silent !' ..
+ \ ((args =~? '\v<\f+\.(exe|com|bat|cmd)>') ?
+ \ 'cmd.exe /c start /b ' .. args :
+ \ 'nohup ' .. args .. ' ' .. s:redir() .. ' &')
+ \ | redraw!
+ endfun
+ else
+ fun! netrw#Launch(args)
+ exe ':silent ! nohup' a:args s:redir() (has('gui_running') ? '' : '&') | redraw!
+ endfun
+ endif
+elseif has('win32')
+ fun! netrw#Launch(args)
+ exe 'silent !' .. (&shell =~? '\<cmd\.exe\>' ? '' : 'cmd.exe /c')
+ \ 'start "" /b' a:args s:redir() | redraw!
+ endfun
+else
+ fun! netrw#Launch(dummy)
+ echom 'No common launcher found'
+ endfun
+endif
+
+" Git Bash
+if has('win32unix')
+ " (cyg)start suffices
+ let s:os_viewer = ''
+" Windows / WSL
+elseif executable('explorer.exe')
+ let s:os_viewer = 'explorer.exe'
+" Linux / BSD
+elseif executable('xdg-open')
+ let s:os_viewer = 'xdg-open'
+" MacOS
+elseif executable('open')
+ let s:os_viewer = 'open'
+endif
+
+fun! s:viewer()
+ " g:netrw_browsex_viewer could be a string of program + its arguments,
+ " test if first argument is executable
+ if exists('g:netrw_browsex_viewer') && executable(split(g:netrw_browsex_viewer)[0])
+ " extract any viewing options. Assumes that they're set apart by spaces.
+ " call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>"))
+ if g:netrw_browsex_viewer =~ '\s'
+ let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','')
+ let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." "
+ let oviewer = ''
+ let cnt = 1
+ while !executable(viewer) && viewer != oviewer
+ let viewer = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\1','')
+ let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." "
+ let cnt = cnt + 1
+ let oviewer = viewer
+ " call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>"))
+ endwhile
+ else
+ let viewer = g:netrw_browsex_viewer
+ let viewopt = ""
+ endif
+ " call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>"))
+ return viewer .. ' ' .. viewopt
+ else
+ if !exists('s:os_viewer')
+ call netrw#ErrorMsg(s:ERROR,"No program to open this path found. See :help Open for more information.",106)
+ else
+ return s:os_viewer
+ endif
+ endif
+endfun
+
+fun! netrw#Open(file) abort
+ call netrw#Launch(s:viewer() .. ' ' .. shellescape(a:file, 1))
+endfun
+
+if !exists('g:netrw_regex_url')
+ let g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}'
+endif
+
+" ---------------------------------------------------------------------
+" netrw#BrowseX: (implements "x" and "gx") executes a special "viewer" script or program for the {{{2
+" given filename; typically this means given their extension.
+" 0=local, 1=remote
+fun! netrw#BrowseX(fname,remote)
+ if a:remote == 1 && a:fname !~ '^https\=:' && a:fname =~ '/$'
+ " remote directory, not a webpage access, looks like an attempt to do a directory listing
+ norm! gf
+ endif
+
+ if exists("g:netrw_browsex_viewer") && exists("g:netrw_browsex_support_remote") && !g:netrw_browsex_support_remote
+ let remote = a:remote
+ else
+ let remote = 0
+ endif
+
+ let ykeep = @@
+ let screenposn = winsaveview()
+
+ " need to save and restore aw setting as gx can invoke this function from non-netrw buffers
+ let awkeep = &aw
+ set noaw
+
+ " special core dump handler
+ if a:fname =~ '/core\(\.\d\+\)\=$'
+ if exists("g:Netrw_corehandler")
+ if type(g:Netrw_corehandler) == 2
+ " g:Netrw_corehandler is a function reference (see :help Funcref)
+ call g:Netrw_corehandler(s:NetrwFile(a:fname))
+ elseif type(g:Netrw_corehandler) == 3
+ " g:Netrw_corehandler is a List of function references (see :help Funcref)
+ for Fncref in g:Netrw_corehandler
+ if type(Fncref) == 2
+ call Fncref(a:fname)
+ endif
+ endfor
+ endif
+ call winrestview(screenposn)
+ let @@= ykeep
+ let &aw= awkeep
+ return
+ endif
+ endif
+
+ " set up the filename
+ " (lower case the extension, make a local copy of a remote file)
+ let exten= substitute(a:fname,'.*\.\(.\{-}\)','\1','e')
+ if has("win32")
+ let exten= substitute(exten,'^.*$','\L&\E','')
+ endif
+ if exten =~ "[\\/]"
+ let exten= ""
+ endif
+
+ if remote == 1
+ " create a local copy
+ setl bh=delete
+ call netrw#NetRead(3,a:fname)
+ " attempt to rename tempfile
+ let basename= substitute(a:fname,'^\(.*\)/\(.*\)\.\([^.]*\)$','\2','')
+ let newname = substitute(s:netrw_tmpfile,'^\(.*\)/\(.*\)\.\([^.]*\)$','\1/'.basename.'.\3','')
+ if s:netrw_tmpfile != newname && newname != ""
+ if rename(s:netrw_tmpfile,newname) == 0
+ " renaming succeeded
+ let fname= newname
+ else
+ " renaming failed
+ let fname= s:netrw_tmpfile
+ endif
+ else
+ let fname= s:netrw_tmpfile
+ endif
+ else
+ let fname= a:fname
+ " special ~ handler for local
+ if fname =~ '^\~' && expand("$HOME") != ""
+ let fname= s:NetrwFile(substitute(fname,'^\~',expand("$HOME"),''))
+ endif
+ endif
+
+ " although shellescape(..., 1) is used in netrw#Open(), it's insufficient
+ call netrw#Open(escape(fname, '#%'))
+
+ " cleanup: remove temporary file,
+ " delete current buffer if success with handler,
+ " return to prior buffer (directory listing)
+ " Feb 12, 2008: had to de-activate removal of
+ " temporary file because it wasn't getting seen.
+ " if remote == 1 && fname != a:fname
+ " call s:NetrwDelete(fname)
+ " endif
+
+ if remote == 1
+ setl bh=delete bt=nofile
+ if g:netrw_use_noswf
+ setl noswf
+ endif
+ exe "sil! NetrwKeepj norm! \<c-o>"
+ endif
+ call winrestview(screenposn)
+ let @@ = ykeep
+ let &aw= awkeep
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#GX: gets word under cursor for gx support {{{2
+" See also: netrw#BrowseXVis
+" netrw#BrowseX
+fun! netrw#GX()
+ " call Dfunc("netrw#GX()")
+ if &ft == "netrw"
+ let fname= s:NetrwGetWord()
+ else
+ let fname= exists("g:netrw_gx")? expand(g:netrw_gx) : s:GetURL()
+ endif
+ " call Dret("netrw#GX <".fname.">")
+ return fname
+endfun
+
+fun! s:GetURL() abort
+ let URL = ''
+ if exists('*Netrw_get_URL_' .. &filetype)
+ let URL = call('Netrw_get_URL_' .. &filetype, [])
+ endif
+ if !empty(URL) | return URL | endif
+ " URLs end in letter, digit or forward slash
+ let URL = matchstr(expand("<cWORD>"), '\<' .. g:netrw_regex_url .. '\ze[^A-Za-z0-9/]*$')
+ if !empty(URL) | return URL | endif
+
+ " Is it a file in the current work dir ...
+ let file = expand("<cfile>")
+ if filereadable(file) | return file | endif
+ " ... or in that of the current buffer?
+ let path = fnamemodify(expand('%'), ':p')
+ if isdirectory(path)
+ let dir = path
+ elseif filereadable(path)
+ let dir = fnamemodify(path, ':h')
+ endif
+ if exists('dir') && filereadable(dir..'/'..file) | return dir..'/'..file | endif
+
+ return ''
+endf
+
+" ---------------------------------------------------------------------
+" netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2
+fun! netrw#BrowseXVis()
+ let dict={}
+ let dict.a=[getreg('a'), getregtype('a')]
+ norm! gv"ay
+ let gxfile= @a
+ call s:RestoreRegister(dict)
+ call netrw#BrowseX(gxfile,netrw#CheckIfRemote(gxfile))
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBufRename: renames a buffer without the side effect of retaining an unlisted buffer having the old name {{{2
+" Using the file command on a "[No Name]" buffer does not seem to cause the old "[No Name]" buffer
+" to become an unlisted buffer, so in that case don't bwipe it.
+fun! s:NetrwBufRename(newname)
+ " call Dfunc("s:NetrwBufRename(newname<".a:newname.">) buf(%)#".bufnr("%")."<".bufname(bufnr("%")).">")
+ " call Dredir("ls!","s:NetrwBufRename (before rename)")
+ let oldbufname= bufname(bufnr("%"))
+ " call Decho("buf#".bufnr("%").": oldbufname<".oldbufname.">",'~'.expand("<slnum>"))
+
+ if oldbufname != a:newname
+ " call Decho("do buffer rename: oldbufname<".oldbufname."> ≠ a:newname<".a:newname.">",'~'.expand("<slnum>"))
+ let b:junk= 1
+ " call Decho("rename buffer: sil! keepj keepalt file ".fnameescape(a:newname),'~'.expand("<slnum>"))
+ exe 'sil! keepj keepalt file '.fnameescape(a:newname)
+ " call Dredir("ls!","s:NetrwBufRename (before bwipe)~".expand("<slnum>"))
+ let oldbufnr= bufnr(oldbufname)
+ " call Decho("oldbufname<".oldbufname."> oldbufnr#".oldbufnr,'~'.expand("<slnum>"))
+ " call Decho("bufnr(%)=".bufnr("%"),'~'.expand("<slnum>"))
+ if oldbufname != "" && oldbufnr != -1 && oldbufnr != bufnr("%")
+ " call Decho("bwipe ".oldbufnr,'~'.expand("<slnum>"))
+ exe "bwipe! ".oldbufnr
+ " else " Decho
+ " call Decho("did *not* bwipe buf#".oldbufnr,'~'.expand("<slnum>"))
+ " call Decho("..reason: if oldbufname<".oldbufname."> is empty",'~'.expand("<slnum>"))"
+ " call Decho("..reason: if oldbufnr#".oldbufnr." is -1",'~'.expand("<slnum>"))"
+ " call Decho("..reason: if oldbufnr#".oldbufnr." != bufnr(%)#".bufnr("%"),'~'.expand("<slnum>"))"
+ endif
+ " call Dredir("ls!","s:NetrwBufRename (after rename)")
+ " else " Decho
+ " call Decho("oldbufname<".oldbufname."> == a:newname: did *not* rename",'~'.expand("<slnum>"))
+ endif
+
+ " call Dret("s:NetrwBufRename : buf#".bufnr("%").": oldname<".oldbufname."> newname<".a:newname."> expand(%)<".expand("%").">")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#CheckIfRemote: returns 1 if current file looks like an url, 0 else {{{2
+fun! netrw#CheckIfRemote(...)
+ " call Dfunc("netrw#CheckIfRemote() a:0=".a:0)
+ if a:0 > 0
+ let curfile= a:1
+ else
+ let curfile= expand("%")
+ endif
+ " Ignore terminal buffers
+ if &buftype ==# 'terminal'
+ return 0
+ endif
+ " call Decho("curfile<".curfile.">")
+ if curfile =~ '^\a\{3,}://'
+ " call Dret("netrw#CheckIfRemote 1")
+ return 1
+ else
+ " call Dret("netrw#CheckIfRemote 0")
+ return 0
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwChgPerm: (implements "gp") change file permission {{{2
+fun! s:NetrwChgPerm(islocal,curdir)
+ let ykeep = @@
+ call inputsave()
+ let newperm= input("Enter new permission: ")
+ call inputrestore()
+ let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',s:ShellEscape(expand("<cfile>")),'')
+ let chgperm= substitute(chgperm,'\<PERM\>',s:ShellEscape(newperm),'')
+ call system(chgperm)
+ if v:shell_error != 0
+ NetrwKeepj call netrw#ErrorMsg(1,"changing permission on file<".expand("<cfile>")."> seems to have failed",75)
+ endif
+ if a:islocal
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ endif
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:CheckIfKde: checks if kdeinit is running {{{2
+" Returns 0: kdeinit not running
+" 1: kdeinit is running
+fun! s:CheckIfKde()
+ " call Dfunc("s:CheckIfKde()")
+ " seems kde systems often have gnome-open due to dependencies, even though
+ " gnome-open's subsidiary display tools are largely absent. Kde systems
+ " usually have "kdeinit" running, though... (tnx Mikolaj Machowski)
+ if !exists("s:haskdeinit")
+ if has("unix") && executable("ps") && !has("win32unix")
+ let s:haskdeinit= system("ps -e") =~ '\<kdeinit'
+ if v:shell_error
+ let s:haskdeinit = 0
+ endif
+ else
+ let s:haskdeinit= 0
+ endif
+ " call Decho("setting s:haskdeinit=".s:haskdeinit,'~'.expand("<slnum>"))
+ endif
+
+ " call Dret("s:CheckIfKde ".s:haskdeinit)
+ return s:haskdeinit
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwClearExplore: clear explore variables (if any) {{{2
+fun! s:NetrwClearExplore()
+ " call Dfunc("s:NetrwClearExplore()")
+ 2match none
+ if exists("s:explore_match") |unlet s:explore_match |endif
+ if exists("s:explore_indx") |unlet s:explore_indx |endif
+ if exists("s:netrw_explore_prvdir") |unlet s:netrw_explore_prvdir |endif
+ if exists("s:dirstarstar") |unlet s:dirstarstar |endif
+ if exists("s:explore_prvdir") |unlet s:explore_prvdir |endif
+ if exists("w:netrw_explore_indx") |unlet w:netrw_explore_indx |endif
+ if exists("w:netrw_explore_listlen")|unlet w:netrw_explore_listlen|endif
+ if exists("w:netrw_explore_list") |unlet w:netrw_explore_list |endif
+ if exists("w:netrw_explore_bufnr") |unlet w:netrw_explore_bufnr |endif
+ " redraw!
+ " call Dret("s:NetrwClearExplore")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwEditBuf: decides whether or not to use keepalt to edit a buffer {{{2
+fun! s:NetrwEditBuf(bufnum)
+ " call Dfunc("s:NetrwEditBuf(fname<".a:bufnum.">)")
+ if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw"
+ " call Decho("exe sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum))
+ exe "sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum)
+ else
+ " call Decho("exe sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum))
+ exe "sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum)
+ endif
+ " call Dret("s:NetrwEditBuf")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwEditFile: decides whether or not to use keepalt to edit a file {{{2
+" NetrwKeepj [keepalt] <OPT> <CMD> <FILENAME>
+fun! s:NetrwEditFile(cmd,opt,fname)
+ " call Dfunc("s:NetrwEditFile(cmd<".a:cmd.">,opt<".a:opt.">,fname<".a:fname.">) ft<".&ft.">")
+ if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw"
+ " call Decho("exe NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname))
+ exe "NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname)
+ else
+ " call Decho("exe NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname))
+ if a:cmd =~# 'e\%[new]!' && !&hidden && getbufvar(bufname('%'), '&modified', 0)
+ call setbufvar(bufname('%'), '&bufhidden', 'hide')
+ endif
+ exe "NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname)
+ endif
+ " call Dret("s:NetrwEditFile")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwExploreListUniq: {{{2
+fun! s:NetrwExploreListUniq(explist)
+ " this assumes that the list is already sorted
+ let newexplist= []
+ for member in a:explist
+ if !exists("uniqmember") || member != uniqmember
+ let uniqmember = member
+ let newexplist = newexplist + [ member ]
+ endif
+ endfor
+ return newexplist
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwForceChgDir: (gd support) Force treatment as a directory {{{2
+fun! s:NetrwForceChgDir(islocal,newdir)
+ let ykeep= @@
+ if a:newdir !~ '/$'
+ " ok, looks like force is needed to get directory-style treatment
+ if a:newdir =~ '@$'
+ let newdir= substitute(a:newdir,'@$','/','')
+ elseif a:newdir =~ '[*=|\\]$'
+ let newdir= substitute(a:newdir,'.$','/','')
+ else
+ let newdir= a:newdir.'/'
+ endif
+ else
+ " should already be getting treatment as a directory
+ let newdir= a:newdir
+ endif
+ let newdir= s:NetrwBrowseChgDir(a:islocal,newdir,0)
+ call s:NetrwBrowse(a:islocal,newdir)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwGlob: does glob() if local, remote listing otherwise {{{2
+" direntry: this is the name of the directory. Will be fnameescape'd to prevent wildcard handling by glob()
+" expr : this is the expression to follow the directory. Will use s:ComposePath()
+" pare =1: remove the current directory from the resulting glob() filelist
+" =0: leave the current directory in the resulting glob() filelist
+fun! s:NetrwGlob(direntry,expr,pare)
+ " call Dfunc("s:NetrwGlob(direntry<".a:direntry."> expr<".a:expr."> pare=".a:pare.")")
+ if netrw#CheckIfRemote()
+ keepalt 1sp
+ keepalt enew
+ let keep_liststyle = w:netrw_liststyle
+ let w:netrw_liststyle = s:THINLIST
+ if s:NetrwRemoteListing() == 0
+ keepj keepalt %s@/@@
+ let filelist= getline(1,$)
+ q!
+ else
+ " remote listing error -- leave treedict unchanged
+ let filelist= w:netrw_treedict[a:direntry]
+ endif
+ let w:netrw_liststyle= keep_liststyle
+ else
+ let path= s:ComposePath(fnameescape(a:direntry), a:expr)
+ if has("win32")
+ " escape [ so it is not detected as wildcard character, see :h wildcard
+ let path= substitute(path, '[', '[[]', 'g')
+ endif
+ if v:version > 704 || (v:version == 704 && has("patch656"))
+ let filelist= glob(path,0,1,1)
+ else
+ let filelist= glob(path,0,1)
+ endif
+ if a:pare
+ let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")')
+ endif
+ endif
+ return filelist
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwForceFile: (gf support) Force treatment as a file {{{2
+fun! s:NetrwForceFile(islocal,newfile)
+ if a:newfile =~ '[/@*=|\\]$'
+ let newfile= substitute(a:newfile,'.$','','')
+ else
+ let newfile= a:newfile
+ endif
+ if a:islocal
+ call s:NetrwBrowseChgDir(a:islocal,newfile,0)
+ else
+ call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,newfile,0))
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwHide: this function is invoked by the "a" map for browsing {{{2
+" and switches the hiding mode. The actual hiding is done by
+" s:NetrwListHide().
+" g:netrw_hide= 0: show all
+" 1: show not-hidden files
+" 2: show hidden files only
+fun! s:NetrwHide(islocal)
+ let ykeep= @@
+ let svpos= winsaveview()
+
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+
+ " hide the files in the markfile list
+ for fname in s:netrwmarkfilelist_{bufnr("%")}
+ if match(g:netrw_list_hide,'\<'.fname.'\>') != -1
+ " remove fname from hiding list
+ let g:netrw_list_hide= substitute(g:netrw_list_hide,'..\<'.escape(fname,g:netrw_fname_escape).'\>..','','')
+ let g:netrw_list_hide= substitute(g:netrw_list_hide,',,',',','g')
+ let g:netrw_list_hide= substitute(g:netrw_list_hide,'^,\|,$','','')
+ else
+ " append fname to hiding list
+ if exists("g:netrw_list_hide") && g:netrw_list_hide != ""
+ let g:netrw_list_hide= g:netrw_list_hide.',\<'.escape(fname,g:netrw_fname_escape).'\>'
+ else
+ let g:netrw_list_hide= '\<'.escape(fname,g:netrw_fname_escape).'\>'
+ endif
+ endif
+ endfor
+ NetrwKeepj call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir)
+ let g:netrw_hide= 1
+
+ else
+
+ " switch between show-all/show-not-hidden/show-hidden
+ let g:netrw_hide=(g:netrw_hide+1)%3
+ exe "NetrwKeepj norm! 0"
+ if g:netrw_hide && g:netrw_list_hide == ""
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your hiding list is empty!",49)
+ let @@= ykeep
+ return
+ endif
+ endif
+
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwHideEdit: allows user to edit the file/directory hiding list {{{2
+fun! s:NetrwHideEdit(islocal)
+ let ykeep= @@
+ " save current cursor position
+ let svpos= winsaveview()
+
+ " get new hiding list from user
+ call inputsave()
+ let newhide= input("Edit Hiding List: ",g:netrw_list_hide)
+ call inputrestore()
+ let g:netrw_list_hide= newhide
+
+ " refresh the listing
+ sil NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,"./",0))
+
+ " restore cursor position
+ call winrestview(svpos)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwHidden: invoked by "gh" {{{2
+fun! s:NetrwHidden(islocal)
+ let ykeep= @@
+ " save current position
+ let svpos = winsaveview()
+
+ if g:netrw_list_hide =~ '\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+'
+ " remove .file pattern from hiding list
+ let g:netrw_list_hide= substitute(g:netrw_list_hide,'\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+','','')
+ elseif s:Strlen(g:netrw_list_hide) >= 1
+ let g:netrw_list_hide= g:netrw_list_hide . ',\(^\|\s\s\)\zs\.\S\+'
+ else
+ let g:netrw_list_hide= '\(^\|\s\s\)\zs\.\S\+'
+ endif
+ if g:netrw_list_hide =~ '^,'
+ let g:netrw_list_hide= strpart(g:netrw_list_hide,1)
+ endif
+
+ " refresh screen and return to saved position
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwHome: this function determines a "home" for saving bookmarks and history {{{2
+fun! s:NetrwHome()
+ if exists("g:netrw_home")
+ let home= expand(g:netrw_home)
+ else
+ let home = stdpath('data')
+ endif
+ " insure that the home directory exists
+ if g:netrw_dirhistmax > 0 && !isdirectory(s:NetrwFile(home))
+ " call Decho("insure that the home<".home."> directory exists")
+ if exists("g:netrw_mkdir")
+ " call Decho("call system(".g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home)).")")
+ call system(g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home)))
+ else
+ " call Decho("mkdir(".home.")")
+ call mkdir(home)
+ endif
+ endif
+ let g:netrw_home= home
+ return home
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwLeftmouse: handles the <leftmouse> when in a netrw browsing window {{{2
+fun! s:NetrwLeftmouse(islocal)
+ if exists("s:netrwdrag")
+ return
+ endif
+ if &ft != "netrw"
+ return
+ endif
+
+ let ykeep= @@
+ " check if the status bar was clicked on instead of a file/directory name
+ while getchar(0) != 0
+ "clear the input stream
+ endwhile
+ call feedkeys("\<LeftMouse>")
+ let c = getchar()
+ let mouse_lnum = v:mouse_lnum
+ let wlastline = line('w$')
+ let lastline = line('$')
+ if mouse_lnum >= wlastline + 1 || v:mouse_win != winnr()
+ " appears to be a status bar leftmouse click
+ let @@= ykeep
+ return
+ endif
+ " Dec 04, 2013: following test prevents leftmouse selection/deselection of directories and files in treelist mode
+ " Windows are separated by vertical separator bars - but the mouse seems to be doing what it should when dragging that bar
+ " without this test when its disabled.
+ " May 26, 2014: edit file, :Lex, resize window -- causes refresh. Reinstated a modified test. See if problems develop.
+ if v:mouse_col > virtcol('.')
+ let @@= ykeep
+ return
+ endif
+
+ if a:islocal
+ if exists("b:netrw_curdir")
+ NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1))
+ endif
+ else
+ if exists("b:netrw_curdir")
+ NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
+ endif
+ endif
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwCLeftmouse: used to select a file/directory for a target {{{2
+fun! s:NetrwCLeftmouse(islocal)
+ if &ft != "netrw"
+ return
+ endif
+ call s:NetrwMarkFileTgt(a:islocal)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwServerEdit: edit file in a server gvim, usually NETRWSERVER (implements <c-r>){{{2
+" a:islocal=0 : <c-r> not used, remote
+" a:islocal=1 : <c-r> not used, local
+" a:islocal=2 : <c-r> used, remote
+" a:islocal=3 : <c-r> used, local
+fun! s:NetrwServerEdit(islocal,fname)
+ " call Dfunc("s:NetrwServerEdit(islocal=".a:islocal.",fname<".a:fname.">)")
+ let islocal = a:islocal%2 " =0: remote =1: local
+ let ctrlr = a:islocal >= 2 " =0: <c-r> not used =1: <c-r> used
+
+ if (islocal && isdirectory(s:NetrwFile(a:fname))) || (!islocal && a:fname =~ '/$')
+ " handle directories in the local window -- not in the remote vim server
+ " user must have closed the NETRWSERVER window. Treat as normal editing from netrw.
+ let g:netrw_browse_split= 0
+ if exists("s:netrw_browse_split") && exists("s:netrw_browse_split_".winnr())
+ let g:netrw_browse_split= s:netrw_browse_split_{winnr()}
+ unlet s:netrw_browse_split_{winnr()}
+ endif
+ call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0))
+ return
+ endif
+
+ if has("clientserver") && executable("gvim")
+
+ if exists("g:netrw_browse_split") && type(g:netrw_browse_split) == 3
+ let srvrname = g:netrw_browse_split[0]
+ let tabnum = g:netrw_browse_split[1]
+ let winnum = g:netrw_browse_split[2]
+
+ if serverlist() !~ '\<'.srvrname.'\>'
+ if !ctrlr
+ " user must have closed the server window and the user did not use <c-r>, but
+ " used something like <cr>.
+ if exists("g:netrw_browse_split")
+ unlet g:netrw_browse_split
+ endif
+ let g:netrw_browse_split= 0
+ if exists("s:netrw_browse_split_".winnr())
+ let g:netrw_browse_split= s:netrw_browse_split_{winnr()}
+ endif
+ call s:NetrwBrowseChgDir(islocal,a:fname,0)
+ return
+
+ elseif has("win32") && executable("start")
+ " start up remote netrw server under windows
+ call system("start gvim --servername ".srvrname)
+
+ else
+ " start up remote netrw server under linux
+ call system("gvim --servername ".srvrname)
+ endif
+ endif
+
+ call remote_send(srvrname,":tabn ".tabnum."\<cr>")
+ call remote_send(srvrname,":".winnum."wincmd w\<cr>")
+ call remote_send(srvrname,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>")
+ else
+
+ if serverlist() !~ '\<'.g:netrw_servername.'\>'
+
+ if !ctrlr
+ if exists("g:netrw_browse_split")
+ unlet g:netrw_browse_split
+ endif
+ let g:netrw_browse_split= 0
+ call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0))
+ return
+
+ else
+ if has("win32") && executable("start")
+ " start up remote netrw server under windows
+ call system("start gvim --servername ".g:netrw_servername)
+ else
+ " start up remote netrw server under linux
+ call system("gvim --servername ".g:netrw_servername)
+ endif
+ endif
+ endif
+
+ while 1
+ try
+ call remote_send(g:netrw_servername,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>")
+ break
+ catch /^Vim\%((\a\+)\)\=:E241/
+ sleep 200m
+ endtry
+ endwhile
+
+ if exists("g:netrw_browse_split")
+ if type(g:netrw_browse_split) != 3
+ let s:netrw_browse_split_{winnr()}= g:netrw_browse_split
+ endif
+ unlet g:netrw_browse_split
+ endif
+ let g:netrw_browse_split= [g:netrw_servername,1,1]
+ endif
+
+ else
+ call netrw#ErrorMsg(s:ERROR,"you need a gui-capable vim and client-server to use <ctrl-r>",98)
+ endif
+
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSLeftmouse: marks the file under the cursor. May be dragged to select additional files {{{2
+fun! s:NetrwSLeftmouse(islocal)
+ if &ft != "netrw"
+ return
+ endif
+ " call Dfunc("s:NetrwSLeftmouse(islocal=".a:islocal.")")
+
+ let s:ngw= s:NetrwGetWord()
+ call s:NetrwMarkFile(a:islocal,s:ngw)
+
+ " call Dret("s:NetrwSLeftmouse")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSLeftdrag: invoked via a shift-leftmouse and dragging {{{2
+" Used to mark multiple files.
+fun! s:NetrwSLeftdrag(islocal)
+ " call Dfunc("s:NetrwSLeftdrag(islocal=".a:islocal.")")
+ if !exists("s:netrwdrag")
+ let s:netrwdrag = winnr()
+ if a:islocal
+ nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(1)<cr>
+ else
+ nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(0)<cr>
+ endif
+ endif
+ let ngw = s:NetrwGetWord()
+ if !exists("s:ngw") || s:ngw != ngw
+ call s:NetrwMarkFile(a:islocal,ngw)
+ endif
+ let s:ngw= ngw
+ " call Dret("s:NetrwSLeftdrag : s:netrwdrag=".s:netrwdrag." buf#".bufnr("%"))
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSLeftrelease: terminates shift-leftmouse dragging {{{2
+fun! s:NetrwSLeftrelease(islocal)
+ " call Dfunc("s:NetrwSLeftrelease(islocal=".a:islocal.") s:netrwdrag=".s:netrwdrag." buf#".bufnr("%"))
+ if exists("s:netrwdrag")
+ nunmap <s-leftrelease>
+ let ngw = s:NetrwGetWord()
+ if !exists("s:ngw") || s:ngw != ngw
+ call s:NetrwMarkFile(a:islocal,ngw)
+ endif
+ if exists("s:ngw")
+ unlet s:ngw
+ endif
+ unlet s:netrwdrag
+ endif
+ " call Dret("s:NetrwSLeftrelease")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwListHide: uses [range]g~...~d to delete files that match {{{2
+" comma-separated patterns given in g:netrw_list_hide
+fun! s:NetrwListHide()
+ " call Dfunc("s:NetrwListHide() g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">")
+ " call Decho("initial: ".string(getline(w:netrw_bannercnt,'$')))
+ let ykeep= @@
+
+ " find a character not in the "hide" string to use as a separator for :g and :v commands
+ " How-it-works: take the hiding command, convert it into a range.
+ " Duplicate characters don't matter.
+ " Remove all such characters from the '/~@#...890' string.
+ " Use the first character left as a separator character.
+ " call Decho("find a character not in the hide string to use as a separator",'~'.expand("<slnum>"))
+ let listhide= g:netrw_list_hide
+ let sep = strpart(substitute('~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1)
+ " call Decho("sep<".sep."> (sep not in hide string)",'~'.expand("<slnum>"))
+
+ while listhide != ""
+ if listhide =~ ','
+ let hide = substitute(listhide,',.*$','','e')
+ let listhide = substitute(listhide,'^.\{-},\(.*\)$','\1','e')
+ else
+ let hide = listhide
+ let listhide = ""
+ endif
+ " call Decho("..extracted pattern from listhide: hide<".hide."> g:netrw_sort_by<".g:netrw_sort_by.'>','~'.expand("<slnum>"))
+ if g:netrw_sort_by =~ '^[ts]'
+ if hide =~ '^\^'
+ " call Decho("..modify hide to handle a \"^...\" pattern",'~'.expand("<slnum>"))
+ let hide= substitute(hide,'^\^','^\(\\d\\+/\)','')
+ elseif hide =~ '^\\(\^'
+ let hide= substitute(hide,'^\\(\^','\\(^\\(\\d\\+/\\)','')
+ endif
+ " call Decho("..hide<".hide."> listhide<".listhide.'>','~'.expand("<slnum>"))
+ endif
+
+ " Prune the list by hiding any files which match
+ " call Decho("..prune the list by hiding any files which ".((g:netrw_hide == 1)? "" : "don't")."match hide<".hide.">")
+ if g:netrw_hide == 1
+ " call Decho("..hiding<".hide.">",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d'
+ elseif g:netrw_hide == 2
+ " call Decho("..showing<".hide.">",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'s@^@ /-KEEP-/ @'
+ endif
+ " call Decho("..result: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>"))
+ endwhile
+
+ if g:netrw_hide == 2
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$v@^ /-KEEP-/ @d'
+ " call Decho("..v KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s@^\%( /-KEEP-/ \)\+@@e'
+ " call Decho("..g KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>"))
+ endif
+
+ " remove any blank lines that have somehow remained.
+ " This seems to happen under Windows.
+ exe 'sil! NetrwKeepj 1,$g@^\s*$@d'
+
+ let @@= ykeep
+ " call Dret("s:NetrwListHide")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMakeDir: this function makes a directory (both local and remote) {{{2
+" implements the "d" mapping.
+fun! s:NetrwMakeDir(usrhost)
+
+ let ykeep= @@
+ " get name of new directory from user. A bare <CR> will skip.
+ " if its currently a directory, also request will be skipped, but with
+ " a message.
+ call inputsave()
+ let newdirname= input("Please give directory name: ")
+ call inputrestore()
+
+ if newdirname == ""
+ let @@= ykeep
+ return
+ endif
+
+ if a:usrhost == ""
+
+ " Local mkdir:
+ " sanity checks
+ let fullnewdir= b:netrw_curdir.'/'.newdirname
+ if isdirectory(s:NetrwFile(fullnewdir))
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a directory!",24)
+ endif
+ let @@= ykeep
+ return
+ endif
+ if s:FileReadable(fullnewdir)
+ if !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a file!",25)
+ endif
+ let @@= ykeep
+ return
+ endif
+
+ " requested new local directory is neither a pre-existing file or
+ " directory, so make it!
+ if exists("*mkdir")
+ if has("unix")
+ call mkdir(fullnewdir,"p",xor(0777, system("umask")))
+ else
+ call mkdir(fullnewdir,"p")
+ endif
+ else
+ let netrw_origdir= s:NetrwGetcwd(1)
+ if s:NetrwLcd(b:netrw_curdir)
+ return
+ endif
+ call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(newdirname,1))
+ if v:shell_error != 0
+ let @@= ykeep
+ call netrw#ErrorMsg(s:ERROR,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80)
+ return
+ endif
+ if !g:netrw_keepdir
+ if s:NetrwLcd(netrw_origdir)
+ return
+ endif
+ endif
+ endif
+
+ if v:shell_error == 0
+ " refresh listing
+ let svpos= winsaveview()
+ call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
+ call winrestview(svpos)
+ elseif !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",26)
+ endif
+
+ elseif !exists("b:netrw_method") || b:netrw_method == 4
+ " Remote mkdir: using ssh
+ let mkdircmd = s:MakeSshCmd(g:netrw_mkdir_cmd)
+ let newdirname= substitute(b:netrw_curdir,'^\%(.\{-}/\)\{3}\(.*\)$','\1','').newdirname
+ call s:NetrwExe("sil! !".mkdircmd." ".s:ShellEscape(newdirname,1))
+ if v:shell_error == 0
+ " refresh listing
+ let svpos= winsaveview()
+ NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ elseif !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",27)
+ endif
+
+ elseif b:netrw_method == 2
+ " Remote mkdir: using ftp+.netrc
+ let svpos= winsaveview()
+ if exists("b:netrw_fname")
+ let remotepath= b:netrw_fname
+ else
+ let remotepath= ""
+ endif
+ call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"')
+ NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
+ NetrwKeepj call winrestview(svpos)
+
+ elseif b:netrw_method == 3
+ " Remote mkdir: using ftp + machine, id, passwd, and fname (ie. no .netrc)
+ let svpos= winsaveview()
+ if exists("b:netrw_fname")
+ let remotepath= b:netrw_fname
+ else
+ let remotepath= ""
+ endif
+ call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"')
+ NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ endif
+
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:TreeSqueezeDir: allows a shift-cr (gvim only) to squeeze the current tree-listing directory {{{2
+fun! s:TreeSqueezeDir(islocal)
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
+ " its a tree-listing style
+ let curdepth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e')
+ let stopline = (exists("w:netrw_bannercnt")? (w:netrw_bannercnt + 1) : 1)
+ let depth = strchars(substitute(curdepth,' ','','g'))
+ let srch = -1
+ if depth >= 2
+ NetrwKeepj norm! 0
+ let curdepthm1= substitute(curdepth,'^'.s:treedepthstring,'','')
+ let srch = search('^'.curdepthm1.'\%('.s:treedepthstring.'\)\@!','bW',stopline)
+ elseif depth == 1
+ NetrwKeepj norm! 0
+ let treedepthchr= substitute(s:treedepthstring,' ','','')
+ let srch = search('^[^'.treedepthchr.']','bW',stopline)
+ endif
+ if srch > 0
+ call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,s:NetrwGetWord(),1))
+ exe srch
+ endif
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMaps: {{{2
+fun! s:NetrwMaps(islocal)
+
+ " mouse <Plug> maps: {{{3
+ if g:netrw_mousemaps && g:netrw_retmap
+ " call Decho("set up Rexplore 2-leftmouse",'~'.expand("<slnum>"))
+ if !hasmapto("<Plug>NetrwReturn")
+ if maparg("<2-leftmouse>","n") == "" || maparg("<2-leftmouse>","n") =~ '^-$'
+ nmap <unique> <silent> <2-leftmouse> <Plug>NetrwReturn
+ elseif maparg("<c-leftmouse>","n") == ""
+ nmap <unique> <silent> <c-leftmouse> <Plug>NetrwReturn
+ endif
+ endif
+ nno <silent> <Plug>NetrwReturn :Rexplore<cr>
+ endif
+
+ " generate default <Plug> maps {{{3
+ if !hasmapto('<Plug>NetrwHide') |nmap <buffer> <silent> <nowait> a <Plug>NetrwHide_a|endif
+ if !hasmapto('<Plug>NetrwBrowseUpDir') |nmap <buffer> <silent> <nowait> - <Plug>NetrwBrowseUpDir|endif
+ if !hasmapto('<Plug>NetrwOpenFile') |nmap <buffer> <silent> <nowait> % <Plug>NetrwOpenFile|endif
+ if !hasmapto('<Plug>NetrwBadd_cb') |nmap <buffer> <silent> <nowait> cb <Plug>NetrwBadd_cb|endif
+ if !hasmapto('<Plug>NetrwBadd_cB') |nmap <buffer> <silent> <nowait> cB <Plug>NetrwBadd_cB|endif
+ if !hasmapto('<Plug>NetrwLcd') |nmap <buffer> <silent> <nowait> cd <Plug>NetrwLcd|endif
+ if !hasmapto('<Plug>NetrwSetChgwin') |nmap <buffer> <silent> <nowait> C <Plug>NetrwSetChgwin|endif
+ if !hasmapto('<Plug>NetrwRefresh') |nmap <buffer> <silent> <nowait> <c-l> <Plug>NetrwRefresh|endif
+ if !hasmapto('<Plug>NetrwLocalBrowseCheck') |nmap <buffer> <silent> <nowait> <cr> <Plug>NetrwLocalBrowseCheck|endif
+ if !hasmapto('<Plug>NetrwServerEdit') |nmap <buffer> <silent> <nowait> <c-r> <Plug>NetrwServerEdit|endif
+ if !hasmapto('<Plug>NetrwMakeDir') |nmap <buffer> <silent> <nowait> d <Plug>NetrwMakeDir|endif
+ if !hasmapto('<Plug>NetrwBookHistHandler_gb')|nmap <buffer> <silent> <nowait> gb <Plug>NetrwBookHistHandler_gb|endif
+
+ if a:islocal
+ " local normal-mode maps {{{3
+ nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(1)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(1)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(1)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(1,0)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(1,1)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(3,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwMakeDir :<c-u>call <SID>NetrwMakeDir("")<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr>
+ " ---------------------------------------------------------------------
+ nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(1,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(1,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(1)<cr>
+ nnoremap <buffer> <silent> <nowait> gn :<c-u>call netrw#SetTreetop(0,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr>
+ nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(1)<cr>
+ nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(1,0)<cr>
+ nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(1,1)<cr>
+ nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(1)<cr>
+ nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(1)<cr>
+ nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(1)<cr>
+ nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr>
+ 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>
+ nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(1)<cr>
+ nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(1)<cr>
+ nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr>
+ nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(1,0)<cr>
+ nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(1,1)<cr>
+ nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(1)<cr>
+ nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(1)<cr>
+ nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(3)<cr>
+ nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr>
+ nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(1)<cr>
+ nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr>
+ nnoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(1,getloclist(v:count))<cr>
+ nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(1)<cr>
+ nnoremap <buffer> <silent> <nowait> S :<c-u>call <SID>NetSortSequence(1)<cr>
+ nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt(1,'b',v:count1)<cr>
+ nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(4)<cr>
+ nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(1,'h',v:count)<cr>
+ nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr>
+ nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr>
+ nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(5)<cr>
+ nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,0),0)"<cr>
+ nnoremap <buffer> <silent> <nowait> X :<c-u>call <SID>NetrwLocalExecute(expand("<cword>"))"<cr>
+
+ nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./',0))<cr>
+ if !hasmapto('<Plug>NetrwHideEdit')
+ nmap <buffer> <unique> <c-h> <Plug>NetrwHideEdit
+ endif
+ nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(1)<cr>
+ if !hasmapto('<Plug>NetrwRefresh')
+ nmap <buffer> <unique> <c-l> <Plug>NetrwRefresh
+ endif
+ nnoremap <buffer> <silent> <Plug>NetrwRefresh <c-l>:call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,(exists("w:netrw_liststyle") && exists("w:netrw_treetop") && w:netrw_liststyle == 3)? w:netrw_treetop : './',0))<cr>
+ if s:didstarstar || !mapcheck("<s-down>","n")
+ nnoremap <buffer> <silent> <s-down> :Nexplore<cr>
+ endif
+ if s:didstarstar || !mapcheck("<s-up>","n")
+ nnoremap <buffer> <silent> <s-up> :Pexplore<cr>
+ endif
+ if !hasmapto('<Plug>NetrwTreeSqueeze')
+ nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze
+ endif
+ nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(1)<cr>
+ let mapsafecurdir = escape(b:netrw_curdir, s:netrw_map_escape)
+ if g:netrw_mousemaps == 1
+ nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse
+ nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse
+ nmap <buffer> <middlemouse> <Plug>NetrwMiddlemouse
+ nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse
+ nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag
+ nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse
+ imap <buffer> <leftmouse> <Plug>ILeftmouse
+ imap <buffer> <middlemouse> <Plug>IMiddlemouse
+ nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(1)<cr>
+ nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(1)<cr>
+ nno <buffer> <silent> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(1)<cr>
+ nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(1)<cr>
+ nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(1)<cr>
+ nmap <buffer> <silent> <Plug>Netrw2Leftmouse -
+ exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
+ endif
+ exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
+ exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
+ exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>'
+ exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("")<cr>'
+ exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>'
+ nnoremap <buffer> <F1> :he netrw-quickhelp<cr>
+
+ " support user-specified maps
+ call netrw#UserMaps(1)
+
+ else
+ " remote normal-mode maps {{{3
+ call s:RemotePathAnalysis(b:netrw_curdir)
+ nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(0)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(0)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(0)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(0,0)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(0,1)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwRefresh :<c-u>call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1))<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(2,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr>
+ " ---------------------------------------------------------------------
+ nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(0,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(0,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(0)<cr>
+ nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr>
+ nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(0)<cr>
+ nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(0,0)<cr>
+ nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(0,1)<cr>
+ nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(0)<cr>
+ nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(0)<cr>
+ nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(0)<cr>
+ nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr>
+ 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>
+ nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(0)<cr>
+ nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(0)<cr>
+ nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr>
+ nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(0,0)<cr>
+ nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(0,1)<cr>
+ nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(0)<cr>
+ nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(0)<cr>
+ nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(0)<cr>
+ nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr>
+ nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(0)<cr>
+ nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr>
+ nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr>
+ nnoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(0,getloclist(v:count))<cr>
+ nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr>
+ nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(0)<cr>
+ nnoremap <buffer> <silent> <nowait> S :<c-u>call <SID>NetSortSequence(0)<cr>
+ nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt(0,'b',v:count1)<cr>
+ nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(1)<cr>
+ nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(0,'h',v:count)<cr>
+ nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr>
+ nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr>
+ nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1),1)<cr>
+ nmap <buffer> <nowait> gx x
+ if !hasmapto('<Plug>NetrwHideEdit')
+ nmap <buffer> <c-h> <Plug>NetrwHideEdit
+ endif
+ nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(0)<cr>
+ if !hasmapto('<Plug>NetrwRefresh')
+ nmap <buffer> <c-l> <Plug>NetrwRefresh
+ endif
+ if !hasmapto('<Plug>NetrwTreeSqueeze')
+ nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze
+ endif
+ nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(0)<cr>
+
+ let mapsafepath = escape(s:path, s:netrw_map_escape)
+ let mapsafeusermach = escape(((s:user == "")? "" : s:user."@").s:machine, s:netrw_map_escape)
+
+ nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr>
+ if g:netrw_mousemaps == 1
+ nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse
+ nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(0)<cr>
+ nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse
+ nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(0)<cr>
+ nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse
+ nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(0)<cr>
+ nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag
+ nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(0)<cr>
+ nmap <middlemouse> <Plug>NetrwMiddlemouse
+ nno <buffer> <silent> <middlemouse> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(0)<cr>
+ nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse
+ nmap <buffer> <silent> <Plug>Netrw2Leftmouse -
+ imap <buffer> <leftmouse> <Plug>ILeftmouse
+ imap <buffer> <middlemouse> <Plug>IMiddlemouse
+ imap <buffer> <s-leftmouse> <Plug>ISLeftmouse
+ exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ endif
+ exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>'
+ exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>'
+ nnoremap <buffer> <F1> :he netrw-quickhelp<cr>
+
+ " support user-specified maps
+ call netrw#UserMaps(0)
+ endif " }}}3
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwCommands: set up commands {{{2
+" If -buffer, the command is only available from within netrw buffers
+" Otherwise, the command is available from any window, so long as netrw
+" has been used at least once in the session.
+fun! s:NetrwCommands(islocal)
+ " call Dfunc("s:NetrwCommands(islocal=".a:islocal.")")
+
+ com! -nargs=* -complete=file -bang NetrwMB call s:NetrwBookmark(<bang>0,<f-args>)
+ com! -nargs=* NetrwC call s:NetrwSetChgwin(<q-args>)
+ com! Rexplore if exists("w:netrw_rexlocal")|call s:NetrwRexplore(w:netrw_rexlocal,exists("w:netrw_rexdir")? w:netrw_rexdir : ".")|else|call netrw#ErrorMsg(s:WARNING,"win#".winnr()." not a former netrw window",79)|endif
+ if a:islocal
+ com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(1,<f-args>)
+ else
+ com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(0,<f-args>)
+ endif
+ com! -buffer -nargs=? -complete=file MT call s:NetrwMarkTarget(<q-args>)
+
+ " call Dret("s:NetrwCommands")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFiles: apply s:NetrwMarkFile() to named file(s) {{{2
+" glob()ing only works with local files
+fun! s:NetrwMarkFiles(islocal,...)
+ " call Dfunc("s:NetrwMarkFiles(islocal=".a:islocal."...) a:0=".a:0)
+ let curdir = s:NetrwGetCurdir(a:islocal)
+ let i = 1
+ while i <= a:0
+ if a:islocal
+ if v:version > 704 || (v:version == 704 && has("patch656"))
+ let mffiles= glob(a:{i},0,1,1)
+ else
+ let mffiles= glob(a:{i},0,1)
+ endif
+ else
+ let mffiles= [a:{i}]
+ endif
+ " call Decho("mffiles".string(mffiles),'~'.expand("<slnum>"))
+ for mffile in mffiles
+ " call Decho("mffile<".mffile.">",'~'.expand("<slnum>"))
+ call s:NetrwMarkFile(a:islocal,mffile)
+ endfor
+ let i= i + 1
+ endwhile
+ " call Dret("s:NetrwMarkFiles")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkTarget: implements :MT (mark target) {{{2
+fun! s:NetrwMarkTarget(...)
+ if a:0 == 0 || (a:0 == 1 && a:1 == "")
+ let curdir = s:NetrwGetCurdir(1)
+ let tgt = b:netrw_curdir
+ else
+ let curdir = s:NetrwGetCurdir((a:1 =~ '^\a\{3,}://')? 0 : 1)
+ let tgt = a:1
+ endif
+ let s:netrwmftgt = tgt
+ let s:netrwmftgt_islocal = tgt !~ '^\a\{3,}://'
+ let curislocal = b:netrw_curdir !~ '^\a\{3,}://'
+ let svpos = winsaveview()
+ call s:NetrwRefresh(curislocal,s:NetrwBrowseChgDir(curislocal,'./',0))
+ call winrestview(svpos)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFile: (invoked by mf) This function is used to both {{{2
+" mark and unmark files. If a markfile list exists,
+" then the rename and delete functions will use it instead
+" of whatever may happen to be under the cursor at that
+" moment. When the mouse and gui are available,
+" shift-leftmouse may also be used to mark files.
+"
+" Creates two lists
+" s:netrwmarkfilelist -- holds complete paths to all marked files
+" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr())
+"
+" Creates a marked file match string
+" s:netrwmarfilemtch_# -- used with 2match to display marked files
+"
+" Creates a buffer version of islocal
+" b:netrw_islocal
+fun! s:NetrwMarkFile(islocal,fname)
+ " call Dfunc("s:NetrwMarkFile(islocal=".a:islocal." fname<".a:fname.">)")
+ " call Decho("bufnr(%)=".bufnr("%").": ".bufname("%"),'~'.expand("<slnum>"))
+
+ " sanity check
+ if empty(a:fname)
+ " call Dret("s:NetrwMarkFile : empty fname")
+ return
+ endif
+ let curdir = s:NetrwGetCurdir(a:islocal)
+
+ let ykeep = @@
+ let curbufnr= bufnr("%")
+ let leader= '\%(^\|\s\)\zs'
+ if a:fname =~ '\a$'
+ let trailer = '\>[@=|\/\*]\=\ze\%( \|\t\|$\)'
+ else
+ let trailer = '[@=|\/\*]\=\ze\%( \|\t\|$\)'
+ endif
+
+ if exists("s:netrwmarkfilelist_".curbufnr)
+ " markfile list pre-exists
+ " call Decho("case s:netrwmarkfilelist_".curbufnr." already exists",'~'.expand("<slnum>"))
+ " call Decho("starting s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
+ " call Decho("starting s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>"))
+ let b:netrw_islocal= a:islocal
+
+ if index(s:netrwmarkfilelist_{curbufnr},a:fname) == -1
+ " append filename to buffer's markfilelist
+ " call Decho("append filename<".a:fname."> to local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
+ call add(s:netrwmarkfilelist_{curbufnr},a:fname)
+ let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(a:fname,g:netrw_markfileesc).trailer
+
+ else
+ " remove filename from buffer's markfilelist
+ " call Decho("remove filename<".a:fname."> from local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
+ call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname')
+ if s:netrwmarkfilelist_{curbufnr} == []
+ " local markfilelist is empty; remove it entirely
+ " call Decho("markfile list now empty",'~'.expand("<slnum>"))
+ call s:NetrwUnmarkList(curbufnr,curdir)
+ else
+ " rebuild match list to display markings correctly
+ " call Decho("rebuild s:netrwmarkfilemtch_".curbufnr,'~'.expand("<slnum>"))
+ let s:netrwmarkfilemtch_{curbufnr}= ""
+ let first = 1
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ if first
+ let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.leader.escape(fname,g:netrw_markfileesc).trailer
+ else
+ let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(fname,g:netrw_markfileesc).trailer
+ endif
+ let first= 0
+ endfor
+ " call Decho("ending s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
+ endif
+ endif
+
+ else
+ " initialize new markfilelist
+ " call Decho("case: initialize new markfilelist",'~'.expand("<slnum>"))
+
+ " call Decho("add fname<".a:fname."> to new markfilelist_".curbufnr,'~'.expand("<slnum>"))
+ let s:netrwmarkfilelist_{curbufnr}= []
+ call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','',''))
+ " call Decho("ending s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>"))
+
+ " build initial markfile matching pattern
+ if a:fname =~ '/$'
+ let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc)
+ else
+ let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc).trailer
+ endif
+ " call Decho("ending s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>"))
+ endif
+
+ " handle global markfilelist
+ if exists("s:netrwmarkfilelist")
+ let dname= s:ComposePath(b:netrw_curdir,a:fname)
+ if index(s:netrwmarkfilelist,dname) == -1
+ " append new filename to global markfilelist
+ call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname))
+ " call Decho("append filename<".a:fname."> to global s:markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
+ else
+ " remove new filename from global markfilelist
+ " call Decho("remove new filename from global s:markfilelist",'~'.expand("<slnum>"))
+ " call Decho("..filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>"))
+ call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"')
+ " call Decho("..ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
+ if s:netrwmarkfilelist == []
+ " call Decho("s:netrwmarkfilelist is empty; unlet it",'~'.expand("<slnum>"))
+ unlet s:netrwmarkfilelist
+ endif
+ endif
+ else
+ " initialize new global-directory markfilelist
+ let s:netrwmarkfilelist= []
+ call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname))
+ " call Decho("init s:netrwmarkfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
+ endif
+
+ " set up 2match'ing to netrwmarkfilemtch_# list
+ if has("syntax") && exists("g:syntax_on") && g:syntax_on
+ if exists("s:netrwmarkfilemtch_{curbufnr}") && s:netrwmarkfilemtch_{curbufnr} != ""
+ " " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/",'~'.expand("<slnum>"))
+ if exists("g:did_drchip_netrwlist_syntax")
+ exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/"
+ endif
+ else
+ " " call Decho("2match none",'~'.expand("<slnum>"))
+ 2match none
+ endif
+ endif
+ let @@= ykeep
+ " call Decho("s:netrwmarkfilelist[".(exists("s:netrwmarkfilelist")? string(s:netrwmarkfilelist) : "")."] (avail in all buffers)",'~'.expand("<slnum>"))
+ " call Dret("s:NetrwMarkFile : s:netrwmarkfilelist_".curbufnr."<".(exists("s:netrwmarkfilelist_{curbufnr}")? string(s:netrwmarkfilelist_{curbufnr}) : " doesn't exist")."> (buf#".curbufnr."list)")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileArgList: ma: move the marked file list to the argument list (tomflist=0) {{{2
+" mA: move the argument list to marked file list (tomflist=1)
+" Uses the global marked file list
+fun! s:NetrwMarkFileArgList(islocal,tomflist)
+ let svpos = winsaveview()
+ let curdir = s:NetrwGetCurdir(a:islocal)
+ let curbufnr = bufnr("%")
+
+ if a:tomflist
+ " mA: move argument list to marked file list
+ while argc()
+ let fname= argv(0)
+ exe "argdel ".fnameescape(fname)
+ call s:NetrwMarkFile(a:islocal,fname)
+ endwhile
+
+ else
+ " ma: move marked file list to argument list
+ if exists("s:netrwmarkfilelist")
+
+ " for every filename in the marked list
+ for fname in s:netrwmarkfilelist
+ exe "argadd ".fnameescape(fname)
+ endfor " for every file in the marked list
+
+ " unmark list and refresh
+ call s:NetrwUnmarkList(curbufnr,curdir)
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ endif
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileCompress: (invoked by mz) This function is used to {{{2
+" compress/decompress files using the programs
+" in g:netrw_compress and g:netrw_uncompress,
+" using g:netrw_compress_suffix to know which to
+" do. By default:
+" g:netrw_compress = "gzip"
+" g:netrw_decompress = { ".gz" : "gunzip" , ".bz2" : "bunzip2" , ".zip" : "unzip" , ".tar" : "tar -xf", ".xz" : "unxz"}
+fun! s:NetrwMarkFileCompress(islocal)
+ let svpos = winsaveview()
+ let curdir = s:NetrwGetCurdir(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)
+ return
+ endif
+
+ if exists("s:netrwmarkfilelist_{curbufnr}") && exists("g:netrw_compress") && exists("g:netrw_decompress")
+
+ " for every filename in the marked list
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ let sfx= substitute(fname,'^.\{-}\(\.[[:alnum:]]\+\)$','\1','')
+ if exists("g:netrw_decompress['".sfx."']")
+ " fname has a suffix indicating that its compressed; apply associated decompression routine
+ let exe= g:netrw_decompress[sfx]
+ let exe= netrw#WinPath(exe)
+ if a:islocal
+ if g:netrw_keepdir
+ let fname= s:ShellEscape(s:ComposePath(curdir,fname))
+ endif
+ call system(exe." ".fname)
+ if v:shell_error
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to apply<".exe."> to file<".fname.">",50)
+ endif
+ else
+ let fname= s:ShellEscape(b:netrw_curdir.fname,1)
+ NetrwKeepj call s:RemoteSystem(exe." ".fname)
+ endif
+
+ endif
+ unlet sfx
+
+ if exists("exe")
+ unlet exe
+ elseif a:islocal
+ " fname not a compressed file, so compress it
+ call system(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,fname)))
+ if v:shell_error
+ call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_compress<".g:netrw_compress."> to something that works",104)
+ endif
+ else
+ " fname not a compressed file, so compress it
+ NetrwKeepj call s:RemoteSystem(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(fname))
+ endif
+ endfor " for every file in the marked list
+
+ call s:NetrwUnmarkList(curbufnr,curdir)
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileCopy: (invoked by mc) copy marked files to target {{{2
+" If no marked files, then set up directory as the
+" target. Currently does not support copying entire
+" directories. Uses the local-buffer marked file list.
+" Returns 1=success (used by NetrwMarkFileMove())
+" 0=failure
+fun! s:NetrwMarkFileCopy(islocal,...)
+ " call Dfunc("s:NetrwMarkFileCopy(islocal=".a:islocal.") target<".(exists("s:netrwmftgt")? s:netrwmftgt : '---')."> a:0=".a:0)
+
+ let curdir = s:NetrwGetCurdir(a:islocal)
+ let curbufnr = bufnr("%")
+ if b:netrw_curdir !~ '/$'
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= curdir
+ endif
+ let b:netrw_curdir= b:netrw_curdir."/"
+ endif
+
+ " 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:NetrwMarkFileCopy")
+ return
+ endif
+ " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
+
+ if !exists("s:netrwmftgt")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your marked file target is empty! (:help netrw-mt)",67)
+ " call Dret("s:NetrwMarkFileCopy 0")
+ return 0
+ endif
+ " call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>"))
+
+ if a:islocal && s:netrwmftgt_islocal
+ " Copy marked files, local directory to local directory
+ " call Decho("copy from local to local",'~'.expand("<slnum>"))
+ if !executable(g:netrw_localcopycmd)
+ call netrw#ErrorMsg(s:ERROR,"g:netrw_localcopycmd<".g:netrw_localcopycmd."> not executable on your system, aborting",91)
+ " call Dfunc("s:NetrwMarkFileMove : g:netrw_localcopycmd<".g:netrw_localcopycmd."> n/a!")
+ return
+ endif
+
+ " copy marked files while within the same directory (ie. allow renaming)
+ if s:StripTrailingSlash(simplify(s:netrwmftgt)) == s:StripTrailingSlash(simplify(b:netrw_curdir))
+ if len(s:netrwmarkfilelist_{bufnr('%')}) == 1
+ " only one marked file
+ " call Decho("case: only one marked file",'~'.expand("<slnum>"))
+ let args = s:ShellEscape(b:netrw_curdir.s:netrwmarkfilelist_{bufnr('%')}[0])
+ let oldname = s:netrwmarkfilelist_{bufnr('%')}[0]
+ elseif a:0 == 1
+ " call Decho("case: handling one input argument",'~'.expand("<slnum>"))
+ " this happens when the next case was used to recursively call s:NetrwMarkFileCopy()
+ let args = s:ShellEscape(b:netrw_curdir.a:1)
+ let oldname = a:1
+ else
+ " copy multiple marked files inside the same directory
+ " call Decho("case: handling a multiple marked files",'~'.expand("<slnum>"))
+ let s:recursive= 1
+ for oldname in s:netrwmarkfilelist_{bufnr("%")}
+ let ret= s:NetrwMarkFileCopy(a:islocal,oldname)
+ if ret == 0
+ break
+ endif
+ endfor
+ unlet s:recursive
+ call s:NetrwUnmarkList(curbufnr,curdir)
+ " call Dret("s:NetrwMarkFileCopy ".ret)
+ return ret
+ endif
+
+ call inputsave()
+ let newname= input("Copy ".oldname." to : ",oldname,"file")
+ call inputrestore()
+ if newname == ""
+ " call Dret("s:NetrwMarkFileCopy 0")
+ return 0
+ endif
+ let args= s:ShellEscape(oldname)
+ let tgt = s:ShellEscape(s:netrwmftgt.'/'.newname)
+ else
+ let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"s:ShellEscape(b:netrw_curdir.\"/\".v:val)"))
+ let tgt = s:ShellEscape(s:netrwmftgt)
+ endif
+ if !g:netrw_cygwin && has("win32")
+ let args= substitute(args,'/','\\','g')
+ let tgt = substitute(tgt, '/','\\','g')
+ endif
+ if args =~ "'" |let args= substitute(args,"'\\(.*\\)'",'\1','')|endif
+ if tgt =~ "'" |let tgt = substitute(tgt ,"'\\(.*\\)'",'\1','')|endif
+ if args =~ '//'|let args= substitute(args,'//','/','g')|endif
+ if tgt =~ '//'|let tgt = substitute(tgt ,'//','/','g')|endif
+ " call Decho("args <".args.">",'~'.expand("<slnum>"))
+ " call Decho("tgt <".tgt.">",'~'.expand("<slnum>"))
+ if isdirectory(s:NetrwFile(args))
+ " call Decho("args<".args."> is a directory",'~'.expand("<slnum>"))
+ let copycmd= g:netrw_localcopydircmd
+ " call Decho("using copydircmd<".copycmd.">",'~'.expand("<slnum>"))
+ if !g:netrw_cygwin && has("win32")
+ " window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's
+ " contents to a target. One must append the source directory name to the target to get xcopy to
+ " do the right thing.
+ let tgt= tgt.'\'.substitute(a:1,'^.*[\\/]','','')
+ " call Decho("modified tgt for xcopy",'~'.expand("<slnum>"))
+ endif
+ else
+ let copycmd= g:netrw_localcopycmd
+ endif
+ if g:netrw_localcopycmd =~ '\s'
+ let copycmd = substitute(copycmd,'\s.*$','','')
+ let copycmdargs = substitute(copycmd,'^.\{-}\(\s.*\)$','\1','')
+ let copycmd = netrw#WinPath(copycmd).copycmdargs
+ else
+ let copycmd = netrw#WinPath(copycmd)
+ endif
+ " call Decho("args <".args.">",'~'.expand("<slnum>"))
+ " call Decho("tgt <".tgt.">",'~'.expand("<slnum>"))
+ " call Decho("copycmd<".copycmd.">",'~'.expand("<slnum>"))
+ " call Decho("system(".copycmd." '".args."' '".tgt."')",'~'.expand("<slnum>"))
+ call system(copycmd.g:netrw_localcopycmdopt." '".args."' '".tgt."'")
+ if v:shell_error != 0
+ if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && g:netrw_keepdir
+ call netrw#ErrorMsg(s:ERROR,"copy failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",101)
+ else
+ call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localcopycmd<".g:netrw_localcopycmd.">; it doesn't work!",80)
+ endif
+ " call Dret("s:NetrwMarkFileCopy 0 : failed: system(".g:netrw_localcopycmd." ".args." ".s:ShellEscape(s:netrwmftgt))
+ return 0
+ endif
+
+ elseif a:islocal && !s:netrwmftgt_islocal
+ " Copy marked files, local directory to remote directory
+ " call Decho("copy from local to remote",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
+
+ elseif !a:islocal && s:netrwmftgt_islocal
+ " Copy marked files, remote directory to local directory
+ " call Decho("copy from remote to local",'~'.expand("<slnum>"))
+ NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt)
+
+ elseif !a:islocal && !s:netrwmftgt_islocal
+ " Copy marked files, remote directory to remote directory
+ " call Decho("copy from remote to remote",'~'.expand("<slnum>"))
+ let curdir = getcwd()
+ let tmpdir = s:GetTempfile("")
+ if tmpdir !~ '/'
+ let tmpdir= curdir."/".tmpdir
+ endif
+ if exists("*mkdir")
+ call mkdir(tmpdir)
+ else
+ call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(tmpdir,1))
+ if v:shell_error != 0
+ call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80)
+ " call Dret("s:NetrwMarkFileCopy : failed: sil! !".g:netrw_localmkdir.' '.s:ShellEscape(tmpdir,1) )
+ return
+ endif
+ endif
+ if isdirectory(s:NetrwFile(tmpdir))
+ if s:NetrwLcd(tmpdir)
+ " call Dret("s:NetrwMarkFileCopy : lcd failure")
+ return
+ endif
+ NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir)
+ let localfiles= map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")')
+ NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt)
+ if getcwd() == tmpdir
+ for fname in s:netrwmarkfilelist_{bufnr('%')}
+ NetrwKeepj call s:NetrwDelete(fname)
+ endfor
+ if s:NetrwLcd(curdir)
+ " call Dret("s:NetrwMarkFileCopy : lcd failure")
+ return
+ endif
+ if delete(tmpdir,"d")
+ call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".tmpdir.">!",103)
+ endif
+ else
+ if s:NetrwLcd(curdir)
+ " call Dret("s:NetrwMarkFileCopy : lcd failure")
+ return
+ endif
+ endif
+ endif
+ endif
+
+ " -------
+ " cleanup
+ " -------
+ " call Decho("cleanup",'~'.expand("<slnum>"))
+ " remove markings from local buffer
+ call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
+ " call Decho(" g:netrw_fastbrowse =".g:netrw_fastbrowse,'~'.expand("<slnum>"))
+ " call Decho(" s:netrwmftgt =".s:netrwmftgt,'~'.expand("<slnum>"))
+ " call Decho(" s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>"))
+ " call Decho(" curdir =".curdir,'~'.expand("<slnum>"))
+ " call Decho(" a:islocal =".a:islocal,'~'.expand("<slnum>"))
+ " call Decho(" curbufnr =".curbufnr,'~'.expand("<slnum>"))
+ if exists("s:recursive")
+ " call Decho(" s:recursive =".s:recursive,'~'.expand("<slnum>"))
+ else
+ " call Decho(" s:recursive =n/a",'~'.expand("<slnum>"))
+ endif
+ " see s:LocalFastBrowser() for g:netrw_fastbrowse interpretation (refreshing done for both slow and medium)
+ if g:netrw_fastbrowse <= 1
+ NetrwKeepj call s:LocalBrowseRefresh()
+ else
+ " refresh local and targets for fast browsing
+ if !exists("s:recursive")
+ " remove markings from local buffer
+ " call Decho(" remove markings from local buffer",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir)
+ endif
+
+ " refresh buffers
+ if s:netrwmftgt_islocal
+ " call Decho(" refresh s:netrwmftgt=".s:netrwmftgt,'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt)
+ endif
+ if a:islocal && s:netrwmftgt != curdir
+ " call Decho(" refresh curdir=".curdir,'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwRefreshDir(a:islocal,curdir)
+ endif
+ endif
+
+ " call Dret("s:NetrwMarkFileCopy 1")
+ return 1
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileDiff: (invoked by md) This function is used to {{{2
+" invoke vim's diff mode on the marked files.
+" Either two or three files can be so handled.
+" Uses the global marked file list.
+fun! s:NetrwMarkFileDiff(islocal)
+ " call Dfunc("s:NetrwMarkFileDiff(islocal=".a:islocal.") b:netrw_curdir<".b:netrw_curdir.">")
+ 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:NetrwMarkFileDiff")
+ return
+ endif
+ let curdir= s:NetrwGetCurdir(a:islocal)
+ " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
+
+ if exists("s:netrwmarkfilelist_{".curbufnr."}")
+ let cnt = 0
+ for fname in s:netrwmarkfilelist
+ let cnt= cnt + 1
+ if cnt == 1
+ " call Decho("diffthis: fname<".fname.">",'~'.expand("<slnum>"))
+ exe "NetrwKeepj e ".fnameescape(fname)
+ diffthis
+ elseif cnt == 2 || cnt == 3
+ below vsplit
+ " call Decho("diffthis: ".fname,'~'.expand("<slnum>"))
+ exe "NetrwKeepj e ".fnameescape(fname)
+ diffthis
+ else
+ break
+ endif
+ endfor
+ call s:NetrwUnmarkList(curbufnr,curdir)
+ endif
+
+ " call Dret("s:NetrwMarkFileDiff")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileEdit: (invoked by me) put marked files on arg list and start editing them {{{2
+" Uses global markfilelist
+fun! s:NetrwMarkFileEdit(islocal)
+ " call Dfunc("s:NetrwMarkFileEdit(islocal=".a:islocal.")")
+
+ let curdir = s:NetrwGetCurdir(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:NetrwMarkFileEdit")
+ return
+ endif
+ " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
+
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+ call s:SetRexDir(a:islocal,curdir)
+ let flist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)"))
+ " unmark markedfile list
+ " call s:NetrwUnmarkList(curbufnr,curdir)
+ call s:NetrwUnmarkAll()
+ " call Decho("exe sil args ".flist,'~'.expand("<slnum>"))
+ exe "sil args ".flist
+ endif
+ echo "(use :bn, :bp to navigate files; :Rex to return)"
+
+ " call Dret("s:NetrwMarkFileEdit")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileQFEL: convert a quickfix-error or location list into a marked file list {{{2
+fun! s:NetrwMarkFileQFEL(islocal,qfel)
+ " call Dfunc("s:NetrwMarkFileQFEL(islocal=".a:islocal.",qfel)")
+ call s:NetrwUnmarkAll()
+ let curbufnr= bufnr("%")
+
+ if !empty(a:qfel)
+ for entry in a:qfel
+ let bufnmbr= entry["bufnr"]
+ " call Decho("bufname(".bufnmbr.")<".bufname(bufnmbr)."> line#".entry["lnum"]." text=".entry["text"],'~'.expand("<slnum>"))
+ if !exists("s:netrwmarkfilelist_{curbufnr}")
+ " call Decho("case: no marked file list",'~'.expand("<slnum>"))
+ call s:NetrwMarkFile(a:islocal,bufname(bufnmbr))
+ elseif index(s:netrwmarkfilelist_{curbufnr},bufname(bufnmbr)) == -1
+ " s:NetrwMarkFile will remove duplicate entries from the marked file list.
+ " So, this test lets two or more hits on the same pattern to be ignored.
+ " call Decho("case: ".bufname(bufnmbr)." not currently in marked file list",'~'.expand("<slnum>"))
+ call s:NetrwMarkFile(a:islocal,bufname(bufnmbr))
+ else
+ " call Decho("case: ".bufname(bufnmbr)." already in marked file list",'~'.expand("<slnum>"))
+ endif
+ endfor
+ echo "(use me to edit marked files)"
+ else
+ call netrw#ErrorMsg(s:WARNING,"can't convert quickfix error list; its empty!",92)
+ endif
+
+ " call Dret("s:NetrwMarkFileQFEL")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileExe: (invoked by mx and mX) execute arbitrary system command on marked files {{{2
+" mx enbloc=0: Uses the local marked-file list, applies command to each file individually
+" mX enbloc=1: Uses the global marked-file list, applies command to entire list
+fun! s:NetrwMarkFileExe(islocal,enbloc)
+ let svpos = winsaveview()
+ let curdir = s:NetrwGetCurdir(a:islocal)
+ let curbufnr = bufnr("%")
+
+ if a:enbloc == 0
+ " individually apply command to files, one at a time
+ " 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)
+ return
+ endif
+
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+ " get the command
+ call inputsave()
+ let cmd= input("Enter command: ","","file")
+ call inputrestore()
+ if cmd == ""
+ return
+ endif
+
+ " apply command to marked files, individually. Substitute: filename -> %
+ " If no %, then append a space and the filename to the command
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ if a:islocal
+ if g:netrw_keepdir
+ let fname= s:ShellEscape(netrw#WinPath(s:ComposePath(curdir,fname)))
+ endif
+ else
+ let fname= s:ShellEscape(netrw#WinPath(b:netrw_curdir.fname))
+ endif
+ if cmd =~ '%'
+ let xcmd= substitute(cmd,'%',fname,'g')
+ else
+ let xcmd= cmd.' '.fname
+ endif
+ if a:islocal
+ let ret= system(xcmd)
+ else
+ let ret= s:RemoteSystem(xcmd)
+ endif
+ if v:shell_error < 0
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54)
+ break
+ else
+ if ret !=# ''
+ echo "\n"
+ " skip trailing new line
+ echo ret[0:-2]
+ else
+ echo ret
+ endif
+ endif
+ endfor
+
+ " unmark marked file list
+ call s:NetrwUnmarkList(curbufnr,curdir)
+
+ " refresh the listing
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
+ endif
+
+ else " apply command to global list of files, en bloc
+
+ call inputsave()
+ let cmd= input("Enter command: ","","file")
+ call inputrestore()
+ if cmd == ""
+ return
+ endif
+ if cmd =~ '%'
+ let cmd= substitute(cmd,'%',join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' '),'g')
+ else
+ let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' ')
+ endif
+ if a:islocal
+ call system(cmd)
+ if v:shell_error < 0
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54)
+ endif
+ else
+ let ret= s:RemoteSystem(cmd)
+ endif
+ call s:NetrwUnmarkAll()
+
+ " refresh the listing
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix
+" as the marked file(s) (toggles suffix presence)
+" Uses the local marked file list.
+fun! s:NetrwMarkHideSfx(islocal)
+ let svpos = winsaveview()
+ let curbufnr = bufnr("%")
+
+ " s:netrwmarkfilelist_{curbufnr}: the List of marked files
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ " construct suffix pattern
+ if fname =~ '\.'
+ let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','')
+ else
+ let sfxpat= '^\%(\%(\.\)\@!.\)*$'
+ endif
+ " determine if its in the hiding list or not
+ let inhidelist= 0
+ if g:netrw_list_hide != ""
+ let itemnum = 0
+ let hidelist= split(g:netrw_list_hide,',')
+ for hidepat in hidelist
+ if sfxpat == hidepat
+ let inhidelist= 1
+ break
+ endif
+ let itemnum= itemnum + 1
+ endfor
+ endif
+ if inhidelist
+ " remove sfxpat from list
+ call remove(hidelist,itemnum)
+ let g:netrw_list_hide= join(hidelist,",")
+ elseif g:netrw_list_hide != ""
+ " append sfxpat to non-empty list
+ let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat
+ else
+ " set hiding list to sfxpat
+ let g:netrw_list_hide= sfxpat
+ endif
+ endfor
+
+ " refresh the listing
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileVimCmd: (invoked by mv) execute arbitrary vim command on marked files, one at a time {{{2
+" Uses the local marked-file list.
+fun! s:NetrwMarkFileVimCmd(islocal)
+ let svpos = winsaveview()
+ let curdir = s:NetrwGetCurdir(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)
+ return
+ endif
+
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+ " get the command
+ call inputsave()
+ let cmd= input("Enter vim command: ","","file")
+ call inputrestore()
+ if cmd == ""
+ return
+ endif
+
+ " apply command to marked files. Substitute: filename -> %
+ " If no %, then append a space and the filename to the command
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ if a:islocal
+ 1split
+ exe "sil! NetrwKeepj keepalt e ".fnameescape(fname)
+ exe cmd
+ exe "sil! keepalt wq!"
+ else
+ echo "sorry, \"mv\" not supported yet for remote files"
+ endif
+ endfor
+
+ " unmark marked file list
+ call s:NetrwUnmarkList(curbufnr,curdir)
+
+ " refresh the listing
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix
+" as the marked file(s) (toggles suffix presence)
+" Uses the local marked file list.
+fun! s:NetrwMarkHideSfx(islocal)
+ let svpos = winsaveview()
+ let curbufnr = bufnr("%")
+
+ " s:netrwmarkfilelist_{curbufnr}: the List of marked files
+ if exists("s:netrwmarkfilelist_{curbufnr}")
+
+ for fname in s:netrwmarkfilelist_{curbufnr}
+ " construct suffix pattern
+ if fname =~ '\.'
+ let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','')
+ else
+ let sfxpat= '^\%(\%(\.\)\@!.\)*$'
+ endif
+ " determine if its in the hiding list or not
+ let inhidelist= 0
+ if g:netrw_list_hide != ""
+ let itemnum = 0
+ let hidelist= split(g:netrw_list_hide,',')
+ for hidepat in hidelist
+ if sfxpat == hidepat
+ let inhidelist= 1
+ break
+ endif
+ let itemnum= itemnum + 1
+ endfor
+ endif
+ if inhidelist
+ " remove sfxpat from list
+ call remove(hidelist,itemnum)
+ let g:netrw_list_hide= join(hidelist,",")
+ elseif g:netrw_list_hide != ""
+ " append sfxpat to non-empty list
+ let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat
+ else
+ " set hiding list to sfxpat
+ let g:netrw_list_hide= sfxpat
+ endif
+ endfor
+
+ " refresh the listing
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59)
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileGrep: (invoked by mg) This function applies vimgrep to marked files {{{2
+" Uses the global markfilelist
+fun! s:NetrwMarkFileGrep(islocal)
+ " call Dfunc("s:NetrwMarkFileGrep(islocal=".a:islocal.")")
+ let svpos = winsaveview()
+ " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ let curbufnr = bufnr("%")
+ let curdir = s:NetrwGetCurdir(a:islocal)
+
+ if exists("s:netrwmarkfilelist")
+ " call Decho("using s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>"))
+ let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)"))
+ " call Decho("keeping copy of s:netrwmarkfilelist in function-local variable,'~'.expand("<slnum>"))"
+ call s:NetrwUnmarkAll()
+ else
+ " call Decho('no marked files, using "*"','~'.expand("<slnum>"))
+ let netrwmarkfilelist= "*"
+ endif
+
+ " ask user for pattern
+ " call Decho("ask user for search pattern",'~'.expand("<slnum>"))
+ call inputsave()
+ let pat= input("Enter pattern: ","")
+ call inputrestore()
+ let patbang = ""
+ if pat =~ '^!'
+ let patbang = "!"
+ let pat = strpart(pat,2)
+ endif
+ if pat =~ '^\i'
+ let pat = escape(pat,'/')
+ let pat = '/'.pat.'/'
+ else
+ let nonisi = pat[0]
+ endif
+
+ " use vimgrep for both local and remote
+ " call Decho("exe vimgrep".patbang." ".pat." ".netrwmarkfilelist,'~'.expand("<slnum>"))
+ try
+ exe "NetrwKeepj noautocmd vimgrep".patbang." ".pat." ".netrwmarkfilelist
+ catch /^Vim\%((\a\+)\)\=:E480/
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pat.">",76)
+ " call Dret("s:NetrwMarkFileGrep : unable to find pattern<".pat.">")
+ return
+ endtry
+ echo "(use :cn, :cp to navigate, :Rex to return)"
+
+ 2match none
+ " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ NetrwKeepj call winrestview(svpos)
+
+ if exists("nonisi")
+ " original, user-supplied pattern did not begin with a character from isident
+ " call Decho("looking for trailing nonisi<".nonisi."> followed by a j, gj, or jg",'~'.expand("<slnum>"))
+ if pat =~# nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$'
+ call s:NetrwMarkFileQFEL(a:islocal,getqflist())
+ endif
+ endif
+
+ " call Dret("s:NetrwMarkFileGrep")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileMove: (invoked by mm) execute arbitrary command on marked files, one at a time {{{2
+" uses the global marked file list
+" s:netrwmfloc= 0: target directory is remote
+" = 1: target directory is local
+fun! s:NetrwMarkFileMove(islocal)
+ " call Dfunc("s:NetrwMarkFileMove(islocal=".a:islocal.")")
+ let curdir = s:NetrwGetCurdir(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:NetrwMarkFileMove")
+ return
+ endif
+ " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>"))
+
+ if !exists("s:netrwmftgt")
+ NetrwKeepj call netrw#ErrorMsg(2,"your marked file target is empty! (:help netrw-mt)",67)
+ " call Dret("s:NetrwMarkFileCopy 0")
+ return 0
+ endif
+ " call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>"))
+
+ if a:islocal && s:netrwmftgt_islocal
+ " move: local -> local
+ " call Decho("move from local to local",'~'.expand("<slnum>"))
+ " call Decho("local to local move",'~'.expand("<slnum>"))
+ if !executable(g:netrw_localmovecmd)
+ call netrw#ErrorMsg(s:ERROR,"g:netrw_localmovecmd<".g:netrw_localmovecmd."> not executable on your system, aborting",90)
+ " call Dfunc("s:NetrwMarkFileMove : g:netrw_localmovecmd<".g:netrw_localmovecmd."> n/a!")
+ return
+ endif
+ let tgt = s:ShellEscape(s:netrwmftgt)
+ " call Decho("tgt<".tgt.">",'~'.expand("<slnum>"))
+ if !g:netrw_cygwin && has("win32")
+ let tgt= substitute(tgt, '/','\\','g')
+ " call Decho("windows exception: tgt<".tgt.">",'~'.expand("<slnum>"))
+ if g:netrw_localmovecmd =~ '\s'
+ let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','')
+ let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','')
+ let movecmd = netrw#WinPath(movecmd).movecmdargs
+ " call Decho("windows exception: movecmd<".movecmd."> (#1: had a space)",'~'.expand("<slnum>"))
+ else
+ let movecmd = netrw#WinPath(g:netrw_localmovecmd)
+ " call Decho("windows exception: movecmd<".movecmd."> (#2: no space)",'~'.expand("<slnum>"))
+ endif
+ else
+ let movecmd = netrw#WinPath(g:netrw_localmovecmd)
+ " call Decho("movecmd<".movecmd."> (#3 linux or cygwin)",'~'.expand("<slnum>"))
+ endif
+ for fname in s:netrwmarkfilelist_{bufnr("%")}
+ if g:netrw_keepdir
+ " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1
+ let fname= b:netrw_curdir."/".fname
+ endif
+ if !g:netrw_cygwin && has("win32")
+ let fname= substitute(fname,'/','\\','g')
+ endif
+ " call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>"))
+ let ret= system(movecmd.g:netrw_localmovecmdopt." ".s:ShellEscape(fname)." ".tgt)
+ if v:shell_error != 0
+ if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir
+ call netrw#ErrorMsg(s:ERROR,"move failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",100)
+ else
+ call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localmovecmd<".g:netrw_localmovecmd.">; it doesn't work!",54)
+ endif
+ break
+ endif
+ endfor
+
+ elseif a:islocal && !s:netrwmftgt_islocal
+ " move: local -> remote
+ " call Decho("move from local to remote",'~'.expand("<slnum>"))
+ " call Decho("copy",'~'.expand("<slnum>"))
+ let mflist= s:netrwmarkfilelist_{bufnr("%")}
+ NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
+ " call Decho("remove",'~'.expand("<slnum>"))
+ for fname in mflist
+ let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
+ let ok = s:NetrwLocalRmFile(b:netrw_curdir,barefname,1)
+ endfor
+ unlet mflist
+
+ elseif !a:islocal && s:netrwmftgt_islocal
+ " move: remote -> local
+ " call Decho("move from remote to local",'~'.expand("<slnum>"))
+ " call Decho("copy",'~'.expand("<slnum>"))
+ let mflist= s:netrwmarkfilelist_{bufnr("%")}
+ NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
+ " call Decho("remove",'~'.expand("<slnum>"))
+ for fname in mflist
+ let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
+ let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1)
+ endfor
+ unlet mflist
+
+ elseif !a:islocal && !s:netrwmftgt_islocal
+ " move: remote -> remote
+ " call Decho("move from remote to remote",'~'.expand("<slnum>"))
+ " call Decho("copy",'~'.expand("<slnum>"))
+ let mflist= s:netrwmarkfilelist_{bufnr("%")}
+ NetrwKeepj call s:NetrwMarkFileCopy(a:islocal)
+ " call Decho("remove",'~'.expand("<slnum>"))
+ for fname in mflist
+ let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','')
+ let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1)
+ endfor
+ unlet mflist
+ endif
+
+ " -------
+ " cleanup
+ " -------
+ " call Decho("cleanup",'~'.expand("<slnum>"))
+
+ " remove markings from local buffer
+ call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
+
+ " refresh buffers
+ if !s:netrwmftgt_islocal
+ " call Decho("refresh netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt)
+ endif
+ if a:islocal
+ " call Decho("refresh b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwRefreshDir(a:islocal,b:netrw_curdir)
+ endif
+ if g:netrw_fastbrowse <= 1
+ " call Decho("since g:netrw_fastbrowse=".g:netrw_fastbrowse.", perform shell cmd refresh",'~'.expand("<slnum>"))
+ NetrwKeepj call s:LocalBrowseRefresh()
+ endif
+
+ " call Dret("s:NetrwMarkFileMove")
+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).
+fun! s:NetrwMarkFileRegexp(islocal)
+ " call Dfunc("s:NetrwMarkFileRegexp(islocal=".a:islocal.")")
+
+ " get the regular expression
+ call inputsave()
+ let regexp= input("Enter regexp: ","","file")
+ call inputrestore()
+
+ if a:islocal
+ let curdir= s:NetrwGetCurdir(a:islocal)
+ " call Decho("curdir<".fnameescape(curdir).">")
+ " get the matching list of files using local glob()
+ " call Decho("handle local regexp",'~'.expand("<slnum>"))
+ let dirname = escape(b:netrw_curdir,g:netrw_glob_escape)
+ if v:version > 704 || (v:version == 704 && has("patch656"))
+ let filelist= glob(s:ComposePath(dirname,regexp),0,1,1)
+ else
+ let files = glob(s:ComposePath(dirname,regexp),0,0)
+ let filelist= split(files,"\n")
+ endif
+ " call Decho("files<".string(filelist).">",'~'.expand("<slnum>"))
+
+ " mark the list of files
+ for fname in filelist
+ if fname =~ '^'.fnameescape(curdir)
+ " call Decho("fname<".substitute(fname,'^'.fnameescape(curdir).'/','','').">",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^'.fnameescape(curdir).'/','',''))
+ else
+ " call Decho("fname<".fname.">",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^.*/','',''))
+ endif
+ endfor
+
+ else
+ " call Decho("handle remote regexp",'~'.expand("<slnum>"))
+
+ " convert displayed listing into a filelist
+ let eikeep = &ei
+ let areg = @a
+ sil NetrwKeepj %y a
+ setl ei=all ma
+ " call Decho("setl ei=all ma",'~'.expand("<slnum>"))
+ 1split
+ NetrwKeepj call s:NetrwEnew()
+ NetrwKeepj call s:NetrwOptionsSafe(a:islocal)
+ sil NetrwKeepj norm! "ap
+ NetrwKeepj 2
+ let bannercnt= search('^" =====','W')
+ exe "sil NetrwKeepj 1,".bannercnt."d"
+ setl bt=nofile
+ if g:netrw_liststyle == s:LONGLIST
+ sil NetrwKeepj %s/\s\{2,}\S.*$//e
+ call histdel("/",-1)
+ elseif g:netrw_liststyle == s:WIDELIST
+ sil NetrwKeepj %s/\s\{2,}/\r/ge
+ call histdel("/",-1)
+ elseif g:netrw_liststyle == s:TREELIST
+ exe 'sil NetrwKeepj %s/^'.s:treedepthstring.' //e'
+ sil! NetrwKeepj g/^ .*$/d
+ call histdel("/",-1)
+ call histdel("/",-1)
+ endif
+ " convert regexp into the more usual glob-style format
+ let regexp= substitute(regexp,'\*','.*','g')
+ " call Decho("regexp<".regexp.">",'~'.expand("<slnum>"))
+ exe "sil! NetrwKeepj v/".escape(regexp,'/')."/d"
+ call histdel("/",-1)
+ let filelist= getline(1,line("$"))
+ q!
+ for filename in filelist
+ NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(filename,'^.*/','',''))
+ endfor
+ unlet filelist
+ let @a = areg
+ let &ei = eikeep
+ endif
+ echo " (use me to edit marked files)"
+
+ " call Dret("s:NetrwMarkFileRegexp")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileSource: (invoked by ms) This function sources marked files {{{2
+" Uses the local marked file list.
+fun! s:NetrwMarkFileSource(islocal)
+ " call Dfunc("s:NetrwMarkFileSource(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:NetrwMarkFileSource")
+ 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_{bufnr("%")}
+ 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
+ " the autocmds will handle sourcing both local and remote files
+ " call Decho("exe so ".fnameescape(fname),'~'.expand("<slnum>"))
+ exe "so ".fnameescape(fname)
+ endfor
+ 2match none
+ endif
+ " call Dret("s:NetrwMarkFileSource")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileTag: (invoked by mT) This function applies g:netrw_ctags to marked files {{{2
+" Uses the global markfilelist
+fun! s:NetrwMarkFileTag(islocal)
+ let svpos = winsaveview()
+ let curdir = s:NetrwGetCurdir(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)
+ return
+ endif
+
+ if exists("s:netrwmarkfilelist")
+ let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "s:ShellEscape(v:val,".!a:islocal.")"))
+ call s:NetrwUnmarkAll()
+
+ if a:islocal
+
+ call system(g:netrw_ctags." ".netrwmarkfilelist)
+ if v:shell_error
+ call netrw#ErrorMsg(s:ERROR,"g:netrw_ctags<".g:netrw_ctags."> is not executable!",51)
+ endif
+
+ else
+ let cmd = s:RemoteSystem(g:netrw_ctags." ".netrwmarkfilelist)
+ call netrw#Obtain(a:islocal,"tags")
+ let curdir= b:netrw_curdir
+ 1split
+ NetrwKeepj e tags
+ let path= substitute(curdir,'^\(.*\)/[^/]*$','\1/','')
+ exe 'NetrwKeepj %s/\t\(\S\+\)\t/\t'.escape(path,"/\n\r\\").'\1\t/e'
+ call histdel("/",-1)
+ wq!
+ endif
+ 2match none
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ call winrestview(svpos)
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMarkFileTgt: (invoked by mt) This function sets up a marked file target {{{2
+" Sets up two variables,
+" s:netrwmftgt : holds the target directory
+" s:netrwmftgt_islocal : 0=target directory is remote
+" 1=target directory is local
+fun! s:NetrwMarkFileTgt(islocal)
+ let svpos = winsaveview()
+ let curdir = s:NetrwGetCurdir(a:islocal)
+ let hadtgt = exists("s:netrwmftgt")
+ if !exists("w:netrw_bannercnt")
+ let w:netrw_bannercnt= b:netrw_bannercnt
+ endif
+
+ " set up target
+ if line(".") < w:netrw_bannercnt
+ " if cursor in banner region, use b:netrw_curdir for the target unless its already the target
+ if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") && s:netrwmftgt == b:netrw_curdir
+ unlet s:netrwmftgt s:netrwmftgt_islocal
+ if g:netrw_fastbrowse <= 1
+ call s:LocalBrowseRefresh()
+ endif
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ call winrestview(svpos)
+ return
+ else
+ let s:netrwmftgt= b:netrw_curdir
+ endif
+
+ else
+ " get word under cursor.
+ " * If directory, use it for the target.
+ " * If file, use b:netrw_curdir for the target
+ let curword= s:NetrwGetWord()
+ let tgtdir = s:ComposePath(curdir,curword)
+ if a:islocal && isdirectory(s:NetrwFile(tgtdir))
+ let s:netrwmftgt = tgtdir
+ elseif !a:islocal && tgtdir =~ '/$'
+ let s:netrwmftgt = tgtdir
+ else
+ let s:netrwmftgt = curdir
+ endif
+ endif
+ if a:islocal
+ " simplify the target (eg. /abc/def/../ghi -> /abc/ghi)
+ let s:netrwmftgt= simplify(s:netrwmftgt)
+ endif
+ if g:netrw_cygwin
+ let s:netrwmftgt= substitute(system("cygpath ".s:ShellEscape(s:netrwmftgt)),'\n$','','')
+ let s:netrwmftgt= substitute(s:netrwmftgt,'\n$','','')
+ endif
+ let s:netrwmftgt_islocal= a:islocal
+
+ " need to do refresh so that the banner will be updated
+ " s:LocalBrowseRefresh handles all local-browsing buffers when not fast browsing
+ if g:netrw_fastbrowse <= 1
+ call s:LocalBrowseRefresh()
+ endif
+ " call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,w:netrw_treetop,0))
+ else
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ endif
+ call winrestview(svpos)
+ if !hadtgt
+ sil! NetrwKeepj norm! j
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwGetCurdir: gets current directory and sets up b:netrw_curdir if necessary {{{2
+fun! s:NetrwGetCurdir(islocal)
+ " call Dfunc("s:NetrwGetCurdir(islocal=".a:islocal.")")
+
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ let b:netrw_curdir = s:NetrwTreePath(w:netrw_treetop)
+ " call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used s:NetrwTreeDir)",'~'.expand("<slnum>"))
+ elseif !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ " call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)",'~'.expand("<slnum>"))
+ endif
+
+ " call Decho("b:netrw_curdir<".b:netrw_curdir."> ".((b:netrw_curdir !~ '\<\a\{3,}://')? "does not match" : "matches")." url pattern",'~'.expand("<slnum>"))
+ if b:netrw_curdir !~ '\<\a\{3,}://'
+ let curdir= b:netrw_curdir
+ " call Decho("g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>"))
+ if g:netrw_keepdir == 0
+ call s:NetrwLcd(curdir)
+ endif
+ endif
+
+ " call Dret("s:NetrwGetCurdir <".curdir.">")
+ return b:netrw_curdir
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwOpenFile: query user for a filename and open it {{{2
+fun! s:NetrwOpenFile(islocal)
+ " call Dfunc("s:NetrwOpenFile(islocal=".a:islocal.")")
+ let ykeep= @@
+ call inputsave()
+ let fname= input("Enter filename: ")
+ call inputrestore()
+ " call Decho("(s:NetrwOpenFile) fname<".fname.">",'~'.expand("<slnum>"))
+
+ " determine if Lexplore is in use
+ if exists("t:netrw_lexbufnr")
+ " check if t:netrw_lexbufnr refers to a netrw window
+ " call Decho("(s:netrwOpenFile) ..t:netrw_lexbufnr=".t:netrw_lexbufnr,'~'.expand("<slnum>"))
+ let lexwinnr = bufwinnr(t:netrw_lexbufnr)
+ if lexwinnr != -1 && exists("g:netrw_chgwin") && g:netrw_chgwin != -1
+ " call Decho("(s:netrwOpenFile) ..Lexplore in use",'~'.expand("<slnum>"))
+ exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w"
+ exe "NetrwKeepj e ".fnameescape(fname)
+ let @@= ykeep
+ " call Dret("s:NetrwOpenFile : creating a file with Lexplore mode")
+ endif
+ endif
+
+ " Does the filename contain a path?
+ if fname !~ '[/\\]'
+ if exists("b:netrw_curdir")
+ if exists("g:netrw_quiet")
+ let netrw_quiet_keep = g:netrw_quiet
+ endif
+ let g:netrw_quiet = 1
+ " save position for benefit of Rexplore
+ let s:rexposn_{bufnr("%")}= winsaveview()
+ " call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>"))
+ if b:netrw_curdir =~ '/$'
+ exe "NetrwKeepj e ".fnameescape(b:netrw_curdir.fname)
+ else
+ exe "e ".fnameescape(b:netrw_curdir."/".fname)
+ endif
+ if exists("netrw_quiet_keep")
+ let g:netrw_quiet= netrw_quiet_keep
+ else
+ unlet g:netrw_quiet
+ endif
+ endif
+ else
+ exe "NetrwKeepj e ".fnameescape(fname)
+ endif
+ let @@= ykeep
+ " call Dret("s:NetrwOpenFile")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Shrink: shrinks/expands a netrw or Lexplorer window {{{2
+" For the mapping to this function be made via
+" netrwPlugin, you'll need to have had
+" g:netrw_usetab set to non-zero.
+fun! netrw#Shrink()
+ " call Dfunc("netrw#Shrink() ft<".&ft."> winwidth=".winwidth(0)." lexbuf#".((exists("t:netrw_lexbufnr"))? t:netrw_lexbufnr : 'n/a'))
+ let curwin = winnr()
+ let wiwkeep = &wiw
+ set wiw=1
+
+ if &ft == "netrw"
+ if winwidth(0) > g:netrw_wiw
+ let t:netrw_winwidth= winwidth(0)
+ exe "vert resize ".g:netrw_wiw
+ wincmd l
+ if winnr() == curwin
+ wincmd h
+ endif
+ " call Decho("vert resize 0",'~'.expand("<slnum>"))
+ else
+ exe "vert resize ".t:netrw_winwidth
+ " call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>"))
+ endif
+
+ elseif exists("t:netrw_lexbufnr")
+ exe bufwinnr(t:netrw_lexbufnr)."wincmd w"
+ if winwidth(bufwinnr(t:netrw_lexbufnr)) > g:netrw_wiw
+ let t:netrw_winwidth= winwidth(0)
+ exe "vert resize ".g:netrw_wiw
+ wincmd l
+ if winnr() == curwin
+ wincmd h
+ endif
+ " call Decho("vert resize 0",'~'.expand("<slnum>"))
+ elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0
+ exe "vert resize ".t:netrw_winwidth
+ " call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>"))
+ else
+ call netrw#Lexplore(0,0)
+ endif
+
+ else
+ call netrw#Lexplore(0,0)
+ endif
+ let wiw= wiwkeep
+
+ " call Dret("netrw#Shrink")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetSortSequence: allows user to edit the sorting sequence {{{2
+fun! s:NetSortSequence(islocal)
+ let ykeep= @@
+ let svpos= winsaveview()
+ call inputsave()
+ let newsortseq= input("Edit Sorting Sequence: ",g:netrw_sort_sequence)
+ call inputrestore()
+
+ " refresh the listing
+ let g:netrw_sort_sequence= newsortseq
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwUnmarkList: delete local marked file list and remove their contents from the global marked-file list {{{2
+" User access provided by the <mF> mapping. (see :help netrw-mF)
+" Used by many MarkFile functions.
+fun! s:NetrwUnmarkList(curbufnr,curdir)
+ " call Dfunc("s:NetrwUnmarkList(curbufnr=".a:curbufnr." curdir<".a:curdir.">)")
+
+ " remove all files in local marked-file list from global list
+ if exists("s:netrwmarkfilelist")
+ for mfile in s:netrwmarkfilelist_{a:curbufnr}
+ let dfile = s:ComposePath(a:curdir,mfile) " prepend directory to mfile
+ let idx = index(s:netrwmarkfilelist,dfile) " get index in list of dfile
+ call remove(s:netrwmarkfilelist,idx) " remove from global list
+ endfor
+ if s:netrwmarkfilelist == []
+ unlet s:netrwmarkfilelist
+ endif
+
+ " getting rid of the local marked-file lists is easy
+ unlet s:netrwmarkfilelist_{a:curbufnr}
+ endif
+ if exists("s:netrwmarkfilemtch_{a:curbufnr}")
+ unlet s:netrwmarkfilemtch_{a:curbufnr}
+ endif
+ 2match none
+ " call Dret("s:NetrwUnmarkList")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwUnmarkAll: remove the global marked file list and all local ones {{{2
+fun! s:NetrwUnmarkAll()
+ " call Dfunc("s:NetrwUnmarkAll()")
+ if exists("s:netrwmarkfilelist")
+ unlet s:netrwmarkfilelist
+ endif
+ sil call s:NetrwUnmarkAll2()
+ 2match none
+ " call Dret("s:NetrwUnmarkAll")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwUnmarkAll2: unmark all files from all buffers {{{2
+fun! s:NetrwUnmarkAll2()
+ " call Dfunc("s:NetrwUnmarkAll2()")
+ redir => netrwmarkfilelist_let
+ let
+ redir END
+ let netrwmarkfilelist_list= split(netrwmarkfilelist_let,'\n') " convert let string into a let list
+ call filter(netrwmarkfilelist_list,"v:val =~ '^s:netrwmarkfilelist_'") " retain only those vars that start as s:netrwmarkfilelist_
+ call map(netrwmarkfilelist_list,"substitute(v:val,'\\s.*$','','')") " remove what the entries are equal to
+ for flist in netrwmarkfilelist_list
+ let curbufnr= substitute(flist,'s:netrwmarkfilelist_','','')
+ unlet s:netrwmarkfilelist_{curbufnr}
+ unlet s:netrwmarkfilemtch_{curbufnr}
+ endfor
+ " call Dret("s:NetrwUnmarkAll2")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwUnMarkFile: called via mu map; unmarks *all* marked files, both global and buffer-local {{{2
+"
+" Marked files are in two types of lists:
+" s:netrwmarkfilelist -- holds complete paths to all marked files
+" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr())
+"
+" Marked files suitable for use with 2match are in:
+" s:netrwmarkfilemtch_# -- used with 2match to display marked files
+fun! s:NetrwUnMarkFile(islocal)
+ let svpos = winsaveview()
+ let curbufnr = bufnr("%")
+
+ " unmark marked file list
+ " (although I expect s:NetrwUpload() to do it, I'm just making sure)
+ if exists("s:netrwmarkfilelist")
+ " " call Decho("unlet'ing: s:netrwmarkfilelist",'~'.expand("<slnum>"))
+ unlet s:netrwmarkfilelist
+ endif
+
+ let ibuf= 1
+ while ibuf < bufnr("$")
+ if exists("s:netrwmarkfilelist_".ibuf)
+ unlet s:netrwmarkfilelist_{ibuf}
+ unlet s:netrwmarkfilemtch_{ibuf}
+ endif
+ let ibuf = ibuf + 1
+ endwhile
+ 2match none
+
+ " call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ call winrestview(svpos)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwMenu: generates the menu for gvim and netrw {{{2
+fun! s:NetrwMenu(domenu)
+
+ if !exists("g:NetrwMenuPriority")
+ let g:NetrwMenuPriority= 80
+ endif
+
+ if has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
+ " call Dfunc("NetrwMenu(domenu=".a:domenu.")")
+
+ if !exists("s:netrw_menu_enabled") && a:domenu
+ " call Decho("initialize menu",'~'.expand("<slnum>"))
+ let s:netrw_menu_enabled= 1
+ exe 'sil! menu '.g:NetrwMenuPriority.'.1 '.g:NetrwTopLvlMenu.'Help<tab><F1> <F1>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.5 '.g:NetrwTopLvlMenu.'-Sep1- :'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.6 '.g:NetrwTopLvlMenu.'Go\ Up\ Directory<tab>- -'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.7 '.g:NetrwTopLvlMenu.'Apply\ Special\ Viewer<tab>x x'
+ if g:netrw_dirhistmax > 0
+ exe 'sil! menu '.g:NetrwMenuPriority.'.8.1 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Current\ Directory<tab>mb mb'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.8.4 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Prev\ Dir\ (History)<tab>u u'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.8.5 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Next\ Dir\ (History)<tab>U U'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.8.6 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.List<tab>qb qb'
+ else
+ exe 'sil! menu '.g:NetrwMenuPriority.'.8 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History :echo "(disabled)"'."\<cr>"
+ endif
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.1 '.g:NetrwTopLvlMenu.'Browsing\ Control.Horizontal\ Split<tab>o o'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.2 '.g:NetrwTopLvlMenu.'Browsing\ Control.Vertical\ Split<tab>v v'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.3 '.g:NetrwTopLvlMenu.'Browsing\ Control.New\ Tab<tab>t t'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.4 '.g:NetrwTopLvlMenu.'Browsing\ Control.Preview<tab>p p'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.5 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ File\ Hiding\ List<tab><ctrl-h>'." \<c-h>'"
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.6 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ Sorting\ Sequence<tab>S S'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.7 '.g:NetrwTopLvlMenu.'Browsing\ Control.Quick\ Hide/Unhide\ Dot\ Files<tab>'."gh gh"
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.8 '.g:NetrwTopLvlMenu.'Browsing\ Control.Refresh\ Listing<tab>'."<ctrl-l> \<c-l>"
+ exe 'sil! menu '.g:NetrwMenuPriority.'.9.9 '.g:NetrwTopLvlMenu.'Browsing\ Control.Settings/Options<tab>:NetrwSettings '.":NetrwSettings\<cr>"
+ exe 'sil! menu '.g:NetrwMenuPriority.'.10 '.g:NetrwTopLvlMenu.'Delete\ File/Directory<tab>D D'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Create\ New\ File<tab>% %'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Current\ Window<tab><cr> '."\<cr>"
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.2 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Preview\ File/Directory<tab>p p'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.3 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Previous\ Window<tab>P P'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.4 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Window<tab>o o'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Tab<tab>t t'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Vertical\ Window<tab>v v'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.1 '.g:NetrwTopLvlMenu.'Explore.Directory\ Name :Explore '
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (curdir\ only)<tab>:Explore\ */ :Explore */'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (+subdirs)<tab>:Explore\ **/ :Explore **/'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.3 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (curdir\ only)<tab>:Explore\ *// :Explore *//'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (+subdirs)<tab>:Explore\ **// :Explore **//'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Next\ Match<tab>:Nexplore :Nexplore<cr>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Prev\ Match<tab>:Pexplore :Pexplore<cr>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.13 '.g:NetrwTopLvlMenu.'Make\ Subdirectory<tab>d d'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.1 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ File<tab>mf mf'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.2 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ Files\ by\ Regexp<tab>mr mr'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.3 '.g:NetrwTopLvlMenu.'Marked\ Files.Hide-Show-List\ Control<tab>a a'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.4 '.g:NetrwTopLvlMenu.'Marked\ Files.Copy\ To\ Target<tab>mc mc'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.5 '.g:NetrwTopLvlMenu.'Marked\ Files.Delete<tab>D D'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.6 '.g:NetrwTopLvlMenu.'Marked\ Files.Diff<tab>md md'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.7 '.g:NetrwTopLvlMenu.'Marked\ Files.Edit<tab>me me'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.8 '.g:NetrwTopLvlMenu.'Marked\ Files.Exe\ Cmd<tab>mx mx'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.9 '.g:NetrwTopLvlMenu.'Marked\ Files.Move\ To\ Target<tab>mm mm'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.10 '.g:NetrwTopLvlMenu.'Marked\ Files.Obtain<tab>O O'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.11 '.g:NetrwTopLvlMenu.'Marked\ Files.Print<tab>mp mp'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.12 '.g:NetrwTopLvlMenu.'Marked\ Files.Replace<tab>R R'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.13 '.g:NetrwTopLvlMenu.'Marked\ Files.Set\ Target<tab>mt mt'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.14 '.g:NetrwTopLvlMenu.'Marked\ Files.Tag<tab>mT mT'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.14.15 '.g:NetrwTopLvlMenu.'Marked\ Files.Zip/Unzip/Compress/Uncompress<tab>mz mz'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.15 '.g:NetrwTopLvlMenu.'Obtain\ File<tab>O O'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.thin<tab>i :let w:netrw_liststyle=0<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.long<tab>i :let w:netrw_liststyle=1<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.wide<tab>i :let w:netrw_liststyle=2<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.tree<tab>i :let w:netrw_liststyle=3<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.1 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Show\ All<tab>a :let g:netrw_hide=0<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.3 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Normal<tab>a :let g:netrw_hide=1<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.2 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Hidden\ Only<tab>a :let g:netrw_hide=2<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.3 '.g:NetrwTopLvlMenu.'Style.Reverse\ Sorting\ Order<tab>'."r r"
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.1 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Name<tab>s :let g:netrw_sort_by="name"<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.2 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Time<tab>s :let g:netrw_sort_by="time"<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Size<tab>s :let g:netrw_sort_by="size"<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Exten<tab>s :let g:netrw_sort_by="exten"<cr><c-L>'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.17 '.g:NetrwTopLvlMenu.'Rename\ File/Directory<tab>R R'
+ exe 'sil! menu '.g:NetrwMenuPriority.'.18 '.g:NetrwTopLvlMenu.'Set\ Current\ Directory<tab>c c'
+ let s:netrw_menucnt= 28
+ call s:NetrwBookmarkMenu() " provide some history! uses priorities 2,3, reserves 4, 8.2.x
+ call s:NetrwTgtMenu() " let bookmarks and history be easy targets
+
+ elseif !a:domenu
+ let s:netrwcnt = 0
+ let curwin = winnr()
+ windo if getline(2) =~# "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif
+ exe curwin."wincmd w"
+
+ if s:netrwcnt <= 1
+ " call Decho("clear menus",'~'.expand("<slnum>"))
+ exe 'sil! unmenu '.g:NetrwTopLvlMenu
+ " call Decho('exe sil! unmenu '.g:NetrwTopLvlMenu.'*','~'.expand("<slnum>"))
+ sil! unlet s:netrw_menu_enabled
+ endif
+ endif
+ " call Dret("NetrwMenu")
+ return
+ endif
+
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwObtain: obtain file under cursor or from markfile list {{{2
+" Used by the O maps (as <SID>NetrwObtain())
+fun! s:NetrwObtain(islocal)
+ " call Dfunc("NetrwObtain(islocal=".a:islocal.")")
+
+ let ykeep= @@
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+ let islocal= s:netrwmarkfilelist_{bufnr('%')}[1] !~ '^\a\{3,}://'
+ call netrw#Obtain(islocal,s:netrwmarkfilelist_{bufnr('%')})
+ call s:NetrwUnmarkList(bufnr('%'),b:netrw_curdir)
+ else
+ call netrw#Obtain(a:islocal,s:NetrwGetWord())
+ endif
+ let @@= ykeep
+
+ " call Dret("NetrwObtain")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwPrevWinOpen: open file/directory in previous window. {{{2
+" If there's only one window, then the window will first be split.
+" Returns:
+" choice = 0 : didn't have to choose
+" choice = 1 : saved modified file in window first
+" choice = 2 : didn't save modified file, opened window
+" choice = 3 : cancel open
+fun! s:NetrwPrevWinOpen(islocal)
+ let ykeep= @@
+ " grab a copy of the b:netrw_curdir to pass it along to newly split windows
+ let curdir = b:netrw_curdir
+
+ " get last window number and the word currently under the cursor
+ let origwin = winnr()
+ let lastwinnr = winnr("$")
+ let curword = s:NetrwGetWord()
+ let choice = 0
+ let s:prevwinopen= 1 " lets s:NetrwTreeDir() know that NetrwPrevWinOpen called it (s:NetrwTreeDir() will unlet s:prevwinopen)
+ let s:treedir = s:NetrwTreeDir(a:islocal)
+ let curdir = s:treedir
+
+ let didsplit = 0
+ if lastwinnr == 1
+ " if only one window, open a new one first
+ " g:netrw_preview=0: preview window shown in a horizontally split window
+ " g:netrw_preview=1: preview window shown in a vertically split window
+ if g:netrw_preview
+ " vertically split preview window
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
+ exe (g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s"
+ else
+ " horizontally split preview window
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
+ exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
+ endif
+ let didsplit = 1
+
+ else
+ NetrwKeepj call s:SaveBufVars()
+ let eikeep= &ei
+ setl ei=all
+ wincmd p
+
+ if exists("s:lexplore_win") && s:lexplore_win == winnr()
+ " whoops -- user trying to open file in the Lexplore window.
+ " Use Lexplore's opening-file window instead.
+ " exe g:netrw_chgwin."wincmd w"
+ wincmd p
+ call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
+ endif
+
+ " prevwinnr: the window number of the "prev" window
+ " prevbufnr: the buffer number of the buffer in the "prev" window
+ " bnrcnt : the qty of windows open on the "prev" buffer
+ let prevwinnr = winnr()
+ let prevbufnr = bufnr("%")
+ let prevbufname = bufname("%")
+ let prevmod = &mod
+ let bnrcnt = 0
+ NetrwKeepj call s:RestoreBufVars()
+
+ " if the previous window's buffer has been changed (ie. its modified flag is set),
+ " and it doesn't appear in any other extant window, then ask the
+ " user if s/he wants to abandon modifications therein.
+ if prevmod
+ windo if winbufnr(0) == prevbufnr | let bnrcnt=bnrcnt+1 | endif
+ exe prevwinnr."wincmd w"
+
+ if bnrcnt == 1 && &hidden == 0
+ " only one copy of the modified buffer in a window, and
+ " hidden not set, so overwriting will lose the modified file. Ask first...
+ let choice = confirm("Save modified buffer<".prevbufname."> first?","&Yes\n&No\n&Cancel")
+ let &ei= eikeep
+
+ if choice == 1
+ " Yes -- write file & then browse
+ let v:errmsg= ""
+ sil w
+ if v:errmsg != ""
+ call netrw#ErrorMsg(s:ERROR,"unable to write <".(exists("prevbufname")? prevbufname : 'n/a').">!",30)
+ exe origwin."wincmd w"
+ let &ei = eikeep
+ let @@ = ykeep
+ return choice
+ endif
+
+ elseif choice == 2
+ " No -- don't worry about changed file, just browse anyway
+ echomsg "**note** changes to ".prevbufname." abandoned"
+
+ else
+ " Cancel -- don't do this
+ exe origwin."wincmd w"
+ let &ei= eikeep
+ let @@ = ykeep
+ return choice
+ endif
+ endif
+ endif
+ let &ei= eikeep
+ endif
+
+ " restore b:netrw_curdir (window split/enew may have lost it)
+ let b:netrw_curdir= curdir
+ if a:islocal < 2
+ if a:islocal
+ call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(a:islocal,curword,0))
+ else
+ call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,curword,0))
+ endif
+ endif
+ let @@= ykeep
+ return choice
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwUpload: load fname to tgt (used by NetrwMarkFileCopy()) {{{2
+" Always assumed to be local -> remote
+" call s:NetrwUpload(filename, target)
+" call s:NetrwUpload(filename, target, fromdirectory)
+fun! s:NetrwUpload(fname,tgt,...)
+ " call Dfunc("s:NetrwUpload(fname<".((type(a:fname) == 1)? a:fname : string(a:fname))."> tgt<".a:tgt.">) a:0=".a:0)
+
+ if a:tgt =~ '^\a\{3,}://'
+ let tgtdir= substitute(a:tgt,'^\a\{3,}://[^/]\+/\(.\{-}\)$','\1','')
+ else
+ let tgtdir= substitute(a:tgt,'^\(.*\)/[^/]*$','\1','')
+ endif
+ " call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>"))
+
+ if a:0 > 0
+ let fromdir= a:1
+ else
+ let fromdir= getcwd()
+ endif
+ " call Decho("fromdir<".fromdir.">",'~'.expand("<slnum>"))
+
+ if type(a:fname) == 1
+ " handle uploading a single file using NetWrite
+ " call Decho("handle uploading a single file via NetWrite",'~'.expand("<slnum>"))
+ 1split
+ " call Decho("exe e ".fnameescape(s:NetrwFile(a:fname)),'~'.expand("<slnum>"))
+ exe "NetrwKeepj e ".fnameescape(s:NetrwFile(a:fname))
+ " call Decho("now locally editing<".expand("%").">, has ".line("$")." lines",'~'.expand("<slnum>"))
+ if a:tgt =~ '/$'
+ let wfname= substitute(a:fname,'^.*/','','')
+ " call Decho("exe w! ".fnameescape(wfname),'~'.expand("<slnum>"))
+ exe "w! ".fnameescape(a:tgt.wfname)
+ else
+ " call Decho("writing local->remote: exe w ".fnameescape(a:tgt),'~'.expand("<slnum>"))
+ exe "w ".fnameescape(a:tgt)
+ " call Decho("done writing local->remote",'~'.expand("<slnum>"))
+ endif
+ q!
+
+ elseif type(a:fname) == 3
+ " handle uploading a list of files via scp
+ " call Decho("handle uploading a list of files via scp",'~'.expand("<slnum>"))
+ let curdir= getcwd()
+ if a:tgt =~ '^scp:'
+ if s:NetrwLcd(fromdir)
+ " call Dret("s:NetrwUpload : lcd failure")
+ return
+ endif
+ let filelist= deepcopy(s:netrwmarkfilelist_{bufnr('%')})
+ let args = join(map(filelist,"s:ShellEscape(v:val, 1)"))
+ if exists("g:netrw_port") && g:netrw_port != ""
+ let useport= " ".g:netrw_scpport." ".g:netrw_port
+ else
+ let useport= ""
+ endif
+ let machine = substitute(a:tgt,'^scp://\([^/:]\+\).*$','\1','')
+ let tgt = substitute(a:tgt,'^scp://[^/]\+/\(.*\)$','\1','')
+ call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".args." ".s:ShellEscape(machine.":".tgt,1))
+ if s:NetrwLcd(curdir)
+ " call Dret("s:NetrwUpload : lcd failure")
+ return
+ endif
+
+ elseif a:tgt =~ '^ftp:'
+ call s:NetrwMethod(a:tgt)
+
+ if b:netrw_method == 2
+ " handle uploading a list of files via ftp+.netrc
+ let netrw_fname = b:netrw_fname
+ sil NetrwKeepj new
+ " call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
+
+ NetrwKeepj put =g:netrw_ftpmode
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+
+ if tgtdir == ""
+ let tgtdir= '/'
+ endif
+ NetrwKeepj call setline(line("$")+1,'cd "'.tgtdir.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+
+ for fname in a:fname
+ NetrwKeepj call setline(line("$")+1,'put "'.s:NetrwFile(fname).'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endfor
+
+ if exists("g:netrw_port") && g:netrw_port != ""
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1))
+ else
+ " call Decho("filter input window#".winnr(),'~'.expand("<slnum>"))
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1))
+ endif
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ sil NetrwKeepj g/Local directory now/d
+ call histdel("/",-1)
+ if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
+ call netrw#ErrorMsg(s:ERROR,getline(1),14)
+ else
+ bw!|q
+ endif
+
+ elseif b:netrw_method == 3
+ " upload with ftp + machine, id, passwd, and fname (ie. no .netrc)
+ let netrw_fname= b:netrw_fname
+ NetrwKeepj call s:SaveBufVars()|sil NetrwKeepj new|NetrwKeepj call s:RestoreBufVars()
+ let tmpbufnr= bufnr("%")
+ setl ff=unix
+
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_uid") && g:netrw_uid != ""
+ if exists("g:netrw_ftp") && g:netrw_ftp == 1
+ NetrwKeepj put =g:netrw_uid
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ if exists("s:netrw_passwd")
+ NetrwKeepj call setline(line("$")+1,'"'.s:netrw_passwd.'"')
+ endif
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ elseif exists("s:netrw_passwd")
+ NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+ endif
+
+ NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+
+ if exists("b:netrw_fname") && b:netrw_fname != ""
+ NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endif
+
+ for fname in a:fname
+ NetrwKeepj call setline(line("$")+1,'put "'.fname.'"')
+ " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>"))
+ endfor
+
+ " perform ftp:
+ " -i : turns off interactive prompting from ftp
+ " -n unix : DON'T use <.netrc>, even though it exists
+ " -n win32: quit being obnoxious about password
+ NetrwKeepj norm! 1G"_dd
+ call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
+ " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar)
+ sil NetrwKeepj g/Local directory now/d
+ call histdel("/",-1)
+ if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying '
+ let debugkeep= &debug
+ setl debug=msg
+ call netrw#ErrorMsg(s:ERROR,getline(1),15)
+ let &debug = debugkeep
+ let mod = 1
+ else
+ bw!|q
+ endif
+ elseif !exists("b:netrw_method") || b:netrw_method < 0
+ " call Dret("s:#NetrwUpload : unsupported method")
+ return
+ endif
+ else
+ call netrw#ErrorMsg(s:ERROR,"can't obtain files with protocol from<".a:tgt.">",63)
+ endif
+ endif
+
+ " call Dret("s:NetrwUpload")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwPreview: supports netrw's "p" map {{{2
+fun! s:NetrwPreview(path) range
+ " call Dfunc("NetrwPreview(path<".a:path.">)")
+ " call Decho("g:netrw_alto =".(exists("g:netrw_alto")? g:netrw_alto : 'n/a'),'~'.expand("<slnum>"))
+ " call Decho("g:netrw_preview=".(exists("g:netrw_preview")? g:netrw_preview : 'n/a'),'~'.expand("<slnum>"))
+ let ykeep= @@
+ NetrwKeepj call s:NetrwOptionsSave("s:")
+ if a:path !~ '^\*\{1,2}/' && a:path !~ '^\a\{3,}://'
+ NetrwKeepj call s:NetrwOptionsSafe(1)
+ else
+ NetrwKeepj call s:NetrwOptionsSafe(0)
+ endif
+ if has("quickfix")
+ " call Decho("has quickfix",'~'.expand("<slnum>"))
+ if !isdirectory(s:NetrwFile(a:path))
+ " call Decho("good; not previewing a directory",'~'.expand("<slnum>"))
+ if g:netrw_preview
+ " vertical split
+ let pvhkeep = &pvh
+ let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
+ let &pvh = winwidth(0) - winsz
+ " call Decho("g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>"))
+ else
+ " horizontal split
+ let pvhkeep = &pvh
+ let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
+ let &pvh = winheight(0) - winsz
+ " call Decho("!g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>"))
+ endif
+ " g:netrw_preview g:netrw_alto
+ " 1 : vert 1: top -- preview window is vertically split off and on the left
+ " 1 : vert 0: bot -- preview window is vertically split off and on the right
+ " 0 : 1: top -- preview window is horizontally split off and on the top
+ " 0 : 0: bot -- preview window is horizontally split off and on the bottom
+ "
+ " Note that the file being previewed is already known to not be a directory, hence we can avoid doing a LocalBrowseCheck() check via
+ " the BufEnter event set up in netrwPlugin.vim
+ " call Decho("exe ".(g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path),'~'.expand("<slnum>"))
+ let eikeep = &ei
+ set ei=BufEnter
+ exe (g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path)
+ let &ei= eikeep
+ " call Decho("winnr($)=".winnr("$"),'~'.expand("<slnum>"))
+ if exists("pvhkeep")
+ let &pvh= pvhkeep
+ endif
+ elseif !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, cannot preview a directory such as <".a:path.">",38)
+ endif
+ elseif !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, to preview your vim needs the quickfix feature compiled in",39)
+ endif
+ NetrwKeepj call s:NetrwOptionsRestore("s:")
+ let @@= ykeep
+ " call Dret("NetrwPreview")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRefresh: {{{2
+fun! s:NetrwRefresh(islocal,dirname)
+ " call Dfunc("s:NetrwRefresh(islocal<".a:islocal.">,dirname=".a:dirname.") g:netrw_hide=".g:netrw_hide." g:netrw_sort_direction=".g:netrw_sort_direction)
+ " at the current time (Mar 19, 2007) all calls to NetrwRefresh() call NetrwBrowseChgDir() first.
+ setl ma noro
+ " call Decho("setl ma noro",'~'.expand("<slnum>"))
+ " call Decho("clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
+ let ykeep = @@
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ if !exists("w:netrw_treetop")
+ if exists("b:netrw_curdir")
+ let w:netrw_treetop= b:netrw_curdir
+ else
+ let w:netrw_treetop= getcwd()
+ endif
+ endif
+ NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop)
+ endif
+
+ " save the cursor position before refresh.
+ let screenposn = winsaveview()
+ " call Decho("saving posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>"))
+
+ " call Decho("win#".winnr().": ".winheight(0)."x".winwidth(0)." curfile<".expand("%").">",'~'.expand("<slnum>"))
+ " call Decho("clearing buffer prior to refresh",'~'.expand("<slnum>"))
+ sil! NetrwKeepj %d _
+ if a:islocal
+ NetrwKeepj call netrw#LocalBrowseCheck(a:dirname)
+ else
+ NetrwKeepj call s:NetrwBrowse(a:islocal,a:dirname)
+ endif
+
+ " restore position
+ " call Decho("restoring posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>"))
+ NetrwKeepj call winrestview(screenposn)
+
+ " restore file marks
+ if has("syntax") && exists("g:syntax_on") && g:syntax_on
+ if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != ""
+ " " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/",'~'.expand("<slnum>"))
+ exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/"
+ else
+ " " call Decho("2match none (bufnr(%)=".bufnr("%")."<".bufname("%").">)",'~'.expand("<slnum>"))
+ 2match none
+ endif
+ endif
+
+ " restore
+ let @@= ykeep
+ " call Dret("s:NetrwRefresh")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRefreshDir: refreshes a directory by name {{{2
+" Called by NetrwMarkFileCopy()
+" Interfaces to s:NetrwRefresh() and s:LocalBrowseRefresh()
+fun! s:NetrwRefreshDir(islocal,dirname)
+ if g:netrw_fastbrowse == 0
+ " slowest mode (keep buffers refreshed, local or remote)
+ let tgtwin= bufwinnr(a:dirname)
+
+ if tgtwin > 0
+ " tgtwin is being displayed, so refresh it
+ let curwin= winnr()
+ exe tgtwin."wincmd w"
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ exe curwin."wincmd w"
+
+ elseif bufnr(a:dirname) > 0
+ let bn= bufnr(a:dirname)
+ exe "sil keepj bd ".bn
+ endif
+
+ elseif g:netrw_fastbrowse <= 1
+ NetrwKeepj call s:LocalBrowseRefresh()
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSetChgwin: set g:netrw_chgwin; a <cr> will use the specified
+" window number to do its editing in.
+" Supports [count]C where the count, if present, is used to specify
+" a window to use for editing via the <cr> mapping.
+fun! s:NetrwSetChgwin(...)
+ " call Dfunc("s:NetrwSetChgwin() v:count=".v:count)
+ if a:0 > 0
+ " call Decho("a:1<".a:1.">",'~'.expand("<slnum>"))
+ if a:1 == "" " :NetrwC win#
+ let g:netrw_chgwin= winnr()
+ else " :NetrwC
+ let g:netrw_chgwin= a:1
+ endif
+ elseif v:count > 0 " [count]C
+ let g:netrw_chgwin= v:count
+ else " C
+ let g:netrw_chgwin= winnr()
+ endif
+ echo "editing window now set to window#".g:netrw_chgwin
+ " call Dret("s:NetrwSetChgwin : g:netrw_chgwin=".g:netrw_chgwin)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSetSort: sets up the sort based on the g:netrw_sort_sequence {{{2
+" What this function does is to compute a priority for the patterns
+" in the g:netrw_sort_sequence. It applies a substitute to any
+" "files" that satisfy each pattern, putting the priority / in
+" front. An "*" pattern handles the default priority.
+fun! s:NetrwSetSort()
+ " call Dfunc("SetSort() bannercnt=".w:netrw_bannercnt)
+ let ykeep= @@
+ if w:netrw_liststyle == s:LONGLIST
+ let seqlist = substitute(g:netrw_sort_sequence,'\$','\\%(\t\\|\$\\)','ge')
+ else
+ let seqlist = g:netrw_sort_sequence
+ endif
+ " sanity check -- insure that * appears somewhere
+ if seqlist == ""
+ let seqlist= '*'
+ elseif seqlist !~ '\*'
+ let seqlist= seqlist.',*'
+ endif
+ let priority = 1
+ while seqlist != ""
+ if seqlist =~ ','
+ let seq = substitute(seqlist,',.*$','','e')
+ let seqlist = substitute(seqlist,'^.\{-},\(.*\)$','\1','e')
+ else
+ let seq = seqlist
+ let seqlist = ""
+ endif
+ if priority < 10
+ let spriority= "00".priority.g:netrw_sepchr
+ elseif priority < 100
+ let spriority= "0".priority.g:netrw_sepchr
+ else
+ let spriority= priority.g:netrw_sepchr
+ endif
+ " call Decho("priority=".priority." spriority<".spriority."> seq<".seq."> seqlist<".seqlist.">",'~'.expand("<slnum>"))
+
+ " sanity check
+ if w:netrw_bannercnt > line("$")
+ " apparently no files were left after a Hiding pattern was used
+ " call Dret("SetSort : no files left after hiding")
+ return
+ endif
+ if seq == '*'
+ let starpriority= spriority
+ else
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/'.seq.'/s/^/'.spriority.'/'
+ call histdel("/",-1)
+ " sometimes multiple sorting patterns will match the same file or directory.
+ " The following substitute is intended to remove the excess matches.
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^\d\{3}'.g:netrw_sepchr.'\d\{3}\//s/^\d\{3}'.g:netrw_sepchr.'\(\d\{3}\/\).\@=/\1/e'
+ NetrwKeepj call histdel("/",-1)
+ endif
+ let priority = priority + 1
+ endwhile
+ if exists("starpriority")
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v/^\d\{3}'.g:netrw_sepchr.'/s/^/'.starpriority.'/e'
+ NetrwKeepj call histdel("/",-1)
+ endif
+
+ " Following line associated with priority -- items that satisfy a priority
+ " pattern get prefixed by ###/ which permits easy sorting by priority.
+ " Sometimes files can satisfy multiple priority patterns -- only the latest
+ " priority pattern needs to be retained. So, at this point, these excess
+ " priority prefixes need to be removed, but not directories that happen to
+ " be just digits themselves.
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{3}'.g:netrw_sepchr.'\)\%(\d\{3}'.g:netrw_sepchr.'\)\+\ze./\1/e'
+ NetrwKeepj call histdel("/",-1)
+ let @@= ykeep
+
+ " call Dret("SetSort")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSetTgt: sets the target to the specified choice index {{{2
+" Implements [count]Tb (bookhist<b>)
+" [count]Th (bookhist<h>)
+" See :help netrw-qb for how to make the choice.
+fun! s:NetrwSetTgt(islocal,bookhist,choice)
+ " call Dfunc("s:NetrwSetTgt(islocal=".a:islocal." bookhist<".a:bookhist."> choice#".a:choice.")")
+
+ if a:bookhist == 'b'
+ " supports choosing a bookmark as a target using a qb-generated list
+ let choice= a:choice - 1
+ if exists("g:netrw_bookmarklist[".choice."]")
+ call netrw#MakeTgt(g:netrw_bookmarklist[choice])
+ else
+ echomsg "Sorry, bookmark#".a:choice." doesn't exist!"
+ endif
+
+ elseif a:bookhist == 'h'
+ " supports choosing a history stack entry as a target using a qb-generated list
+ let choice= (a:choice % g:netrw_dirhistmax) + 1
+ if exists("g:netrw_dirhist_".choice)
+ let histentry = g:netrw_dirhist_{choice}
+ call netrw#MakeTgt(histentry)
+ else
+ echomsg "Sorry, history#".a:choice." not available!"
+ endif
+ endif
+
+ " refresh the display
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ endif
+ call s:NetrwRefresh(a:islocal,b:netrw_curdir)
+
+ " call Dret("s:NetrwSetTgt")
+endfun
+
+" =====================================================================
+" s:NetrwSortStyle: change sorting style (name - time - size - exten) and refresh display {{{2
+fun! s:NetrwSortStyle(islocal)
+ NetrwKeepj call s:NetrwSaveWordPosn()
+ let svpos= winsaveview()
+
+ let g:netrw_sort_by= (g:netrw_sort_by =~# '^n')? 'time' : (g:netrw_sort_by =~# '^t')? 'size' : (g:netrw_sort_by =~# '^siz')? 'exten' : 'name'
+ NetrwKeepj norm! 0
+ NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ NetrwKeepj call winrestview(svpos)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwSplit: mode {{{2
+" =0 : net and o
+" =1 : net and t
+" =2 : net and v
+" =3 : local and o
+" =4 : local and t
+" =5 : local and v
+fun! s:NetrwSplit(mode)
+
+ let ykeep= @@
+ call s:SaveWinVars()
+
+ if a:mode == 0
+ " remote and o
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
+ if winsz == 0|let winsz= ""|endif
+ exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
+ let s:didsplit= 1
+ NetrwKeepj call s:RestoreWinVars()
+ NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
+ unlet s:didsplit
+
+ elseif a:mode == 1
+ " remote and t
+ let newdir = s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)
+ tabnew
+ let s:didsplit= 1
+ NetrwKeepj call s:RestoreWinVars()
+ NetrwKeepj call s:NetrwBrowse(0,newdir)
+ unlet s:didsplit
+
+ elseif a:mode == 2
+ " remote and v
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
+ if winsz == 0|let winsz= ""|endif
+ exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v"
+ let s:didsplit= 1
+ NetrwKeepj call s:RestoreWinVars()
+ NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1))
+ unlet s:didsplit
+
+ elseif a:mode == 3
+ " local and o
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize
+ if winsz == 0|let winsz= ""|endif
+ exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s"
+ let s:didsplit= 1
+ NetrwKeepj call s:RestoreWinVars()
+ NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1))
+ unlet s:didsplit
+
+ elseif a:mode == 4
+ " local and t
+ let cursorword = s:NetrwGetWord()
+ let eikeep = &ei
+ let netrw_winnr = winnr()
+ let netrw_line = line(".")
+ let netrw_col = virtcol(".")
+ NetrwKeepj norm! H0
+ let netrw_hline = line(".")
+ setl ei=all
+ exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>"
+ exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>"
+ let &ei = eikeep
+ let netrw_curdir = s:NetrwTreeDir(0)
+ tabnew
+ let b:netrw_curdir = netrw_curdir
+ let s:didsplit = 1
+ NetrwKeepj call s:RestoreWinVars()
+ NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,cursorword,0))
+ if &ft == "netrw"
+ setl ei=all
+ exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>"
+ exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>"
+ let &ei= eikeep
+ endif
+ unlet s:didsplit
+
+ elseif a:mode == 5
+ " local and v
+ let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize
+ if winsz == 0|let winsz= ""|endif
+ exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v"
+ let s:didsplit= 1
+ NetrwKeepj call s:RestoreWinVars()
+ NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1))
+ unlet s:didsplit
+
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"(NetrwSplit) unsupported mode=".a:mode,45)
+ endif
+
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwTgtMenu: {{{2
+fun! s:NetrwTgtMenu()
+ if !exists("s:netrw_menucnt")
+ return
+ endif
+ " call Dfunc("s:NetrwTgtMenu()")
+
+ " the following test assures that gvim is running, has menus available, and has menus enabled.
+ if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
+ if exists("g:NetrwTopLvlMenu")
+ " call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>"))
+ exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Targets'
+ endif
+ if !exists("s:netrw_initbookhist")
+ call s:NetrwBookHistRead()
+ endif
+
+ " try to cull duplicate entries
+ let tgtdict={}
+
+ " target bookmarked places
+ if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0
+ " call Decho("installing bookmarks as easy targets",'~'.expand("<slnum>"))
+ let cnt= 1
+ for bmd in g:netrw_bookmarklist
+ if has_key(tgtdict,bmd)
+ let cnt= cnt + 1
+ continue
+ endif
+ let tgtdict[bmd]= cnt
+ let ebmd= escape(bmd,g:netrw_menu_escape)
+ " show bookmarks for goto menu
+ " call Decho("menu: Targets: ".bmd,'~'.expand("<slnum>"))
+ exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.1.".cnt." ".g:NetrwTopLvlMenu.'Targets.'.ebmd." :call netrw#MakeTgt('".bmd."')\<cr>"
+ let cnt= cnt + 1
+ endfor
+ endif
+
+ " target directory browsing history
+ if exists("g:netrw_dirhistmax") && g:netrw_dirhistmax > 0
+ " call Decho("installing history as easy targets (histmax=".g:netrw_dirhistmax.")",'~'.expand("<slnum>"))
+ let histcnt = 1
+ while histcnt <= g:netrw_dirhistmax
+ let priority = g:netrw_dirhistcnt + histcnt
+ if exists("g:netrw_dirhist_{histcnt}")
+ let histentry = g:netrw_dirhist_{histcnt}
+ if has_key(tgtdict,histentry)
+ let histcnt = histcnt + 1
+ continue
+ endif
+ let tgtdict[histentry] = histcnt
+ let ehistentry = escape(histentry,g:netrw_menu_escape)
+ " call Decho("menu: Targets: ".histentry,'~'.expand("<slnum>"))
+ exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.2.".priority." ".g:NetrwTopLvlMenu.'Targets.'.ehistentry." :call netrw#MakeTgt('".histentry."')\<cr>"
+ endif
+ let histcnt = histcnt + 1
+ endwhile
+ endif
+ endif
+ " call Dret("s:NetrwTgtMenu")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwTreeDir: determine tree directory given current cursor position {{{2
+" (full path directory with trailing slash returned)
+fun! s:NetrwTreeDir(islocal)
+
+ if exists("s:treedir") && exists("s:prevwinopen")
+ " s:NetrwPrevWinOpen opens a "previous" window -- and thus needs to and does call s:NetrwTreeDir early
+ let treedir= s:treedir
+ unlet s:treedir
+ unlet s:prevwinopen
+ return treedir
+ endif
+ if exists("s:prevwinopen")
+ unlet s:prevwinopen
+ endif
+
+ if !exists("b:netrw_curdir") || b:netrw_curdir == ""
+ let b:netrw_curdir= getcwd()
+ endif
+ let treedir = b:netrw_curdir
+ let s:treecurpos= winsaveview()
+
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+
+ " extract tree directory if on a line specifying a subdirectory (ie. ends with "/")
+ let curline= substitute(getline('.'),"\t -->.*$",'','')
+ if curline =~ '/$'
+ let treedir= substitute(getline('.'),'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e')
+ elseif curline =~ '@$'
+ let potentialdir= resolve(s:NetrwTreePath(w:netrw_treetop))
+ else
+ let treedir= ""
+ endif
+
+ " detect user attempting to close treeroot
+ if curline !~ '^'.s:treedepthstring && getline('.') != '..'
+ " now force a refresh
+ sil! NetrwKeepj %d _
+ return b:netrw_curdir
+ endif
+
+ " COMBAK: a symbolic link may point anywhere -- so it will be used to start a new treetop
+ " if a:islocal && curline =~ '@$' && isdirectory(s:NetrwFile(potentialdir))
+ " let newdir = w:netrw_treetop.'/'.potentialdir
+ if a:islocal && curline =~ '@$'
+ if isdirectory(s:NetrwFile(potentialdir))
+ let treedir = potentialdir
+ let w:netrw_treetop = treedir
+ endif
+ else
+ let potentialdir= s:NetrwFile(substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)@$','\1',''))
+ let treedir = s:NetrwTreePath(w:netrw_treetop)
+ endif
+ endif
+
+ " sanity maintenance: keep those //s away...
+ let treedir= substitute(treedir,'//$','/','')
+ return treedir
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwTreeDisplay: recursive tree display {{{2
+fun! s:NetrwTreeDisplay(dir,depth)
+ " ensure that there are no folds
+ setl nofen
+
+ " install ../ and shortdir
+ if a:depth == ""
+ call setline(line("$")+1,'../')
+ endif
+ if a:dir =~ '^\a\{3,}://'
+ if a:dir == w:netrw_treetop
+ let shortdir= a:dir
+ else
+ let shortdir= substitute(a:dir,'^.*/\([^/]\+\)/$','\1/','e')
+ endif
+ call setline(line("$")+1,a:depth.shortdir)
+ else
+ let shortdir= substitute(a:dir,'^.*/','','e')
+ call setline(line("$")+1,a:depth.shortdir.'/')
+ endif
+ " append a / to dir if its missing one
+ let dir= a:dir
+
+ " display subtrees (if any)
+ let depth= s:treedepthstring.a:depth
+
+ " implement g:netrw_hide for tree listings (uses g:netrw_list_hide)
+ if g:netrw_hide == 1
+ " hide given patterns
+ let listhide= split(g:netrw_list_hide,',')
+ for pat in listhide
+ call filter(w:netrw_treedict[dir],'v:val !~ "'.escape(pat,'\\').'"')
+ endfor
+
+ elseif g:netrw_hide == 2
+ " show given patterns (only)
+ let listhide= split(g:netrw_list_hide,',')
+ let entries=[]
+ for entry in w:netrw_treedict[dir]
+ for pat in listhide
+ if entry =~ pat
+ call add(entries,entry)
+ break
+ endif
+ endfor
+ endfor
+ let w:netrw_treedict[dir]= entries
+ endif
+ if depth != ""
+ " always remove "." and ".." entries when there's depth
+ call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\.$"')
+ call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\./$"')
+ call filter(w:netrw_treedict[dir],'v:val !~ "\\.$"')
+ call filter(w:netrw_treedict[dir],'v:val !~ "\\./$"')
+ endif
+
+ for entry in w:netrw_treedict[dir]
+ if dir =~ '/$'
+ let direntry= substitute(dir.entry,'[@/]$','','e')
+ else
+ let direntry= substitute(dir.'/'.entry,'[@/]$','','e')
+ endif
+ if entry =~ '/$' && has_key(w:netrw_treedict,direntry)
+ NetrwKeepj call s:NetrwTreeDisplay(direntry,depth)
+ elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/')
+ NetrwKeepj call s:NetrwTreeDisplay(direntry.'/',depth)
+ elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@')
+ NetrwKeepj call s:NetrwTreeDisplay(direntry.'@',depth)
+ else
+ sil! NetrwKeepj call setline(line("$")+1,depth.entry)
+ endif
+ endfor
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRefreshTreeDict: updates the contents information for a tree (w:netrw_treedict) {{{2
+fun! s:NetrwRefreshTreeDict(dir)
+ if !exists("w:netrw_treedict")
+ return
+ endif
+
+ for entry in w:netrw_treedict[a:dir]
+ let direntry= substitute(a:dir.'/'.entry,'[@/]$','','e')
+
+ if entry =~ '/$' && has_key(w:netrw_treedict,direntry)
+ NetrwKeepj call s:NetrwRefreshTreeDict(direntry)
+ let filelist = s:NetrwLocalListingList(direntry,0)
+ let w:netrw_treedict[direntry] = sort(filelist)
+
+ elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/')
+ NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/')
+ let filelist = s:NetrwLocalListingList(direntry.'/',0)
+ let w:netrw_treedict[direntry] = sort(filelist)
+
+ elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@')
+ NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/')
+ let liststar = s:NetrwGlob(direntry.'/','*',1)
+ let listdotstar= s:NetrwGlob(direntry.'/','.*',1)
+
+ else
+ endif
+ endfor
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwTreeListing: displays tree listing from treetop on down, using NetrwTreeDisplay() {{{2
+" Called by s:PerformListing()
+fun! s:NetrwTreeListing(dirname)
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+
+ " update the treetop
+ if !exists("w:netrw_treetop")
+ let w:netrw_treetop= a:dirname
+ let s:netrw_treetop= w:netrw_treetop
+ " use \V in case the directory contains specials chars like '$' or '~'
+ elseif (w:netrw_treetop =~ ('^'.'\V'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop))
+ \ || a:dirname !~ ('^'.'\V'.w:netrw_treetop)
+ let w:netrw_treetop= a:dirname
+ let s:netrw_treetop= w:netrw_treetop
+ endif
+ if exists("w:netrw_treetop")
+ let s:netrw_treetop= w:netrw_treetop
+ else
+ let w:netrw_treetop= getcwd()
+ let s:netrw_treetop= w:netrw_treetop
+ endif
+
+ if !exists("w:netrw_treedict")
+ " insure that we have a treedict, albeit empty
+ let w:netrw_treedict= {}
+ endif
+
+ " update the dictionary for the current directory
+ exe "sil! NetrwKeepj keepp ".w:netrw_bannercnt.',$g@^\.\.\=/$@d _'
+ let w:netrw_treedict[a:dirname]= getline(w:netrw_bannercnt,line("$"))
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _"
+
+ " if past banner, record word
+ if exists("w:netrw_bannercnt") && line(".") > w:netrw_bannercnt
+ let fname= expand("<cword>")
+ else
+ let fname= ""
+ endif
+
+ " display from treetop on down
+ NetrwKeepj call s:NetrwTreeDisplay(w:netrw_treetop,"")
+
+ " remove any blank line remaining as line#1 (happens in treelisting mode with banner suppressed)
+ while getline(1) =~ '^\s*$' && byte2line(1) > 0
+ 1d
+ endwhile
+
+ exe "setl ".g:netrw_bufsettings
+
+ return
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwTreePath: returns path to current file/directory in tree listing {{{2
+" Normally, treetop is w:netrw_treetop, but a
+" user of the function ( netrw#SetTreetop() )
+" wipes that out prior to calling this function
+fun! s:NetrwTreePath(treetop)
+ " call Dfunc("s:NetrwTreePath(treetop<".a:treetop.">) line#".line(".")."<".getline(".").">")
+ if line(".") < w:netrw_bannercnt + 2
+ let treedir= a:treetop
+ if treedir !~ '/$'
+ let treedir= treedir.'/'
+ endif
+ " call Dret("s:NetrwTreePath ".treedir." : line#".line(".")." ≤ ".(w:netrw_bannercnt+2))
+ return treedir
+ endif
+
+ let svpos = winsaveview()
+ " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e')
+ " call Decho("depth<".depth."> 1st subst",'~'.expand("<slnum>"))
+ let depth = substitute(depth,'^'.s:treedepthstring,'','')
+ " call Decho("depth<".depth."> 2nd subst (first depth removed)",'~'.expand("<slnum>"))
+ let curline= getline('.')
+ " call Decho("curline<".curline.'>','~'.expand("<slnum>"))
+ if curline =~ '/$'
+ " call Decho("extract tree directory from current line",'~'.expand("<slnum>"))
+ let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e')
+ " call Decho("treedir<".treedir.">",'~'.expand("<slnum>"))
+ elseif curline =~ '@\s\+-->'
+ " call Decho("extract tree directory using symbolic link",'~'.expand("<slnum>"))
+ let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e')
+ let treedir= substitute(treedir,'@\s\+-->.*$','','e')
+ " call Decho("treedir<".treedir.">",'~'.expand("<slnum>"))
+ else
+ " call Decho("do not extract tree directory from current line and set treedir to empty",'~'.expand("<slnum>"))
+ let treedir= ""
+ endif
+ " construct treedir by searching backwards at correct depth
+ " call Decho("construct treedir by searching backwards for correct depth",'~'.expand("<slnum>"))
+ " call Decho("initial treedir<".treedir."> depth<".depth.">",'~'.expand("<slnum>"))
+ while depth != "" && search('^'.depth.'[^'.s:treedepthstring.'].\{-}/$','bW')
+ let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e')
+ let treedir= dirname.treedir
+ let depth = substitute(depth,'^'.s:treedepthstring,'','')
+ " call Decho("constructing treedir<".treedir.">: dirname<".dirname."> while depth<".depth.">",'~'.expand("<slnum>"))
+ endwhile
+ " call Decho("treedir#1<".treedir.">",'~'.expand("<slnum>"))
+ if a:treetop =~ '/$'
+ let treedir= a:treetop.treedir
+ else
+ let treedir= a:treetop.'/'.treedir
+ endif
+ " call Decho("treedir#2<".treedir.">",'~'.expand("<slnum>"))
+ let treedir= substitute(treedir,'//$','/','')
+ " call Decho("treedir#3<".treedir.">",'~'.expand("<slnum>"))
+ " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))"
+ call winrestview(svpos)
+ " call Dret("s:NetrwTreePath <".treedir.">")
+ return treedir
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwWideListing: {{{2
+fun! s:NetrwWideListing()
+
+ if w:netrw_liststyle == s:WIDELIST
+ " call Dfunc("NetrwWideListing() w:netrw_liststyle=".w:netrw_liststyle.' fo='.&fo.' l:fo='.&l:fo)
+ " look for longest filename (cpf=characters per filename)
+ " cpf: characters per filename
+ " fpl: filenames per line
+ " fpc: filenames per column
+ setl ma noro
+ let dict={}
+ " save the unnamed register and register 0-9 and a
+ let dict.a=[getreg('a'), getregtype('a')]
+ for i in range(0, 9)
+ let dict[i] = [getreg(i), getregtype(i)]
+ endfor
+ let dict.unnamed = [getreg(''), getregtype('')]
+ " call Decho("setl ma noro",'~'.expand("<slnum>"))
+ let b:netrw_cpf= 0
+ if line("$") >= w:netrw_bannercnt
+ " determine the maximum filename size; use that to set cpf
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif'
+ NetrwKeepj call histdel("/",-1)
+ else
+ " restore stored registers
+ call s:RestoreRegister(dict)
+ " call Dret("NetrwWideListing")
+ return
+ endif
+ " allow for two spaces to separate columns
+ let b:netrw_cpf= b:netrw_cpf + 2
+ " call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf,'~'.expand("<slnum>"))
+
+ " determine qty files per line (fpl)
+ let w:netrw_fpl= winwidth(0)/b:netrw_cpf
+ if w:netrw_fpl <= 0
+ let w:netrw_fpl= 1
+ endif
+ " call Decho("fpl= [winwidth=".winwidth(0)."]/[b:netrw_cpf=".b:netrw_cpf.']='.w:netrw_fpl,'~'.expand("<slnum>"))
+
+ " make wide display
+ " fpc: files per column of wide listing
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^.*$/\=escape(printf("%-'.b:netrw_cpf.'S",submatch(0)),"\\")/'
+ NetrwKeepj call histdel("/",-1)
+ let fpc = (line("$") - w:netrw_bannercnt + w:netrw_fpl)/w:netrw_fpl
+ let newcolstart = w:netrw_bannercnt + fpc
+ let newcolend = newcolstart + fpc - 1
+ " call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>"))
+ if !has('nvim') && has("clipboard") && g:netrw_clipboard
+ " call Decho("(s:NetrwWideListing) save @* and @+",'~'.expand("<slnum>"))
+ sil! let keepregstar = @*
+ sil! let keepregplus = @+
+ endif
+ while line("$") >= newcolstart
+ if newcolend > line("$") | let newcolend= line("$") | endif
+ let newcolqty= newcolend - newcolstart
+ exe newcolstart
+ " COMBAK: both of the visual-mode using lines below are problematic vis-a-vis @*
+ if newcolqty == 0
+ exe "sil! NetrwKeepj norm! 0\<c-v>$h\"ax".w:netrw_bannercnt."G$\"ap"
+ else
+ exe "sil! NetrwKeepj norm! 0\<c-v>".newcolqty.'j$h"ax'.w:netrw_bannercnt.'G$"ap'
+ endif
+ exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _'
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt
+ endwhile
+ if !has('nvim') && has("clipboard")
+ " call Decho("(s:NetrwWideListing) restore @* and @+",'~'.expand("<slnum>"))
+ if @* != keepregstar | sil! let @* = keepregstar | endif
+ if @+ != keepregplus | sil! let @+ = keepregplus | endif
+ endif
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e'
+ NetrwKeepj call histdel("/",-1)
+ exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>"
+ exe 'nno <buffer> <silent> b :call search(''^.\\|\s\s\zs\S'',''bW'')'."\<cr>"
+ " call Decho("NetrwWideListing) setl noma nomod ro",'~'.expand("<slnum>"))
+ exe "setl ".g:netrw_bufsettings
+ call s:RestoreRegister(dict)
+ " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ " call Dret("NetrwWideListing")
+ return
+ else
+ if hasmapto("w","n")
+ sil! nunmap <buffer> w
+ endif
+ if hasmapto("b","n")
+ sil! nunmap <buffer> b
+ endif
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:PerformListing: {{{2
+fun! s:PerformListing(islocal)
+ " call Dfunc("s:PerformListing(islocal=".a:islocal.")")
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
+ " call Decho("settings: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (enter)"." ei<".&ei.">",'~'.expand("<slnum>"))
+ sil! NetrwKeepj %d _
+ " call DechoBuf(bufnr("%"))
+
+ " set up syntax highlighting {{{3
+ " call Decho("--set up syntax highlighting (ie. setl ft=netrw)",'~'.expand("<slnum>"))
+ sil! setl ft=netrw
+
+ NetrwKeepj call s:NetrwOptionsSafe(a:islocal)
+ setl noro ma
+ " call Decho("setl noro ma bh=".&bh,'~'.expand("<slnum>"))
+
+ " if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 " Decho
+ " call Decho("Processing your browsing request...",'~'.expand("<slnum>"))
+ " endif " Decho
+
+ " call Decho('w:netrw_liststyle='.(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
+ " force a refresh for tree listings
+ " call Decho("force refresh for treelisting: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>"))
+ sil! NetrwKeepj %d _
+ endif
+
+ " save current directory on directory history list
+ NetrwKeepj call s:NetrwBookHistHandler(3,b:netrw_curdir)
+
+ " Set up the banner {{{3
+ if g:netrw_banner
+ " call Decho("--set up banner",'~'.expand("<slnum>"))
+ NetrwKeepj call setline(1,'" ============================================================================')
+ if exists("g:netrw_pchk")
+ " this undocumented option allows pchk to run with different versions of netrw without causing spurious
+ " failure detections.
+ NetrwKeepj call setline(2,'" Netrw Directory Listing')
+ else
+ NetrwKeepj call setline(2,'" Netrw Directory Listing (netrw '.g:loaded_netrw.')')
+ endif
+ if exists("g:netrw_pchk")
+ let curdir= substitute(b:netrw_curdir,expand("$HOME"),'~','')
+ else
+ let curdir= b:netrw_curdir
+ endif
+ if exists("g:netrw_bannerbackslash") && g:netrw_bannerbackslash
+ NetrwKeepj call setline(3,'" '.substitute(curdir,'/','\\','g'))
+ else
+ NetrwKeepj call setline(3,'" '.curdir)
+ endif
+ let w:netrw_bannercnt= 3
+ NetrwKeepj exe "sil! NetrwKeepj ".w:netrw_bannercnt
+ else
+ " call Decho("--no banner",'~'.expand("<slnum>"))
+ NetrwKeepj 1
+ let w:netrw_bannercnt= 1
+ endif
+ " call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." win#".winnr(),'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
+
+ " construct sortby string: [name|time|size|exten] [reversed]
+ let sortby= g:netrw_sort_by
+ if g:netrw_sort_direction =~# "^r"
+ let sortby= sortby." reversed"
+ endif
+
+ " Sorted by... {{{3
+ if g:netrw_banner
+ " call Decho("--handle specified sorting: g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>"))
+ if g:netrw_sort_by =~# "^n"
+ " call Decho("directories will be sorted by name",'~'.expand("<slnum>"))
+ " sorted by name (also includes the sorting sequence in the banner)
+ NetrwKeepj put ='\" Sorted by '.sortby
+ NetrwKeepj put ='\" Sort sequence: '.g:netrw_sort_sequence
+ let w:netrw_bannercnt= w:netrw_bannercnt + 2
+ else
+ " call Decho("directories will be sorted by size or time",'~'.expand("<slnum>"))
+ " sorted by time, size, exten
+ NetrwKeepj put ='\" Sorted by '.sortby
+ let w:netrw_bannercnt= w:netrw_bannercnt + 1
+ endif
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt
+ " else " Decho
+ " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+ endif
+
+ " show copy/move target, if any {{{3
+ if g:netrw_banner
+ if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal")
+ " call Decho("--show copy/move target<".s:netrwmftgt.">",'~'.expand("<slnum>"))
+ NetrwKeepj put =''
+ if s:netrwmftgt_islocal
+ sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (local)')
+ else
+ sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (remote)')
+ endif
+ let w:netrw_bannercnt= w:netrw_bannercnt + 1
+ else
+ " call Decho("s:netrwmftgt does not exist, don't make Copy/Move Tgt",'~'.expand("<slnum>"))
+ endif
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt
+ endif
+
+ " Hiding... -or- Showing... {{{3
+ if g:netrw_banner
+ " call Decho("--handle hiding/showing in banner (g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">)",'~'.expand("<slnum>"))
+ if g:netrw_list_hide != "" && g:netrw_hide
+ if g:netrw_hide == 1
+ NetrwKeepj put ='\" Hiding: '.g:netrw_list_hide
+ else
+ NetrwKeepj put ='\" Showing: '.g:netrw_list_hide
+ endif
+ let w:netrw_bannercnt= w:netrw_bannercnt + 1
+ endif
+ exe "NetrwKeepj ".w:netrw_bannercnt
+
+ " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ let quickhelp = g:netrw_quickhelp%len(s:QuickHelp)
+ " call Decho("quickhelp =".quickhelp,'~'.expand("<slnum>"))
+ NetrwKeepj put ='\" Quick Help: <F1>:help '.s:QuickHelp[quickhelp]
+ " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ NetrwKeepj put ='\" =============================================================================='
+ let w:netrw_bannercnt= w:netrw_bannercnt + 2
+ " else " Decho
+ " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+ endif
+
+ " bannercnt should index the line just after the banner
+ if g:netrw_banner
+ let w:netrw_bannercnt= w:netrw_bannercnt + 1
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt
+ " call Decho("--w:netrw_bannercnt=".w:netrw_bannercnt." (should index line just after banner) line($)=".line("$"),'~'.expand("<slnum>"))
+ " else " Decho
+ " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+ endif
+
+ " get list of files
+ " call Decho("--Get list of files - islocal=".a:islocal,'~'.expand("<slnum>"))
+ if a:islocal
+ NetrwKeepj call s:LocalListing()
+ else " remote
+ NetrwKeepj let badresult= s:NetrwRemoteListing()
+ if badresult
+ " call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
+ " call Dret("s:PerformListing : error detected by NetrwRemoteListing")
+ return
+ endif
+ endif
+
+ " manipulate the directory listing (hide, sort) {{{3
+ if !exists("w:netrw_bannercnt")
+ let w:netrw_bannercnt= 0
+ endif
+ " call Decho("--manipulate directory listing (hide, sort)",'~'.expand("<slnum>"))
+ " call Decho("g:netrw_banner=".g:netrw_banner." w:netrw_bannercnt=".w:netrw_bannercnt." (banner complete)",'~'.expand("<slnum>"))
+ " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+
+ if !g:netrw_banner || line("$") >= w:netrw_bannercnt
+ " call Decho("manipulate directory listing (support hide)",'~'.expand("<slnum>"))
+ " call Decho("g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>"))
+ if g:netrw_hide && g:netrw_list_hide != ""
+ NetrwKeepj call s:NetrwListHide()
+ endif
+ if !g:netrw_banner || line("$") >= w:netrw_bannercnt
+ " call Decho("manipulate directory listing (sort) : g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>"))
+
+ if g:netrw_sort_by =~# "^n"
+ " sort by name
+ " call Decho("sort by name",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwSetSort()
+
+ if !g:netrw_banner || w:netrw_bannercnt < line("$")
+ " call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+ if g:netrw_sort_direction =~# 'n'
+ " name: sort by name of file
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
+ else
+ " reverse direction sorting
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options
+ endif
+ endif
+
+ " remove priority pattern prefix
+ " call Decho("remove priority pattern prefix",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e'
+ NetrwKeepj call histdel("/",-1)
+
+ elseif g:netrw_sort_by =~# "^ext"
+ " exten: sort by extension
+ " The histdel(...,-1) calls remove the last search from the search history
+ " call Decho("sort by extension",'~'.expand("<slnum>"))
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/'
+ NetrwKeepj call histdel("/",-1)
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+[./]+s/^/002'.g:netrw_sepchr.'/'
+ NetrwKeepj call histdel("/",-1)
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+['.g:netrw_sepchr.'/]+s/^\(.*\.\)\(.\{-\}\)$/\2'.g:netrw_sepchr.'&/e'
+ NetrwKeepj call histdel("/",-1)
+ if !g:netrw_banner || w:netrw_bannercnt < line("$")
+ " call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+ if g:netrw_sort_direction =~# 'n'
+ " normal direction sorting
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
+ else
+ " reverse direction sorting
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options
+ endif
+ endif
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^.\{-}'.g:netrw_sepchr.'//e'
+ NetrwKeepj call histdel("/",-1)
+
+ elseif a:islocal
+ if !g:netrw_banner || w:netrw_bannercnt < line("$")
+ " call Decho("g:netrw_sort_direction=".g:netrw_sort_direction,'~'.expand("<slnum>"))
+ if g:netrw_sort_direction =~# 'n'
+ " call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort','~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
+ else
+ " call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!','~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options
+ endif
+ " call Decho("remove leading digits/ (sorting) information from listing",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e'
+ NetrwKeepj call histdel("/",-1)
+ endif
+ endif
+
+ elseif g:netrw_sort_direction =~# 'r'
+ " call Decho('(s:PerformListing) reverse the sorted listing','~'.expand("<slnum>"))
+ if !g:netrw_banner || w:netrw_bannercnt < line('$')
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g/^/m '.w:netrw_bannercnt
+ call histdel("/",-1)
+ endif
+ endif
+ endif
+ " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>"))
+
+ " convert to wide/tree listing {{{3
+ " call Decho("--modify display if wide/tree listing style",'~'.expand("<slnum>"))
+ " 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. " (internal#1)",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwWideListing()
+ " 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. " (internal#2)",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwTreeListing(b:netrw_curdir)
+ " 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. " (internal#3)",'~'.expand("<slnum>"))
+
+ " resolve symbolic links if local and (thin or tree)
+ if a:islocal && (w:netrw_liststyle == s:THINLIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST))
+ " call Decho("--resolve symbolic links if local and thin|tree",'~'.expand("<slnum>"))
+ sil! keepp g/@$/call s:ShowLink()
+ endif
+
+ if exists("w:netrw_bannercnt") && (line("$") >= w:netrw_bannercnt || !g:netrw_banner)
+ " place cursor on the top-left corner of the file listing
+ " call Decho("--place cursor on top-left corner of file listing",'~'.expand("<slnum>"))
+ exe 'sil! '.w:netrw_bannercnt
+ sil! NetrwKeepj norm! 0
+ " call Decho(" tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
+ else
+ " call Decho("--did NOT place cursor on top-left corner",'~'.expand("<slnum>"))
+ " call Decho(" w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a'),'~'.expand("<slnum>"))
+ " call Decho(" line($)=".line("$"),'~'.expand("<slnum>"))
+ " call Decho(" g:netrw_banner=".(exists("g:netrw_banner")? g:netrw_banner : 'n/a'),'~'.expand("<slnum>"))
+ endif
+
+ " record previous current directory
+ let w:netrw_prvdir= b:netrw_curdir
+ " call Decho("--record netrw_prvdir<".w:netrw_prvdir.">",'~'.expand("<slnum>"))
+
+ " save certain window-oriented variables into buffer-oriented variables {{{3
+ " call Decho("--save some window-oriented variables into buffer oriented variables",'~'.expand("<slnum>"))
+ " 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. " (internal#4)",'~'.expand("<slnum>"))
+ NetrwKeepj call s:SetBufWinVars()
+ " 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. " (internal#5)",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwOptionsRestore("w:")
+ " 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. " (internal#6)",'~'.expand("<slnum>"))
+
+ " set display to netrw display settings
+ " call Decho("--set display to netrw display settings (".g:netrw_bufsettings.")",'~'.expand("<slnum>"))
+ exe "setl ".g:netrw_bufsettings
+ " 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. " (internal#7)",'~'.expand("<slnum>"))
+ if g:netrw_liststyle == s:LONGLIST
+ " call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>"))
+ exe "setl ts=".(g:netrw_maxfilenamelen+1)
+ endif
+ " call Decho("PerformListing buffer:",'~'.expand("<slnum>"))
+ " call DechoBuf(bufnr("%"))
+
+ if exists("s:treecurpos")
+ " call Decho("s:treecurpos exists; restore posn",'~'.expand("<slnum>"))
+ " 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. " (internal#8)",'~'.expand("<slnum>"))
+ " call Decho("restoring posn to s:treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>"))
+ NetrwKeepj call winrestview(s:treecurpos)
+ unlet s:treecurpos
+ 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. " (return)",'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
+ " call Dret("s:PerformListing : curpos<".string(getpos(".")).">")
+endfun
+
+" ---------------------------------------------------------------------
+" s:SetupNetrwStatusLine: {{{2
+fun! s:SetupNetrwStatusLine(statline)
+ " call Dfunc("SetupNetrwStatusLine(statline<".a:statline.">)")
+
+ if !exists("s:netrw_setup_statline")
+ let s:netrw_setup_statline= 1
+ " call Decho("do first-time status line setup",'~'.expand("<slnum>"))
+
+ if !exists("s:netrw_users_stl")
+ let s:netrw_users_stl= &stl
+ endif
+ if !exists("s:netrw_users_ls")
+ let s:netrw_users_ls= &laststatus
+ endif
+
+ " set up User9 highlighting as needed
+ let dict={}
+ let dict.a=[getreg('a'), getregtype('a')]
+ redir @a
+ try
+ hi User9
+ catch /^Vim\%((\a\{3,})\)\=:E411/
+ if &bg == "dark"
+ hi User9 ctermfg=yellow ctermbg=blue guifg=yellow guibg=blue
+ else
+ hi User9 ctermbg=yellow ctermfg=blue guibg=yellow guifg=blue
+ endif
+ endtry
+ redir END
+ call s:RestoreRegister(dict)
+ endif
+
+ " set up status line (may use User9 highlighting)
+ " insure that windows have a statusline
+ " make sure statusline is displayed
+ let &l:stl=a:statline
+ setl laststatus=2
+ " call Decho("stl=".&stl,'~'.expand("<slnum>"))
+ redraw
+
+ " call Dret("SetupNetrwStatusLine : stl=".&stl)
+endfun
+
+" =========================================
+" Remote Directory Browsing Support: {{{1
+" =========================================
+
+" ---------------------------------------------------------------------
+" s:NetrwRemoteFtpCmd: unfortunately, not all ftp servers honor options for ls {{{2
+" This function assumes that a long listing will be received. Size, time,
+" and reverse sorts will be requested of the server but not otherwise
+" enforced here.
+fun! s:NetrwRemoteFtpCmd(path,listcmd)
+ " call Dfunc("NetrwRemoteFtpCmd(path<".a:path."> listcmd<".a:listcmd.">) w:netrw_method=".(exists("w:netrw_method")? w:netrw_method : (exists("b:netrw_method")? b:netrw_method : "???")))
+ " call Decho("line($)=".line("$")." win#".winnr()." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>"))
+ " sanity check: {{{3
+ if !exists("w:netrw_method")
+ if exists("b:netrw_method")
+ let w:netrw_method= b:netrw_method
+ else
+ call netrw#ErrorMsg(2,"(s:NetrwRemoteFtpCmd) internal netrw error",93)
+ " call Dret("NetrwRemoteFtpCmd")
+ return
+ endif
+ endif
+
+ " WinXX ftp uses unix style input, so set ff to unix " {{{3
+ let ffkeep= &ff
+ setl ma ff=unix noro
+ " call Decho("setl ma ff=unix noro",'~'.expand("<slnum>"))
+
+ " clear off any older non-banner lines " {{{3
+ " note that w:netrw_bannercnt indexes the line after the banner
+ " call Decho('exe sil! NetrwKeepj '.w:netrw_bannercnt.",$d _ (clear off old non-banner lines)",'~'.expand("<slnum>"))
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _"
+
+ ".........................................
+ if w:netrw_method == 2 || w:netrw_method == 5 " {{{3
+ " ftp + <.netrc>: Method #2
+ if a:path != ""
+ NetrwKeepj put ='cd \"'.a:path.'\"'
+ endif
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ NetrwKeepj call setline(line("$")+1,a:listcmd)
+ " exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))'
+ if exists("g:netrw_port") && g:netrw_port != ""
+ " call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1),'~'.expand("<slnum>"))
+ exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)
+ else
+ " call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1),'~'.expand("<slnum>"))
+ exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)
+ endif
+
+ ".........................................
+ elseif w:netrw_method == 3 " {{{3
+ " ftp + machine,id,passwd,filename: Method #3
+ setl ff=unix
+ if exists("g:netrw_port") && g:netrw_port != ""
+ NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port
+ else
+ NetrwKeepj put ='open '.g:netrw_machine
+ endif
+
+ " handle userid and password
+ let host= substitute(g:netrw_machine,'\..*$','','')
+ " call Decho("host<".host.">",'~'.expand("<slnum>"))
+ if exists("s:netrw_hup") && exists("s:netrw_hup[host]")
+ call NetUserPass("ftp:".host)
+ endif
+ if exists("g:netrw_uid") && g:netrw_uid != ""
+ if exists("g:netrw_ftp") && g:netrw_ftp == 1
+ NetrwKeepj put =g:netrw_uid
+ if exists("s:netrw_passwd") && s:netrw_passwd != ""
+ NetrwKeepj put ='\"'.s:netrw_passwd.'\"'
+ endif
+ elseif exists("s:netrw_passwd")
+ NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"'
+ endif
+ endif
+
+ if a:path != ""
+ NetrwKeepj put ='cd \"'.a:path.'\"'
+ endif
+ if exists("g:netrw_ftpextracmd")
+ NetrwKeepj put =g:netrw_ftpextracmd
+ " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>"))
+ endif
+ NetrwKeepj call setline(line("$")+1,a:listcmd)
+
+ " perform ftp:
+ " -i : turns off interactive prompting from ftp
+ " -n unix : DON'T use <.netrc>, even though it exists
+ " -n win32: quit being obnoxious about password
+ if exists("w:netrw_bannercnt")
+ " exe w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))'
+ call s:NetrwExe(s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." ".g:netrw_ftp_options)
+ " else " Decho
+ " call Decho("WARNING: w:netrw_bannercnt doesn't exist!",'~'.expand("<slnum>"))
+ " g/^./call Decho("SKIPPING ftp#".line(".").": ".getline("."),'~'.expand("<slnum>"))
+ endif
+
+ ".........................................
+ elseif w:netrw_method == 9 " {{{3
+ " sftp username@machine: Method #9
+ " s:netrw_sftp_cmd
+ setl ff=unix
+
+ " restore settings
+ let &l:ff= ffkeep
+ " call Dret("NetrwRemoteFtpCmd")
+ return
+
+ ".........................................
+ else " {{{3
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . bufname("%") . ">",23)
+ endif
+
+ " cleanup for Windows " {{{3
+ if has("win32")
+ sil! NetrwKeepj %s/\r$//e
+ NetrwKeepj call histdel("/",-1)
+ endif
+ if a:listcmd == "dir"
+ " infer directory/link based on the file permission string
+ sil! NetrwKeepj g/d\%([-r][-w][-x]\)\{3}/NetrwKeepj s@$@/@e
+ sil! NetrwKeepj g/l\%([-r][-w][-x]\)\{3}/NetrwKeepj s/$/@/e
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST)
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/^\%(\S\+\s\+\)\{8}//e'
+ NetrwKeepj call histdel("/",-1)
+ endif
+ endif
+
+ " ftp's listing doesn't seem to include ./ or ../ " {{{3
+ if !search('^\.\/$\|\s\.\/$','wn')
+ exe 'NetrwKeepj '.w:netrw_bannercnt
+ NetrwKeepj put ='./'
+ endif
+ if !search('^\.\.\/$\|\s\.\.\/$','wn')
+ exe 'NetrwKeepj '.w:netrw_bannercnt
+ NetrwKeepj put ='../'
+ endif
+
+ " restore settings " {{{3
+ let &l:ff= ffkeep
+ " call Dret("NetrwRemoteFtpCmd")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRemoteListing: {{{2
+fun! s:NetrwRemoteListing()
+ " call Dfunc("s:NetrwRemoteListing() b:netrw_curdir<".b:netrw_curdir.">) win#".winnr())
+
+ if !exists("w:netrw_bannercnt") && exists("s:bannercnt")
+ let w:netrw_bannercnt= s:bannercnt
+ endif
+ if !exists("w:netrw_bannercnt") && exists("b:bannercnt")
+ let w:netrw_bannercnt= b:bannercnt
+ endif
+
+ call s:RemotePathAnalysis(b:netrw_curdir)
+
+ " sanity check:
+ if exists("b:netrw_method") && b:netrw_method =~ '[235]'
+ " call Decho("b:netrw_method=".b:netrw_method,'~'.expand("<slnum>"))
+ if !executable("ftp")
+ " call Decho("ftp is not executable",'~'.expand("<slnum>"))
+ if !exists("g:netrw_quiet")
+ call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ftp",18)
+ endif
+ call s:NetrwOptionsRestore("w:")
+ " call Dret("s:NetrwRemoteListing -1")
+ return -1
+ endif
+
+ elseif !exists("g:netrw_list_cmd") || g:netrw_list_cmd == ''
+ " call Decho("g:netrw_list_cmd<",(exists("g:netrw_list_cmd")? 'n/a' : "-empty-").">",'~'.expand("<slnum>"))
+ if !exists("g:netrw_quiet")
+ if g:netrw_list_cmd == ""
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your g:netrw_list_cmd is empty; perhaps ".g:netrw_ssh_cmd." is not executable on your system",47)
+ else
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ".g:netrw_list_cmd,19)
+ endif
+ endif
+
+ NetrwKeepj call s:NetrwOptionsRestore("w:")
+ " call Dret("s:NetrwRemoteListing -1")
+ return -1
+ endif " (remote handling sanity check)
+ " call Decho("passed remote listing sanity checks",'~'.expand("<slnum>"))
+
+ if exists("b:netrw_method")
+ " call Decho("setting w:netrw_method to b:netrw_method<".b:netrw_method.">",'~'.expand("<slnum>"))
+ let w:netrw_method= b:netrw_method
+ endif
+
+ if s:method == "ftp"
+ " use ftp to get remote file listing {{{3
+ " call Decho("use ftp to get remote file listing",'~'.expand("<slnum>"))
+ let s:method = "ftp"
+ let listcmd = g:netrw_ftp_list_cmd
+ if g:netrw_sort_by =~# '^t'
+ let listcmd= g:netrw_ftp_timelist_cmd
+ elseif g:netrw_sort_by =~# '^s'
+ let listcmd= g:netrw_ftp_sizelist_cmd
+ endif
+ " call Decho("listcmd<".listcmd."> (using g:netrw_ftp_list_cmd)",'~'.expand("<slnum>"))
+ call s:NetrwRemoteFtpCmd(s:path,listcmd)
+ " exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("raw listing: ".getline("."),''~''.expand("<slnum>"))'
+
+ " report on missing file or directory messages
+ if search('[Nn]o such file or directory\|Failed to change directory')
+ let mesg= getline(".")
+ if exists("w:netrw_bannercnt")
+ setl ma
+ exe w:netrw_bannercnt.",$d _"
+ setl noma
+ endif
+ NetrwKeepj call s:NetrwOptionsRestore("w:")
+ call netrw#ErrorMsg(s:WARNING,mesg,96)
+ " call Dret("s:NetrwRemoteListing : -1")
+ return -1
+ endif
+
+ if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST)
+ " shorten the listing
+ " call Decho("generate short listing",'~'.expand("<slnum>"))
+ exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt
+
+ " cleanup
+ if g:netrw_ftp_browse_reject != ""
+ exe "sil! keepalt NetrwKeepj g/".g:netrw_ftp_browse_reject."/NetrwKeepj d"
+ NetrwKeepj call histdel("/",-1)
+ endif
+ sil! NetrwKeepj %s/\r$//e
+ NetrwKeepj call histdel("/",-1)
+
+ " if there's no ../ listed, then put ../ in
+ let line1= line(".")
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt
+ let line2= search('\.\.\/\%(\s\|$\)','cnW')
+ " call Decho("search(".'\.\.\/\%(\s\|$\)'."','cnW')=".line2." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>"))
+ if line2 == 0
+ " call Decho("netrw is putting ../ into listing",'~'.expand("<slnum>"))
+ sil! NetrwKeepj put='../'
+ endif
+ exe "sil! NetrwKeepj ".line1
+ sil! NetrwKeepj norm! 0
+
+ " call Decho("line1=".line1." line2=".line2." line(.)=".line("."),'~'.expand("<slnum>"))
+ if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup
+ " call Decho("M$ ftp cleanup",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+//'
+ NetrwKeepj call histdel("/",-1)
+ else " normal ftp cleanup
+ " call Decho("normal ftp cleanup",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2/e'
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*/$#/#e'
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*$#/#e'
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ endif
+ endif
+
+ else
+ " use ssh to get remote file listing {{{3
+ " call Decho("use ssh to get remote file listing: s:path<".s:path.">",'~'.expand("<slnum>"))
+ let listcmd= s:MakeSshCmd(g:netrw_list_cmd)
+ " call Decho("listcmd<".listcmd."> (using g:netrw_list_cmd)",'~'.expand("<slnum>"))
+ if g:netrw_scp_cmd =~ '^pscp'
+ " call Decho("1: exe r! ".s:ShellEscape(listcmd.s:path, 1),'~'.expand("<slnum>"))
+ exe "NetrwKeepj r! ".listcmd.s:ShellEscape(s:path, 1)
+ " remove rubbish and adjust listing format of 'pscp' to 'ssh ls -FLa' like
+ sil! NetrwKeepj g/^Listing directory/NetrwKeepj d
+ sil! NetrwKeepj g/^d[-rwx][-rwx][-rwx]/NetrwKeepj s+$+/+e
+ sil! NetrwKeepj g/^l[-rwx][-rwx][-rwx]/NetrwKeepj s+$+@+e
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ if g:netrw_liststyle != s:LONGLIST
+ sil! NetrwKeepj g/^[dlsp-][-rwx][-rwx][-rwx]/NetrwKeepj s/^.*\s\(\S\+\)$/\1/e
+ NetrwKeepj call histdel("/",-1)
+ endif
+ else
+ if s:path == ""
+ " call Decho("2: exe r! ".listcmd,'~'.expand("<slnum>"))
+ exe "NetrwKeepj keepalt r! ".listcmd
+ else
+ " call Decho("3: exe r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1),'~'.expand("<slnum>"))
+ exe "NetrwKeepj keepalt r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1)
+ " call Decho("listcmd<".listcmd."> path<".s:path.">",'~'.expand("<slnum>"))
+ endif
+ endif
+
+ " cleanup
+ if g:netrw_ssh_browse_reject != ""
+ " call Decho("cleanup: exe sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d",'~'.expand("<slnum>"))
+ exe "sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d"
+ NetrwKeepj call histdel("/",-1)
+ endif
+ endif
+
+ if w:netrw_liststyle == s:LONGLIST
+ " do a long listing; these substitutions need to be done prior to sorting {{{3
+ " call Decho("fix long listing:",'~'.expand("<slnum>"))
+
+ if s:method == "ftp"
+ " cleanup
+ exe "sil! NetrwKeepj ".w:netrw_bannercnt
+ while getline('.') =~# g:netrw_ftp_browse_reject
+ sil! NetrwKeepj d
+ endwhile
+ " if there's no ../ listed, then put ../ in
+ let line1= line(".")
+ sil! NetrwKeepj 1
+ sil! NetrwKeepj call search('^\.\.\/\%(\s\|$\)','W')
+ let line2= line(".")
+ if line2 == 0
+ if b:netrw_curdir != '/'
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt."put='../'"
+ endif
+ endif
+ exe "sil! NetrwKeepj ".line1
+ sil! NetrwKeepj norm! 0
+ endif
+
+ if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup
+ " call Decho("M$ ftp site listing cleanup",'~'.expand("<slnum>"))
+ exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+\)\(\w.*\)$/\2\t\1/'
+ elseif exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$")
+ " call Decho("normal ftp site listing cleanup: bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("<slnum>"))
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/ -> .*$//e'
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2 \t\1/e'
+ exe 'sil NetrwKeepj '.w:netrw_bannercnt
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ NetrwKeepj call histdel("/",-1)
+ endif
+ endif
+
+ " if exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") " Decho
+ " exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("listing: ".getline("."),''~''.expand("<slnum>"))'
+ " endif " Decho
+
+ " call Dret("s:NetrwRemoteListing 0")
+ return 0
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRemoteRm: remove/delete a remote file or directory {{{2
+fun! s:NetrwRemoteRm(usrhost,path) range
+ let svpos= winsaveview()
+
+ let all= 0
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+ " remove all marked files
+ for fname in s:netrwmarkfilelist_{bufnr("%")}
+ let ok= s:NetrwRemoteRmFile(a:path,fname,all)
+ if ok =~# 'q\%[uit]'
+ break
+ elseif ok =~# 'a\%[ll]'
+ let all= 1
+ endif
+ endfor
+ call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir)
+
+ else
+ " remove files specified by range
+
+ " preparation for removing multiple files/directories
+ let keepsol = &l:sol
+ setl nosol
+ let ctr = a:firstline
+
+ " remove multiple files and directories
+ while ctr <= a:lastline
+ exe "NetrwKeepj ".ctr
+ let ok= s:NetrwRemoteRmFile(a:path,s:NetrwGetWord(),all)
+ if ok =~# 'q\%[uit]'
+ break
+ elseif ok =~# 'a\%[ll]'
+ let all= 1
+ endif
+ let ctr= ctr + 1
+ endwhile
+ let &l:sol = keepsol
+ endif
+
+ " refresh the (remote) directory listing
+ NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
+ NetrwKeepj call winrestview(svpos)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRemoteRmFile: {{{2
+fun! s:NetrwRemoteRmFile(path,rmfile,all)
+ " call Dfunc("s:NetrwRemoteRmFile(path<".a:path."> rmfile<".a:rmfile.">) all=".a:all)
+
+ let all= a:all
+ let ok = ""
+
+ if a:rmfile !~ '^"' && (a:rmfile =~ '@$' || a:rmfile !~ '[\/]$')
+ " attempt to remove file
+ " call Decho("attempt to remove file (all=".all.")",'~'.expand("<slnum>"))
+ if !all
+ echohl Statement
+ " call Decho("case all=0:",'~'.expand("<slnum>"))
+ call inputsave()
+ let ok= input("Confirm deletion of file<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
+ call inputrestore()
+ echohl NONE
+ if ok == ""
+ let ok="no"
+ endif
+ let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
+ if ok =~# 'a\%[ll]'
+ let all= 1
+ endif
+ endif
+
+ if all || ok =~# 'y\%[es]' || ok == ""
+ " call Decho("case all=".all." or ok<".ok.">".(exists("w:netrw_method")? ': netrw_method='.w:netrw_method : ""),'~'.expand("<slnum>"))
+ if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
+ " call Decho("case ftp:",'~'.expand("<slnum>"))
+ let path= a:path
+ if path =~ '^\a\{3,}://'
+ let path= substitute(path,'^\a\{3,}://[^/]\+/','','')
+ endif
+ sil! NetrwKeepj .,$d _
+ call s:NetrwRemoteFtpCmd(path,"delete ".'"'.a:rmfile.'"')
+ else
+ " call Decho("case ssh: g:netrw_rm_cmd<".g:netrw_rm_cmd.">",'~'.expand("<slnum>"))
+ let netrw_rm_cmd= s:MakeSshCmd(g:netrw_rm_cmd)
+ " call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>"))
+ if !exists("b:netrw_curdir")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53)
+ let ok="q"
+ else
+ let remotedir= substitute(b:netrw_curdir,'^.\{-}//[^/]\+/\(.*\)$','\1','')
+ " call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>"))
+ " call Decho("remotedir<".remotedir.">",'~'.expand("<slnum>"))
+ " call Decho("rmfile<".a:rmfile.">",'~'.expand("<slnum>"))
+ if remotedir != ""
+ let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(remotedir.a:rmfile))
+ else
+ let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(a:rmfile))
+ endif
+ " call Decho("call system(".netrw_rm_cmd.")",'~'.expand("<slnum>"))
+ let ret= system(netrw_rm_cmd)
+ if v:shell_error != 0
+ if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir
+ call netrw#ErrorMsg(s:ERROR,"remove failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",102)
+ else
+ call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60)
+ endif
+ elseif ret != 0
+ call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60)
+ endif
+ " call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>"))
+ endif
+ endif
+ elseif ok =~# 'q\%[uit]'
+ " call Decho("ok==".ok,'~'.expand("<slnum>"))
+ endif
+
+ else
+ " attempt to remove directory
+ " call Decho("attempt to remove directory",'~'.expand("<slnum>"))
+ if !all
+ call inputsave()
+ let ok= input("Confirm deletion of directory<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
+ call inputrestore()
+ if ok == ""
+ let ok="no"
+ endif
+ let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
+ if ok =~# 'a\%[ll]'
+ let all= 1
+ endif
+ endif
+
+ if all || ok =~# 'y\%[es]' || ok == ""
+ if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
+ NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rmdir ".a:rmfile)
+ else
+ let rmfile = substitute(a:path.a:rmfile,'/$','','')
+ let netrw_rmdir_cmd = s:MakeSshCmd(netrw#WinPath(g:netrw_rmdir_cmd)).' '.s:ShellEscape(netrw#WinPath(rmfile))
+ " call Decho("attempt to remove dir: system(".netrw_rmdir_cmd.")",'~'.expand("<slnum>"))
+ let ret= system(netrw_rmdir_cmd)
+ " call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>"))
+
+ if v:shell_error != 0
+ " call Decho("v:shell_error not 0",'~'.expand("<slnum>"))
+ let netrw_rmf_cmd= s:MakeSshCmd(netrw#WinPath(g:netrw_rmf_cmd)).' '.s:ShellEscape(netrw#WinPath(substitute(rmfile,'[\/]$','','e')))
+ " call Decho("2nd attempt to remove dir: system(".netrw_rmf_cmd.")",'~'.expand("<slnum>"))
+ let ret= system(netrw_rmf_cmd)
+ " call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>"))
+
+ if v:shell_error != 0 && !exists("g:netrw_quiet")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",22)
+ endif
+ endif
+ endif
+
+ elseif ok =~# 'q\%[uit]'
+ " call Decho("ok==".ok,'~'.expand("<slnum>"))
+ endif
+ endif
+
+ " call Dret("s:NetrwRemoteRmFile ".ok)
+ return ok
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRemoteRename: rename a remote file or directory {{{2
+fun! s:NetrwRemoteRename(usrhost,path) range
+
+ " preparation for removing multiple files/directories
+ let svpos = winsaveview()
+ " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
+ let ctr = a:firstline
+ let rename_cmd = s:MakeSshCmd(g:netrw_rename_cmd)
+
+ " rename files given by the markfilelist
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+ for oldname in s:netrwmarkfilelist_{bufnr("%")}
+ if exists("subfrom")
+ let newname= substitute(oldname,subfrom,subto,'')
+ else
+ call inputsave()
+ let newname= input("Moving ".oldname." to : ",oldname)
+ call inputrestore()
+ if newname =~ '^s/'
+ let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','')
+ let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','')
+ let newname = substitute(oldname,subfrom,subto,'')
+ endif
+ endif
+
+ if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
+ NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname)
+ else
+ let oldname= s:ShellEscape(a:path.oldname)
+ let newname= s:ShellEscape(a:path.newname)
+ let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname)
+ endif
+
+ endfor
+ call s:NetrwUnMarkFile(1)
+
+ else
+
+ " attempt to rename files/directories
+ let keepsol= &l:sol
+ setl nosol
+ while ctr <= a:lastline
+ exe "NetrwKeepj ".ctr
+
+ let oldname= s:NetrwGetWord()
+
+ call inputsave()
+ let newname= input("Moving ".oldname." to : ",oldname)
+ call inputrestore()
+
+ if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3)
+ call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname)
+ else
+ let oldname= s:ShellEscape(a:path.oldname)
+ let newname= s:ShellEscape(a:path.newname)
+ let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname)
+ endif
+
+ let ctr= ctr + 1
+ endwhile
+ let &l:sol= keepsol
+ endif
+
+ " refresh the directory
+ NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0))
+ NetrwKeepj call winrestview(svpos)
+endfun
+
+" ==========================================
+" Local Directory Browsing Support: {{{1
+" ==========================================
+
+" ---------------------------------------------------------------------
+" netrw#FileUrlEdit: handles editing file://* files {{{2
+" Should accept: file://localhost/etc/fstab
+" file:///etc/fstab
+" file:///c:/WINDOWS/clock.avi
+" file:///c|/WINDOWS/clock.avi
+" file://localhost/c:/WINDOWS/clock.avi
+" file://localhost/c|/WINDOWS/clock.avi
+" file://c:/foo.txt
+" file:///c:/foo.txt
+" and %XX (where X is [0-9a-fA-F] is converted into a character with the given hexadecimal value
+fun! netrw#FileUrlEdit(fname)
+ " call Dfunc("netrw#FileUrlEdit(fname<".a:fname.">)")
+ let fname = a:fname
+ if fname =~ '^file://localhost/'
+ " call Decho('converting file://localhost/ -to- file:///','~'.expand("<slnum>"))
+ let fname= substitute(fname,'^file://localhost/','file:///','')
+ " call Decho("fname<".fname.">",'~'.expand("<slnum>"))
+ endif
+ if has("win32")
+ if fname =~ '^file:///\=\a[|:]/'
+ " call Decho('converting file:///\a|/ -to- file://\a:/','~'.expand("<slnum>"))
+ let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','')
+ " call Decho("fname<".fname.">",'~'.expand("<slnum>"))
+ endif
+ endif
+ let fname2396 = netrw#RFC2396(fname)
+ let fname2396e= fnameescape(fname2396)
+ let plainfname= substitute(fname2396,'file://\(.*\)','\1',"")
+ if has("win32")
+ " call Decho("windows exception for plainfname",'~'.expand("<slnum>"))
+ if plainfname =~ '^/\+\a:'
+ " call Decho('removing leading "/"s','~'.expand("<slnum>"))
+ let plainfname= substitute(plainfname,'^/\+\(\a:\)','\1','')
+ endif
+ endif
+
+ " call Decho("fname2396<".fname2396.">",'~'.expand("<slnum>"))
+ " call Decho("plainfname<".plainfname.">",'~'.expand("<slnum>"))
+ exe "sil doau BufReadPre ".fname2396e
+ exe 'NetrwKeepj keepalt edit '.plainfname
+ exe 'sil! NetrwKeepj keepalt bdelete '.fnameescape(a:fname)
+
+ " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ " call Dret("netrw#FileUrlEdit")
+ exe "sil doau BufReadPost ".fname2396e
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#LocalBrowseCheck: {{{2
+fun! netrw#LocalBrowseCheck(dirname)
+ " This function is called by netrwPlugin.vim's s:LocalBrowseCheck(), s:NetrwRexplore(),
+ " and by <cr> when atop a listed file/directory (via a buffer-local map)
+ "
+ " unfortunate interaction -- split window debugging can't be used here, must use
+ " D-echoRemOn or D-echoTabOn as the BufEnter event triggers
+ " another call to LocalBrowseCheck() when attempts to write
+ " to the DBG buffer are made.
+ "
+ " The &ft == "netrw" test was installed because the BufEnter event
+ " would hit when re-entering netrw windows, creating unexpected
+ " refreshes (and would do so in the middle of NetrwSaveOptions(), too)
+ " call Dfunc("netrw#LocalBrowseCheck(dirname<".a:dirname.">)")
+ " call Decho("isdir<".a:dirname."> =".isdirectory(s:NetrwFile(a:dirname)).((exists("s:treeforceredraw")? " treeforceredraw" : "")).'~'.expand("<slnum>"))
+ " 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,'~'.expand("<slnum>"))
+ " getting E930: Cannot use :redir inside execute
+ "" call Dredir("ls!","netrw#LocalBrowseCheck")
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Decho("current buffer#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>"))
+
+ let ykeep= @@
+ if isdirectory(s:NetrwFile(a:dirname))
+ " call Decho("is-directory ft<".&ft."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : " doesn't exist")."> dirname<".a:dirname.">"." line($)=".line("$")." ft<".&ft."> g:netrw_fastbrowse=".g:netrw_fastbrowse,'~'.expand("<slnum>"))
+
+ if &ft != "netrw" || (exists("b:netrw_curdir") && b:netrw_curdir != a:dirname) || g:netrw_fastbrowse <= 1
+ " call Decho("case 1 : ft=".&ft,'~'.expand("<slnum>"))
+ " call Decho("s:rexposn_".bufnr("%")."<".bufname("%")."> ".(exists("s:rexposn_".bufnr("%"))? "exists" : "does not exist"),'~'.expand("<slnum>"))
+ sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname)
+
+ elseif &ft == "netrw" && line("$") == 1
+ " call Decho("case 2 (ft≡netrw && line($)≡1)",'~'.expand("<slnum>"))
+ sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname)
+
+ elseif exists("s:treeforceredraw")
+ " call Decho("case 3 (treeforceredraw)",'~'.expand("<slnum>"))
+ unlet s:treeforceredraw
+ sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname)
+ endif
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " call Dret("netrw#LocalBrowseCheck")
+ return
+ endif
+
+ " The following code wipes out currently unused netrw buffers
+ " IF g:netrw_fastbrowse is zero (ie. slow browsing selected)
+ " AND IF the listing style is not a tree listing
+ if exists("g:netrw_fastbrowse") && g:netrw_fastbrowse == 0 && g:netrw_liststyle != s:TREELIST
+ " call Decho("wiping out currently unused netrw buffers",'~'.expand("<slnum>"))
+ let ibuf = 1
+ let buflast = bufnr("$")
+ while ibuf <= buflast
+ if bufwinnr(ibuf) == -1 && isdirectory(s:NetrwFile(bufname(ibuf)))
+ exe "sil! keepj keepalt ".ibuf."bw!"
+ endif
+ let ibuf= ibuf + 1
+ endwhile
+ endif
+ let @@= ykeep
+ " 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,'~'.expand("<slnum>"))
+ " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
+ " not a directory, ignore it
+ " call Dret("netrw#LocalBrowseCheck : not a directory, ignoring it; dirname<".a:dirname.">")
+endfun
+
+" ---------------------------------------------------------------------
+" s:LocalBrowseRefresh: this function is called after a user has {{{2
+" performed any shell command. The idea is to cause all local-browsing
+" buffers to be refreshed after a user has executed some shell command,
+" on the chance that s/he removed/created a file/directory with it.
+fun! s:LocalBrowseRefresh()
+ " determine which buffers currently reside in a tab
+ if !exists("s:netrw_browselist")
+ return
+ endif
+ if !exists("w:netrw_bannercnt")
+ return
+ endif
+ if !empty(getcmdwintype())
+ " cannot move away from cmdline window, see :h E11
+ return
+ endif
+ if exists("s:netrw_events") && s:netrw_events == 1
+ " s:LocalFastBrowser gets called (indirectly) from a
+ let s:netrw_events= 2
+ return
+ endif
+ let itab = 1
+ let buftablist = []
+ let ykeep = @@
+ while itab <= tabpagenr("$")
+ let buftablist = buftablist + tabpagebuflist()
+ let itab = itab + 1
+ sil! tabn
+ endwhile
+ " GO through all buffers on netrw_browselist (ie. just local-netrw buffers):
+ " | refresh any netrw window
+ " | wipe out any non-displaying netrw buffer
+ let curwinid = win_getid(winnr())
+ let ibl = 0
+ for ibuf in s:netrw_browselist
+ if bufwinnr(ibuf) == -1 && index(buftablist,ibuf) == -1
+ " wipe out any non-displaying netrw buffer
+ " (ibuf not shown in a current window AND
+ " ibuf not in any tab)
+ exe "sil! keepj bd ".fnameescape(ibuf)
+ call remove(s:netrw_browselist,ibl)
+ continue
+ elseif index(tabpagebuflist(),ibuf) != -1
+ " refresh any netrw buffer
+ exe bufwinnr(ibuf)."wincmd w"
+ if getline(".") =~# 'Quick Help'
+ " decrement g:netrw_quickhelp to prevent refresh from changing g:netrw_quickhelp
+ " (counteracts s:NetrwBrowseChgDir()'s incrementing)
+ let g:netrw_quickhelp= g:netrw_quickhelp - 1
+ endif
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop)
+ endif
+ NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
+ endif
+ let ibl= ibl + 1
+ endfor
+ call win_gotoid(curwinid)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:LocalFastBrowser: handles setting up/taking down fast browsing for the local browser {{{2
+"
+" g:netrw_ Directory Is
+" fastbrowse Local Remote
+" slow 0 D D D=Deleting a buffer implies it will not be re-used (slow)
+" med 1 D H H=Hiding a buffer implies it may be re-used (fast)
+" fast 2 H H
+"
+" Deleting a buffer means that it will be re-loaded when examined, hence "slow".
+" Hiding a buffer means that it will be re-used when examined, hence "fast".
+" (re-using a buffer may not be as accurate)
+"
+" s:netrw_events : doesn't exist, s:LocalFastBrowser() will install autocmds with medium-speed or fast browsing
+" =1: autocmds installed, but ignore next FocusGained event to avoid initial double-refresh of listing.
+" BufEnter may be first event, then a FocusGained event. Ignore the first FocusGained event.
+" If :Explore used: it sets s:netrw_events to 2, so no FocusGained events are ignored.
+" =2: autocmds installed (doesn't ignore any FocusGained events)
+fun! s:LocalFastBrowser()
+
+ " initialize browselist, a list of buffer numbers that the local browser has used
+ if !exists("s:netrw_browselist")
+ let s:netrw_browselist= []
+ endif
+
+ " append current buffer to fastbrowse list
+ if empty(s:netrw_browselist) || bufnr("%") > s:netrw_browselist[-1]
+ call add(s:netrw_browselist,bufnr("%"))
+ endif
+
+ " enable autocmd events to handle refreshing/removing local browser buffers
+ " If local browse buffer is currently showing: refresh it
+ " If local browse buffer is currently hidden : wipe it
+ " g:netrw_fastbrowse=0 : slow speed, never re-use directory listing
+ " =1 : medium speed, re-use directory listing for remote only
+ " =2 : fast speed, always re-use directory listing when possible
+ if g:netrw_fastbrowse <= 1 && !exists("#ShellCmdPost") && !exists("s:netrw_events")
+ let s:netrw_events= 1
+ augroup AuNetrwEvent
+ au!
+ if has("win32")
+ au ShellCmdPost * call s:LocalBrowseRefresh()
+ else
+ au ShellCmdPost,FocusGained * call s:LocalBrowseRefresh()
+ endif
+ augroup END
+
+ " user must have changed fastbrowse to its fast setting, so remove
+ " the associated autocmd events
+ elseif g:netrw_fastbrowse > 1 && exists("#ShellCmdPost") && exists("s:netrw_events")
+ unlet s:netrw_events
+ augroup AuNetrwEvent
+ au!
+ augroup END
+ augroup! AuNetrwEvent
+ endif
+endfun
+
+fun! s:NetrwLocalListingList(dirname,setmaxfilenamelen)
+ " get the list of files contained in the current directory
+ let dirname = a:dirname
+ let dirnamelen = strlen(dirname)
+ let filelist = s:NetrwGlob(dirname,"*",0)
+ let filelist = filelist + s:NetrwGlob(dirname,".*",0)
+
+ if g:netrw_cygwin == 0 && has("win32")
+ elseif index(filelist,'..') == -1 && dirname !~ '/'
+ " include ../ in the glob() entry if its missing
+ let filelist= filelist+[s:ComposePath(dirname,"../")]
+ endif
+
+ if a:setmaxfilenamelen && get(g:, 'netrw_dynamic_maxfilenamelen', 0)
+ let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")')
+ let g:netrw_maxfilenamelen = max(map(filelistcopy,'len(v:val)')) + 1
+ endif
+
+ let resultfilelist = []
+ for filename in filelist
+
+ if getftype(filename) == "link"
+ " indicate a symbolic link
+ let pfile= filename."@"
+
+ elseif getftype(filename) == "socket"
+ " indicate a socket
+ let pfile= filename."="
+
+ elseif getftype(filename) == "fifo"
+ " indicate a fifo
+ let pfile= filename."|"
+
+ elseif isdirectory(s:NetrwFile(filename))
+ " indicate a directory
+ let pfile= filename."/"
+
+ elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(s:NetrwFile(filename))
+ if has("win32")
+ if filename =~ '\.[eE][xX][eE]$' || filename =~ '\.[cC][oO][mM]$' || filename =~ '\.[bB][aA][tT]$'
+ " indicate an executable
+ let pfile= filename."*"
+ else
+ " normal file
+ let pfile= filename
+ endif
+ elseif executable(filename)
+ " indicate an executable
+ let pfile= filename."*"
+ else
+ " normal file
+ let pfile= filename
+ endif
+
+ else
+ " normal file
+ let pfile= filename
+ endif
+
+ if pfile =~ '//$'
+ let pfile= substitute(pfile,'//$','/','e')
+ endif
+ let pfile= strpart(pfile,dirnamelen)
+ let pfile= substitute(pfile,'^[/\\]','','e')
+
+ if w:netrw_liststyle == s:LONGLIST
+ let longfile = printf("%-".g:netrw_maxfilenamelen."S",pfile)
+ let sz = getfsize(filename)
+ let szlen = 15 - (strdisplaywidth(longfile) - g:netrw_maxfilenamelen)
+ let szlen = (szlen > 0) ? szlen : 0
+
+ if g:netrw_sizestyle =~# "[hH]"
+ let sz= s:NetrwHumanReadable(sz)
+ endif
+ let fsz = printf("%".szlen."S",sz)
+ let pfile= longfile." ".fsz." ".strftime(g:netrw_timefmt,getftime(filename))
+ endif
+
+ if g:netrw_sort_by =~# "^t"
+ " sort by time (handles time up to 1 quintillion seconds, US)
+ " Decorate listing by prepending a timestamp/ . Sorting will then be done based on time.
+ let t = getftime(filename)
+ let ft = printf("%018d",t)
+ let ftpfile= ft.'/'.pfile
+ let resultfilelist += [ftpfile]
+
+ elseif g:netrw_sort_by =~ "^s"
+ " sort by size (handles file sizes up to 1 quintillion bytes, US)
+ let sz = getfsize(filename)
+ let fsz = printf("%018d",sz)
+ let fszpfile= fsz.'/'.pfile
+ let resultfilelist += [fszpfile]
+
+ else
+ " sort by name
+ let resultfilelist += [pfile]
+ endif
+ endfor
+
+ return resultfilelist
+endfun
+
+" ---------------------------------------------------------------------
+" s:LocalListing: does the job of "ls" for local directories {{{2
+fun! s:LocalListing()
+
+ let filelist = s:NetrwLocalListingList(b:netrw_curdir, 1)
+ for filename in filelist
+ sil! NetrwKeepj put =filename
+ endfor
+
+ " cleanup any windows mess at end-of-line
+ sil! NetrwKeepj g/^$/d
+ sil! NetrwKeepj %s/\r$//e
+ call histdel("/",-1)
+ exe "setl ts=".(g:netrw_maxfilenamelen+1)
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwLocalExecute: uses system() to execute command under cursor ("X" command support) {{{2
+fun! s:NetrwLocalExecute(cmd)
+ " call Dfunc("s:NetrwLocalExecute(cmd<".a:cmd.">)")
+ let ykeep= @@
+ " sanity check
+ if !executable(a:cmd)
+ call netrw#ErrorMsg(s:ERROR,"the file<".a:cmd."> is not executable!",89)
+ let @@= ykeep
+ " call Dret("s:NetrwLocalExecute")
+ return
+ endif
+
+ let optargs= input(":!".a:cmd,"","file")
+ " call Decho("optargs<".optargs.">",'~'.expand("<slnum>"))
+ let result= system(a:cmd.optargs)
+ " call Decho("result,'~'.expand("<slnum>"))
+
+ " strip any ansi escape sequences off
+ let result = substitute(result,"\e\\[[0-9;]*m","","g")
+
+ " show user the result(s)
+ echomsg result
+ let @@= ykeep
+
+ " call Dret("s:NetrwLocalExecute")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwLocalRename: rename a local file or directory {{{2
+fun! s:NetrwLocalRename(path) range
+
+ if !exists("w:netrw_bannercnt")
+ let w:netrw_bannercnt= b:netrw_bannercnt
+ endif
+
+ " preparation for removing multiple files/directories
+ let ykeep = @@
+ let ctr = a:firstline
+ let svpos = winsaveview()
+ let all = 0
+
+ " rename files given by the markfilelist
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+ for oldname in s:netrwmarkfilelist_{bufnr("%")}
+ if exists("subfrom")
+ let newname= substitute(oldname,subfrom,subto,'')
+ else
+ call inputsave()
+ let newname= input("Moving ".oldname." to : ",oldname,"file")
+ call inputrestore()
+ if newname =~ ''
+ " two ctrl-x's : ignore all of string preceding the ctrl-x's
+ let newname = substitute(newname,'^.*','','')
+ elseif newname =~ ''
+ " one ctrl-x : ignore portion of string preceding ctrl-x but after last /
+ let newname = substitute(newname,'[^/]*','','')
+ endif
+ if newname =~ '^s/'
+ let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','')
+ let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','')
+ let newname = substitute(oldname,subfrom,subto,'')
+ endif
+ endif
+ if !all && filereadable(newname)
+ call inputsave()
+ let response= input("File<".newname."> already exists; do you want to overwrite it? (y/all/n) ")
+ call inputrestore()
+ if response == "all"
+ let all= 1
+ elseif response != "y" && response != "yes"
+ " refresh the directory
+ NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ let @@= ykeep
+ return
+ endif
+ endif
+ call rename(oldname,newname)
+ endfor
+ call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir)
+
+ else
+
+ " attempt to rename files/directories
+ while ctr <= a:lastline
+ exe "NetrwKeepj ".ctr
+
+ " sanity checks
+ if line(".") < w:netrw_bannercnt
+ let ctr= ctr + 1
+ continue
+ endif
+ let curword= s:NetrwGetWord()
+ if curword == "./" || curword == "../"
+ let ctr= ctr + 1
+ continue
+ endif
+
+ NetrwKeepj norm! 0
+ let oldname= s:ComposePath(a:path,curword)
+
+ call inputsave()
+ let newname= input("Moving ".oldname." to : ",substitute(oldname,'/*$','','e'))
+ call inputrestore()
+
+ call rename(oldname,newname)
+ let ctr= ctr + 1
+ endwhile
+ endif
+
+ " refresh the directory
+ NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwLocalRm: {{{2
+fun! s:NetrwLocalRm(path) range
+ if !exists("w:netrw_bannercnt")
+ let w:netrw_bannercnt= b:netrw_bannercnt
+ endif
+
+ " preparation for removing multiple files/directories
+ let ykeep = @@
+ let ret = 0
+ let all = 0
+ let svpos = winsaveview()
+
+ if exists("s:netrwmarkfilelist_{bufnr('%')}")
+ " remove all marked files
+ for fname in s:netrwmarkfilelist_{bufnr("%")}
+ let ok= s:NetrwLocalRmFile(a:path,fname,all)
+ if ok =~# 'q\%[uit]' || ok == "no"
+ break
+ elseif ok =~# '^a\%[ll]$'
+ let all= 1
+ endif
+ endfor
+ call s:NetrwUnMarkFile(1)
+
+ else
+ " remove (multiple) files and directories
+
+ let keepsol= &l:sol
+ setl nosol
+ let ctr = a:firstline
+ while ctr <= a:lastline
+ exe "NetrwKeepj ".ctr
+
+ " sanity checks
+ if line(".") < w:netrw_bannercnt
+ let ctr= ctr + 1
+ continue
+ endif
+ let curword= s:NetrwGetWord()
+ if curword == "./" || curword == "../"
+ let ctr= ctr + 1
+ continue
+ endif
+ let ok= s:NetrwLocalRmFile(a:path,curword,all)
+ if ok =~# 'q\%[uit]' || ok == "no"
+ break
+ elseif ok =~# '^a\%[ll]$'
+ let all= 1
+ endif
+ let ctr= ctr + 1
+ endwhile
+ let &l:sol= keepsol
+ endif
+
+ " refresh the directory
+ if bufname("%") != "NetrwMessage"
+ NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0))
+ NetrwKeepj call winrestview(svpos)
+ endif
+ let @@= ykeep
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwLocalRmFile: remove file fname given the path {{{2
+" Give confirmation prompt unless all==1
+fun! s:NetrwLocalRmFile(path,fname,all)
+ " call Dfunc("s:NetrwLocalRmFile(path<".a:path."> fname<".a:fname."> all=".a:all)
+
+ let all= a:all
+ let ok = ""
+ NetrwKeepj norm! 0
+ let rmfile= s:NetrwFile(s:ComposePath(a:path,escape(a:fname, '\\')))
+ " call Decho("rmfile<".rmfile.">",'~'.expand("<slnum>"))
+
+ if rmfile !~ '^"' && (rmfile =~ '@$' || rmfile !~ '[\/]$')
+ " attempt to remove file
+ " call Decho("attempt to remove file<".rmfile.">",'~'.expand("<slnum>"))
+ if !all
+ echohl Statement
+ call inputsave()
+ let ok= input("Confirm deletion of file <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
+ call inputrestore()
+ echohl NONE
+ if ok == ""
+ let ok="no"
+ endif
+ " call Decho("response: ok<".ok.">",'~'.expand("<slnum>"))
+ let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
+ " call Decho("response: ok<".ok."> (after sub)",'~'.expand("<slnum>"))
+ if ok =~# '^a\%[ll]$'
+ let all= 1
+ endif
+ endif
+
+ if all || ok =~# '^y\%[es]$' || ok == ""
+ let ret= s:NetrwDelete(rmfile)
+ " call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>"))
+ endif
+
+ else
+ " attempt to remove directory
+ if !all
+ echohl Statement
+ call inputsave()
+ let ok= input("Confirm *recursive* deletion of directory <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ")
+ call inputrestore()
+ let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
+ if ok == ""
+ let ok="no"
+ endif
+ if ok =~# '^a\%[ll]$'
+ let all= 1
+ endif
+ endif
+ let rmfile= substitute(rmfile,'[\/]$','','e')
+
+ if all || ok =~# '^y\%[es]$' || ok == ""
+ if delete(rmfile,"rf")
+ call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103)
+ endif
+ endif
+ endif
+
+ " call Dret("s:NetrwLocalRmFile ".ok)
+ return ok
+endfun
+
+" =====================================================================
+" Support Functions: {{{1
+
+" ---------------------------------------------------------------------
+" netrw#Access: intended to provide access to variable values for netrw's test suite {{{2
+" 0: marked file list of current buffer
+" 1: marked file target
+fun! netrw#Access(ilist)
+ if a:ilist == 0
+ if exists("s:netrwmarkfilelist_".bufnr('%'))
+ return s:netrwmarkfilelist_{bufnr('%')}
+ else
+ return "no-list-buf#".bufnr('%')
+ endif
+ elseif a:ilist == 1
+ return s:netrwmftgt
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Call: allows user-specified mappings to call internal netrw functions {{{2
+fun! netrw#Call(funcname,...)
+ return call("s:".a:funcname,a:000)
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2
+" I expect this function to be used in
+" :PChkAssert netrw#Expose("netrwmarkfilelist")
+" for example.
+fun! netrw#Expose(varname)
+ " call Dfunc("netrw#Expose(varname<".a:varname.">)")
+ if exists("s:".a:varname)
+ exe "let retval= s:".a:varname
+ " call Decho("retval=".retval,'~'.expand("<slnum>"))
+ if exists("g:netrw_pchk")
+ " call Decho("type(g:netrw_pchk=".g:netrw_pchk.")=".type(retval),'~'.expand("<slnum>"))
+ if type(retval) == 3
+ let retval = copy(retval)
+ let i = 0
+ while i < len(retval)
+ let retval[i]= substitute(retval[i],expand("$HOME"),'~','')
+ let i = i + 1
+ endwhile
+ endif
+ " call Dret("netrw#Expose ".string(retval)),'~'.expand("<slnum>"))
+ return string(retval)
+ else
+ " call Decho("g:netrw_pchk doesn't exist",'~'.expand("<slnum>"))
+ endif
+ else
+ " call Decho("s:".a:varname." doesn't exist",'~'.expand("<slnum>"))
+ let retval= "n/a"
+ endif
+
+ " call Dret("netrw#Expose ".string(retval))
+ return retval
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#Modify: allows UserMaps to set (modify) script-local variables {{{2
+fun! netrw#Modify(varname,newvalue)
+ " call Dfunc("netrw#Modify(varname<".a:varname.">,newvalue<".string(a:newvalue).">)")
+ exe "let s:".a:varname."= ".string(a:newvalue)
+ " call Dret("netrw#Modify")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#RFC2396: converts %xx into characters {{{2
+fun! netrw#RFC2396(fname)
+ " call Dfunc("netrw#RFC2396(fname<".a:fname.">)")
+ let fname = escape(substitute(a:fname,'%\(\x\x\)','\=printf("%c","0x".submatch(1))','ge')," \t")
+ " call Dret("netrw#RFC2396 ".fname)
+ return fname
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#UserMaps: supports user-specified maps {{{2
+" see :help function()
+"
+" g:Netrw_UserMaps is a List with members such as:
+" [[keymap sequence, function reference],...]
+"
+" The referenced function may return a string,
+" refresh : refresh the display
+" -other- : this string will be executed
+" or it may return a List of strings.
+"
+" Each keymap-sequence will be set up with a nnoremap
+" to invoke netrw#UserMaps(a:islocal).
+" Related functions:
+" netrw#Expose(varname) -- see s:varname variables
+" netrw#Modify(varname,newvalue) -- modify value of s:varname variable
+" netrw#Call(funcname,...) -- call internal netrw function with optional arguments
+fun! netrw#UserMaps(islocal)
+ " call Dfunc("netrw#UserMaps(islocal=".a:islocal.")")
+ " call Decho("g:Netrw_UserMaps ".(exists("g:Netrw_UserMaps")? "exists" : "does NOT exist"),'~'.expand("<slnum>"))
+
+ " set up usermaplist
+ if exists("g:Netrw_UserMaps") && type(g:Netrw_UserMaps) == 3
+ " call Decho("g:Netrw_UserMaps has type 3<List>",'~'.expand("<slnum>"))
+ for umap in g:Netrw_UserMaps
+ " call Decho("type(umap[0]<".string(umap[0]).">)=".type(umap[0])." (should be 1=string)",'~'.expand("<slnum>"))
+ " call Decho("type(umap[1])=".type(umap[1])." (should be 1=string)",'~'.expand("<slnum>"))
+ " if umap[0] is a string and umap[1] is a string holding a function name
+ if type(umap[0]) == 1 && type(umap[1]) == 1
+ " call Decho("nno <buffer> <silent> ".umap[0]." :call s:UserMaps(".a:islocal.",".string(umap[1]).")<cr>",'~'.expand("<slnum>"))
+ exe "nno <buffer> <silent> ".umap[0]." :call <SID>UserMaps(".a:islocal.",'".umap[1]."')<cr>"
+ else
+ call netrw#ErrorMsg(s:WARNING,"ignoring usermap <".string(umap[0])."> -- not a [string,funcref] entry",99)
+ endif
+ endfor
+ endif
+ " call Dret("netrw#UserMaps")
+endfun
+
+" ---------------------------------------------------------------------
+" netrw#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{2
+fun! netrw#WinPath(path)
+ " call Dfunc("netrw#WinPath(path<".a:path.">)")
+ if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && has("win32")
+ " remove cygdrive prefix, if present
+ let path = substitute(a:path,g:netrw_cygdrive.'/\(.\)','\1:','')
+ " remove trailing slash (Win95)
+ let path = substitute(path, '\(\\\|/\)$', '', 'g')
+ " remove escaped spaces
+ let path = substitute(path, '\ ', ' ', 'g')
+ " convert slashes to backslashes
+ let path = substitute(path, '/', '\', 'g')
+ else
+ let path= a:path
+ endif
+ " call Dret("netrw#WinPath <".path.">")
+ return path
+endfun
+
+" ---------------------------------------------------------------------
+" s:StripTrailingSlash: removes trailing slashes from a path {{{2
+fun! s:StripTrailingSlash(path)
+ " remove trailing slash
+ return substitute(a:path, '[/\\]$', '', 'g')
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBadd: adds marked files to buffer list or vice versa {{{2
+" cb : bl2mf=0 add marked files to buffer list
+" cB : bl2mf=1 use bufferlist to mark files
+" (mnemonic: cb = copy (marked files) to buffer list)
+fun! s:NetrwBadd(islocal,bl2mf)
+ " " call Dfunc("s:NetrwBadd(islocal=".a:islocal." mf2bl=".mf2bl.")")
+ if a:bl2mf
+ " cB: add buffer list to marked files
+ redir => bufl
+ ls
+ redir END
+ let bufl = map(split(bufl,"\n"),'substitute(v:val,''^.\{-}"\(.*\)".\{-}$'',''\1'','''')')
+ for fname in bufl
+ call s:NetrwMarkFile(a:islocal,fname)
+ endfor
+ else
+ " cb: add marked files to buffer list
+ for fname in s:netrwmarkfilelist_{bufnr("%")}
+ " " call Decho("badd ".fname,'~'.expand("<slnum>"))
+ exe "badd ".fnameescape(fname)
+ endfor
+ let curbufnr = bufnr("%")
+ let curdir = s:NetrwGetCurdir(a:islocal)
+ call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer
+ endif
+ " call Dret("s:NetrwBadd")
+endfun
+
+" ---------------------------------------------------------------------
+" s:ComposePath: Appends a new part to a path taking different systems into consideration {{{2
+fun! s:ComposePath(base,subdir)
+ " call Dfunc("s:ComposePath(base<".a:base."> subdir<".a:subdir.">)")
+
+ if has("amiga")
+ " call Decho("amiga",'~'.expand("<slnum>"))
+ let ec = a:base[s:Strlen(a:base)-1]
+ if ec != '/' && ec != ':'
+ let ret = a:base."/" . a:subdir
+ else
+ let ret = a:base.a:subdir
+ endif
+
+ " COMBAK: test on windows with changing to root directory: :e C:/
+ elseif a:subdir =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32")
+ " call Decho("windows",'~'.expand("<slnum>"))
+ let ret= a:subdir
+
+ elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32")
+ " call Decho("windows",'~'.expand("<slnum>"))
+ if a:base =~ '[/\\]$'
+ let ret= a:base.a:subdir
+ else
+ let ret= a:base.'/'.a:subdir
+ endif
+
+ elseif a:base =~ '^\a\{3,}://'
+ " call Decho("remote linux/macos",'~'.expand("<slnum>"))
+ let urlbase = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\1','')
+ let curpath = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\2','')
+ if a:subdir == '../'
+ if curpath =~ '[^/]/[^/]\+/$'
+ let curpath= substitute(curpath,'[^/]\+/$','','')
+ else
+ let curpath=""
+ endif
+ let ret= urlbase.curpath
+ else
+ let ret= urlbase.curpath.a:subdir
+ endif
+ " call Decho("urlbase<".urlbase.">",'~'.expand("<slnum>"))
+ " call Decho("curpath<".curpath.">",'~'.expand("<slnum>"))
+ " call Decho("ret<".ret.">",'~'.expand("<slnum>"))
+
+ else
+ " call Decho("local linux/macos",'~'.expand("<slnum>"))
+ let ret = substitute(a:base."/".a:subdir,"//","/","g")
+ if a:base =~ '^//'
+ " keeping initial '//' for the benefit of network share listing support
+ let ret= '/'.ret
+ endif
+ let ret= simplify(ret)
+ endif
+
+ " call Dret("s:ComposePath ".ret)
+ return ret
+endfun
+
+" ---------------------------------------------------------------------
+" s:DeleteBookmark: deletes a file/directory from Netrw's bookmark system {{{2
+" Related Functions: s:MakeBookmark() s:NetrwBookHistHandler() s:NetrwBookmark()
+fun! s:DeleteBookmark(fname)
+ " call Dfunc("s:DeleteBookmark(fname<".a:fname.">)")
+ call s:MergeBookmarks()
+
+ if exists("g:netrw_bookmarklist")
+ let indx= index(g:netrw_bookmarklist,a:fname)
+ if indx == -1
+ let indx= 0
+ while indx < len(g:netrw_bookmarklist)
+ if g:netrw_bookmarklist[indx] =~ a:fname
+ call remove(g:netrw_bookmarklist,indx)
+ let indx= indx - 1
+ endif
+ let indx= indx + 1
+ endwhile
+ else
+ " remove exact match
+ call remove(g:netrw_bookmarklist,indx)
+ endif
+ endif
+
+ " call Dret("s:DeleteBookmark")
+endfun
+
+" ---------------------------------------------------------------------
+" s:FileReadable: o/s independent filereadable {{{2
+fun! s:FileReadable(fname)
+ " call Dfunc("s:FileReadable(fname<".a:fname.">)")
+
+ if g:netrw_cygwin
+ let ret= filereadable(s:NetrwFile(substitute(a:fname,g:netrw_cygdrive.'/\(.\)','\1:/','')))
+ else
+ let ret= filereadable(s:NetrwFile(a:fname))
+ endif
+
+ " call Dret("s:FileReadable ".ret)
+ return ret
+endfun
+
+" ---------------------------------------------------------------------
+" s:GetTempfile: gets a tempname that'll work for various o/s's {{{2
+" Places correct suffix on end of temporary filename,
+" using the suffix provided with fname
+fun! s:GetTempfile(fname)
+ " call Dfunc("s:GetTempfile(fname<".a:fname.">)")
+
+ if !exists("b:netrw_tmpfile")
+ " get a brand new temporary filename
+ let tmpfile= tempname()
+ " call Decho("tmpfile<".tmpfile."> : from tempname()",'~'.expand("<slnum>"))
+
+ let tmpfile= substitute(tmpfile,'\','/','ge')
+ " call Decho("tmpfile<".tmpfile."> : chgd any \\ -> /",'~'.expand("<slnum>"))
+
+ " sanity check -- does the temporary file's directory exist?
+ if !isdirectory(s:NetrwFile(substitute(tmpfile,'[^/]\+$','','e')))
+ " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your <".substitute(tmpfile,'[^/]\+$','','e')."> directory is missing!",2)
+ " call Dret("s:GetTempfile getcwd<".getcwd().">")
+ return ""
+ endif
+
+ " let netrw#NetSource() know about the tmpfile
+ let s:netrw_tmpfile= tmpfile " used by netrw#NetSource() and netrw#BrowseX()
+ " call Decho("tmpfile<".tmpfile."> s:netrw_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>"))
+
+ " o/s dependencies
+ if g:netrw_cygwin != 0
+ let tmpfile = substitute(tmpfile,'^\(\a\):',g:netrw_cygdrive.'/\1','e')
+ elseif has("win32")
+ if !exists("+shellslash") || !&ssl
+ let tmpfile = substitute(tmpfile,'/','\','g')
+ endif
+ else
+ let tmpfile = tmpfile
+ endif
+ let b:netrw_tmpfile= tmpfile
+ " call Decho("o/s dependent fixed tempname<".tmpfile.">",'~'.expand("<slnum>"))
+ else
+ " re-use temporary filename
+ let tmpfile= b:netrw_tmpfile
+ " call Decho("tmpfile<".tmpfile."> re-using",'~'.expand("<slnum>"))
+ endif
+
+ " use fname's suffix for the temporary file
+ if a:fname != ""
+ if a:fname =~ '\.[^./]\+$'
+ " call Decho("using fname<".a:fname.">'s suffix",'~'.expand("<slnum>"))
+ if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$'
+ let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
+ elseif a:fname =~ '.txz$'
+ let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
+ else
+ let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e')
+ endif
+ " call Decho("suffix<".suffix.">",'~'.expand("<slnum>"))
+ let tmpfile= substitute(tmpfile,'\.tmp$','','e')
+ " call Decho("chgd tmpfile<".tmpfile."> (removed any .tmp suffix)",'~'.expand("<slnum>"))
+ let tmpfile .= suffix
+ " call Decho("chgd tmpfile<".tmpfile."> (added ".suffix." suffix) netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>"))
+ let s:netrw_tmpfile= tmpfile " supports netrw#NetSource()
+ endif
+ endif
+
+ " 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:GetTempfile <".tmpfile.">")
+ return tmpfile
+endfun
+
+" ---------------------------------------------------------------------
+" s:MakeSshCmd: transforms input command using USEPORT HOSTNAME into {{{2
+" a correct command for use with a system() call
+fun! s:MakeSshCmd(sshcmd)
+ " call Dfunc("s:MakeSshCmd(sshcmd<".a:sshcmd.">) user<".s:user."> machine<".s:machine.">")
+ if s:user == ""
+ let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:machine,'')
+ else
+ let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:user."@".s:machine,'')
+ endif
+ if exists("g:netrw_port") && g:netrw_port != ""
+ let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'')
+ elseif exists("s:port") && s:port != ""
+ let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'')
+ else
+ let sshcmd= substitute(sshcmd,"USEPORT ",'','')
+ endif
+ " call Dret("s:MakeSshCmd <".sshcmd.">")
+ return sshcmd
+endfun
+
+" ---------------------------------------------------------------------
+" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2
+fun! s:MakeBookmark(fname)
+ " call Dfunc("s:MakeBookmark(fname<".a:fname.">)")
+
+ if !exists("g:netrw_bookmarklist")
+ let g:netrw_bookmarklist= []
+ endif
+
+ if index(g:netrw_bookmarklist,a:fname) == -1
+ " curdir not currently in g:netrw_bookmarklist, so include it
+ if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$'
+ call add(g:netrw_bookmarklist,a:fname.'/')
+ elseif a:fname !~ '/'
+ call add(g:netrw_bookmarklist,getcwd()."/".a:fname)
+ else
+ call add(g:netrw_bookmarklist,a:fname)
+ endif
+ call sort(g:netrw_bookmarklist)
+ endif
+
+ " call Dret("s:MakeBookmark")
+endfun
+
+" ---------------------------------------------------------------------
+" s:MergeBookmarks: merge current bookmarks with saved bookmarks {{{2
+fun! s:MergeBookmarks()
+ " call Dfunc("s:MergeBookmarks() : merge current bookmarks into .netrwbook")
+ " get bookmarks from .netrwbook file
+ let savefile= s:NetrwHome()."/.netrwbook"
+ if filereadable(s:NetrwFile(savefile))
+ " call Decho("merge bookmarks (active and file)",'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwBookHistSave()
+ " call Decho("bookmark delete savefile<".savefile.">",'~'.expand("<slnum>"))
+ NetrwKeepj call delete(savefile)
+ endif
+ " call Dret("s:MergeBookmarks")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBMShow: {{{2
+fun! s:NetrwBMShow()
+ " call Dfunc("s:NetrwBMShow()")
+ redir => bmshowraw
+ menu
+ redir END
+ let bmshowlist = split(bmshowraw,'\n')
+ if bmshowlist != []
+ let bmshowfuncs= filter(bmshowlist,'v:val =~# "<SNR>\\d\\+_BMShow()"')
+ if bmshowfuncs != []
+ let bmshowfunc = substitute(bmshowfuncs[0],'^.*:\(call.*BMShow()\).*$','\1','')
+ if bmshowfunc =~# '^call.*BMShow()'
+ exe "sil! NetrwKeepj ".bmshowfunc
+ endif
+ endif
+ endif
+ " call Dret("s:NetrwBMShow : bmshowfunc<".(exists("bmshowfunc")? bmshowfunc : 'n/a').">")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwCursor: responsible for setting cursorline/cursorcolumn based upon g:netrw_cursor {{{2
+fun! s:NetrwCursor(editfile)
+ if !exists("w:netrw_liststyle")
+ let w:netrw_liststyle= g:netrw_liststyle
+ endif
+ " call Dfunc("s:NetrwCursor() ft<".&ft."> liststyle=".w:netrw_liststyle." g:netrw_cursor=".g:netrw_cursor." s:netrw_usercuc=".s:netrw_usercuc." s:netrw_usercul=".s:netrw_usercul)
+
+ " call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
+
+ if &ft != "netrw"
+ " if the current window isn't a netrw directory listing window, then use user cursorline/column
+ " settings. Affects when netrw is used to read/write a file using scp/ftp/etc.
+ " call Decho("case ft!=netrw: use user cul,cuc",'~'.expand("<slnum>"))
+
+ elseif g:netrw_cursor == 8
+ if w:netrw_liststyle == s:WIDELIST
+ setl cursorline
+ setl cursorcolumn
+ else
+ setl cursorline
+ endif
+ elseif g:netrw_cursor == 7
+ setl cursorline
+ elseif g:netrw_cursor == 6
+ if w:netrw_liststyle == s:WIDELIST
+ setl cursorline
+ endif
+ elseif g:netrw_cursor == 4
+ " all styles: cursorline, cursorcolumn
+ " call Decho("case g:netrw_cursor==4: setl cul cuc",'~'.expand("<slnum>"))
+ setl cursorline
+ setl cursorcolumn
+
+ elseif g:netrw_cursor == 3
+ " thin-long-tree: cursorline, user's cursorcolumn
+ " wide : cursorline, cursorcolumn
+ if w:netrw_liststyle == s:WIDELIST
+ " call Decho("case g:netrw_cursor==3 and wide: setl cul cuc",'~'.expand("<slnum>"))
+ setl cursorline
+ setl cursorcolumn
+ else
+ " call Decho("case g:netrw_cursor==3 and not wide: setl cul (use user's cuc)",'~'.expand("<slnum>"))
+ setl cursorline
+ endif
+
+ elseif g:netrw_cursor == 2
+ " thin-long-tree: cursorline, user's cursorcolumn
+ " wide : cursorline, user's cursorcolumn
+ " call Decho("case g:netrw_cursor==2: setl cuc (use user's cul)",'~'.expand("<slnum>"))
+ setl cursorline
+
+ elseif g:netrw_cursor == 1
+ " thin-long-tree: user's cursorline, user's cursorcolumn
+ " wide : cursorline, user's cursorcolumn
+ if w:netrw_liststyle == s:WIDELIST
+ " call Decho("case g:netrw_cursor==2 and wide: setl cul (use user's cuc)",'~'.expand("<slnum>"))
+ setl cursorline
+ else
+ " call Decho("case g:netrw_cursor==2 and not wide: (use user's cul,cuc)",'~'.expand("<slnum>"))
+ endif
+
+ else
+ " all styles: user's cursorline, user's cursorcolumn
+ " call Decho("default: (use user's cul,cuc)",'~'.expand("<slnum>"))
+ let &l:cursorline = s:netrw_usercul
+ let &l:cursorcolumn = s:netrw_usercuc
+ endif
+
+ " call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
+ " call Dret("s:NetrwCursor : l:cursorline=".&l:cursorline." l:cursorcolumn=".&l:cursorcolumn)
+endfun
+
+" ---------------------------------------------------------------------
+" s:RestoreCursorline: restores cursorline/cursorcolumn to original user settings {{{2
+fun! s:RestoreCursorline()
+ " call Dfunc("s:RestoreCursorline() currently, cul=".&l:cursorline." cuc=".&l:cursorcolumn." win#".winnr()." buf#".bufnr("%"))
+ if exists("s:netrw_usercul")
+ let &l:cursorline = s:netrw_usercul
+ endif
+ if exists("s:netrw_usercuc")
+ let &l:cursorcolumn = s:netrw_usercuc
+ endif
+ " call Decho("(s:RestoreCursorline) COMBAK: cuc=".&l:cuc." cul=".&l:cul)
+ " call Dret("s:RestoreCursorline : restored cul=".&l:cursorline." cuc=".&l:cursorcolumn)
+endfun
+
+" s:RestoreRegister: restores all registers given in the dict {{{2
+fun! s:RestoreRegister(dict)
+ for [key, val] in items(a:dict)
+ if key == 'unnamed'
+ let key = ''
+ endif
+ call setreg(key, val[0], val[1])
+ endfor
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwDelete: Deletes a file. {{{2
+" Uses Steve Hall's idea to insure that Windows paths stay
+" acceptable. No effect on Unix paths.
+" Examples of use: let result= s:NetrwDelete(path)
+fun! s:NetrwDelete(path)
+ " call Dfunc("s:NetrwDelete(path<".a:path.">)")
+
+ let path = netrw#WinPath(a:path)
+ if !g:netrw_cygwin && has("win32")
+ if exists("+shellslash")
+ let sskeep= &shellslash
+ setl noshellslash
+ let result = delete(path)
+ let &shellslash = sskeep
+ else
+ " call Decho("exe let result= ".a:cmd."('".path."')",'~'.expand("<slnum>"))
+ let result= delete(path)
+ endif
+ else
+ " call Decho("let result= delete(".path.")",'~'.expand("<slnum>"))
+ let result= delete(path)
+ endif
+ if result < 0
+ NetrwKeepj call netrw#ErrorMsg(s:WARNING,"delete(".path.") failed!",71)
+ endif
+
+ " call Dret("s:NetrwDelete ".result)
+ return result
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwBufRemover: removes a buffer that: {{{2s
+" has buffer-id > 1
+" is unlisted
+" is unnamed
+" does not appear in any window
+fun! s:NetrwBufRemover(bufid)
+ " call Dfunc("s:NetrwBufRemover(".a:bufid.")")
+ " call Decho("buf#".a:bufid." ".((a:bufid > 1)? ">" : "≯")." must be >1 for removal","~".expand("<slnum>"))
+ " call Decho("buf#".a:bufid." is ".(buflisted(a:bufid)? "listed" : "unlisted"),"~".expand("<slnum>"))
+ " call Decho("buf#".a:bufid." has name <".bufname(a:bufid).">","~".expand("<slnum>"))
+ " call Decho("buf#".a:bufid." has winid#".bufwinid(a:bufid),"~".expand("<slnum>"))
+
+ if a:bufid > 1 && !buflisted(a:bufid) && bufloaded(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1
+ " call Decho("(s:NetrwBufRemover) removing buffer#".a:bufid,"~".expand("<slnum>"))
+ exe "sil! bd! ".a:bufid
+ endif
+
+ " call Dret("s:NetrwBufRemover")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwEnew: opens a new buffer, passes netrw buffer variables through {{{2
+fun! s:NetrwEnew(...)
+ " call Dfunc("s:NetrwEnew() a:0=".a:0." win#".winnr()." winnr($)=".winnr("$")." bufnr($)=".bufnr("$")." expand(%)<".expand("%").">")
+ " call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
+
+ " Clean out the last buffer:
+ " Check if the last buffer has # > 1, is unlisted, is unnamed, and does not appear in a window
+ " If so, delete it.
+ call s:NetrwBufRemover(bufnr("$"))
+
+ " grab a function-local-variable copy of buffer variables
+ " call Decho("make function-local copy of netrw variables",'~'.expand("<slnum>"))
+ if exists("b:netrw_bannercnt") |let netrw_bannercnt = b:netrw_bannercnt |endif
+ if exists("b:netrw_browser_active") |let netrw_browser_active = b:netrw_browser_active |endif
+ if exists("b:netrw_cpf") |let netrw_cpf = b:netrw_cpf |endif
+ if exists("b:netrw_curdir") |let netrw_curdir = b:netrw_curdir |endif
+ if exists("b:netrw_explore_bufnr") |let netrw_explore_bufnr = b:netrw_explore_bufnr |endif
+ if exists("b:netrw_explore_indx") |let netrw_explore_indx = b:netrw_explore_indx |endif
+ if exists("b:netrw_explore_line") |let netrw_explore_line = b:netrw_explore_line |endif
+ if exists("b:netrw_explore_list") |let netrw_explore_list = b:netrw_explore_list |endif
+ if exists("b:netrw_explore_listlen")|let netrw_explore_listlen = b:netrw_explore_listlen|endif
+ if exists("b:netrw_explore_mtchcnt")|let netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif
+ if exists("b:netrw_fname") |let netrw_fname = b:netrw_fname |endif
+ if exists("b:netrw_lastfile") |let netrw_lastfile = b:netrw_lastfile |endif
+ if exists("b:netrw_liststyle") |let netrw_liststyle = b:netrw_liststyle |endif
+ if exists("b:netrw_method") |let netrw_method = b:netrw_method |endif
+ if exists("b:netrw_option") |let netrw_option = b:netrw_option |endif
+ if exists("b:netrw_prvdir") |let netrw_prvdir = b:netrw_prvdir |endif
+
+ NetrwKeepj call s:NetrwOptionsRestore("w:")
+ " call Decho("generate a buffer with NetrwKeepj enew!",'~'.expand("<slnum>"))
+ " when tree listing uses file TreeListing... a new buffer is made.
+ " Want the old buffer to be unlisted.
+ " COMBAK: this causes a problem, see P43
+ " setl nobl
+ let netrw_keepdiff= &l:diff
+ call s:NetrwEditFile("enew!","","")
+ let &l:diff= netrw_keepdiff
+ " call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>"))
+ NetrwKeepj call s:NetrwOptionsSave("w:")
+
+ " copy function-local-variables to buffer variable equivalents
+ " call Decho("copy function-local variables back to buffer netrw variables",'~'.expand("<slnum>"))
+ if exists("netrw_bannercnt") |let b:netrw_bannercnt = netrw_bannercnt |endif
+ if exists("netrw_browser_active") |let b:netrw_browser_active = netrw_browser_active |endif
+ if exists("netrw_cpf") |let b:netrw_cpf = netrw_cpf |endif
+ if exists("netrw_curdir") |let b:netrw_curdir = netrw_curdir |endif
+ if exists("netrw_explore_bufnr") |let b:netrw_explore_bufnr = netrw_explore_bufnr |endif
+ if exists("netrw_explore_indx") |let b:netrw_explore_indx = netrw_explore_indx |endif
+ if exists("netrw_explore_line") |let b:netrw_explore_line = netrw_explore_line |endif
+ if exists("netrw_explore_list") |let b:netrw_explore_list = netrw_explore_list |endif
+ if exists("netrw_explore_listlen")|let b:netrw_explore_listlen = netrw_explore_listlen|endif
+ if exists("netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt = netrw_explore_mtchcnt|endif
+ if exists("netrw_fname") |let b:netrw_fname = netrw_fname |endif
+ if exists("netrw_lastfile") |let b:netrw_lastfile = netrw_lastfile |endif
+ if exists("netrw_liststyle") |let b:netrw_liststyle = netrw_liststyle |endif
+ if exists("netrw_method") |let b:netrw_method = netrw_method |endif
+ if exists("netrw_option") |let b:netrw_option = netrw_option |endif
+ if exists("netrw_prvdir") |let b:netrw_prvdir = netrw_prvdir |endif
+
+ if a:0 > 0
+ let b:netrw_curdir= a:1
+ if b:netrw_curdir =~ '/$'
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
+ setl nobl
+ file NetrwTreeListing
+ setl nobl bt=nowrite bh=hide
+ nno <silent> <buffer> [ :sil call <SID>TreeListMove('[')<cr>
+ nno <silent> <buffer> ] :sil call <SID>TreeListMove(']')<cr>
+ else
+ call s:NetrwBufRename(b:netrw_curdir)
+ endif
+ endif
+ endif
+ if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on")
+ let &l:bexpr = "netrw#BalloonHelp()"
+ endif
+
+ " call Dret("s:NetrwEnew : buf#".bufnr("%")."<".bufname("%")."> expand(%)<".expand("%")."> expand(#)<".expand("#")."> bh=".&bh." win#".winnr()." winnr($)#".winnr("$"))
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwExe: executes a string using "!" {{{2
+fun! s:NetrwExe(cmd)
+ if has("win32") && exepath(&shell) !~? '\v[\/]?(cmd|pwsh|powershell)(\.exe)?$' && !g:netrw_cygwin
+ let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash]
+ set shell& shellcmdflag& shellxquote& shellxescape&
+ set shellquote& shellpipe& shellredir& shellslash&
+ try
+ exe a:cmd
+ finally
+ let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell
+ endtry
+ else
+ exe a:cmd
+ endif
+ if v:shell_error
+ call netrw#ErrorMsg(s:WARNING,"shell signalled an error",106)
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2
+fun! s:NetrwInsureWinVars()
+ if !exists("w:netrw_liststyle")
+ " call Dfunc("s:NetrwInsureWinVars() win#".winnr())
+ let curbuf = bufnr("%")
+ let curwin = winnr()
+ let iwin = 1
+ while iwin <= winnr("$")
+ exe iwin."wincmd w"
+ if winnr() != curwin && bufnr("%") == curbuf && exists("w:netrw_liststyle")
+ " looks like ctrl-w_s or ctrl-w_v was used to split a netrw buffer
+ let winvars= w:
+ break
+ endif
+ let iwin= iwin + 1
+ endwhile
+ exe "keepalt ".curwin."wincmd w"
+ if exists("winvars")
+ " call Decho("copying w#".iwin." window variables to w#".curwin,'~'.expand("<slnum>"))
+ for k in keys(winvars)
+ let w:{k}= winvars[k]
+ endfor
+ endif
+ " call Dret("s:NetrwInsureWinVars win#".winnr())
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwLcd: handles changing the (local) directory {{{2
+" Returns: 0=success
+" -1=failed
+fun! s:NetrwLcd(newdir)
+ " call Dfunc("s:NetrwLcd(newdir<".a:newdir.">)")
+ " call Decho("changing local directory",'~'.expand("<slnum>"))
+
+ let err472= 0
+ try
+ exe 'NetrwKeepj sil lcd '.fnameescape(a:newdir)
+ catch /^Vim\%((\a\+)\)\=:E344/
+ " Vim's lcd fails with E344 when attempting to go above the 'root' of a Windows share.
+ " Therefore, detect if a Windows share is present, and if E344 occurs, just settle at
+ " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or
+ " forward slashes ('//Foo'), depending on whether backslashes have been converted to
+ " forward slashes by earlier code; so check for both.
+ if has("win32") && !g:netrw_cygwin
+ if a:newdir =~ '^\\\\\w\+' || a:newdir =~ '^//\w\+'
+ let dirname = '\'
+ exe 'NetrwKeepj sil lcd '.fnameescape(dirname)
+ endif
+ endif
+ catch /^Vim\%((\a\+)\)\=:E472/
+ let err472= 1
+ endtry
+
+ if err472
+ call netrw#ErrorMsg(s:ERROR,"unable to change directory to <".a:newdir."> (permissions?)",61)
+ if exists("w:netrw_prvdir")
+ let a:newdir= w:netrw_prvdir
+ else
+ call s:NetrwOptionsRestore("w:")
+ " call Decho("setl noma nomod nowrap",'~'.expand("<slnum>"))
+ exe "setl ".g:netrw_bufsettings
+ " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
+ let a:newdir= dirname
+ endif
+ " call Dret("s:NetrwBrowse -1 : reusing buffer#".(exists("bufnum")? bufnum : 'N/A')."<".dirname."> getcwd<".getcwd().">")
+ return -1
+ endif
+
+ " call Decho("getcwd <".getcwd().">")
+ " call Decho("b:netrw_curdir<".b:netrw_curdir.">")
+ " call Dret("s:NetrwLcd 0")
+ return 0
+endfun
+
+" ------------------------------------------------------------------------
+" s:NetrwSaveWordPosn: used to keep cursor on same word after refresh, {{{2
+" changed sorting, etc. Also see s:NetrwRestoreWordPosn().
+fun! s:NetrwSaveWordPosn()
+ " call Dfunc("NetrwSaveWordPosn()")
+ let s:netrw_saveword= '^'.fnameescape(getline('.')).'$'
+ " call Dret("NetrwSaveWordPosn : saveword<".s:netrw_saveword.">")
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwHumanReadable: takes a number and makes it "human readable" {{{2
+" 1000 -> 1K, 1000000 -> 1M, 1000000000 -> 1G
+fun! s:NetrwHumanReadable(sz)
+ " call Dfunc("s:NetrwHumanReadable(sz=".a:sz.") type=".type(a:sz)." style=".g:netrw_sizestyle )
+
+ if g:netrw_sizestyle == 'h'
+ if a:sz >= 1000000000
+ let sz = printf("%.1f",a:sz/1000000000.0)."g"
+ elseif a:sz >= 10000000
+ let sz = printf("%d",a:sz/1000000)."m"
+ elseif a:sz >= 1000000
+ let sz = printf("%.1f",a:sz/1000000.0)."m"
+ elseif a:sz >= 10000
+ let sz = printf("%d",a:sz/1000)."k"
+ elseif a:sz >= 1000
+ let sz = printf("%.1f",a:sz/1000.0)."k"
+ else
+ let sz= a:sz
+ endif
+
+ elseif g:netrw_sizestyle == 'H'
+ if a:sz >= 1073741824
+ let sz = printf("%.1f",a:sz/1073741824.0)."G"
+ elseif a:sz >= 10485760
+ let sz = printf("%d",a:sz/1048576)."M"
+ elseif a:sz >= 1048576
+ let sz = printf("%.1f",a:sz/1048576.0)."M"
+ elseif a:sz >= 10240
+ let sz = printf("%d",a:sz/1024)."K"
+ elseif a:sz >= 1024
+ let sz = printf("%.1f",a:sz/1024.0)."K"
+ else
+ let sz= a:sz
+ endif
+
+ else
+ let sz= a:sz
+ endif
+
+ " call Dret("s:NetrwHumanReadable ".sz)
+ return sz
+endfun
+
+" ---------------------------------------------------------------------
+" s:NetrwRestoreWordPosn: used to keep cursor on same word after refresh, {{{2
+" changed sorting, etc. Also see s:NetrwSaveWordPosn().
+fun! s:NetrwRestoreWordPosn()
+ " call Dfunc("NetrwRestoreWordPosn()")
+ sil! call search(s:netrw_saveword,'w')
+ " call Dret("NetrwRestoreWordPosn")
+endfun
+
+" ---------------------------------------------------------------------
+" s:RestoreBufVars: {{{2
+fun! s:RestoreBufVars()
+ " call Dfunc("s:RestoreBufVars()")
+
+ if exists("s:netrw_curdir") |let b:netrw_curdir = s:netrw_curdir |endif
+ if exists("s:netrw_lastfile") |let b:netrw_lastfile = s:netrw_lastfile |endif
+ if exists("s:netrw_method") |let b:netrw_method = s:netrw_method |endif
+ if exists("s:netrw_fname") |let b:netrw_fname = s:netrw_fname |endif
+ if exists("s:netrw_machine") |let b:netrw_machine = s:netrw_machine |endif
+ if exists("s:netrw_browser_active")|let b:netrw_browser_active = s:netrw_browser_active|endif
+
+ " call Dret("s:RestoreBufVars")
+endfun
+
+" ---------------------------------------------------------------------
+" s:RemotePathAnalysis: {{{2
+fun! s:RemotePathAnalysis(dirname)
+ " call Dfunc("s:RemotePathAnalysis(a:dirname<".a:dirname.">)")
+
+ " method :// user @ machine :port /path
+ let dirpat = '^\(\w\{-}\)://\(\(\w\+\)@\)\=\([^/:#]\+\)\%([:#]\(\d\+\)\)\=/\(.*\)$'
+ let s:method = substitute(a:dirname,dirpat,'\1','')
+ let s:user = substitute(a:dirname,dirpat,'\3','')
+ let s:machine = substitute(a:dirname,dirpat,'\4','')
+ let s:port = substitute(a:dirname,dirpat,'\5','')
+ let s:path = substitute(a:dirname,dirpat,'\6','')
+ let s:fname = substitute(s:path,'^.*/\ze.','','')
+ if s:machine =~ '@'
+ let dirpat = '^\(.*\)@\(.\{-}\)$'
+ let s:user = s:user.'@'.substitute(s:machine,dirpat,'\1','')
+ let s:machine = substitute(s:machine,dirpat,'\2','')
+ endif
+
+ " call Decho("set up s:method <".s:method .">",'~'.expand("<slnum>"))
+ " call Decho("set up s:user <".s:user .">",'~'.expand("<slnum>"))
+ " call Decho("set up s:machine<".s:machine.">",'~'.expand("<slnum>"))
+ " call Decho("set up s:port <".s:port.">",'~'.expand("<slnum>"))
+ " call Decho("set up s:path <".s:path .">",'~'.expand("<slnum>"))
+ " call Decho("set up s:fname <".s:fname .">",'~'.expand("<slnum>"))
+
+ " call Dret("s:RemotePathAnalysis")
+endfun
+
+" ---------------------------------------------------------------------
+" s:RemoteSystem: runs a command on a remote host using ssh {{{2
+" Returns status
+" Runs system() on
+" [cd REMOTEDIRPATH;] a:cmd
+" Note that it doesn't do s:ShellEscape(a:cmd)!
+fun! s:RemoteSystem(cmd)
+ " call Dfunc("s:RemoteSystem(cmd<".a:cmd.">)")
+ if !executable(g:netrw_ssh_cmd)
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"g:netrw_ssh_cmd<".g:netrw_ssh_cmd."> is not executable!",52)
+ elseif !exists("b:netrw_curdir")
+ NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53)
+ else
+ let cmd = s:MakeSshCmd(g:netrw_ssh_cmd." USEPORT HOSTNAME")
+ let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','')
+ if remotedir != ""
+ let cmd= cmd.' cd '.s:ShellEscape(remotedir).";"
+ else
+ let cmd= cmd.' '
+ endif
+ let cmd= cmd.a:cmd
+ " call Decho("call system(".cmd.")",'~'.expand("<slnum>"))
+ let ret= system(cmd)
+ endif
+ " call Dret("s:RemoteSystem ".ret)
+ return ret
+endfun
+
+" ---------------------------------------------------------------------
+" s:RestoreWinVars: (used by Explore() and NetrwSplit()) {{{2
+fun! s:RestoreWinVars()
+ " call Dfunc("s:RestoreWinVars()")
+ if exists("s:bannercnt") |let w:netrw_bannercnt = s:bannercnt |unlet s:bannercnt |endif
+ if exists("s:col") |let w:netrw_col = s:col |unlet s:col |endif
+ if exists("s:curdir") |let w:netrw_curdir = s:curdir |unlet s:curdir |endif
+ if exists("s:explore_bufnr") |let w:netrw_explore_bufnr = s:explore_bufnr |unlet s:explore_bufnr |endif
+ if exists("s:explore_indx") |let w:netrw_explore_indx = s:explore_indx |unlet s:explore_indx |endif
+ if exists("s:explore_line") |let w:netrw_explore_line = s:explore_line |unlet s:explore_line |endif
+ if exists("s:explore_listlen")|let w:netrw_explore_listlen = s:explore_listlen|unlet s:explore_listlen|endif
+ if exists("s:explore_list") |let w:netrw_explore_list = s:explore_list |unlet s:explore_list |endif
+ if exists("s:explore_mtchcnt")|let w:netrw_explore_mtchcnt = s:explore_mtchcnt|unlet s:explore_mtchcnt|endif
+ if exists("s:fpl") |let w:netrw_fpl = s:fpl |unlet s:fpl |endif
+ if exists("s:hline") |let w:netrw_hline = s:hline |unlet s:hline |endif
+ if exists("s:line") |let w:netrw_line = s:line |unlet s:line |endif
+ if exists("s:liststyle") |let w:netrw_liststyle = s:liststyle |unlet s:liststyle |endif
+ if exists("s:method") |let w:netrw_method = s:method |unlet s:method |endif
+ if exists("s:prvdir") |let w:netrw_prvdir = s:prvdir |unlet s:prvdir |endif
+ if exists("s:treedict") |let w:netrw_treedict = s:treedict |unlet s:treedict |endif
+ if exists("s:treetop") |let w:netrw_treetop = s:treetop |unlet s:treetop |endif
+ if exists("s:winnr") |let w:netrw_winnr = s:winnr |unlet s:winnr |endif
+ " call Dret("s:RestoreWinVars")
+endfun
+
+" ---------------------------------------------------------------------
+" s:Rexplore: implements returning from a buffer to a netrw directory {{{2
+"
+" s:SetRexDir() sets up <2-leftmouse> maps (if g:netrw_retmap
+" is true) and a command, :Rexplore, which call this function.
+"
+" s:netrw_posn is set up by s:NetrwBrowseChgDir()
+"
+" s:rexposn_BUFNR used to save/restore cursor position
+fun! s:NetrwRexplore(islocal,dirname)
+ if exists("s:netrwdrag")
+ return
+ endif
+ " call Dfunc("s:NetrwRexplore() w:netrw_rexlocal=".w:netrw_rexlocal." w:netrw_rexdir<".w:netrw_rexdir."> win#".winnr())
+ " call Decho("currently in bufname<".bufname("%").">",'~'.expand("<slnum>"))
+ " call Decho("ft=".&ft." win#".winnr()." w:netrw_rexfile<".(exists("w:netrw_rexfile")? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>"))
+
+ if &ft == "netrw" && exists("w:netrw_rexfile") && w:netrw_rexfile != ""
+ " a :Rex while in a netrw buffer means: edit the file in w:netrw_rexfile
+ " call Decho("in netrw buffer, will edit file<".w:netrw_rexfile.">",'~'.expand("<slnum>"))
+ exe "NetrwKeepj e ".w:netrw_rexfile
+ unlet w:netrw_rexfile
+ " call Dret("s:NetrwRexplore returning from netrw to buf#".bufnr("%")."<".bufname("%")."> (ft=".&ft.")")
+ return
+ " else " Decho
+ " call Decho("treating as not-netrw-buffer: ft=".&ft.((&ft == "netrw")? " == netrw" : "!= netrw"),'~'.expand("<slnum>"))
+ " call Decho("treating as not-netrw-buffer: w:netrw_rexfile<".((exists("w:netrw_rexfile"))? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>"))
+ endif
+
+ " ---------------------------
+ " :Rex issued while in a file
+ " ---------------------------
+
+ " record current file so :Rex can return to it from netrw
+ let w:netrw_rexfile= expand("%")
+ " call Decho("set w:netrw_rexfile<".w:netrw_rexfile."> (win#".winnr().")",'~'.expand("<slnum>"))
+
+ if !exists("w:netrw_rexlocal")
+ " call Dret("s:NetrwRexplore w:netrw_rexlocal doesn't exist (".&ft." win#".winnr().")")
+ return
+ 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,'~'.expand("<slnum>"))
+ if w:netrw_rexlocal
+ NetrwKeepj call netrw#LocalBrowseCheck(w:netrw_rexdir)
+ else
+ NetrwKeepj call s:NetrwBrowse(0,w:netrw_rexdir)
+ endif
+ if exists("s:initbeval")
+ setl beval
+ endif
+ if exists("s:rexposn_".bufnr("%"))
+ " call Decho("restore posn, then unlet s:rexposn_".bufnr('%')."<".bufname("%").">",'~'.expand("<slnum>"))
+ " restore position in directory listing
+ " call Decho("restoring posn to s:rexposn_".bufnr('%')."<".string(s:rexposn_{bufnr('%')}).">",'~'.expand("<slnum>"))
+ NetrwKeepj call winrestview(s:rexposn_{bufnr('%')})
+ if exists("s:rexposn_".bufnr('%'))
+ unlet s:rexposn_{bufnr('%')}
+ endif
+ else
+ " call Decho("s:rexposn_".bufnr('%')."<".bufname("%")."> doesn't exist",'~'.expand("<slnum>"))
+ endif
+
+ if has("syntax") && exists("g:syntax_on") && g:syntax_on
+ if exists("s:explore_match")
+ exe "2match netrwMarkFile /".s:explore_match."/"
+ endif
+ 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,'~'.expand("<slnum>"))
+ " call Dret("s:NetrwRexplore : ft=".&ft)
+endfun
+
+" ---------------------------------------------------------------------
+" s:SaveBufVars: save selected b: variables to s: variables {{{2
+" use s:RestoreBufVars() to restore b: variables from s: variables
+fun! s:SaveBufVars()
+ " call Dfunc("s:SaveBufVars() buf#".bufnr("%"))
+
+ if exists("b:netrw_curdir") |let s:netrw_curdir = b:netrw_curdir |endif
+ if exists("b:netrw_lastfile") |let s:netrw_lastfile = b:netrw_lastfile |endif
+ if exists("b:netrw_method") |let s:netrw_method = b:netrw_method |endif
+ if exists("b:netrw_fname") |let s:netrw_fname = b:netrw_fname |endif
+ if exists("b:netrw_machine") |let s:netrw_machine = b:netrw_machine |endif
+ if exists("b:netrw_browser_active")|let s:netrw_browser_active = b:netrw_browser_active|endif
+
+ " call Dret("s:SaveBufVars")
+endfun
+
+" ---------------------------------------------------------------------
+" s:SavePosn: saves position associated with current buffer into a dictionary {{{2
+fun! s:SavePosn(posndict)
+ " call Dfunc("s:SavePosn(posndict) curbuf#".bufnr("%")."<".bufname("%").">")
+
+ if !exists("a:posndict[bufnr('%')]")
+ let a:posndict[bufnr("%")]= []
+ endif
+ " call Decho("before push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
+ call add(a:posndict[bufnr("%")],winsaveview())
+ " call Decho("after push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
+
+ " call Dret("s:SavePosn posndict")
+ return a:posndict
+endfun
+
+" ---------------------------------------------------------------------
+" s:RestorePosn: restores position associated with current buffer using dictionary {{{2
+fun! s:RestorePosn(posndict)
+ " call Dfunc("s:RestorePosn(posndict) curbuf#".bufnr("%")."<".bufname("%").">")
+ if exists("a:posndict")
+ if has_key(a:posndict,bufnr("%"))
+ " call Decho("before pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
+ let posnlen= len(a:posndict[bufnr("%")])
+ if posnlen > 0
+ let posnlen= posnlen - 1
+ " call Decho("restoring posn posndict[".bufnr("%")."][".posnlen."]=".string(a:posndict[bufnr("%")][posnlen]),'~'.expand("<slnum>"))
+ call winrestview(a:posndict[bufnr("%")][posnlen])
+ call remove(a:posndict[bufnr("%")],posnlen)
+ " call Decho("after pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')]))
+ endif
+ endif
+ endif
+ " call Dret("s:RestorePosn")
+endfun
+
+" ---------------------------------------------------------------------
+" s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2
+fun! s:SaveWinVars()
+ " call Dfunc("s:SaveWinVars() win#".winnr())
+ if exists("w:netrw_bannercnt") |let s:bannercnt = w:netrw_bannercnt |endif
+ if exists("w:netrw_col") |let s:col = w:netrw_col |endif
+ if exists("w:netrw_curdir") |let s:curdir = w:netrw_curdir |endif
+ if exists("w:netrw_explore_bufnr") |let s:explore_bufnr = w:netrw_explore_bufnr |endif
+ if exists("w:netrw_explore_indx") |let s:explore_indx = w:netrw_explore_indx |endif
+ if exists("w:netrw_explore_line") |let s:explore_line = w:netrw_explore_line |endif
+ if exists("w:netrw_explore_listlen")|let s:explore_listlen = w:netrw_explore_listlen|endif
+ if exists("w:netrw_explore_list") |let s:explore_list = w:netrw_explore_list |endif
+ if exists("w:netrw_explore_mtchcnt")|let s:explore_mtchcnt = w:netrw_explore_mtchcnt|endif
+ if exists("w:netrw_fpl") |let s:fpl = w:netrw_fpl |endif
+ if exists("w:netrw_hline") |let s:hline = w:netrw_hline |endif
+ if exists("w:netrw_line") |let s:line = w:netrw_line |endif
+ if exists("w:netrw_liststyle") |let s:liststyle = w:netrw_liststyle |endif
+ if exists("w:netrw_method") |let s:method = w:netrw_method |endif
+ if exists("w:netrw_prvdir") |let s:prvdir = w:netrw_prvdir |endif
+ if exists("w:netrw_treedict") |let s:treedict = w:netrw_treedict |endif
+ if exists("w:netrw_treetop") |let s:treetop = w:netrw_treetop |endif
+ if exists("w:netrw_winnr") |let s:winnr = w:netrw_winnr |endif
+ " call Dret("s:SaveWinVars")
+endfun
+
+" ---------------------------------------------------------------------
+" s:SetBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck()) {{{2
+" To allow separate windows to have their own activities, such as
+" Explore **/pattern, several variables have been made window-oriented.
+" However, when the user splits a browser window (ex: ctrl-w s), these
+" variables are not inherited by the new window. SetBufWinVars() and
+" UseBufWinVars() get around that.
+fun! s:SetBufWinVars()
+ " call Dfunc("s:SetBufWinVars() win#".winnr())
+ if exists("w:netrw_liststyle") |let b:netrw_liststyle = w:netrw_liststyle |endif
+ if exists("w:netrw_bannercnt") |let b:netrw_bannercnt = w:netrw_bannercnt |endif
+ if exists("w:netrw_method") |let b:netrw_method = w:netrw_method |endif
+ if exists("w:netrw_prvdir") |let b:netrw_prvdir = w:netrw_prvdir |endif
+ if exists("w:netrw_explore_indx") |let b:netrw_explore_indx = w:netrw_explore_indx |endif
+ if exists("w:netrw_explore_listlen")|let b:netrw_explore_listlen= w:netrw_explore_listlen|endif
+ if exists("w:netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt= w:netrw_explore_mtchcnt|endif
+ if exists("w:netrw_explore_bufnr") |let b:netrw_explore_bufnr = w:netrw_explore_bufnr |endif
+ if exists("w:netrw_explore_line") |let b:netrw_explore_line = w:netrw_explore_line |endif
+ if exists("w:netrw_explore_list") |let b:netrw_explore_list = w:netrw_explore_list |endif
+ " call Dret("s:SetBufWinVars")
+endfun
+
+" ---------------------------------------------------------------------
+" s:SetRexDir: set directory for :Rexplore {{{2
+fun! s:SetRexDir(islocal,dirname)
+ " call Dfunc("s:SetRexDir(islocal=".a:islocal." dirname<".a:dirname.">) win#".winnr())
+ let w:netrw_rexdir = a:dirname
+ let w:netrw_rexlocal = a:islocal
+ let s:rexposn_{bufnr("%")} = winsaveview()
+ " call Decho("setting w:netrw_rexdir =".w:netrw_rexdir,'~'.expand("<slnum>"))
+ " call Decho("setting w:netrw_rexlocal=".w:netrw_rexlocal,'~'.expand("<slnum>"))
+ " call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>"))
+ " call Decho("setting s:rexposn_".bufnr("%")."<".bufname("%")."> to ".string(winsaveview()),'~'.expand("<slnum>"))
+ " call Dret("s:SetRexDir : win#".winnr()." ".(a:islocal? "local" : "remote")." dir: ".a:dirname)
+endfun
+
+" ---------------------------------------------------------------------
+" s:ShowLink: used to modify thin and tree listings to show links {{{2
+fun! s:ShowLink()
+ if exists("b:netrw_curdir")
+ keepp :norm! $?\a
+ "call histdel("/",-1)
+ if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop")
+ let basedir = s:NetrwTreePath(w:netrw_treetop)
+ else
+ let basedir = b:netrw_curdir.'/'
+ endif
+ let fname = basedir.s:NetrwGetWord()
+ let resname = resolve(fname)
+ if resname =~ '^\M'.basedir
+ let dirlen = strlen(basedir)
+ let resname = strpart(resname,dirlen)
+ endif
+ let modline = getline(".")."\t --> ".resname
+ setl noro ma
+ call setline(".",modline)
+ setl ro noma nomod
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:ShowStyle: {{{2
+fun! s:ShowStyle()
+ if !exists("w:netrw_liststyle")
+ let liststyle= g:netrw_liststyle
+ else
+ let liststyle= w:netrw_liststyle
+ endif
+ if liststyle == s:THINLIST
+ return s:THINLIST.":thin"
+ elseif liststyle == s:LONGLIST
+ return s:LONGLIST.":long"
+ elseif liststyle == s:WIDELIST
+ return s:WIDELIST.":wide"
+ elseif liststyle == s:TREELIST
+ return s:TREELIST.":tree"
+ else
+ return 'n/a'
+ endif
+endfun
+
+" ---------------------------------------------------------------------
+" s:Strlen: this function returns the length of a string, even if its using multi-byte characters. {{{2
+" Solution from Nicolai Weibull, vim docs (:help strlen()),
+" Tony Mechelynck, and my own invention.
+fun! s:Strlen(x)
+ " "" call Dfunc("s:Strlen(x<".a:x."> g:Align_xstrlen=".g:Align_xstrlen.")")
+
+ if v:version >= 703 && exists("*strdisplaywidth")
+ let ret= strdisplaywidth(a:x)
+
+ elseif type(g:Align_xstrlen) == 1
+ " allow user to specify a function to compute the string length (ie. let g:Align_xstrlen="mystrlenfunc")
+ exe "let ret= ".g:Align_xstrlen."('".substitute(a:x,"'","''","g")."')"
+
+ elseif g:Align_xstrlen == 1
+ " number of codepoints (Latin a + combining circumflex is two codepoints)
+ " (comment from TM, solution from NW)
+ let ret= strlen(substitute(a:x,'.','c','g'))
+
+ elseif g:Align_xstrlen == 2
+ " number of spacing codepoints (Latin a + combining circumflex is one spacing
+ " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.)
+ " (comment from TM, solution from TM)
+ let ret=strlen(substitute(a:x, '.\Z', 'x', 'g'))
+
+ elseif g:Align_xstrlen == 3
+ " virtual length (counting, for instance, tabs as anything between 1 and
+ " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately
+ " preceded by lam, one otherwise, etc.)
+ " (comment from TM, solution from me)
+ let modkeep= &l:mod
+ exe "norm! o\<esc>"
+ call setline(line("."),a:x)
+ let ret= virtcol("$") - 1
+ d
+ NetrwKeepj norm! k
+ let &l:mod= modkeep
+
+ else
+ " at least give a decent default
+ let ret= strlen(a:x)
+ endif
+ " "" call Dret("s:Strlen ".ret)
+ return ret
+endfun
+
+" ---------------------------------------------------------------------
+" s:ShellEscape: shellescape(), or special windows handling {{{2
+fun! s:ShellEscape(s, ...)
+ if has('win32') && $SHELL == '' && &shellslash
+ return printf('"%s"', substitute(a:s, '"', '""', 'g'))
+ endif
+ let f = a:0 > 0 ? a:1 : 0
+ return shellescape(a:s, f)
+endfun
+
+" ---------------------------------------------------------------------
+" s:TreeListMove: supports [[, ]], [], and ][ in tree mode {{{2
+fun! s:TreeListMove(dir)
+ " call Dfunc("s:TreeListMove(dir<".a:dir.">)")
+ let curline = getline('.')
+ let prvline = (line(".") > 1)? getline(line(".")-1) : ''
+ let nxtline = (line(".") < line("$"))? getline(line(".")+1) : ''
+ let curindent = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e')
+ let indentm1 = substitute(curindent,'^'.s:treedepthstring,'','')
+ let treedepthchr = substitute(s:treedepthstring,' ','','g')
+ let stopline = exists("w:netrw_bannercnt")? w:netrw_bannercnt : 1
+ " call Decho("prvline <".prvline."> #".(line(".")-1), '~'.expand("<slnum>"))
+ " call Decho("curline <".curline."> #".line(".") , '~'.expand("<slnum>"))
+ " call Decho("nxtline <".nxtline."> #".(line(".")+1), '~'.expand("<slnum>"))
+ " call Decho("curindent<".curindent.">" , '~'.expand("<slnum>"))
+ " call Decho("indentm1 <".indentm1.">" , '~'.expand("<slnum>"))
+ " COMBAK : need to handle when on a directory
+ " COMBAK : need to handle ]] and ][. In general, needs work!!!
+ if curline !~ '/$'
+ if a:dir == '[[' && prvline != ''
+ NetrwKeepj norm! 0
+ let nl = search('^'.indentm1.'\%('.s:treedepthstring.'\)\@!','bWe',stopline) " search backwards
+ " call Decho("regfile srch back: ".nl,'~'.expand("<slnum>"))
+ elseif a:dir == '[]' && nxtline != ''
+ NetrwKeepj norm! 0
+ " call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>','~'.expand("<slnum>"))
+ let nl = search('^\%('.curindent.'\)\@!','We') " search forwards
+ if nl != 0
+ NetrwKeepj norm! k
+ else
+ NetrwKeepj norm! G
+ endif
+ " call Decho("regfile srch fwd: ".nl,'~'.expand("<slnum>"))
+ endif
+ endif
+
+ " call Dret("s:TreeListMove")
+endfun
+
+" ---------------------------------------------------------------------
+" s:UpdateBuffersMenu: does emenu Buffers.Refresh (but due to locale, the menu item may not be called that) {{{2
+" The Buffers.Refresh menu calls s:BMShow(); unfortunately, that means that that function
+" can't be called except via emenu. But due to locale, that menu line may not be called
+" Buffers.Refresh; hence, s:NetrwBMShow() utilizes a "cheat" to call that function anyway.
+fun! s:UpdateBuffersMenu()
+ " call Dfunc("s:UpdateBuffersMenu()")
+ if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu
+ try
+ sil emenu Buffers.Refresh\ menu
+ catch /^Vim\%((\a\+)\)\=:E/
+ let v:errmsg= ""
+ sil NetrwKeepj call s:NetrwBMShow()
+ endtry
+ endif
+ " call Dret("s:UpdateBuffersMenu")
+endfun
+
+" ---------------------------------------------------------------------
+" s:UseBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck() {{{2
+" Matching function to s:SetBufWinVars()
+fun! s:UseBufWinVars()
+ " call Dfunc("s:UseBufWinVars()")
+ if exists("b:netrw_liststyle") && !exists("w:netrw_liststyle") |let w:netrw_liststyle = b:netrw_liststyle |endif
+ if exists("b:netrw_bannercnt") && !exists("w:netrw_bannercnt") |let w:netrw_bannercnt = b:netrw_bannercnt |endif
+ if exists("b:netrw_method") && !exists("w:netrw_method") |let w:netrw_method = b:netrw_method |endif
+ if exists("b:netrw_prvdir") && !exists("w:netrw_prvdir") |let w:netrw_prvdir = b:netrw_prvdir |endif
+ if exists("b:netrw_explore_indx") && !exists("w:netrw_explore_indx") |let w:netrw_explore_indx = b:netrw_explore_indx |endif
+ if exists("b:netrw_explore_listlen") && !exists("w:netrw_explore_listlen")|let w:netrw_explore_listlen = b:netrw_explore_listlen|endif
+ if exists("b:netrw_explore_mtchcnt") && !exists("w:netrw_explore_mtchcnt")|let w:netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif
+ if exists("b:netrw_explore_bufnr") && !exists("w:netrw_explore_bufnr") |let w:netrw_explore_bufnr = b:netrw_explore_bufnr |endif
+ if exists("b:netrw_explore_line") && !exists("w:netrw_explore_line") |let w:netrw_explore_line = b:netrw_explore_line |endif
+ if exists("b:netrw_explore_list") && !exists("w:netrw_explore_list") |let w:netrw_explore_list = b:netrw_explore_list |endif
+ " call Dret("s:UseBufWinVars")
+endfun
+
+" ---------------------------------------------------------------------
+" s:UserMaps: supports user-defined UserMaps {{{2
+" * calls a user-supplied funcref(islocal,curdir)
+" * interprets result
+" See netrw#UserMaps()
+fun! s:UserMaps(islocal,funcname)
+ if !exists("b:netrw_curdir")
+ let b:netrw_curdir= getcwd()
+ endif
+ let Funcref = function(a:funcname)
+ let result = Funcref(a:islocal)
+
+ if type(result) == 1
+ " if result from user's funcref is a string...
+ if result == "refresh"
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ elseif result != ""
+ exe result
+ endif
+
+ elseif type(result) == 3
+ " if result from user's funcref is a List...
+ for action in result
+ if action == "refresh"
+ call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0))
+ elseif action != ""
+ exe action
+ endif
+ endfor
+ endif
+endfun
+
+" ==========================
+" Settings Restoration: {{{1
+" ==========================
+let &cpo= s:keepcpo
+unlet s:keepcpo
+
+" ===============
+" Modelines: {{{1
+" ===============
+" vim:ts=8 sts=2 sw=2 et fdm=marker
diff --git a/runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim b/runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim
new file mode 100644
index 0000000000..884d9ce081
--- /dev/null
+++ b/runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim
@@ -0,0 +1,242 @@
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Former Maintainer: Charles E Campbell
+" Upstream: <https://github.com/saccarosium/netrw.vim>
+" Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{
+" Permission is hereby granted to use and distribute this code,
+" with or without modifications, provided that this copyright
+" notice is copied with it. Like anything else that's free,
+" netrwSettings.vim is provided *as is* and comes with no
+" warranty of any kind, either expressed or implied. By using
+" this plugin, you agree that in no event will the copyright
+" holder be liable for any damages resulting from the use
+" of this software. }}}
+
+if &cp || exists("g:loaded_netrwSettings")
+ finish
+endif
+
+let g:loaded_netrwSettings = "v175"
+if v:version < 700
+ echohl WarningMsg
+ echo "***warning*** this version of netrwSettings needs vim 7.0"
+ echohl Normal
+ finish
+endif
+
+" NetrwSettings: {{{
+
+function! netrwSettings#NetrwSettings()
+ " this call is here largely just to insure that netrw has been loaded
+ call netrw#WinPath("")
+ if !exists("g:loaded_netrw")
+ echohl WarningMsg
+ echomsg "***sorry*** netrw needs to be loaded prior to using NetrwSettings"
+ echohl None
+ return
+ endif
+
+ above wincmd s
+ enew
+ setlocal noswapfile bh=wipe
+ set ft=vim
+ file Netrw\ Settings
+
+ " these variables have the following default effects when they don't
+ " exist (ie. have not been set by the user in his/her .vimrc)
+ if !exists("g:netrw_liststyle")
+ let g:netrw_liststyle= 0
+ let g:netrw_list_cmd= "ssh HOSTNAME ls -FLa"
+ endif
+ if !exists("g:netrw_silent")
+ let g:netrw_silent= 0
+ endif
+ if !exists("g:netrw_use_nt_rcp")
+ let g:netrw_use_nt_rcp= 0
+ endif
+ if !exists("g:netrw_ftp")
+ let g:netrw_ftp= 0
+ endif
+ if !exists("g:netrw_ignorenetrc")
+ let g:netrw_ignorenetrc= 0
+ endif
+
+ put ='+ ---------------------------------------------'
+ put ='+ NetrwSettings: by Charles E. Campbell'
+ put ='+ Press <F1> with cursor atop any line for help'
+ put ='+ ---------------------------------------------'
+ let s:netrw_settings_stop= line(".")
+
+ put =''
+ put ='+ Netrw Protocol Commands'
+ put = 'let g:netrw_dav_cmd = '.g:netrw_dav_cmd
+ put = 'let g:netrw_fetch_cmd = '.g:netrw_fetch_cmd
+ put = 'let g:netrw_ftp_cmd = '.g:netrw_ftp_cmd
+ put = 'let g:netrw_http_cmd = '.g:netrw_http_cmd
+ put = 'let g:netrw_rcp_cmd = '.g:netrw_rcp_cmd
+ put = 'let g:netrw_rsync_cmd = '.g:netrw_rsync_cmd
+ put = 'let g:netrw_scp_cmd = '.g:netrw_scp_cmd
+ put = 'let g:netrw_sftp_cmd = '.g:netrw_sftp_cmd
+ put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd
+ let s:netrw_protocol_stop= line(".")
+ put = ''
+
+ put ='+Netrw Transfer Control'
+ put = 'let g:netrw_cygwin = '.g:netrw_cygwin
+ put = 'let g:netrw_ftp = '.g:netrw_ftp
+ put = 'let g:netrw_ftpmode = '.g:netrw_ftpmode
+ put = 'let g:netrw_ignorenetrc = '.g:netrw_ignorenetrc
+ put = 'let g:netrw_sshport = '.g:netrw_sshport
+ put = 'let g:netrw_silent = '.g:netrw_silent
+ put = 'let g:netrw_use_nt_rcp = '.g:netrw_use_nt_rcp
+ let s:netrw_xfer_stop= line(".")
+ put =''
+ put ='+ Netrw Messages'
+ put ='let g:netrw_use_errorwindow = '.g:netrw_use_errorwindow
+
+ put = ''
+ put ='+ Netrw Browser Control'
+ if exists("g:netrw_altfile")
+ put = 'let g:netrw_altfile = '.g:netrw_altfile
+ else
+ put = 'let g:netrw_altfile = 0'
+ endif
+ put = 'let g:netrw_alto = '.g:netrw_alto
+ put = 'let g:netrw_altv = '.g:netrw_altv
+ put = 'let g:netrw_banner = '.g:netrw_banner
+ if exists("g:netrw_bannerbackslash")
+ put = 'let g:netrw_bannerbackslash = '.g:netrw_bannerbackslash
+ else
+ put = '\" let g:netrw_bannerbackslash = (not defined)'
+ endif
+ put = 'let g:netrw_browse_split = '.g:netrw_browse_split
+ if exists("g:netrw_browsex_viewer")
+ put = 'let g:netrw_browsex_viewer = '.g:netrw_browsex_viewer
+ else
+ put = '\" let g:netrw_browsex_viewer = (not defined)'
+ endif
+ put = 'let g:netrw_compress = '.g:netrw_compress
+ if exists("g:Netrw_corehandler")
+ put = 'let g:Netrw_corehandler = '.g:Netrw_corehandler
+ else
+ put = '\" let g:Netrw_corehandler = (not defined)'
+ endif
+ put = 'let g:netrw_ctags = '.g:netrw_ctags
+ put = 'let g:netrw_cursor = '.g:netrw_cursor
+ let decompressline= line("$")
+ put = 'let g:netrw_decompress = '.string(g:netrw_decompress)
+ if exists("g:netrw_dynamic_maxfilenamelen")
+ put = 'let g:netrw_dynamic_maxfilenamelen='.g:netrw_dynamic_maxfilenamelen
+ else
+ put = '\" let g:netrw_dynamic_maxfilenamelen= (not defined)'
+ endif
+ put = 'let g:netrw_dirhistmax = '.g:netrw_dirhistmax
+ put = 'let g:netrw_errorlvl = '.g:netrw_errorlvl
+ put = 'let g:netrw_fastbrowse = '.g:netrw_fastbrowse
+ let fnameescline= line("$")
+ put = 'let g:netrw_fname_escape = '.string(g:netrw_fname_escape)
+ put = 'let g:netrw_ftp_browse_reject = '.g:netrw_ftp_browse_reject
+ put = 'let g:netrw_ftp_list_cmd = '.g:netrw_ftp_list_cmd
+ put = 'let g:netrw_ftp_sizelist_cmd = '.g:netrw_ftp_sizelist_cmd
+ put = 'let g:netrw_ftp_timelist_cmd = '.g:netrw_ftp_timelist_cmd
+ let globescline= line("$")
+ put = 'let g:netrw_glob_escape = '.string(g:netrw_glob_escape)
+ put = 'let g:netrw_hide = '.g:netrw_hide
+ if exists("g:netrw_home")
+ put = 'let g:netrw_home = '.g:netrw_home
+ else
+ put = '\" let g:netrw_home = (not defined)'
+ endif
+ put = 'let g:netrw_keepdir = '.g:netrw_keepdir
+ put = 'let g:netrw_list_cmd = '.g:netrw_list_cmd
+ put = 'let g:netrw_list_hide = '.g:netrw_list_hide
+ put = 'let g:netrw_liststyle = '.g:netrw_liststyle
+ put = 'let g:netrw_localcopycmd = '.g:netrw_localcopycmd
+ put = 'let g:netrw_localcopycmdopt = '.g:netrw_localcopycmdopt
+ put = 'let g:netrw_localmkdir = '.g:netrw_localmkdir
+ put = 'let g:netrw_localmkdiropt = '.g:netrw_localmkdiropt
+ put = 'let g:netrw_localmovecmd = '.g:netrw_localmovecmd
+ put = 'let g:netrw_localmovecmdopt = '.g:netrw_localmovecmdopt
+ put = 'let g:netrw_maxfilenamelen = '.g:netrw_maxfilenamelen
+ put = 'let g:netrw_menu = '.g:netrw_menu
+ put = 'let g:netrw_mousemaps = '.g:netrw_mousemaps
+ put = 'let g:netrw_mkdir_cmd = '.g:netrw_mkdir_cmd
+ if exists("g:netrw_nobeval")
+ put = 'let g:netrw_nobeval = '.g:netrw_nobeval
+ else
+ put = '\" let g:netrw_nobeval = (not defined)'
+ endif
+ put = 'let g:netrw_remote_mkdir = '.g:netrw_remote_mkdir
+ put = 'let g:netrw_preview = '.g:netrw_preview
+ put = 'let g:netrw_rename_cmd = '.g:netrw_rename_cmd
+ put = 'let g:netrw_retmap = '.g:netrw_retmap
+ put = 'let g:netrw_rm_cmd = '.g:netrw_rm_cmd
+ put = 'let g:netrw_rmdir_cmd = '.g:netrw_rmdir_cmd
+ put = 'let g:netrw_rmf_cmd = '.g:netrw_rmf_cmd
+ put = 'let g:netrw_sort_by = '.g:netrw_sort_by
+ put = 'let g:netrw_sort_direction = '.g:netrw_sort_direction
+ put = 'let g:netrw_sort_options = '.g:netrw_sort_options
+ put = 'let g:netrw_sort_sequence = '.g:netrw_sort_sequence
+ put = 'let g:netrw_servername = '.g:netrw_servername
+ put = 'let g:netrw_special_syntax = '.g:netrw_special_syntax
+ put = 'let g:netrw_ssh_browse_reject = '.g:netrw_ssh_browse_reject
+ put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd
+ put = 'let g:netrw_scpport = '.g:netrw_scpport
+ put = 'let g:netrw_sepchr = '.g:netrw_sepchr
+ put = 'let g:netrw_sshport = '.g:netrw_sshport
+ put = 'let g:netrw_timefmt = '.g:netrw_timefmt
+ let tmpfileescline= line("$")
+ put ='let g:netrw_tmpfile_escape...'
+ put = 'let g:netrw_use_noswf = '.g:netrw_use_noswf
+ put = 'let g:netrw_xstrlen = '.g:netrw_xstrlen
+ put = 'let g:netrw_winsize = '.g:netrw_winsize
+
+ put =''
+ put ='+ For help, place cursor on line and press <F1>'
+
+ 1d
+ silent %s/^+/"/e
+ res 99
+ silent %s/= \([^0-9].*\)$/= '\1'/e
+ silent %s/= $/= ''/e
+ 1
+
+ call setline(decompressline, "let g:netrw_decompress = ".substitute(string(g:netrw_decompress),"^'\\(.*\\)'$",'\1',''))
+ call setline(fnameescline, "let g:netrw_fname_escape = '".escape(g:netrw_fname_escape,"'")."'")
+ call setline(globescline, "let g:netrw_glob_escape = '".escape(g:netrw_glob_escape,"'")."'")
+ call setline(tmpfileescline, "let g:netrw_tmpfile_escape = '".escape(g:netrw_tmpfile_escape,"'")."'")
+
+ set nomod
+
+ nmap <buffer> <silent> <F1> :call NetrwSettingHelp()<cr>
+ nnoremap <buffer> <silent> <leftmouse> <leftmouse> :call NetrwSettingHelp()<cr>
+ let tmpfile= tempname()
+ exe 'au BufWriteCmd Netrw\ Settings silent w! '.tmpfile.'|so '.tmpfile.'|call delete("'.tmpfile.'")|set nomod'
+endfunction
+
+" }}}
+" NetrwSettingHelp: {{{
+
+function! NetrwSettingHelp()
+ let curline = getline(".")
+ if curline =~ '='
+ let varhelp = substitute(curline,'^\s*let ','','e')
+ let varhelp = substitute(varhelp,'\s*=.*$','','e')
+ try
+ exe "he ".varhelp
+ catch /^Vim\%((\a\+)\)\=:E149/
+ echo "***sorry*** no help available for <".varhelp.">"
+ endtry
+ elseif line(".") < s:netrw_settings_stop
+ he netrw-settings
+ elseif line(".") < s:netrw_protocol_stop
+ he netrw-externapp
+ elseif line(".") < s:netrw_xfer_stop
+ he netrw-variables
+ else
+ he netrw-browse-var
+ endif
+endfunction
+
+" }}}
+
+" vim:ts=8 sts=4 sw=4 et fdm=marker
diff --git a/runtime/autoload/netrw_gitignore.vim b/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim
index 1b55e2488a..c76d28db04 100644
--- a/runtime/autoload/netrw_gitignore.vim
+++ b/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim
@@ -1,3 +1,7 @@
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Former Maintainer: Bruno Sutic
+" Upstream: <https://github.com/saccarosium/netrw.vim>
+
" netrw_gitignore#Hide: gitignore-based hiding
" Function returns a string of comma separated patterns convenient for
" assignment to `g:netrw_list_hide` option.
@@ -8,7 +12,7 @@
" let g:netrw_list_hide = netrw_gitignore#Hide()
" let g:netrw_list_hide = netrw_gitignore#Hide() . 'more,hide,patterns'
"
-" Copyright: Copyright (C) 2013 Bruno Sutic {{{1
+" Copyright: Copyright (C) 2013 Bruno Sutic {{{
" Permission is hereby granted to use and distribute this code,
" with or without modifications, provided that this copyright
" notice is copied with it. Like anything else that's free,
@@ -16,7 +20,10 @@
" warranty of any kind, either expressed or implied. By using
" this plugin, you agree that in no event will the copyright
" holder be liable for any damages resulting from the use
-" of this software.
+" of this software. }}}
+
function! netrw_gitignore#Hide(...)
- return substitute(substitute(system('git ls-files --other --ignored --exclude-standard --directory'), '\n', ',', 'g'), ',$', '', '')
+ return substitute(substitute(system('git ls-files --other --ignored --exclude-standard --directory'), '\n', ',', 'g'), ',$', '', '')
endfunction
+
+" vim:ts=8 sts=4 sw=4 et fdm=marker
diff --git a/runtime/doc/pi_netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt
index 09d1369d46..9fc7b42bb7 100644
--- a/runtime/doc/pi_netrw.txt
+++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt
@@ -1,14 +1,12 @@
-*pi_netrw.txt* For Vim version 8.2. Last change: 2020 Aug 15
+*netrw.txt* Nvim
------------------------------------------------
NETRW REFERENCE MANUAL by Charles E. Campbell
------------------------------------------------
-Author: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM>
- (remove NOSPAM from Campbell's email first)
-
+Original Author: Charles E. Campbell
Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright*
The VIM LICENSE applies to the files in this package, including
- netrw.vim, pi_netrw.txt, netrwFileHandlers.vim, netrwSettings.vim, and
+ netrw.vim, netrw.txt, netrwSettings.vim, and
syntax/netrw.vim. Like anything else that's free, netrw.vim and its
associated files are provided *as is* and comes with no warranty of
any kind, either expressed or implied. No guarantees of
@@ -110,10 +108,7 @@ Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright*
Selecting Sorting Style.............................|netrw-s|
Setting Editing Window..............................|netrw-C|
10. Problems and Fixes....................................|netrw-problems|
-11. Debugging Netrw Itself................................|netrw-debug|
-12. History...............................................|netrw-history|
-13. Todo..................................................|netrw-todo|
-14. Credits...............................................|netrw-credits|
+11. Credits...............................................|netrw-credits|
==============================================================================
2. Starting With Netrw *netrw-start* {{{1
@@ -2645,10 +2640,34 @@ your browsing preferences. (see also: |netrw-settings|)
netrw last saw |g:netrw_cursor| >= 5 or when
netrw was initially run.
- *g:netrw_decompress* = { ".gz" : "gunzip" ,
- ".bz2" : "bunzip2" ,
- ".zip" : "unzip" ,
- ".tar" : "tar -xf"}
+ *g:netrw_decompress* = { ".lz4": "lz4 -d",
+ ".lzo": "lzop -d",
+ ".lz": "lzip -dk",
+ ".7z": "7za x",
+ ".001": "7za x",
+ ".tar.bz": "tar -xvjf",
+ ".tar.bz2": "tar -xvjf",
+ ".tbz": "tar -xvjf",
+ ".tbz2": "tar -xvjf",
+ ".tar.gz": "tar -xvzf",
+ ".tgz": "tar -xvzf",
+ ".tar.zst": "tar --use-compress-program=unzstd -xvf",
+ ".tzst": "tar --use-compress-program=unzstd -xvf",
+ ".tar": "tar -xvf",
+ ".zip": "unzip",
+ ".bz": "bunzip2 -k",
+ ".bz2": "bunzip2 -k",
+ ".gz": "gunzip -k",
+ ".lzma": "unlzma -T0 -k",
+ ".xz": "unxz -T0 -k",
+ ".zst": "zstd -T0 -d",
+ ".Z": "uncompress -k",
+ ".rar": "unrar x -ad",
+ ".tar.lzma": "tar --lzma -xvf",
+ ".tlz": "tar --lzma -xvf",
+ ".tar.xz": "tar -xvJf",
+ ".txz": "tar -xvJf"}
+
A dictionary mapping suffices to
decompression programs.
@@ -3376,7 +3395,6 @@ Example: Clear netrw's marked file list via a mapping on gu >
10. Problems and Fixes *netrw-problems* {{{1
(This section is likely to grow as I get feedback)
- (also see |netrw-debug|)
*netrw-p1*
P1. I use Windows, and my network browsing with ftp doesn't sort by {{{2
time or size! -or- The remote system is a Windows server; why
@@ -3739,613 +3757,8 @@ Example: Clear netrw's marked file list via a mapping on gu >
directory, which may not be the same as the browsing directory
shown by netrw (see |g:netrw_keepdir|).
-
-==============================================================================
-11. Debugging Netrw Itself *netrw-debug* {{{1
-
-Step 1: check that the problem you've encountered hasn't already been resolved
-by obtaining a copy of the latest (often developmental) netrw at:
-
- http://www.drchip.org/astronaut/vim/index.html#NETRW
-
-The <netrw.vim> script is typically installed on systems as something like:
->
- /usr/local/share/vim/vim8x/plugin/netrwPlugin.vim
- /usr/local/share/vim/vim8x/autoload/netrw.vim
- (see output of :echo &rtp)
-<
-which is loaded automatically at startup (assuming :set nocp). If you
-installed a new netrw, then it will be located at >
-
- $HOME/.vim/plugin/netrwPlugin.vim
- $HOME/.vim/autoload/netrw.vim
-<
-Step 2: assuming that you've installed the latest version of netrw,
-check that your problem is really due to netrw. Create a file
-called netrw.vimrc with the following contents: >
-
- set nocp
- so $HOME/.vim/plugin/netrwPlugin.vim
-<
-Then run netrw as follows: >
-
- vim -u netrw.vimrc --noplugins -i NONE [some path here]
-<
-Perform whatever netrw commands you need to, and check that the problem is
-still present. This procedure sidesteps any issues due to personal .vimrc
-settings, .viminfo file, and other plugins. If the problem does not appear,
-then you need to determine which setting in your .vimrc is causing the
-conflict with netrw or which plugin(s) is/are involved.
-
-Step 3: If the problem still is present, then get a debugging trace from
-netrw:
-
- 1. Get the <Decho.vim> script, available as:
-
- http://www.drchip.org/astronaut/vim/index.html#DECHO
- or
- http://vim.sourceforge.net/scripts/script.php?script_id=120
-
- Decho.vim is provided as a "vimball". You
- should edit the Decho.vba.gz file and source it in: >
-
- vim Decho.vba.gz
- :so %
- :q
-<
- 2. To turn on debug tracing in netrw, then edit the <netrw.vim>
- file by typing: >
-
- vim netrw.vim
- :DechoOn
- :wq
-<
- To restore to normal non-debugging behavior, re-edit <netrw.vim>
- and type >
-
- vim netrw.vim
- :DechoOff
- :wq
-<
- This command, provided by <Decho.vim>, will comment out all
- Decho-debugging statements (Dfunc(), Dret(), Decho(), Dredir()).
-
- 3. Then bring up vim and attempt to evoke the problem by doing a
- transfer or doing some browsing. A set of messages should appear
- concerning the steps that <netrw.vim> took in attempting to
- read/write your file over the network in a separate tab or
- server vim window.
-
- Change the netrw.vimrc file to include the Decho plugin: >
-
- set nocp
- so $HOME/.vim/plugin/Decho.vim
- so $HOME/.vim/plugin/netrwPlugin.vim
-<
- You should continue to run vim with >
-
- vim -u netrw.vimrc --noplugins -i NONE [some path here]
-<
- to avoid entanglements with options and other plugins.
-
- To save the file: under linux, the output will be in a separate
- remote server window; in it, just save the file with >
-
- :w! DBG
-
-< Under a vim that doesn't support clientserver, your debugging
- output will appear in another tab: >
-
- :tabnext
- :set bt=
- :w! DBG
-<
- Furthermore, it'd be helpful if you would type >
-
- :Dsep <command>
-
-< where <command> is the command you're about to type next,
- thereby making it easier to associate which part of the
- debugging trace is due to which command.
-
- Please send that information to <netrw.vim>'s maintainer along
- with the o/s you're using and the vim version that you're using
- (see |:version|) (remove the embedded NOSPAM first) >
-
- NcampObell@SdrPchip.AorgM-NOSPAM
-<
-==============================================================================
-12. History *netrw-history* {{{1
-
- v172: Sep 02, 2021 * (Bram Moolenaar) Changed "l:go" to "go"
- * (Bram Moolenaar) no need for "b" in
- netrw-safe guioptions
- Nov 15, 2021 * removed netrw_localrm and netrw_localrmdir
- references
- Aug 18, 2022 * (Miguel Barro) improving compatibility with
- powershell
- v171: Oct 09, 2020 * included code in s:NetrwOptionsSafe()
- to allow |'bh'| to be set to delete when
- rather than hide when g:netrw_fastbrowse
- was zero.
- * Installed |g:netrw_clipboard| setting
- * Installed option bypass for |'guioptions'|
- a/A settings
- * Changed popup_beval() to popup_atcursor()
- in netrw#ErrorMsg (lacygoill). Apparently
- popup_beval doesn't reliably close the
- popup when the mouse is moved.
- * VimEnter() now using win_execute to examine
- buffers for an attempt to open a directory.
- Avoids issues with popups/terminal from
- command line. (lacygoill)
- Jun 28, 2021 * (zeertzjq) provided a patch for use of
- xmap,xno instead of vmap,vno in
- netrwPlugin.vim. Avoids entanglement with
- select mode.
- Jul 14, 2021 * Fixed problem addressed by tst976; opening
- a file using tree mode, going up a
- directory, and opening a file there was
- opening the file in the wrong directory.
- Jul 28, 2021 * (Ingo Karkat) provided a patch fixing an
- E488 error with netrwPlugin.vim
- (occurred for vim versions < 8.02)
- v170: Mar 11, 2020 * (reported by Reiner Herrmann) netrw+tree
- would not hide with the ^\..* pattern
- correctly.
- * (Marcin Szamotulski) NetrwOptionRestore
- 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())
- Apr 30, 2020 * (reported by Manatsu Takahashi) while
- using Lexplore, a modified file could
- be overwritten. Sol'n: will not overwrite,
- but will emit an |E37| (although one cannot
- add an ! to override)
- Jun 07, 2020 * (reported by Jo Totland) repeatedly invoking
- :Lexplore and quitting it left unused
- hidden buffers. Netrw will now set netrw
- buffers created by :Lexplore to |'bh'|=wipe.
- v169: Dec 20, 2019 * (reported by amkarthik) that netrw's x
- (|netrw-x|) would throw an error when
- attempting to open a local directory.
- v168: Dec 12, 2019 * scp timeout error message not reported,
- hopefully now fixed (Shane Xb Qian)
- v167: Nov 29, 2019 * netrw does a save&restore on @* and @+.
- That causes problems with the clipboard.
- Now restores occurs only if @* or @+ have
- been changed.
- * netrw will change @* or @+ less often.
- Never if I happen to have caught all the
- operations that modify the unnamed
- register (which also writes @*).
- * Modified hiding behavior so that "s"
- will not ignore hiding.
- v166: Nov 06, 2019 * Removed a space from a nmap for "-"
- * Numerous debugging statement changes
- v163: Dec 05, 2017 * (Cristi Balan) reported that a setting ('sel')
- was left changed
- * (Holger Mitschke) reported a problem with
- saving and restoring history. Fixed.
- * Hopefully I fixed a nasty bug that caused a
- file rename to wipe out a buffer that it
- should not have wiped out.
- * (Holger Mitschke) amended this help file
- with additional |g:netrw_special_syntax|
- items
- * Prioritized wget over curl for
- g:netrw_http_cmd
- v162: Sep 19, 2016 * (haya14busa) pointed out two syntax errors
- with a patch; these are now fixed.
- Oct 26, 2016 * I started using mate-terminal and found that
- x and gx (|netrw-x| and |netrw-gx|) were no
- longer working. Fixed (using atril when
- $DESKTOP_SESSION is "mate").
- Nov 04, 2016 * (Martin Vuille) pointed out that @+ was
- being restored with keepregstar rather than
- keepregplus.
- Nov 09, 2016 * Broke apart the command from the options,
- mostly for Windows. Introduced new netrw
- settings: |g:netrw_localcopycmdopt|
- |g:netrw_localcopydircmdopt|
- |g:netrw_localmkdiropt|
- |g:netrw_localmovecmdopt|
- Nov 21, 2016 * (mattn) provided a patch for preview; swapped
- winwidth() with winheight()
- 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
- spaces inside the quotes also had to be
- 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|)
- when atop "Hiding" in the banner also caused
- the active-banner hiding control to occur
- Jan 03, 2017 * (Enno Nagel) reported that attempting to
- apply netrw to a directory that was without
- read permission caused a syntax error.
- Jan 13, 2017 * (Ingo Karkat) provided a patch which makes
- using netrw#Call() better. Now returns
- value of internal routines return, for example.
- Jan 13, 2017 * (Ingo Karkat) changed netrw#FileUrlRead to
- use |:edit| instead of |:read|. I also
- changed the routine name to netrw#FileUrlEdit.
- Jan 16, 2017 * (Sayem) reported a problem where :Lexplore
- could generate a new listing buffer and
- window instead of toggling the netrw display.
- Unfortunately, the directions for eliciting
- the problem weren't complete, so I may or
- may not have fixed that issue.
- Feb 06, 2017 * Implemented cb and cB. Changed "c" to "cd".
- (see |netrw-cb|, |netrw-cB|, and |netrw-cd|)
- Mar 21, 2017 * previously, netrw would specify (safe) settings
- even when the setting was already safe for
- netrw. Netrw now attempts to leave such
- already-netrw-safe settings alone.
- (affects s:NetrwOptionRestore() and
- s:NetrwSafeOptions(); also introduced
- s:NetrwRestoreSetting())
- Jun 26, 2017 * (Christian Brabandt) provided a patch to
- allow curl to follow redirects (ie. -L
- option)
- Jun 26, 2017 * (Callum Howard) reported a problem with
- :Lexpore not removing the Lexplore window
- after a change-directory
- Aug 30, 2017 * (Ingo Karkat) one cannot switch to the
- previously edited file (e.g. with CTRL-^)
- after editing a file:// URL. Patch to
- have a "keepalt" included.
- Oct 17, 2017 * (Adam Faryna) reported that gn (|netrw-gn|)
- did not work on directories in the current
- tree
- v157: Apr 20, 2016 * (Nicola) had set up a "nmap <expr> ..." with
- a function that returned a 0 while silently
- invoking a shell command. The shell command
- activated a ShellCmdPost event which in turn
- called s:LocalBrowseRefresh(). That looks
- over all netrw buffers for changes needing
- refreshes. However, inside a |:map-<expr>|,
- tab and window changes are disallowed. Fixed.
- (affects netrw's s:LocalBrowseRefresh())
- * g:netrw_localrmdir not used any more, but
- the relevant patch that causes |delete()| to
- take over was #1107 (not #1109).
- * |expand()| is now used on |g:netrw_home|;
- consequently, g:netrw_home may now use
- environment variables
- * s:NetrwLeftmouse and s:NetrwCLeftmouse will
- return without doing anything if invoked
- when inside a non-netrw window
- Jun 15, 2016 * gx now calls netrw#GX() which returns
- the word under the cursor. The new
- wrinkle: if one is in a netrw buffer,
- then netrw's s:NetrwGetWord().
- Jun 22, 2016 * Netrw was executing all its associated
- Filetype commands silently; I'm going
- to try doing that "noisily" and see if
- folks have a problem with that.
- Aug 12, 2016 * Changed order of tool selection for
- handling http://... viewing.
- (Nikolay Aleksandrovich Pavlov)
- Aug 21, 2016 * Included hiding/showing/all for tree
- listings
- * Fixed refresh (^L) for tree listings
- v156: Feb 18, 2016 * Changed =~ to =~# where appropriate
- Feb 23, 2016 * s:ComposePath(base,subdir) now uses
- fnameescape() on the base portion
- Mar 01, 2016 * (gt_macki) reported where :Explore would
- make file unlisted. Fixed (tst943)
- Apr 04, 2016 * (reported by John Little) netrw normally
- suppresses browser messages, but sometimes
- those "messages" are what is wanted.
- See |g:netrw_suppress_gx_mesg|
- Apr 06, 2016 * (reported by Carlos Pita) deleting a remote
- file was giving an error message. Fixed.
- Apr 08, 2016 * (Charles Cooper) had a problem with an
- undefined b:netrw_curdir. He also provided
- a fix.
- Apr 20, 2016 * Changed s:NetrwGetBuffer(); now uses
- dictionaries. Also fixed the "No Name"
- buffer problem.
- v155: Oct 29, 2015 * (Timur Fayzrakhmanov) reported that netrw's
- mapping of ctrl-l was not allowing refresh of
- other windows when it was done in a netrw
- window.
- Nov 05, 2015 * Improved s:TreeSqueezeDir() to use search()
- instead of a loop
- * NetrwBrowse() will return line to
- w:netrw_bannercnt if cursor ended up in
- banner
- Nov 16, 2015 * Added a <Plug>NetrwTreeSqueeze (|netrw-s-cr|)
- Nov 17, 2015 * Commented out imaps -- perhaps someone can
- tell me how they're useful and should be
- retained?
- Nov 20, 2015 * Added |netrw-ma| and |netrw-mA| support
- Nov 20, 2015 * gx (|netrw-gx|) on a URL downloaded the
- file in addition to simply bringing up the
- URL in a browser. Fixed.
- Nov 23, 2015 * Added |g:netrw_sizestyle| support
- Nov 27, 2015 * Inserted a lot of <c-u>s into various netrw
- maps.
- Jan 05, 2016 * |netrw-qL| implemented to mark files based
- upon |location-list|s; similar to |netrw-qF|.
- Jan 19, 2016 * using - call delete(directoryname,"d") -
- instead of using g:netrw_localrmdir if
- v7.4 + patch#1107 is available
- Jan 28, 2016 * changed to using |winsaveview()| and
- |winrestview()|
- Jan 28, 2016 * s:NetrwTreePath() now does a save and
- restore of view
- Feb 08, 2016 * Fixed a tree-listing problem with remote
- directories
- v154: Feb 26, 2015 * (Yuri Kanivetsky) reported a situation where
- a file was not treated properly as a file
- due to g:netrw_keepdir == 1
- Mar 25, 2015 * (requested by Ben Friz) one may now sort by
- extension
- Mar 28, 2015 * (requested by Matt Brooks) netrw has a lot
- of buffer-local mappings; however, some
- plugins (such as vim-surround) set up
- conflicting mappings that cause vim to wait.
- The "<nowait>" modifier has been included
- with most of netrw's mappings to avoid that
- delay.
- Jun 26, 2015 * |netrw-gn| mapping implemented
- * :Ntree NotADir resulted in having
- the tree listing expand in the error messages
- window. Fixed.
- Jun 29, 2015 * Attempting to delete a file remotely caused
- an error with "keepsol" mentioned; fixed.
- Jul 08, 2015 * Several changes to keep the |:jumps| table
- correct when working with
- |g:netrw_fastbrowse| set to 2
- * wide listing with accented characters fixed
- (using %-S instead of %-s with a |printf()|
- Jul 13, 2015 * (Daniel Hahler) CheckIfKde() could be true
- but kfmclient not installed. Changed order
- in netrw#BrowseX(): checks if kde and
- kfmclient, then will use xdg-open on a unix
- system (if xdg-open is executable)
- Aug 11, 2015 * (McDonnell) tree listing mode wouldn't
- select a file in a open subdirectory.
- * (McDonnell) when multiple subdirectories
- were concurrently open in tree listing
- mode, a ctrl-L wouldn't refresh properly.
- * The netrw:target menu showed duplicate
- entries
- Oct 13, 2015 * (mattn) provided an exception to handle
- windows with shellslash set but no shell
- Oct 23, 2015 * if g:netrw_usetab and <c-tab> now used
- to control whether NetrwShrink is used
- (see |netrw-c-tab|)
- v153: May 13, 2014 * added another |g:netrw_ffkeep| usage {{{2
- May 14, 2014 * changed s:PerformListing() so that it
- always sets ft=netrw for netrw buffers
- (ie. even when syntax highlighting is
- off, not available, etc)
- May 16, 2014 * introduced the |netrw-ctrl-r| functionality
- May 17, 2014 * introduced the |netrw-:NetrwMB| functionality
- * mb and mB (|netrw-mb|, |netrw-mB|) will
- add/remove marked files from bookmark list
- May 20, 2014 * (Enno Nagel) reported that :Lex <dirname>
- wasn't working. Fixed.
- May 26, 2014 * restored test to prevent leftmouse window
- resizing from causing refresh.
- (see s:NetrwLeftmouse())
- * fixed problem where a refresh caused cursor
- to go just under the banner instead of
- staying put
- May 28, 2014 * (László Bimba) provided a patch for opening
- the |:Lexplore| window 100% high, optionally
- on the right, and will work with remote
- files.
- May 29, 2014 * implemented :NetrwC (see |netrw-:NetrwC|)
- Jun 01, 2014 * Removed some "silent"s from commands used
- to implemented scp://... and pscp://...
- directory listing. Permits request for
- password to appear.
- Jun 05, 2014 * (Enno Nagel) reported that user maps "/"
- caused problems with "b" and "w", which
- are mapped (for wide listings only) to
- skip over files rather than just words.
- Jun 10, 2014 * |g:netrw_gx| introduced to allow users to
- override default "<cfile>" with the gx
- (|netrw-gx|) map
- Jun 11, 2014 * gx (|netrw-gx|), with |'autowrite'| set,
- will write modified files. s:NetrwBrowseX()
- will now save, turn off, and restore the
- |'autowrite'| setting.
- Jun 13, 2014 * added visual map for gx use
- Jun 15, 2014 * (Enno Nagel) reported that with having hls
- set and wide listing style in use, that the
- b and w maps caused unwanted highlighting.
- Jul 05, 2014 * |netrw-mv| and |netrw-mX| commands included
- Jul 09, 2014 * |g:netrw_keepj| included, allowing optional
- keepj
- Jul 09, 2014 * fixing bugs due to previous update
- Jul 21, 2014 * (Bruno Sutic) provided an updated
- netrw_gitignore.vim
- Jul 30, 2014 * (Yavuz Yetim) reported that editing two
- remote files of the same name caused the
- second instance to have a "temporary"
- name. Fixed: now they use the same buffer.
- Sep 18, 2014 * (Yasuhiro Matsumoto) provided a patch which
- allows scp and windows local paths to work.
- Oct 07, 2014 * gx (see |netrw-gx|) when atop a directory,
- will now do |gf| instead
- Nov 06, 2014 * For cygwin: cygstart will be available for
- netrw#BrowseX() to use if its executable.
- Nov 07, 2014 * Began support for file://... urls. Will use
- |g:netrw_file_cmd| (typically elinks or links)
- Dec 02, 2014 * began work on having mc (|netrw-mc|) copy
- directories. Works for linux machines,
- cygwin+vim, but not for windows+gvim.
- Dec 02, 2014 * in tree mode, netrw was not opening
- directories via symbolic links.
- Dec 02, 2014 * added resolved link information to
- thin and tree modes
- Dec 30, 2014 * (issue#231) |:ls| was not showing
- remote-file buffers reliably. Fixed.
- v152: Apr 08, 2014 * uses the |'noswapfile'| option (requires {{{2
- vim 7.4 with patch 213)
- * (Enno Nagel) turn |'rnu'| off in netrw
- buffers.
- * (Quinn Strahl) suggested that netrw
- allow regular window splitting to occur,
- thereby allowing |'equalalways'| to take
- effect.
- * (qingtian zhao) normally, netrw will
- save and restore the |'fileformat'|;
- however, sometimes that isn't wanted
- Apr 14, 2014 * whenever netrw marks a buffer as ro,
- it will also mark it as nomod.
- Apr 16, 2014 * sftp protocol now supported by
- netrw#Obtain(); this means that one
- may use "mc" to copy a remote file
- to a local file using sftp, and that
- the |netrw-O| command can obtain remote
- files via sftp.
- * added [count]C support (see |netrw-C|)
- Apr 18, 2014 * when |g:netrw_chgwin| is one more than
- the last window, then vertically split
- the last window and use it as the
- chgwin window.
- May 09, 2014 * SavePosn was "saving filename under cursor"
- from a non-netrw window when using :Rex.
- v151: Jan 22, 2014 * extended :Rexplore to return to buffer {{{2
- prior to Explore or editing a directory
- * (Ken Takata) netrw gave error when
- clipboard was disabled. Sol'n: Placed
- several if has("clipboard") tests in.
- * Fixed ftp://X@Y@Z// problem; X@Y now
- part of user id, and only Z is part of
- hostname.
- * (A Loumiotis) reported that completion
- using a directory name containing spaces
- did not work. Fixed with a retry in
- netrw#Explore() which removes the
- backslashes vim inserted.
- Feb 26, 2014 * :Rexplore now records the current file
- using w:netrw_rexfile when returning via
- |:Rexplore|
- Mar 08, 2014 * (David Kotchan) provided some patches
- allowing netrw to work properly with
- windows shares.
- * Multiple one-liner help messages available
- by pressing <cr> while atop the "Quick
- Help" line
- * worked on ShellCmdPost, FocusGained event
- handling.
- * |:Lexplore| path: will be used to update
- a left-side netrw browsing directory.
- Mar 12, 2014 * |netrw-s-cr|: use <s-cr> to close
- tree directory implemented
- Mar 13, 2014 * (Tony Mechylynck) reported that using
- the browser with ftp on a directory,
- and selecting a gzipped txt file, that
- an E19 occurred (which was issued by
- gzip.vim). Fixed.
- Mar 14, 2014 * Implemented :MF and :MT (see |netrw-:MF|
- and |netrw-:MT|, respectively)
- Mar 17, 2014 * |:Ntree| [dir] wasn't working properly; fixed
- Mar 18, 2014 * Changed all uses of set to setl
- Mar 18, 2014 * Commented the netrw_btkeep line in
- s:NetrwOptionSave(); the effect is that
- netrw buffers will remain as |'bt'|=nofile.
- This should prevent swapfiles being created
- for netrw buffers.
- Mar 20, 2014 * Changed all uses of lcd to use s:NetrwLcd()
- instead. Consistent error handling results
- and it also handles Window's shares
- * Fixed |netrw-d| command when applied with ftp
- * https: support included for netrw#NetRead()
- v150: Jul 12, 2013 * removed a "keepalt" to allow ":e #" to {{{2
- return to the netrw directory listing
- Jul 13, 2013 * (Jonas Diemer) suggested changing
- a <cWORD> to <cfile>.
- Jul 21, 2013 * (Yuri Kanivetsky) reported that netrw's
- use of mkdir did not produce directories
- following the user's umask.
- Aug 27, 2013 * introduced |g:netrw_altfile| option
- Sep 05, 2013 * s:Strlen() now uses |strdisplaywidth()|
- when available, by default
- Sep 12, 2013 * (Selyano Baldo) reported that netrw wasn't
- opening some directories properly from the
- command line.
- Nov 09, 2013 * |:Lexplore| introduced
- * (Ondrej Platek) reported an issue with
- netrw's trees (P15). Fixed.
- * (Jorge Solis) reported that "t" in
- tree mode caused netrw to forget its
- line position.
- Dec 05, 2013 * Added <s-leftmouse> file marking
- (see |netrw-mf|)
- Dec 05, 2013 * (Yasuhiro Matsumoto) Explore should use
- strlen() instead s:Strlen() when handling
- multibyte chars with strpart()
- (ie. strpart() is byte oriented, not
- display-width oriented).
- Dec 09, 2013 * (Ken Takata) Provided a patch; File sizes
- and a portion of timestamps were wrongly
- highlighted with the directory color when
- setting `:let g:netrw_liststyle=1` on Windows.
- * (Paul Domaskis) noted that sometimes
- cursorline was activating in non-netrw
- windows. All but one setting of cursorline
- was done via setl; there was one that was
- overlooked. Fixed.
- Dec 24, 2013 * (esquifit) asked that netrw allow the
- /cygdrive prefix be a user-alterable
- parameter.
- Jan 02, 2014 * Fixed a problem with netrw-based balloon
- evaluation (ie. netrw#NetrwBalloonHelp()
- not having been loaded error messages)
- Jan 03, 2014 * Fixed a problem with tree listings
- * New command installed: |:Ntree|
- Jan 06, 2014 * (Ivan Brennan) reported a problem with
- |netrw-P|. Fixed.
- Jan 06, 2014 * Fixed a problem with |netrw-P| when the
- modified file was to be abandoned.
- Jan 15, 2014 * (Matteo Cavalleri) reported that when the
- banner is suppressed and tree listing is
- used, a blank line was left at the top of
- the display. Fixed.
- Jan 20, 2014 * (Gideon Go) reported that, in tree listing
- style, with a previous window open, that
- the wrong directory was being used to open
- a file. Fixed. (P21)
- v149: Apr 18, 2013 * in wide listing format, now have maps for {{{2
- w and b to move to next/previous file
- Apr 26, 2013 * one may now copy files in the same
- directory; netrw will issue requests for
- what names the files should be copied under
- Apr 29, 2013 * Trying Benzinger's problem again. Seems
- that commenting out the BufEnter and
- installing VimEnter (only) works. Weird
- problem! (tree listing, vim -O Dir1 Dir2)
- May 01, 2013 * :Explore ftp://... wasn't working. Fixed.
- May 02, 2013 * introduced |g:netrw_bannerbackslash| as
- requested by Paul Domaskis.
- Jul 03, 2013 * Explore now avoids splitting when a buffer
- will be hidden.
- v148: Apr 16, 2013 * changed Netrw's Style menu to allow direct {{{2
- choice of listing style, hiding style, and
- sorting style
-
-==============================================================================
-13. Todo *netrw-todo* {{{1
-
-07/29/09 : banner :|g:netrw_banner| can be used to suppress the
- suppression banner. This feature is new and experimental,
- so its in the process of being debugged.
-09/04/09 : "gp" : See if it can be made to work for remote systems.
- : See if it can be made to work with marked files.
-
==============================================================================
-14. Credits *netrw-credits* {{{1
+11. Credits *netrw-credits* {{{1
Vim editor by Bram Moolenaar (Thanks, Bram!)
dav support by C Campbell
diff --git a/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim b/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim
new file mode 100644
index 0000000000..8d10c00153
--- /dev/null
+++ b/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim
@@ -0,0 +1,214 @@
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Former Maintainer: Charles E Campbell
+" Upstream: <https://github.com/saccarosium/netrw.vim>
+" Copyright: Copyright (C) 1999-2021 Charles E. Campbell {{{
+" Permission is hereby granted to use and distribute this code,
+" with or without modifications, provided that this copyright
+" notice is copied with it. Like anything else that's free,
+" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided
+" *as is* and comes with no warranty of any kind, either
+" expressed or implied. By using this plugin, you agree that
+" in no event will the copyright holder be liable for any damages
+" resulting from the use of this software. }}}
+
+if &cp || exists("g:loaded_netrwPlugin")
+ finish
+endif
+
+let g:loaded_netrwPlugin = "v175"
+
+let s:keepcpo = &cpo
+set cpo&vim
+
+" Commands Launch/URL: {{{
+
+command -complete=shellcmd -nargs=1 Launch call netrw#Launch(trim(<q-args>))
+command -complete=file -nargs=1 Open call netrw#Open(trim(<q-args>))
+
+" }}}
+" Local Browsing Autocmds: {{{
+
+augroup FileExplorer
+ au!
+ au BufLeave * if &ft != "netrw"|let w:netrw_prvfile= expand("%:p")|endif
+ au BufEnter * sil call s:LocalBrowse(expand("<amatch>"))
+ au VimEnter * sil call s:VimEnter(expand("<amatch>"))
+ if has("win32")
+ au BufEnter .* sil call s:LocalBrowse(expand("<amatch>"))
+ endif
+augroup END
+
+" }}}
+" Network Browsing Reading Writing: {{{
+
+augroup Network
+ au!
+ au BufReadCmd file://* call netrw#FileUrlEdit(expand("<amatch>"))
+ au BufReadCmd ftp://*,rcp://*,scp://*,http://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(2,expand("<amatch>"))|exe "sil doau BufReadPost ".fnameescape(expand("<amatch>"))
+ au FileReadCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "sil doau FileReadPost ".fnameescape(expand("<amatch>"))
+ au BufWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau BufWritePost ".fnameescape(expand("<amatch>"))
+ au FileWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau FileWritePost ".fnameescape(expand("<amatch>"))
+ try
+ au SourceCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
+ catch /^Vim\%((\a\+)\)\=:E216/
+ au SourcePre ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
+ endtry
+augroup END
+
+" }}}
+" Commands: :Nread, :Nwrite, :NetUserPass {{{
+
+command! -count=1 -nargs=* Nread let s:svpos= winsaveview()<bar>call netrw#NetRead(<count>,<f-args>)<bar>call winrestview(s:svpos)
+command! -range=% -nargs=* Nwrite let s:svpos= winsaveview()<bar><line1>,<line2>call netrw#NetWrite(<f-args>)<bar>call winrestview(s:svpos)
+command! -nargs=* NetUserPass call NetUserPass(<f-args>)
+command! -nargs=* Nsource let s:svpos= winsaveview()<bar>call netrw#NetSource(<f-args>)<bar>call winrestview(s:svpos)
+command! -nargs=? Ntree call netrw#SetTreetop(1,<q-args>)
+
+" }}}
+" Commands: :Explore, :Sexplore, Hexplore, Vexplore, Lexplore {{{
+
+command! -nargs=* -bar -bang -count=0 -complete=dir Explore call netrw#Explore(<count>, 0, 0+<bang>0, <q-args>)
+command! -nargs=* -bar -bang -count=0 -complete=dir Sexplore call netrw#Explore(<count>, 1, 0+<bang>0, <q-args>)
+command! -nargs=* -bar -bang -count=0 -complete=dir Hexplore call netrw#Explore(<count>, 1, 2+<bang>0, <q-args>)
+command! -nargs=* -bar -bang -count=0 -complete=dir Vexplore call netrw#Explore(<count>, 1, 4+<bang>0, <q-args>)
+command! -nargs=* -bar -count=0 -complete=dir Texplore call netrw#Explore(<count>, 0, 6, <q-args>)
+command! -nargs=* -bar -bang -count=0 -complete=dir Lexplore call netrw#Lexplore(<count>, <bang>0, <q-args>)
+command! -nargs=* -bar -bang Nexplore call netrw#Explore(-1, 0, 0, <q-args>)
+command! -nargs=* -bar -bang Pexplore call netrw#Explore(-2, 0, 0, <q-args>)
+
+" }}}
+" Commands: NetrwSettings {{{
+
+command! -nargs=0 NetrwSettings call netrwSettings#NetrwSettings()
+command! -bang NetrwClean call netrw#Clean(<bang>0)
+
+" }}}
+" Maps: {{{
+
+if !exists("g:netrw_nogx")
+ if maparg('gx','n') == ""
+ if !hasmapto('<Plug>NetrwBrowseX')
+ nmap <unique> gx <Plug>NetrwBrowseX
+ endif
+ nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX()))<cr>
+ endif
+ if maparg('gx','x') == ""
+ if !hasmapto('<Plug>NetrwBrowseXVis')
+ xmap <unique> gx <Plug>NetrwBrowseXVis
+ endif
+ xno <silent> <Plug>NetrwBrowseXVis :<c-u>call netrw#BrowseXVis()<cr>
+ endif
+endif
+
+if exists("g:netrw_usetab") && g:netrw_usetab
+ if maparg('<c-tab>','n') == ""
+ nmap <unique> <c-tab> <Plug>NetrwShrink
+ endif
+ nno <silent> <Plug>NetrwShrink :call netrw#Shrink()<cr>
+endif
+
+" }}}
+" LocalBrowse: invokes netrw#LocalBrowseCheck() on directory buffers {{{
+
+function! s:LocalBrowse(dirname)
+ " do not trigger in the terminal
+ " https://github.com/vim/vim/issues/16463
+ if &buftype ==# 'terminal'
+ return
+ endif
+
+ if !exists("s:vimentered")
+ " If s:vimentered doesn't exist, then the VimEnter event hasn't fired. It will,
+ " and so s:VimEnter() will then be calling this routine, but this time with s:vimentered defined.
+ return
+ endif
+
+ if has("amiga")
+ " The check against '' is made for the Amiga, where the empty
+ " string is the current directory and not checking would break
+ " things such as the help command.
+ if a:dirname != '' && isdirectory(a:dirname)
+ sil! call netrw#LocalBrowseCheck(a:dirname)
+ if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt
+ exe w:netrw_bannercnt
+ endif
+ endif
+ elseif isdirectory(a:dirname)
+ " Jul 13, 2021: for whatever reason, preceding the following call with
+ " a sil! causes an unbalanced if-endif vim error
+ call netrw#LocalBrowseCheck(a:dirname)
+ if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt
+ exe w:netrw_bannercnt
+ endif
+ endif
+endfunction
+
+" }}}
+" s:VimEnter: after all vim startup stuff is done, this function is called. {{{
+" Its purpose: to look over all windows and run s:LocalBrowse() on
+" them, which checks if they're directories and will create a directory
+" listing when appropriate.
+" It also sets s:vimentered, letting s:LocalBrowse() know that s:VimEnter()
+" has already been called.
+function! s:VimEnter(dirname)
+ if has('nvim') || v:version < 802
+ " Johann Höchtl: reported that the call range... line causes an E488: Trailing characters
+ " error with neovim. I suspect its because neovim hasn't updated with recent
+ " vim patches. As is, this code will have problems with popup terminals
+ " instantiated before the VimEnter event runs.
+ " Ingo Karkat : E488 also in Vim 8.1.1602
+ let curwin = winnr()
+ let s:vimentered = 1
+ windo call s:LocalBrowse(expand("%:p"))
+ exe curwin."wincmd w"
+ else
+ " the following complicated expression comes courtesy of lacygoill; largely does the same thing as the windo and
+ " wincmd which are commented out, but avoids some side effects. Allows popup terminal before VimEnter.
+ let s:vimentered = 1
+ call range(1, winnr('$'))->map({_, v -> win_execute(win_getid(v), 'call expand("%:p")->s:LocalBrowse()')})
+ endif
+endfunction
+
+" }}}
+" NetrwStatusLine: {{{
+
+function! NetrwStatusLine()
+ if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list")
+ let &stl= s:netrw_explore_stl
+ unlet! w:netrw_explore_bufnr w:netrw_explore_line
+ return ""
+ else
+ return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen
+ endif
+endfunction
+
+" }}}
+" NetUserPass: set username and password for subsequent ftp transfer {{{
+" Usage: :call NetUserPass() -- will prompt for userid and password
+" :call NetUserPass("uid") -- will prompt for password
+" :call NetUserPass("uid","password") -- sets global userid and password
+function! NetUserPass(...)
+ " get/set userid
+ if a:0 == 0
+ if !exists("g:netrw_uid") || g:netrw_uid == ""
+ " via prompt
+ let g:netrw_uid= input('Enter username: ')
+ endif
+ else " from command line
+ let g:netrw_uid= a:1
+ endif
+
+ " get password
+ if a:0 <= 1 " via prompt
+ let g:netrw_passwd= inputsecret("Enter Password: ")
+ else " from command line
+ let g:netrw_passwd=a:2
+ endif
+endfunction
+
+" }}}
+
+let &cpo= s:keepcpo
+unlet s:keepcpo
+
+" vim:ts=8 sts=4 sw=4 et fdm=marker
diff --git a/runtime/pack/dist/opt/netrw/syntax/netrw.vim b/runtime/pack/dist/opt/netrw/syntax/netrw.vim
new file mode 100644
index 0000000000..8042854a12
--- /dev/null
+++ b/runtime/pack/dist/opt/netrw/syntax/netrw.vim
@@ -0,0 +1,149 @@
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Former Maintainer: Charles E Campbell
+" Upstream: <https://github.com/saccarosium/netrw.vim>
+" Language: Netrw Listing Syntax
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let b:current_syntax = "netrwlist"
+
+" Directory List Syntax Highlighting: {{{
+
+syn cluster NetrwGroup contains=netrwHide,netrwSortBy,netrwSortSeq,netrwQuickHelp,netrwVersion,netrwCopyTgt
+syn cluster NetrwTreeGroup contains=netrwDir,netrwSymLink,netrwExe
+
+syn match netrwPlain "\(\S\+ \)*\S\+" contains=netrwLink,@NoSpell
+syn match netrwSpecial "\%(\S\+ \)*\S\+[*|=]\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
+syn match netrwDir "\.\{1,2}/" contains=netrwClassify,@NoSpell
+syn match netrwDir "\%(\S\+ \)*\S\+/\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
+syn match netrwSizeDate "\<\d\+\s\d\{1,2}/\d\{1,2}/\d\{4}\s" skipwhite contains=netrwDateSep,@NoSpell nextgroup=netrwTime
+syn match netrwSymLink "\%(\S\+ \)*\S\+@\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
+syn match netrwExe "\%(\S\+ \)*\S*[^~]\*\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
+if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4')
+ syn match netrwTreeBar "^\%([-+|│] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup
+else
+ syn match netrwTreeBar "^\%([-+|] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup
+endif
+syn match netrwTreeBarSpace " " contained
+
+syn match netrwClassify "[*=|@/]\ze\%(\s\{2,}\|$\)" contained
+syn match netrwDateSep "/" contained
+syn match netrwTime "\d\{1,2}:\d\{2}:\d\{2}" contained contains=netrwTimeSep
+syn match netrwTimeSep ":"
+
+syn match netrwComment '".*\%(\t\|$\)' contains=@NetrwGroup,@NoSpell
+syn match netrwHide '^"\s*\(Hid\|Show\)ing:' skipwhite contains=@NoSpell nextgroup=netrwHidePat
+syn match netrwSlash "/" contained
+syn match netrwHidePat "[^,]\+" contained skipwhite contains=@NoSpell nextgroup=netrwHideSep
+syn match netrwHideSep "," contained skipwhite nextgroup=netrwHidePat
+syn match netrwSortBy "Sorted by" contained transparent skipwhite nextgroup=netrwList
+syn match netrwSortSeq "Sort sequence:" contained transparent skipwhite nextgroup=netrwList
+syn match netrwCopyTgt "Copy/Move Tgt:" contained transparent skipwhite nextgroup=netrwList
+syn match netrwList ".*$" contained contains=netrwComma,@NoSpell
+syn match netrwComma "," contained
+syn region netrwQuickHelp matchgroup=Comment start="Quick Help:\s\+" end="$" contains=netrwHelpCmd,netrwQHTopic,@NoSpell keepend contained
+syn match netrwHelpCmd "\S\+\ze:" contained skipwhite contains=@NoSpell nextgroup=netrwCmdSep
+syn match netrwQHTopic "([a-zA-Z &]\+)" contained skipwhite
+syn match netrwCmdSep ":" contained nextgroup=netrwCmdNote
+syn match netrwCmdNote ".\{-}\ze " contained contains=@NoSpell
+syn match netrwVersion "(netrw.*)" contained contains=@NoSpell
+syn match netrwLink "-->" contained skipwhite
+
+" }}}
+" Special filetype highlighting {{{
+
+if exists("g:netrw_special_syntax") && g:netrw_special_syntax
+ if exists("+suffixes") && &suffixes != ""
+ let suflist= join(split(&suffixes,','))
+ let suflist= escape(substitute(suflist," ",'\\|','g'),'.~')
+ exe "syn match netrwSpecFile '\\(\\S\\+ \\)*\\S*\\(".suflist."\\)\\>' contains=netrwTreeBar,@NoSpell"
+ endif
+ syn match netrwBak "\(\S\+ \)*\S\+\.bak\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwCompress "\(\S\+ \)*\S\+\.\%(gz\|bz2\|Z\|zip\)\>" contains=netrwTreeBar,@NoSpell
+ if has("unix")
+ syn match netrwCoreDump "\<core\%(\.\d\+\)\=\>" contains=netrwTreeBar,@NoSpell
+ endif
+ syn match netrwLex "\(\S\+ \)*\S\+\.\%(l\|lex\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwYacc "\(\S\+ \)*\S\+\.y\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwData "\(\S\+ \)*\S\+\.dat\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwDoc "\(\S\+ \)*\S\+\.\%(doc\|txt\|pdf\|ps\|docx\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwHdr "\(\S\+ \)*\S\+\.\%(h\|hpp\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwLib "\(\S\+ \)*\S*\.\%(a\|so\|lib\|dll\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwMakeFile "\<[mM]akefile\>\|\(\S\+ \)*\S\+\.mak\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwObj "\(\S\+ \)*\S*\.\%(o\|obj\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwPix "\c\(\S\+ \)*\S*\.\%(bmp\|fits\=\|gif\|je\=pg\|pcx\|ppc\|pgm\|png\|ppm\|psd\|rgb\|tif\|xbm\|xcf\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwTags "\<\(ANmenu\|ANtags\)\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwTags "\<tags\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwTilde "\(\S\+ \)*\S\+\~\*\=\>" contains=netrwTreeBar,@NoSpell
+ syn match netrwTmp "\<tmp\(\S\+ \)*\S\+\>\|\(\S\+ \)*\S*tmp\>" contains=netrwTreeBar,@NoSpell
+endif
+
+" }}}
+" Highlighting Links: {{{
+
+if !exists("did_drchip_netrwlist_syntax")
+ let did_drchip_netrwlist_syntax= 1
+ hi default link netrwClassify Function
+ hi default link netrwCmdSep Delimiter
+ hi default link netrwComment Comment
+ hi default link netrwDir Directory
+ hi default link netrwHelpCmd Function
+ hi default link netrwQHTopic Number
+ hi default link netrwHidePat Statement
+ hi default link netrwHideSep netrwComment
+ hi default link netrwList Statement
+ hi default link netrwVersion Identifier
+ hi default link netrwSymLink Question
+ hi default link netrwExe PreProc
+ hi default link netrwDateSep Delimiter
+
+ hi default link netrwTreeBar Special
+ hi default link netrwTimeSep netrwDateSep
+ hi default link netrwComma netrwComment
+ hi default link netrwHide netrwComment
+ hi default link netrwMarkFile TabLineSel
+ hi default link netrwLink Special
+
+ " special syntax highlighting (see :he g:netrw_special_syntax)
+ hi default link netrwCoreDump WarningMsg
+ hi default link netrwData Folded
+ hi default link netrwHdr netrwPlain
+ hi default link netrwLex netrwPlain
+ hi default link netrwLib DiffChange
+ hi default link netrwMakefile DiffChange
+ hi default link netrwYacc netrwPlain
+ hi default link netrwPix Special
+
+ hi default link netrwBak netrwGray
+ hi default link netrwCompress netrwGray
+ hi default link netrwSpecFile netrwGray
+ hi default link netrwObj netrwGray
+ hi default link netrwTags netrwGray
+ hi default link netrwTilde netrwGray
+ hi default link netrwTmp netrwGray
+endif
+
+" set up netrwGray to be understated (but not Ignore'd or Conceal'd, as those
+" can be hard/impossible to read). Users may override this in a colorscheme by
+" specifying netrwGray highlighting.
+redir => s:netrwgray
+sil hi netrwGray
+redir END
+
+if s:netrwgray !~ 'guifg'
+ if has("gui") && has("gui_running")
+ if &bg == "dark"
+ exe "hi netrwGray gui=NONE guifg=gray30"
+ else
+ exe "hi netrwGray gui=NONE guifg=gray70"
+ endif
+ else
+ hi link netrwGray Folded
+ endif
+endif
+
+" }}}
+
+" vim:ts=8 sts=4 sw=4 et fdm=marker
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index fd8c4b8817..b6da91a833 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -38,7 +38,7 @@
" NEOVIM COMPATIBILITY
"
" The vim specific functionalities were replaced with neovim specific calls:
-" - term_start -> termopen
+" - term_start -> `jobstart(…, {'term': v:true})`
" - term_sendkeys -> chansend
" - term_getline -> getbufline
" - job_info && term_getjob -> nvim_get_chan_info
@@ -251,7 +251,7 @@ endfunc
" Open a terminal window without a job, to run the debugged program in.
func s:StartDebug_term(dict)
execute s:vertical ? 'vnew' : 'new'
- let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
+ let s:pty_job_id = jobstart('tail -f /dev/null;#gdb program', {'term': v:true})
if s:pty_job_id == 0
call s:Echoerr('Invalid argument (or job table is full) while opening terminal window')
return
@@ -323,7 +323,7 @@ func s:StartDebug_term(dict)
execute 'new'
" call ch_log($'executing "{join(gdb_cmd)}"')
- let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')})
+ let s:gdb_job_id = jobstart(gdb_cmd, {'term': v:true, 'on_exit': function('s:EndTermDebug')})
if s:gdb_job_id == 0
call s:Echoerr('Invalid argument (or job table is full) while opening gdb terminal window')
exe 'bwipe! ' . s:ptybufnr
@@ -491,7 +491,7 @@ func s:StartDebug_prompt(dict)
" Unix: Run the debugged program in a terminal window. Open it below the
" gdb window.
belowright new
- let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
+ let s:pty_job_id = jobstart('tail -f /dev/null;#gdb program', {'term': v:true})
if s:pty_job_id == 0
call s:Echoerr('Invalid argument (or job table is full) while opening terminal window')
return
diff --git a/runtime/plugin/editorconfig.lua b/runtime/plugin/editorconfig.lua
index a96919e1fe..357146cabd 100644
--- a/runtime/plugin/editorconfig.lua
+++ b/runtime/plugin/editorconfig.lua
@@ -1,4 +1,4 @@
-local group = vim.api.nvim_create_augroup('editorconfig', {})
+local group = vim.api.nvim_create_augroup('nvim.editorconfig', {})
vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead', 'BufFilePost' }, {
group = group,
callback = function(args)
diff --git a/runtime/plugin/man.lua b/runtime/plugin/man.lua
index 512b1f63e8..e707b68859 100644
--- a/runtime/plugin/man.lua
+++ b/runtime/plugin/man.lua
@@ -8,9 +8,9 @@ vim.api.nvim_create_user_command('Man', function(params)
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)
+ local err = man.open_page(params.count, params.smods, params.fargs)
+ if err then
+ vim.notify('man.lua: ' .. err, vim.log.levels.ERROR)
end
end
end, {
@@ -24,13 +24,16 @@ end, {
end,
})
-local augroup = vim.api.nvim_create_augroup('man', {})
+local augroup = vim.api.nvim_create_augroup('nvim.man', {})
vim.api.nvim_create_autocmd('BufReadCmd', {
group = augroup,
pattern = 'man://*',
nested = true,
callback = function(params)
- require('man').read_page(vim.fn.matchstr(params.match, 'man://\\zs.*'))
+ local err = require('man').read_page(assert(params.match:match('man://(.*)')))
+ if err then
+ vim.notify('man.lua: ' .. err, vim.log.levels.ERROR)
+ end
end,
})
diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim
index 661a34b578..13d1b6824f 100644
--- a/runtime/plugin/matchparen.vim
+++ b/runtime/plugin/matchparen.vim
@@ -109,6 +109,10 @@ func s:Highlight_Matching_Pair()
if !has("syntax") || !exists("g:syntax_on")
let s_skip = "0"
+ elseif exists("b:ts_highlight") && &syntax != 'on'
+ let s_skip = "match(v:lua.vim.treesitter.get_captures_at_cursor(), '"
+ \ .. 'string\|character\|singlequote\|escape\|symbol\|comment'
+ \ .. "') != -1"
else
" Build an expression that detects whether the current cursor position is
" in certain syntax types (string, comment, etc.), for use as
diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim
index 775b650e71..6d7a8660ff 100644
--- a/runtime/plugin/netrwPlugin.vim
+++ b/runtime/plugin/netrwPlugin.vim
@@ -1,233 +1,9 @@
-" netrwPlugin.vim: Handles file transfer and remote directory listing across a network
-" PLUGIN SECTION
-" Maintainer: This runtime file is looking for a new maintainer.
-" Date: Sep 09, 2021
-" Last Change:
-" 2024 May 08 by Vim Project: cleanup legacy Win9X checks
-" 2024 Oct 27 by Vim Project: cleanup gx mapping
-" 2024 Oct 28 by Vim Project: further improvements
-" 2024 Oct 31 by Vim Project: use autoloaded functions
-" Former Maintainer: Charles E Campbell
-" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
-" Copyright: Copyright (C) 1999-2021 Charles E. Campbell {{{1
-" Permission is hereby granted to use and distribute this code,
-" with or without modifications, provided that this copyright
-" notice is copied with it. Like anything else that's free,
-" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided
-" *as is* and comes with no warranty of any kind, either
-" expressed or implied. By using this plugin, you agree that
-" in no event will the copyright holder be liable for any damages
-" resulting from the use of this software.
-"
-" But be doers of the Word, and not only hearers, deluding your own selves {{{1
-" (James 1:22 RSV)
-" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-" Load Once: {{{1
-if &cp || exists("g:loaded_netrwPlugin")
- finish
-endif
-let g:loaded_netrwPlugin = "v173"
-let s:keepcpo = &cpo
-set cpo&vim
-"DechoRemOn
-
-" ---------------------------------------------------------------------
-" Public Interface: {{{1
-
-" Commands Launch/URL {{{2
-command -complete=shellcmd -nargs=1 Launch call netrw#Launch(trim(<q-args>))
-command -complete=file -nargs=1 Open call netrw#Open(trim(<q-args>))
-" " }}}
-" Local Browsing Autocmds: {{{2
-augroup FileExplorer
- au!
- au BufLeave * if &ft != "netrw"|let w:netrw_prvfile= expand("%:p")|endif
- au BufEnter * sil call s:LocalBrowse(expand("<amatch>"))
- au VimEnter * sil call s:VimEnter(expand("<amatch>"))
- if has("win32")
- au BufEnter .* sil call s:LocalBrowse(expand("<amatch>"))
- endif
-augroup END
-
-" Network Browsing Reading Writing: {{{2
-augroup Network
- au!
- au BufReadCmd file://* call netrw#FileUrlEdit(expand("<amatch>"))
- au BufReadCmd ftp://*,rcp://*,scp://*,http://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(2,expand("<amatch>"))|exe "sil doau BufReadPost ".fnameescape(expand("<amatch>"))
- au FileReadCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "sil doau FileReadPost ".fnameescape(expand("<amatch>"))
- au BufWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau BufWritePost ".fnameescape(expand("<amatch>"))
- au FileWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau FileWritePost ".fnameescape(expand("<amatch>"))
- try
- au SourceCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
- catch /^Vim\%((\a\+)\)\=:E216/
- au SourcePre ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>"))
- endtry
-augroup END
-
-" Commands: :Nread, :Nwrite, :NetUserPass {{{2
-com! -count=1 -nargs=* Nread let s:svpos= winsaveview()<bar>call netrw#NetRead(<count>,<f-args>)<bar>call winrestview(s:svpos)
-com! -range=% -nargs=* Nwrite let s:svpos= winsaveview()<bar><line1>,<line2>call netrw#NetWrite(<f-args>)<bar>call winrestview(s:svpos)
-com! -nargs=* NetUserPass call NetUserPass(<f-args>)
-com! -nargs=* Nsource let s:svpos= winsaveview()<bar>call netrw#NetSource(<f-args>)<bar>call winrestview(s:svpos)
-com! -nargs=? Ntree call netrw#SetTreetop(1,<q-args>)
+" Load the netrw package.
-" Commands: :Explore, :Sexplore, Hexplore, Vexplore, Lexplore {{{2
-com! -nargs=* -bar -bang -count=0 -complete=dir Explore call netrw#Explore(<count>,0,0+<bang>0,<q-args>)
-com! -nargs=* -bar -bang -count=0 -complete=dir Sexplore call netrw#Explore(<count>,1,0+<bang>0,<q-args>)
-com! -nargs=* -bar -bang -count=0 -complete=dir Hexplore call netrw#Explore(<count>,1,2+<bang>0,<q-args>)
-com! -nargs=* -bar -bang -count=0 -complete=dir Vexplore call netrw#Explore(<count>,1,4+<bang>0,<q-args>)
-com! -nargs=* -bar -count=0 -complete=dir Texplore call netrw#Explore(<count>,0,6 ,<q-args>)
-com! -nargs=* -bar -bang Nexplore call netrw#Explore(-1,0,0,<q-args>)
-com! -nargs=* -bar -bang Pexplore call netrw#Explore(-2,0,0,<q-args>)
-com! -nargs=* -bar -bang -count=0 -complete=dir Lexplore call netrw#Lexplore(<count>,<bang>0,<q-args>)
-
-" Commands: NetrwSettings {{{2
-com! -nargs=0 NetrwSettings call netrwSettings#NetrwSettings()
-com! -bang NetrwClean call netrw#Clean(<bang>0)
-
-" Maps:
-if !exists("g:netrw_nogx")
- if maparg('gx','n') == ""
- if !hasmapto('<Plug>NetrwBrowseX')
- nmap <unique> gx <Plug>NetrwBrowseX
- endif
- nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX()))<cr>
- endif
- if maparg('gx','x') == ""
- if !hasmapto('<Plug>NetrwBrowseXVis')
- xmap <unique> gx <Plug>NetrwBrowseXVis
- endif
- xno <silent> <Plug>NetrwBrowseXVis :<c-u>call netrw#BrowseXVis()<cr>
- endif
-endif
-if exists("g:netrw_usetab") && g:netrw_usetab
- if maparg('<c-tab>','n') == ""
- nmap <unique> <c-tab> <Plug>NetrwShrink
- endif
- nno <silent> <Plug>NetrwShrink :call netrw#Shrink()<cr>
+if &cp || exists("g:loaded_netrw") || exists("g:loaded_netrwPlugin")
+ finish
endif
-" ---------------------------------------------------------------------
-" LocalBrowse: invokes netrw#LocalBrowseCheck() on directory buffers {{{2
-fun! s:LocalBrowse(dirname)
- " Unfortunate interaction -- only DechoMsg debugging calls can be safely used here.
- " Otherwise, the BufEnter event gets triggered when attempts to write to
- " the DBG buffer are made.
-
- if !exists("s:vimentered")
- " If s:vimentered doesn't exist, then the VimEnter event hasn't fired. It will,
- " and so s:VimEnter() will then be calling this routine, but this time with s:vimentered defined.
-" call Dfunc("s:LocalBrowse(dirname<".a:dirname.">) (s:vimentered doesn't exist)")
-" call Dret("s:LocalBrowse")
- return
- endif
-
-" call Dfunc("s:LocalBrowse(dirname<".a:dirname.">) (s:vimentered=".s:vimentered.")")
-
- if has("amiga")
- " The check against '' is made for the Amiga, where the empty
- " string is the current directory and not checking would break
- " things such as the help command.
-" call Decho("(LocalBrowse) dirname<".a:dirname."> (isdirectory, amiga)")
- if a:dirname != '' && isdirectory(a:dirname)
- sil! call netrw#LocalBrowseCheck(a:dirname)
- if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt
- exe w:netrw_bannercnt
- endif
- endif
-
- elseif isdirectory(a:dirname)
-" call Decho("(LocalBrowse) dirname<".a:dirname."> ft=".&ft." (isdirectory, not amiga)")
-" call Dredir("LocalBrowse ft last set: ","verbose set ft")
- " Jul 13, 2021: for whatever reason, preceding the following call with
- " a sil! causes an unbalanced if-endif vim error
- call netrw#LocalBrowseCheck(a:dirname)
- if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt
- exe w:netrw_bannercnt
- endif
-
- else
- " not a directory, ignore it
-" call Decho("(LocalBrowse) dirname<".a:dirname."> not a directory, ignoring...")
- endif
-
-" call Dret("s:LocalBrowse")
-endfun
-
-" ---------------------------------------------------------------------
-" s:VimEnter: after all vim startup stuff is done, this function is called. {{{2
-" Its purpose: to look over all windows and run s:LocalBrowse() on
-" them, which checks if they're directories and will create a directory
-" listing when appropriate.
-" It also sets s:vimentered, letting s:LocalBrowse() know that s:VimEnter()
-" has already been called.
-fun! s:VimEnter(dirname)
-" call Dfunc("s:VimEnter(dirname<".a:dirname.">) expand(%)<".expand("%").">")
- if has('nvim') || v:version < 802
- " Johann Höchtl: reported that the call range... line causes an E488: Trailing characters
- " error with neovim. I suspect its because neovim hasn't updated with recent
- " vim patches. As is, this code will have problems with popup terminals
- " instantiated before the VimEnter event runs.
- " Ingo Karkat : E488 also in Vim 8.1.1602
- let curwin = winnr()
- let s:vimentered = 1
- windo call s:LocalBrowse(expand("%:p"))
- exe curwin."wincmd w"
- else
- " the following complicated expression comes courtesy of lacygoill; largely does the same thing as the windo and
- " wincmd which are commented out, but avoids some side effects. Allows popup terminal before VimEnter.
- let s:vimentered = 1
- call range(1, winnr('$'))->map({_, v -> win_execute(win_getid(v), 'call expand("%:p")->s:LocalBrowse()')})
- endif
-" call Dret("s:VimEnter")
-endfun
-
-" ---------------------------------------------------------------------
-" NetrwStatusLine: {{{1
-fun! NetrwStatusLine()
-" let g:stlmsg= "Xbufnr=".w:netrw_explore_bufnr." bufnr=".bufnr("%")." Xline#".w:netrw_explore_line." line#".line(".")
- if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list")
- let &stl= s:netrw_explore_stl
- if exists("w:netrw_explore_bufnr")|unlet w:netrw_explore_bufnr|endif
- if exists("w:netrw_explore_line")|unlet w:netrw_explore_line|endif
- return ""
- else
- return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen
- endif
-endfun
-
-" ------------------------------------------------------------------------
-" NetUserPass: set username and password for subsequent ftp transfer {{{1
-" Usage: :call NetUserPass() -- will prompt for userid and password
-" :call NetUserPass("uid") -- will prompt for password
-" :call NetUserPass("uid","password") -- sets global userid and password
-fun! NetUserPass(...)
-
- " get/set userid
- if a:0 == 0
-" call Dfunc("NetUserPass(a:0<".a:0.">)")
- if !exists("g:netrw_uid") || g:netrw_uid == ""
- " via prompt
- let g:netrw_uid= input('Enter username: ')
- endif
- else " from command line
-" call Dfunc("NetUserPass(a:1<".a:1.">) {")
- let g:netrw_uid= a:1
- endif
-
- " get password
- if a:0 <= 1 " via prompt
-" call Decho("a:0=".a:0." case <=1:")
- let g:netrw_passwd= inputsecret("Enter Password: ")
- else " from command line
-" call Decho("a:0=".a:0." case >1: a:2<".a:2.">")
- let g:netrw_passwd=a:2
- endif
-" call Dret("NetUserPass")
-endfun
+packadd netrw
-" ------------------------------------------------------------------------
-" Modelines And Restoration: {{{1
-let &cpo= s:keepcpo
-unlet s:keepcpo
-" vim:ts=8 fdm=marker
+" vim:ts=8 sts=2 sw=2 et
diff --git a/runtime/plugin/osc52.lua b/runtime/plugin/osc52.lua
index 7ffd64342e..c7f1cbe2e3 100644
--- a/runtime/plugin/osc52.lua
+++ b/runtime/plugin/osc52.lua
@@ -6,7 +6,15 @@ for _, ui in ipairs(vim.api.nvim_list_uis()) do
end
end
-if not tty or vim.g.clipboard ~= nil or vim.o.clipboard ~= '' or not os.getenv('SSH_TTY') then
+-- Do not query when any of the following is true:
+-- * TUI is not attached
+-- * OSC 52 support is explicitly disabled via g:termfeatures
+-- * Using a badly behaved terminal
+if
+ not tty
+ or (vim.g.termfeatures ~= nil and vim.g.termfeatures.osc52 == false)
+ or vim.env.TERM_PROGRAM == 'Apple_Terminal'
+then
return
end
@@ -17,28 +25,13 @@ require('vim.termcap').query('Ms', function(cap, found, seq)
assert(cap == 'Ms')
- -- Check 'clipboard' and g:clipboard again to avoid a race condition
- if vim.o.clipboard ~= '' or vim.g.clipboard ~= nil then
- return
- end
-
-- If the terminal reports a sequence other than OSC 52 for the Ms capability
-- then ignore it. We only support OSC 52 (for now)
if not seq or not seq:match('^\027%]52') then
return
end
- local osc52 = require('vim.ui.clipboard.osc52')
-
- vim.g.clipboard = {
- name = 'OSC 52',
- copy = {
- ['+'] = osc52.copy('+'),
- ['*'] = osc52.copy('*'),
- },
- paste = {
- ['+'] = osc52.paste('+'),
- ['*'] = osc52.paste('*'),
- },
- }
+ local termfeatures = vim.g.termfeatures or {}
+ termfeatures.osc52 = true
+ vim.g.termfeatures = termfeatures
end)
diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm
index eba272d5c9..bd6857fd17 100644
--- a/runtime/queries/c/highlights.scm
+++ b/runtime/queries/c/highlights.scm
@@ -252,13 +252,22 @@
; Preproc def / undef
(preproc_def
- name: (_) @constant)
+ name: (_) @constant.macro)
(preproc_call
directive: (preproc_directive) @_u
- argument: (_) @constant
+ argument: (_) @constant.macro
(#eq? @_u "#undef"))
+(preproc_ifdef
+ name: (identifier) @constant.macro)
+
+(preproc_elifdef
+ name: (identifier) @constant.macro)
+
+(preproc_defined
+ (identifier) @constant.macro)
+
(call_expression
function: (identifier) @function.call)
diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm
index 01c280f2d5..79ab165aa7 100644
--- a/runtime/queries/lua/highlights.scm
+++ b/runtime/queries/lua/highlights.scm
@@ -151,8 +151,6 @@
((identifier) @constant
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
-(vararg_expression) @constant
-
(nil) @constant.builtin
[
diff --git a/runtime/queries/markdown_inline/highlights.scm b/runtime/queries/markdown_inline/highlights.scm
index 148ef0fad0..5fb9e911dd 100644
--- a/runtime/queries/markdown_inline/highlights.scm
+++ b/runtime/queries/markdown_inline/highlights.scm
@@ -40,14 +40,12 @@
(image_description)
] @markup.link.label
-(inline_link
- (link_text) @_label
- (link_destination) @_url
+((inline_link
+ (link_destination) @_url) @_label
(#set! @_label url @_url))
-(image
- (image_description) @_label
- (link_destination) @_url
+((image
+ (link_destination) @_url) @_label
(#set! @_label url @_url))
; Conceal image links
@@ -93,9 +91,6 @@
(email_autolink)
] @markup.link.url @nospell
-((link_destination) @_url
- (#set! @_url url @_url))
-
((uri_autolink) @_url
(#offset! @_url 0 1 0 -1)
(#set! @_url url @_url))
diff --git a/runtime/queries/query/highlights.scm b/runtime/queries/query/highlights.scm
index e459b44602..cbd192a8ff 100644
--- a/runtime/queries/query/highlights.scm
+++ b/runtime/queries/query/highlights.scm
@@ -11,6 +11,9 @@
(named_node
name: (identifier) @variable)
+(missing_node
+ name: (identifier) @variable)
+
(field_definition
name: (identifier) @variable.member)
@@ -43,7 +46,12 @@
"#"
] @punctuation.special
-"_" @constant
+(predicate
+ "." @punctuation.special)
+
+"_" @character.special
+
+"MISSING" @keyword
((parameters
(identifier) @number)
diff --git a/runtime/queries/vim/highlights.scm b/runtime/queries/vim/highlights.scm
index 14e5a8128f..df7b3cf483 100644
--- a/runtime/queries/vim/highlights.scm
+++ b/runtime/queries/vim/highlights.scm
@@ -287,6 +287,7 @@
"=~"
"!~"
"="
+ "^="
"+="
"-="
"*="
diff --git a/runtime/syntax/apache.vim b/runtime/syntax/apache.vim
index e73045e4c8..65317fd36a 100644
--- a/runtime/syntax/apache.vim
+++ b/runtime/syntax/apache.vim
@@ -3,8 +3,8 @@
" Maintainer: David Necas (Yeti) <yeti@physics.muni.cz>
" License: This file can be redistribued and/or modified under the same terms
" as Vim itself.
-" Last Change: 2022 Apr 25
-" Notes: Last synced with apache-2.2.3, version 1.x is no longer supported
+" Last Change: 2024 Nov 24
+" Notes: Last synced with apache-2.4.62, version 1.x is no longer supported
" TODO: see particular FIXME's scattered through the file
" make it really linewise?
" + add `display' where appropriate
@@ -14,6 +14,7 @@ if exists("b:current_syntax")
finish
endif
+syn iskeyword @,48-57,_,192-255,-
syn case ignore
" Base constructs
@@ -45,9 +46,9 @@ syn keyword apacheMethodOption GET POST PUT DELETE CONNECT OPTIONS TRACE PATCH P
" Added as suggested by Mikko Koivunalho
syn keyword apacheMethodOption BASELINE-CONTROL CHECKIN CHECKOUT LABEL MERGE MKACTIVITY MKWORKSPACE REPORT UNCHECKOUT UPDATE VERSION-CONTROL contained
syn case ignore
-syn match apacheSection "<\/\=\(Directory\|DirectoryMatch\|Files\|FilesMatch\|IfModule\|IfDefine\|Location\|LocationMatch\|VirtualHost\)[^>]*>" contains=apacheAnything
+syn match apacheSection "<\/\=\(Directory\|Files\|If\|Else\|Location\|VirtualHost\)[^>]*>" contains=apacheAnything
syn match apacheSection "<\/\=\(RequireAll\|RequireAny\|RequireNone\)>" contains=apacheAnything
-syn match apacheLimitSection "<\/\=\(Limit\|LimitExcept\)[^>]*>" contains=apacheLimitSectionKeyword,apacheMethodOption,apacheError
+syn match apacheLimitSection "<\/\=Limit[^>]*>" contains=apacheLimitSectionKeyword,apacheMethodOption,apacheError
syn keyword apacheLimitSectionKeyword Limit LimitExcept contained
syn match apacheAuthType "AuthType\s.*$" contains=apacheAuthTypeValue
syn keyword apacheAuthTypeValue Basic Digest
@@ -68,7 +69,7 @@ syn keyword apacheDeclaration AuthAuthoritative AuthGroupFile AuthUserFile
syn keyword apacheDeclaration AuthBasicAuthoritative AuthBasicProvider
syn keyword apacheDeclaration AuthDigestAlgorithm AuthDigestDomain AuthDigestNcCheck AuthDigestNonceFormat AuthDigestNonceLifetime AuthDigestProvider AuthDigestQop AuthDigestShmemSize
syn keyword apacheOption none auth auth-int MD5 MD5-sess
-syn match apacheSection "<\/\=\(<AuthnProviderAlias\)[^>]*>" contains=apacheAnything
+syn match apacheSection "<\/\=Auth[ntz]ProviderAlias[^>]*>" contains=apacheAnything
syn keyword apacheDeclaration Anonymous Anonymous_Authoritative Anonymous_LogEmail Anonymous_MustGiveEmail Anonymous_NoUserID Anonymous_VerifyEmail
syn keyword apacheDeclaration AuthDBDUserPWQuery AuthDBDUserRealmQuery
syn keyword apacheDeclaration AuthDBMGroupFile AuthDBMAuthoritative
@@ -155,7 +156,7 @@ syn keyword apacheDeclaration PerlCleanupHandler PerlChildInitHandler PerlChildE
syn keyword apacheDeclaration PerlRestartHandler PerlDispatchHandler
syn keyword apacheDeclaration PerlFreshRestart PerlSendHeader
syn keyword apacheDeclaration php_value php_flag php_admin_value php_admin_flag
-syn match apacheSection "<\/\=\(Proxy\|ProxyMatch\)[^>]*>" contains=apacheAnything
+syn match apacheSection "<\/\=\(Macro\|MDomain\|Proxy\)[^>]*>" contains=apacheAnything
syn keyword apacheDeclaration AllowCONNECT NoProxy ProxyBadHeader ProxyBlock ProxyDomain ProxyErrorOverride ProxyIOBufferSize ProxyMaxForwards ProxyPass ProxyPassMatch ProxyPassReverse ProxyPassReverseCookieDomain ProxyPassReverseCookiePath ProxyPreserveHost ProxyReceiveBufferSize ProxyRemote ProxyRemoteMatch ProxyRequests ProxyTimeout ProxyVia
syn keyword apacheDeclaration RewriteBase RewriteCond RewriteEngine RewriteLock RewriteLog RewriteLogLevel RewriteMap RewriteOptions RewriteRule
syn keyword apacheOption inherit
@@ -173,8 +174,8 @@ syn keyword apacheDeclaration SuexecUserGroup
syn keyword apacheDeclaration UserDir
syn keyword apacheDeclaration CookieDomain CookieExpires CookieName CookieStyle CookieTracking
syn keyword apacheOption Netscape Cookie Cookie2 RFC2109 RFC2965
-syn match apacheSection "<\/\=\(<IfVersion\)[^>]*>" contains=apacheAnything
syn keyword apacheDeclaration VirtualDocumentRoot VirtualDocumentRootIP VirtualScriptAlias VirtualScriptAliasIP
+syn keyword apacheDeclaration AcceptErrorsNonFatal AsyncFilter AsyncRequestWorkerFactor AuthBasicFake AuthBasicUseDigestAlgorithm AuthBearerAuthoritative AuthBearerProvider AuthBearerProxy AuthDBMType AuthDBMUserFile AuthFormAuthoritative AuthFormBody AuthFormDisableNoStore AuthFormFakeBasicAuth AuthFormLocation AuthFormLoginRequiredLocation AuthFormLoginSuccessLocation AuthFormLogoutLocation AuthFormMethod AuthFormMimetype AuthFormPassword AuthFormProvider AuthFormSitePassphrase AuthFormSize AuthFormUsername AuthLDAPAuthorizePrefix AuthLDAPBindAuthoritative AuthLDAPCompareAsUser AuthLDAPInitialBindAsUser AuthLDAPInitialBindPattern AuthLDAPMaxSubGroupDepth AuthLDAPRemoteUserAttribute AuthLDAPSearchAsUser AuthLDAPSubGroupAttribute AuthLDAPSubGroupClass AuthLDAPURL AuthMerging AuthnCacheContext AuthnCacheEnable AuthnCacheProvideFor AuthnCacheSOCache AuthnCacheTimeout AuthnzFcgiCheckAuthnProvider AuthnzFcgiDefineProvider AuthtJwtClaim AuthtJwtDriver AuthtJwtSign AuthtJwtVerify AuthzDBDLoginToReferer AuthzDBDQuery AuthzDBDRedirectQuery AuthzSendForbiddenOnFailure BalancerGrowth BalancerInherit BalancerMember BalancerPersist BrotliAlterETag BrotliCompressionMaxInputBlock BrotliCompressionQuality BrotliCompressionWindow BrotliFilterNote BufferSize CacheDetailHeader CacheHeader CacheIgnoreQueryString CacheIgnoreURLSessionIdentifiers CacheKeyBaseURL CacheLock CacheLockMaxAge CacheLockPath CacheMinExpire CacheQuickHandler CacheReadSize CacheReadTime CacheSocache CacheSocacheMaxSize CacheSocacheMaxTime CacheSocacheMinTime CacheSocacheReadSize CacheSocacheReadTime CacheStaleOnError CacheStoreExpired CGIDScriptTimeout CGIPassAuth CGIScriptTimeout CGIVar CheckBasenameMatch ChrootDir CookieHTTPOnly CookieSameSite CookieSecure CryptoCipher CryptoDriver CryptoIV CryptoKey CryptoSize CTAuditStorage CTLogClient CTLogConfigDB CTMaxSCTAge CTProxyAwareness CTSCTStorage CTServerHelloSCTLimit CTStaticLogConfig CTStaticSCTs DBDInitSQL DefaultRuntimeDir DefaultStateDir DeflateAlterETag DeflateInflateLimitRequestBody DeflateInflateRatioBurst DeflateInflateRatioLimit DirectoryCheckHandler DTracePrivileges FallbackResource Files FilesMatch FirehoseConnectionInput FirehoseConnectionOutput FirehoseProxyConnectionInput FirehoseProxyConnectionOutput FirehoseRequestInput FirehoseRequestOutput FlushMaxPipelined FlushMaxThreshold GlobalLog GprofDir H2CopyFiles H2Direct H2EarlyHint H2EarlyHints H2MaxDataFrameLen H2MaxSessionStreams H2MaxWorkerIdleSeconds H2MaxWorkers H2MinWorkers H2ModernTLSOnly H2OutputBuffering H2Padding H2ProxyRequests H2Push H2PushDiarySize H2PushPriority H2PushResource H2SerializeHeaders H2StreamMaxMemSize H2StreamTimeout H2TLSCoolDownSecs H2TLSWarmUpSize H2Upgrade H2WebSockets H2WindowSize HeartbeatAddress HeartbeatListen HeartbeatMaxServers HeartbeatStorage HeartbeatStorage HostnameLookups HttpProtocolOptions IndexForbiddenReturn404 IndexHeadInsert InputSed ISAPIFakeAsync KeptBodySize LDAPConnectionPoolTTL LDAPLibraryDebug LDAPReferralHopLimit LDAPReferrals LDAPRetries LDAPRetryDelay LDAPTimeout Location LocationMatch LogIOTrackTTFB LogIOTrackTTFU LogMessage LuaAuthzProvider LuaCodeCache LuaHookAccessChecker LuaHookAuthChecker LuaHookCheckUserID LuaHookFixups LuaHookInsertFilter LuaHookLog LuaHookMapToStorage LuaHookPreTranslate LuaHookTranslateName LuaHookTypeChecker LuaInherit LuaInputFilter LuaMapHandler LuaOutputFilter LuaPackageCPath LuaPackagePath LuaQuickHandler LuaRoot LuaScope MacroIgnoreBadNesting MacroIgnoreEmptyArgs MaxConnectionsPerChild MaxRangeOverlaps MaxRangeReversals MaxRanges MaxRequestWorkers MDActivationDelay MDBaseServer MDCAChallenges MDCertificateAgreement MDCertificateAuthority MDCertificateCheck MDCertificateFile MDCertificateKeyFile MDCertificateMonitor MDCertificateProtocol MDCertificateStatus MDChallengeDns01 MDChallengeDns01Version MDCheckInterval MDContactEmail MDDriveMode MDExternalAccountBinding MDHttpProxy MDMatchNames MDMember MDMembers MDMessageCmd MDMustStaple MDNotifyCmd MDomain MDPortMap MDPrivateKeys MDRenewMode MDRenewWindow MDRequireHttps MDRetryDelay MDRetryFailover MDServerStatus MDStapleOthers MDStapling MDStaplingKeepResponse MDStaplingRenewWindow MDStoreDir MDStoreLocks MDWarnWindow MemcacheConnTTL MergeSlashes MergeTrailers MimeOptions ModemStandard Mutex Order OutputSed PolicyConditional PolicyConditionalURL PolicyEnvironment PolicyFilter PolicyKeepalive PolicyKeepaliveURL PolicyLength PolicyLengthURL PolicyMaxage PolicyMaxageURL PolicyNocache PolicyNocacheURL PolicyType PolicyTypeURL PolicyValidation PolicyValidationURL PolicyVary PolicyVaryURL PolicyVersion PolicyVersionURL PrivilegesMode Protocol Protocols ProtocolsHonorOrder Proxy100Continue ProxyAddHeaders ProxyExpressDBMFile ProxyExpressDBMType ProxyExpressEnable ProxyFCGIBackendType ProxyFCGISetEnvIf ProxyFtpDirCharset ProxyFtpEscapeWildcards ProxyFtpListOnWildcard ProxyHCExpr ProxyHCTemplate ProxyHCTPsize ProxyHTMLBufSize ProxyHTMLCharsetOut ProxyHTMLDocType ProxyHTMLEnable ProxyHTMLEvents ProxyHTMLExtended ProxyHTMLFixups ProxyHTMLInterp ProxyHTMLLinks ProxyHTMLMeta ProxyHTMLStripComments ProxyHTMLURLMap ProxySCGIInternalRedirect ProxySCGISendfile ProxySet ProxySourceAddress ProxyStatus ProxyWebsocketAsync ProxyWebsocketAsyncDelay ProxyWebsocketFallbackToProxyHttp ProxyWebsocketIdleTimeout QualifyRedirectURL ReadBufferSize ReceiveBufferSize RedisConnPoolTTL RedisTimeout ReflectorHeader RegexDefaultOptions RegisterHttpMethod RemoteIPHeader RemoteIPInternalProxy RemoteIPInternalProxyList RemoteIPProxiesHeader RemoteIPProxyProtocol RemoteIPProxyProtocolExceptions RemoteIPTrustedProxy RemoteIPTrustedProxyList RemoveLanguage RequestReadTimeout SeeRequestTail Session SessionCookieMaxAge SessionCookieName SessionCookieName2 SessionCookieRemove SessionCryptoCipher SessionCryptoDriver SessionCryptoPassphrase SessionCryptoPassphraseFile SessionDBDCookieName SessionDBDCookieName2 SessionDBDCookieRemove SessionDBDDeleteLabel SessionDBDInsertLabel SessionDBDPerUser SessionDBDSelectLabel SessionDBDUpdateLabel SessionEnv SessionExclude SessionExpiryUpdateInterval SessionHeader SessionInclude SessionMaxAge SSIETag SSILastModified SSILegacyExprParser SSLCARevocationCheck SSLClientHelloVars SSLOCSPDefaultResponder SSLOCSPEnable SSLOCSPNoverify SSLOCSPOverrideResponder SSLOCSPProxyURL SSLOCSPResponderCertificateFile SSLOCSPResponderTimeout SSLOCSPResponseMaxAge SSLOCSPResponseTimeSkew SSLOCSPUseRequestNonce SSLOpenSSLConfCmd SSLPolicy SSLProxyCARevocationCheck SSLProxyCheckPeerName SSLSRPUnknownUserSeed SSLSRPVerifierFile SSLStaplingCache SSLStaplingErrorCacheTimeout SSLStaplingFakeTryLater SSLStaplingForceURL SSLStaplingResponderTimeout SSLStaplingResponseMaxAge SSLStaplingResponseTimeSkew SSLStaplingReturnResponderErrors SSLStaplingStandardCacheTimeout SSLUseStapling StrictHostCheck Substitute SubstituteInheritBefore SubstituteMaxLineLength Suexec UNCList UnDefine UndefMacro Use UseCanonicalPhysicalPort VHostCGIMode VHostCGIPrivs VHostGroup VHostPrivs VHostSecure VHostUser Warning WatchdogInterval xml2EncAlias xml2EncDefault xml2StartParse
" Define the default highlighting
diff --git a/runtime/syntax/apkbuild.vim b/runtime/syntax/apkbuild.vim
new file mode 100644
index 0000000000..f969ff0e2e
--- /dev/null
+++ b/runtime/syntax/apkbuild.vim
@@ -0,0 +1,17 @@
+" Vim syntax file
+" Language: apkbuild
+" Maintainer: The Vim Project <https://github.com/vim/vim>
+" Last Change: 2024 Dec 22
+
+" quit when a syntax file was already loaded
+if exists("b:current_syntax")
+ finish
+endif
+
+" The actual syntax is in sh.vim and controlled by buffer-local variables.
+unlet! b:is_bash b:is_kornshell
+let b:is_sh = 1
+
+runtime! syntax/sh.vim
+
+let b:current_syntax = 'apkbuild'
diff --git a/runtime/syntax/asm.vim b/runtime/syntax/asm.vim
index 73f283a4a7..18f3de1c63 100644
--- a/runtime/syntax/asm.vim
+++ b/runtime/syntax/asm.vim
@@ -3,8 +3,8 @@
" Maintainer: Doug Kearns dougkearns@gmail.com
" Previous Maintainers: Erik Wognsen <erik.wognsen@gmail.com>
" Kevin Dahlhausen <kdahlhaus@yahoo.com>
-" Contributors: Ori Avtalion, Lakshay Garg
-" Last Change: 2020 Oct 31
+" Contributors: Ori Avtalion, Lakshay Garg, Nir Lichtman
+" Last Change: 2025 Jan 26
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -32,6 +32,9 @@ syn match asmType "\.single"
syn match asmType "\.space"
syn match asmType "\.string"
syn match asmType "\.word"
+syn match asmType "\.2byte"
+syn match asmType "\.4byte"
+syn match asmType "\.8byte"
syn match asmIdentifier "[a-z_][a-z0-9_]*"
syn match asmLabel "[a-z_][a-z0-9_]*:"he=e-1
diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim
index 30db9438d0..68b6778c73 100644
--- a/runtime/syntax/c.vim
+++ b/runtime/syntax/c.vim
@@ -1,7 +1,7 @@
" Vim syntax file
-" Language: C
-" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2023 Aug 10
+" Language: C
+" Maintainer: The Vim Project <https://github.com/vim/vim>
+" Last Change: 2025 Jan 18
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" Quit when a (custom) syntax file was already loaded
@@ -111,6 +111,20 @@ if (s:ft ==# "c" && !exists("c_no_c11")) || (s:in_cpp_family && !exists("cpp_no_
syn match cSpecialCharacter display "[Uu]'\\x\x\+'"
endif
+if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp17"))
+ syn match cCharacter "u8'[^\\]'"
+ syn match cCharacter "u8'[^']*'" contains=cSpecial
+ if exists("c_gnu")
+ syn match cSpecialError "u8'\\[^'\"?\\abefnrtv]'"
+ syn match cSpecialCharacter "u8'\\['\"?\\abefnrtv]'"
+ else
+ syn match cSpecialError "u8'\\[^'\"?\\abfnrtv]'"
+ syn match cSpecialCharacter "u8'\\['\"?\\abfnrtv]'"
+ endif
+ syn match cSpecialCharacter display "u8'\\\o\{1,3}'"
+ syn match cSpecialCharacter display "u8'\\x\x\+'"
+endif
+
"when wanted, highlight trailing white space
if exists("c_space_errors")
if !exists("c_no_trail_space_error")
@@ -191,12 +205,25 @@ syn case ignore
syn match cNumbers display transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctalError,cOctal
" Same, but without octal error (for comments)
syn match cNumbersCom display contained transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctal
-syn match cNumber display contained "\d\+\%(u\=l\{0,2}\|ll\=u\)\>"
-"hex number
-syn match cNumber display contained "0x\x\+\%(u\=l\{0,2}\|ll\=u\)\>"
-" Flag the first zero of an octal number as something special
-syn match cOctal display contained "0\o\+\%(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero
-syn match cOctalZero display contained "\<0"
+
+" cpp.vim handles these
+if !exists("c_no_c23") && !s:in_cpp_family
+ syn match cNumber display contained "\d\%('\=\d\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>"
+ "hex number
+ syn match cNumber display contained "0x\x\%('\=\x\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>"
+ " Flag the first zero of an octal number as something special
+ syn match cOctal display contained "0\o\%('\=\o\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>" contains=cOctalZero
+ "binary number
+ syn match cNumber display contained "0b[01]\%('\=[01]\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>"
+else
+ syn match cNumber display contained "\d\+\%(u\=l\{0,2}\|ll\=u\)\>"
+ "hex number
+ syn match cNumber display contained "0x\x\+\%(u\=l\{0,2}\|ll\=u\)\>"
+ " Flag the first zero of an octal number as something special
+ syn match cOctal display contained "0\o\+\%(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero
+ syn match cOctalZero display contained "\<0"
+endif
+
"floating point number, with dot, optional exponent
syn match cFloat display contained "\d\+\.\d*\%(e[-+]\=\d\+\)\=[fl]\="
"floating point number, starting with a dot, optional exponent
@@ -277,6 +304,13 @@ if !exists("c_no_c99") " ISO C99
syn keyword cType intptr_t uintptr_t
syn keyword cType intmax_t uintmax_t
endif
+if !exists("c_no_c23") && !s:in_cpp_family
+ syn keyword cOperator typeof typeof_unqual
+ syn keyword cType _BitInt _Decimal32 _Decimal64 _Decimal128
+endif
+if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11"))
+ syn keyword cType nullptr_t
+endif
syn keyword cTypedef typedef
syn keyword cStructure struct union enum
@@ -284,6 +318,9 @@ syn keyword cStorageClass static register auto volatile extern const
if !exists("c_no_c99") && !s:in_cpp_family
syn keyword cStorageClass inline restrict
endif
+if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11"))
+ syn keyword cStorageClass constexpr
+endif
if !exists("c_no_c11")
syn keyword cStorageClass _Alignas alignas
syn keyword cOperator _Alignof alignof
@@ -312,10 +349,15 @@ if !exists("c_no_c11")
syn keyword cType atomic_intmax_t atomic_uintmax_t
endif
+if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp20"))
+ syn keyword cType char8_t
+endif
+
if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu")
if exists("c_gnu")
syn keyword cConstant __GNUC__ __FUNCTION__ __PRETTY_FUNCTION__ __func__
endif
+ " TODO: __STDC_HOSTED__ is C99 and C++11
syn keyword cConstant __LINE__ __FILE__ __DATE__ __TIME__ __STDC__ __STDC_VERSION__ __STDC_HOSTED__
syn keyword cConstant CHAR_BIT MB_LEN_MAX MB_CUR_MAX
syn keyword cConstant UCHAR_MAX UINT_MAX ULONG_MAX USHRT_MAX
@@ -324,6 +366,8 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu")
syn keyword cConstant SCHAR_MIN SINT_MIN SLONG_MIN SSHRT_MIN
syn keyword cConstant SCHAR_MAX SINT_MAX SLONG_MAX SSHRT_MAX
if !exists("c_no_c99")
+ syn keyword cConstant __STDC_ISO_10646__ __STDC_IEC_559_COMPLEX__
+ syn keyword cConstant __STDC_MB_MIGHT_NEQ_WC__
syn keyword cConstant __func__ __VA_ARGS__
syn keyword cConstant LLONG_MIN LLONG_MAX ULLONG_MAX
syn keyword cConstant INT8_MIN INT16_MIN INT32_MIN INT64_MIN
@@ -340,6 +384,26 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu")
syn keyword cConstant PTRDIFF_MIN PTRDIFF_MAX SIG_ATOMIC_MIN SIG_ATOMIC_MAX
syn keyword cConstant SIZE_MAX WCHAR_MIN WCHAR_MAX WINT_MIN WINT_MAX
endif
+ if !exists("c_no_c11")
+ syn keyword cConstant __STDC_UTF_16__ __STDC_UTF_32__ __STDC_ANALYZABLE__
+ syn keyword cConstant __STDC_LIB_EXT1__ __STDC_NO_ATOMICS__
+ syn keyword cConstant __STDC_NO_COMPLEX__ __STDC_NO_THREADS__
+ syn keyword cConstant __STDC_NO_VLA__
+ endif
+ if !exists("c_no_c23")
+ syn keyword cConstant __STDC_UTF_16__ __STDC_UTF_32__
+ syn keyword cConstant __STDC_EMBED_NOT_FOUND__ __STDC_EMBED_FOUND__
+ syn keyword cConstant __STDC_EMBED_EMPTY__ __STDC_IEC_60559_BFP__
+ syn keyword cConstant __STDC_IEC_60559_DFP__ __STDC_IEC_60559_COMPLEX__
+ syn keyword cConstant __STDC_IEC_60559_TYPES__
+ syn keyword cConstant BITINT_MAXWIDTH
+ endif
+ if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp20"))
+ syn keyword cConstant __VA_OPT__
+ endif
+ if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11"))
+ syn keyword cConstant nullptr
+ endif
syn keyword cConstant FLT_RADIX FLT_ROUNDS FLT_DIG FLT_MANT_DIG FLT_EPSILON DBL_DIG DBL_MANT_DIG DBL_EPSILON
syn keyword cConstant LDBL_DIG LDBL_MANT_DIG LDBL_EPSILON FLT_MIN FLT_MAX FLT_MIN_EXP FLT_MAX_EXP FLT_MIN_10_EXP FLT_MAX_10_EXP
syn keyword cConstant DBL_MIN DBL_MAX DBL_MIN_EXP DBL_MAX_EXP DBL_MIN_10_EXP DBL_MAX_10_EXP LDBL_MIN LDBL_MAX LDBL_MIN_EXP LDBL_MAX_EXP
@@ -377,37 +441,77 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu")
endif
if !exists("c_no_c99") " ISO C99
syn keyword cConstant true false
+ syn keyword cConstant INFINITY NAN
+ " math.h
+ syn keyword cConstant HUGE_VAL HUGE_VALF HUGE_VALL
+ syn keyword cConstant FP_FAST_FMAF FP_FAST_FMA FP_FAST_FMAL
+ syn keyword cConstant FP_ILOGB0 FP_ILOGBNAN
+ syn keyword cConstant math_errhandling MATH_ERRNO MATH_ERREXCEPT
+ syn keyword cConstant FP_NORMAL FP_SUBNORMAL FP_ZERO FP_INFINITE FP_NAN
endif
" Accept %: for # (C99)
-syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
+syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti,cBadBlock
+if !exists("c_no_c23")
+ syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(el\)\=\%(if\|ifdef\|ifndef\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
+else
+ syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
+endif
syn match cPreConditMatch display "^\s*\zs\%(%:\|#\)\s*\%(else\|endif\)\>"
if !exists("c_no_if0")
syn cluster cCppOutInGroup contains=cCppInIf,cCppInElse,cCppInElse2,cCppOutIf,cCppOutIf2,cCppOutElse,cCppInSkip,cCppOutSkip
syn region cCppOutWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0\+\s*\%($\|//\|/\*\|&\)" end=".\@=\|$" contains=cCppOutIf,cCppOutElse,@NoSpell fold
syn region cCppOutIf contained start="0\+" matchgroup=cCppOutWrapper end="^\s*\%(%:\|#\)\s*endif\>" contains=cCppOutIf2,cCppOutElse
if !exists("c_no_if0_fold")
- syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold
+ if !exists("c_no_c23")
+ syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold
+ else
+ syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold
+ endif
+ else
+ if !exists("c_no_c23")
+ syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
+ else
+ syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
+ endif
+ endif
+ if !exists("c_no_c23")
+ syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|el\%(if\|ifdef\|ifndef\)\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit
else
- syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
+ syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit
endif
- syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit
syn region cCppInWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\%($\|//\|/\*\||\)" end=".\@=\|$" contains=cCppInIf,cCppInElse fold
syn region cCppInIf contained matchgroup=cCppInWrapper start="\d\+" end="^\s*\%(%:\|#\)\s*endif\>" contains=TOP,cPreCondit
if !exists("c_no_if0_fold")
- syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold
+ if !exists("c_no_c23")
+ syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold
+ else
+ syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold
+ endif
else
- syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2
+ if !exists("c_no_c23")
+ syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2
+ else
+ syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2
+ endif
+ endif
+ if !exists("c_no_c23")
+ syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|el\%(if\|ifdef\|ifndef\)\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
+ else
+ syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
endif
- syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell
syn region cCppOutSkip contained start="^\s*\%(%:\|#\)\s*\%(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" contains=cSpaceError,cCppOutSkip
syn region cCppInSkip contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(if\s\+\%(\d\+\s*\%($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" containedin=cCppOutElse,cCppInIf,cCppInSkip contains=TOP,cPreProc
endif
syn region cIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+
syn match cIncluded display contained "<[^>]*>"
syn match cInclude display "^\s*\zs\%(%:\|#\)\s*include\>\s*["<]" contains=cIncluded
+if !exists("c_no_c23") && !s:in_cpp_family
+ syn region cInclude start="^\s*\zs\%(%:\|#\)\s*embed\>" skip="\\$" end="$" keepend contains=cEmbed,cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
+ syn match cEmbed contained "\%(%:\|#\)\s*embed\>" nextgroup=cIncluded skipwhite transparent
+ syn cluster cPreProcGroup add=cEmbed
+endif
"syn match cLineSkip "\\$"
-syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti,cBadBlock
syn region cDefine start="^\s*\zs\%(%:\|#\)\s*\%(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
syn region cPreProc start="^\s*\zs\%(%:\|#\)\s*\%(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
diff --git a/runtime/syntax/checkhealth.vim b/runtime/syntax/checkhealth.vim
index a4f6e016cb..14c80640ba 100644
--- a/runtime/syntax/checkhealth.vim
+++ b/runtime/syntax/checkhealth.vim
@@ -1,6 +1,5 @@
" Vim syntax file
" Language: Nvim :checkhealth buffer
-" Last Change: 2022 Nov 10
if exists("b:current_syntax")
finish
diff --git a/runtime/syntax/chordpro.vim b/runtime/syntax/chordpro.vim
index 41a0a1e9d1..02c34b8466 100644
--- a/runtime/syntax/chordpro.vim
+++ b/runtime/syntax/chordpro.vim
@@ -2,6 +2,7 @@
" Language: ChordPro 6 (https://www.chordpro.org)
" Maintainer: Niels Bo Andersen <niels@niboan.dk>
" Last Change: 2022-04-15
+" 2024 Dec 31: add "keys" as syntax keyword (via: https://groups.google.com/g/vim_dev/c/vP4epus0euM/m/mNoDY6hsCQAJ)
" Quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -104,7 +105,7 @@ syn match chordproStandardMetadata /instrument\.description/ contained
syn match chordproStandardMetadata /user\.name/ contained
syn match chordproStandardMetadata /user\.fullname/ contained
-syn keyword chordproDefineKeyword contained frets fingers
+syn keyword chordproDefineKeyword contained frets fingers keys
syn match chordproDefineKeyword /base-fret/ contained
syn match chordproArgumentsNumber /\d\+/ contained
diff --git a/runtime/syntax/cmacro.vim b/runtime/syntax/cmacro.vim
new file mode 100644
index 0000000000..1d448f0d1b
--- /dev/null
+++ b/runtime/syntax/cmacro.vim
@@ -0,0 +1,77 @@
+" Vim syntax file
+" Language: C macro for C preprocessor
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 31
+" modified from syntax/c.vim
+
+" C compiler has a preprocessor: `cpp -P test.txt`
+" test.txt doesn't need to be a C file
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Accept %: for # (C99)
+syn region cmacroPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cmacroCppParen,cmacroNumbers
+syn match cmacroPreConditMatch display "^\s*\zs\%(%:\|#\)\s*\%(else\|endif\)\>"
+if !exists("c_no_if0")
+ syn cluster cmacroCppOutInGroup contains=cmacroCppInIf,cmacroCppInElse,cmacroCppInElse2,cmacroCppOutIf,cmacroCppOutIf2,cmacroCppOutElse,cmacroCppInSkip,cmacroCppOutSkip
+ syn region cmacroCppOutWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0\+\s*\%($\|//\|/\*\|&\)" end=".\@=\|$" contains=cmacroCppOutIf,cmacroCppOutElse,@NoSpell fold
+ syn region cmacroCppOutIf contained start="0\+" matchgroup=cmacroCppOutWrapper end="^\s*\%(%:\|#\)\s*endif\>" contains=cmacroCppOutIf2,cmacroCppOutElse
+ if !exists("c_no_if0_fold")
+ syn region cmacroCppOutIf2 contained matchgroup=cmacroCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cmacroCppOutSkip,@Spell fold
+ else
+ syn region cmacroCppOutIf2 contained matchgroup=cmacroCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cmacroCppOutSkip,@Spell
+ endif
+ syn region cmacroCppOutElse contained matchgroup=cmacroCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cmacroPreCondit
+ syn region cmacroCppInWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\%($\|//\|/\*\||\)" end=".\@=\|$" contains=cmacroCppInIf,cmacroCppInElse fold
+ syn region cmacroCppInIf contained matchgroup=cmacroCppInWrapper start="\d\+" end="^\s*\%(%:\|#\)\s*endif\>" contains=TOP,cmacroPreCondit
+ if !exists("c_no_if0_fold")
+ syn region cmacroCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cmacroCppInIf contains=cmacroCppInElse2 fold
+ else
+ syn region cmacroCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cmacroCppInIf contains=cmacroCppInElse2
+ endif
+ syn region cmacroCppInElse2 contained matchgroup=cmacroCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cmacroCppOutSkip,@Spell
+ syn region cmacroCppOutSkip contained start="^\s*\%(%:\|#\)\s*\%(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" contains=cmacroCppOutSkip
+ syn region cmacroCppInSkip contained matchgroup=cmacroCppInWrapper start="^\s*\%(%:\|#\)\s*\%(if\s\+\%(\d\+\s*\%($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" containedin=cmacroCppOutElse,cmacroCppInIf,cmacroCppInSkip contains=TOP,cmacroPreProc
+endif
+syn region cmacroIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+
+syn match cmacroIncluded display contained "<[^>]*>"
+syn match cmacroInclude display "^\s*\zs\%(%:\|#\)\s*include\>\s*["<]" contains=cmacroIncluded
+"syn match cmacroLineSkip "\\$"
+syn cluster cmacroPreProcmacroGroup contains=cmacroPreCondit,cmacroIncluded,cmacroInclude,cmacroDefine,cmacroCppOutWrapper,cmacroCppInWrapper,@cmacroCppOutInGroup,cmacroNumbersCom,@cmacroCommentGroup,cmacroParen,cmacroBracket,cmacroMulti,cmacroBadBlock
+syn region cmacroDefine start="^\s*\zs\%(%:\|#\)\s*\%(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cmacroPreProcmacroGroup,@Spell
+syn region cmacroPreProc start="^\s*\zs\%(%:\|#\)\s*\%(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cmacroPreProcmacroGroup,@Spell
+
+" be able to fold #pragma regions
+syn region cmacroPragma start="^\s*#pragma\s\+region\>" end="^\s*#pragma\s\+endregion\>" transparent keepend extend fold
+
+syn keyword cmacroTodo contained TODO FIXME XXX NOTE
+syn region cmacroComment start='/\*' end='\*/' contains=cmacroTodo,@Spell
+syn match cmacroCommentError "\*/"
+syn region cmacroComment start='//' end='$' contains=cmacroTodo,@Spell
+
+" Define the default highlighting.
+" Only used when an item doesn't have highlighting yet
+hi def link cmacroInclude Include
+hi def link cmacroPreProc PreProc
+hi def link cmacroDefine Macro
+hi def link cmacroIncluded cmacroString
+hi def link cmacroCppInWrapper cmacroCppOutWrapper
+hi def link cmacroCppOutWrapper cmacroPreCondit
+hi def link cmacroPreConditMatch cmacroPreCondit
+hi def link cmacroPreCondit PreCondit
+hi def link cmacroCppOutSkip cmacroCppOutIf2
+hi def link cmacroCppInElse2 cmacroCppOutIf2
+hi def link cmacroCppOutIf2 cmacroCppOut
+hi def link cmacroCppOut Comment
+hi def link cmacroTodo Todo
+hi def link cmacroComment Comment
+hi def link cmacroCommentError Error
+
+let b:current_syntax = "cmacro"
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/dircolors.vim b/runtime/syntax/dircolors.vim
index 74a7068488..d968ed8fdd 100644
--- a/runtime/syntax/dircolors.vim
+++ b/runtime/syntax/dircolors.vim
@@ -85,6 +85,9 @@ endfunction
function! s:get_hi_str(color, place) abort
if a:color >= 0 && a:color <= 255
if has('gui_running') || &termguicolors
+ if ! exists("s:termguicolors")
+ call s:set_guicolors()
+ endif
return ' gui' . a:place . '=' . s:termguicolors[a:color]
elseif a:color <= 7 || &t_Co == 256 || &t_Co == 88
return ' cterm' . a:place . '=' . a:color
diff --git a/runtime/syntax/dockerfile.vim b/runtime/syntax/dockerfile.vim
index 6ec71fcdb6..f1d612f4ad 100644
--- a/runtime/syntax/dockerfile.vim
+++ b/runtime/syntax/dockerfile.vim
@@ -1,6 +1,6 @@
" dockerfile.vim - Syntax highlighting for Dockerfiles
" Maintainer: Honza Pokorny <https://honza.ca>
-" Last Change: 2024 Jul 03
+" Last Change: 2024 Dec 20
" License: BSD
" https://docs.docker.com/engine/reference/builder/
@@ -35,7 +35,6 @@ syntax region dockerfileShell contained keepend start=/\v/ skip=/\v\\\_./ end=/
syntax region dockerfileValue contained keepend start=/\v/ skip=/\v\\\_./ end=/\v$/ contains=dockerfileString
syntax region dockerfileComment start=/\v^\s*#/ end=/\v$/ contains=@Spell
-set commentstring=#\ %s
hi def link dockerfileString String
hi def link dockerfileKeyword Keyword
diff --git a/runtime/syntax/gel.vim b/runtime/syntax/gel.vim
new file mode 100644
index 0000000000..5f3800273c
--- /dev/null
+++ b/runtime/syntax/gel.vim
@@ -0,0 +1,19 @@
+" Vim syntax file
+" Language: TI Code Composer Studio General Extension Language
+" Document: https://downloads.ti.com/ccs/esd/documents/users_guide/ccs_debug-gel.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 25
+
+if exists("b:current_syntax")
+ finish
+endif
+
+runtime! syntax/cpp.vim
+
+syn keyword gelStatement StartUp GEL_AddInputFile GEL_AddOutputFile GEL_AdvancedReset GEL_AsmStepInto GEL_AsmStepOver GEL_BreakPtAdd GEL_BreakPtDel GEL_BreakPtDisable GEL_BreakPtReset GEL_CancelTimer GEL_Connect GEL_Dialog GEL_DisableFileOutput GEL_DisableRealtime GEL_Disconnect GEL_EnableClock GEL_EnableFileOutput GEL_EnableRealtime GEL_EnableZeroFill GEL_EvalOnTarget GEL_GetBoolDebugProperty GEL_GetBoolDriverProperty GEL_GetBoolTargetDbProperty GEL_GetNumericDebugProperty GEL_GetNumericDriverProperty GEL_GetNumericTargetDbProperty GEL_GetStringDebugProperty GEL_GetStringDriverProperty GEL_GetStringTargetDbProperty GEL_Go GEL_Halt GEL_HandleTargetError GEL_HWBreakPtAdd GEL_HWBreakPtDel GEL_HWBreakPtDisable GEL_HWBreakPtReset GEL_IsConnected GEL_IsHalted GEL_IsInRealtimeMode GEL_IsResetSupported GEL_IsTimerSet GEL_Load GEL_LoadBin GEL_LoadGel GEL_LoadProgramOnly GEL_MapAdd GEL_MapAddStr GEL_MapDelete GEL_MapOff GEL_MapOn GEL_MapReset GEL_MatchesConnection GEL_MemoryFill GEL_MemoryListSupportedTypes GEL_MemoryLoad GEL_MemoryLoadData GEL_MemorySave GEL_MemorySaveBin GEL_MemorySaveCoff GEL_MemorySaveData GEL_MemorySaveHex GEL_PatchAssembly GEL_ProbePtAdd GEL_ProbePtDel GEL_ProbePtDisable GEL_ProbePtReset GEL_ReConnect GEL_RefreshWindows GEL_Reload GEL_RemoveDebugState GEL_RemoveInputFile GEL_RemoveOutputFile GEL_Reset GEL_Restart GEL_RestoreDebugState GEL_Run GEL_RunF GEL_SetBlockResetMode GEL_SetBoolDebugProperty GEL_SetClockEvent GEL_SetNumericDebugProperty GEL_SetSemihostingMainArgs GEL_SetStringDebugProperty GEL_SetTimer GEL_SetWaitInResetMode GEL_SrcStepInto GEL_SrcStepOver GEL_StepInto GEL_StepOut GEL_StepOver GEL_StrCat GEL_StrLen GEL_SubStr GEL_SymbolAdd GEL_SymbolAddOffset GEL_SymbolAddRel GEL_SymbolDisable GEL_SymbolEnable GEL_SymbolHideSection GEL_SymbolLoad GEL_SymbolLoadOffset GEL_SymbolLoadRel GEL_SymbolRemove GEL_SymbolShowSection GEL_SyncHalt GEL_SyncRun GEL_SyncStepInto GEL_SyncStepOut GEL_SyncStepOver GEL_System GEL_TargetTextOut GEL_TextOut GEL_Trace GEL_UnloadAllGels GEL_UnloadAllSymbols GEL_UnloadGel GEL_VerifyBinProgram GEL_VerifyProgram OnChildRunning OnFileLoaded OnHalt OnPreFileLoaded OnPreReset OnPreTargetConnect OnReset OnResetDetected OnRestart OnTargetConnect
+syn keyword gelModifier hotmenu menuitem
+
+hi def link gelStatement Statement
+hi def link gelModifier Type
+
+let b:current_syntax = "gel"
diff --git a/runtime/syntax/graphql.vim b/runtime/syntax/graphql.vim
new file mode 100644
index 0000000000..01d5ca25ff
--- /dev/null
+++ b/runtime/syntax/graphql.vim
@@ -0,0 +1,90 @@
+" Vim syntax file
+" Language: graphql
+" Maintainer: Jon Parise <jon@indelible.org>
+" Filenames: *.graphql *.graphqls *.gql
+" URL: https://github.com/jparise/vim-graphql
+" License: MIT <https://opensource.org/license/mit>
+" Last Change: 2024 Dec 21
+
+if !exists('main_syntax')
+ if exists('b:current_syntax')
+ finish
+ endif
+ let main_syntax = 'graphql'
+endif
+
+syn case match
+
+syn match graphqlComment "#.*$" contains=@Spell
+
+syn match graphqlOperator "=" display
+syn match graphqlOperator "!" display
+syn match graphqlOperator "|" display
+syn match graphqlOperator "&" display
+syn match graphqlOperator "\M..." display
+
+syn keyword graphqlBoolean true false
+syn keyword graphqlNull null
+syn match graphqlNumber "-\=\<\%(0\|[1-9]\d*\)\%(\.\d\+\)\=\%([eE][-+]\=\d\+\)\=\>" display
+syn region graphqlString start=+"+ skip=+\\\\\|\\"+ end=+"\|$+
+syn region graphqlString start=+"""+ skip=+\\"""+ end=+"""+
+
+syn keyword graphqlKeyword repeatable nextgroup=graphqlKeyword skipwhite
+syn keyword graphqlKeyword on nextgroup=graphqlType,graphqlDirectiveLocation skipwhite
+
+syn keyword graphqlStructure enum scalar type union nextgroup=graphqlType skipwhite
+syn keyword graphqlStructure input interface subscription nextgroup=graphqlType skipwhite
+syn keyword graphqlStructure implements nextgroup=graphqlType skipwhite
+syn keyword graphqlStructure query mutation fragment nextgroup=graphqlName skipwhite
+syn keyword graphqlStructure directive nextgroup=graphqlDirective skipwhite
+syn keyword graphqlStructure extend nextgroup=graphqlStructure skipwhite
+syn keyword graphqlStructure schema nextgroup=graphqlFold skipwhite
+
+syn match graphqlDirective "\<@\h\w*\>" display
+syn match graphqlVariable "\<\$\h\w*\>" display
+syn match graphqlName "\<\h\w*\>" display
+syn match graphqlType "\<_*\u\w*\>" display
+
+" https://spec.graphql.org/October2021/#ExecutableDirectiveLocation
+syn keyword graphqlDirectiveLocation QUERY MUTATION SUBSCRIPTION FIELD
+syn keyword graphqlDirectiveLocation FRAGMENT_DEFINITION FRAGMENT_SPREAD
+syn keyword graphqlDirectiveLocation INLINE_FRAGMENT VARIABLE_DEFINITION
+" https://spec.graphql.org/October2021/#TypeSystemDirectiveLocation
+syn keyword graphqlDirectiveLocation SCHEMA SCALAR OBJECT FIELD_DEFINITION
+syn keyword graphqlDirectiveLocation ARGUMENT_DEFINITION INTERFACE UNION
+syn keyword graphqlDirectiveLocation ENUM ENUM_VALUE INPUT_OBJECT
+syn keyword graphqlDirectiveLocation INPUT_FIELD_DEFINITION
+
+syn keyword graphqlMetaFields __schema __type __typename
+
+syn region graphqlFold matchgroup=graphqlBraces start="{" end="}" transparent fold contains=ALLBUT,graphqlStructure
+syn region graphqlList matchgroup=graphqlBraces start="\[" end="]" transparent contains=ALLBUT,graphqlDirective,graphqlStructure
+
+if main_syntax ==# 'graphql'
+ syn sync minlines=500
+endif
+
+hi def link graphqlComment Comment
+hi def link graphqlOperator Operator
+
+hi def link graphqlBraces Delimiter
+
+hi def link graphqlBoolean Boolean
+hi def link graphqlNull Keyword
+hi def link graphqlNumber Number
+hi def link graphqlString String
+
+hi def link graphqlDirective PreProc
+hi def link graphqlDirectiveLocation Special
+hi def link graphqlName Identifier
+hi def link graphqlMetaFields Special
+hi def link graphqlKeyword Keyword
+hi def link graphqlStructure Structure
+hi def link graphqlType Type
+hi def link graphqlVariable Identifier
+
+let b:current_syntax = 'graphql'
+
+if main_syntax ==# 'graphql'
+ unlet main_syntax
+endif
diff --git a/runtime/syntax/hyprlang.vim b/runtime/syntax/hyprlang.vim
new file mode 100644
index 0000000000..cde504d9ca
--- /dev/null
+++ b/runtime/syntax/hyprlang.vim
@@ -0,0 +1,59 @@
+" Vim syntax file
+" Language: hyprlang
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Last Change: 2025 Jan 29
+
+if exists("b:current_syntax")
+ finish
+endif
+let b:current_syntax = "hyprlang"
+
+syn case ignore
+
+syn match hyprCommand '^\s*\zs\S\+\ze\s*=' contains=hyprVariable
+syn match hyprValue '=\s*\zs.\+\ze$' contains=hyprNumber,hyprFloat,hyprBoolean,hyprString,hyprColor,hyprModifier,hyprVariable,hyprComment
+
+syn match hyprVariable '\$\w\+' contained
+
+" Category
+syn region hyprCategory matchgroup=hyprCategoryD start='^\s*\k\+\s*{' end='^\s*}' contains=hyprCommand,hyprValue,hyprComment,hyprCategory,hyprCategoryD
+
+" Variables Types
+syn match hyprNumber '\%[-+]\<\d\+\>\%[%]' contained
+syn match hyprFloat '\%[-+]\<\d\+\.\d\+\>\%[%]' contained
+syn match hyprString "'[^']*'" contained
+syn match hyprString '"[^"]*"' contained
+syn match hyprColor 'rgb(\(\w\|\d\)\{6})' contained
+syn match hyprColor 'rgba(\(\w\|\d\)\{8})' contained
+syn match hyprColor '0x\(\w\|\d\)\{8}' contained
+syn keyword hyprBoolean true false yes no on off contained
+
+" Super Shift Alt Ctrl Control
+syn keyword hyprModifier contained
+ \ super supershift superalt superctrl supercontrol
+ \ super_shift super_alt super_ctrl super_control
+ \ shift shiftsuper shiftalt shiftctrl shiftcontrol
+ \ shift_super shift_alt shift_ctrl shift_control
+ \ alt altsuper altshift altctrl altcontrol
+ \ alt_super alt_shift alt_ctrl alt_control
+ \ ctrl ctrlsuper ctrlshift ctrlalt ctrlcontrol
+ \ ctrl_super ctrl_shift ctrl_alt ctrl_control
+ \ control controlsuper controlshift controlalt controlctrl
+ \ control_super control_shift control_alt control_ctrl
+
+" Comments
+syn match hyprComment '#.*$'
+
+" Link to default groups
+hi def link hyprVariable Identifier
+hi def link hyprCategoryD Special
+hi def link hyprComment Comment
+hi def link hyprNumber Constant
+hi def link hyprModifier Constant
+hi def link hyprFloat hyprNumber
+hi def link hyprBoolean Boolean
+hi def link hyprString String
+hi def link hyprColor Structure
+hi def link hyprCommand Keyword
+
+" vim: ts=8 sts=2 sw=2 et
diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim
index b3e17b55f6..9b38ccd4dc 100644
--- a/runtime/syntax/java.vim
+++ b/runtime/syntax/java.vim
@@ -3,7 +3,7 @@
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
" Former Maintainer: Claudio Fleiner <claudio@fleiner.com>
" Repository: https://github.com/zzzyxwvut/java-vim.git
-" Last Change: 2024 Oct 10
+" Last Change: 2025 Jan 02
" Please check ":help java.vim" for comments on some of the options
" available.
@@ -391,18 +391,32 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai
if s:with_markdown
try
syntax include @javaMarkdown syntax/markdown.vim
- let s:ff.WithMarkdown = s:ff.LeftConstant
+
+ try
+ syn clear markdownId markdownLineStart markdownH1 markdownH2 markdownHeadingRule markdownRule markdownCode markdownCodeBlock markdownIdDeclaration
+ let s:ff.WithMarkdown = s:ff.LeftConstant
+ catch /\<E28:/
+ call s:ReportOnce(v:exception)
+ let s:no_support = 1
+ unlet! g:java_ignore_markdown
+ let g:java_ignore_markdown = 28
+ endtry
catch /\<E48[45]:/
call s:ReportOnce(v:exception)
- unlockvar s:with_markdown
- let s:with_markdown = 0
- lockvar s:with_markdown
- hi clear markdownCode
- hi clear markdownCodeBlock
- hi clear markdownCodeDelimiter
- hi clear markdownLinkDelimiter
+ let s:no_support = 1
finally
unlet! b:current_syntax
+
+ if exists("s:no_support")
+ unlet s:no_support
+ unlockvar s:with_markdown
+ let s:with_markdown = 0
+ lockvar s:with_markdown
+ hi clear markdownCode
+ hi clear markdownCodeBlock
+ hi clear markdownCodeDelimiter
+ hi clear markdownLinkDelimiter
+ endif
endtry
endif
@@ -422,7 +436,6 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai
exec 'syn region javaMarkdownCommentTitle contained matchgroup=javaMarkdownComment start="\%(///.*\r\=\n\s*\)\@' . s:ff.Peek('80', '') . '<!///\s*\%({@return\>\)\@=" matchgroup=javaMarkdownCommentTitle end="}\%(\s*\.*\)*" contains=javaMarkdownShortcutLink,@javaMarkdown,javaMarkdownCommentMask,javaTodo,@Spell,@javaDocTags,javaTitleSkipBlock'
exec 'syn region javaMarkdownCommentTitle contained matchgroup=javaMarkdownComment start="\%(///.*\r\=\n\s*\)\@' . s:ff.Peek('80', '') . '<!///\s*\%({@summary\>\)\@=" matchgroup=javaMarkdownCommentTitle end="}" contains=javaMarkdownShortcutLink,@javaMarkdown,javaMarkdownCommentMask,javaTodo,@Spell,@javaDocTags,javaTitleSkipBlock'
- syn clear markdownId markdownLineStart markdownH1 markdownH2 markdownHeadingRule markdownRule markdownCode markdownCodeBlock markdownIdDeclaration
" REDEFINE THE MARKDOWN ITEMS ANCHORED WITH "^", OBSERVING THE
" DEFINITION ORDER.
syn match markdownLineStart contained "^\s*///\s*[<@]\@!" contains=@markdownBlock,javaMarkdownCommentTitle,javaMarkdownCommentMask nextgroup=@markdownBlock,htmlSpecialChar
diff --git a/runtime/syntax/jj.vim b/runtime/syntax/jjdescription.vim
index a2911a0268..04848bcb3b 100644
--- a/runtime/syntax/jj.vim
+++ b/runtime/syntax/jjdescription.vim
@@ -6,7 +6,6 @@
if exists('b:current_syntax')
finish
endif
-let b:current_syntax = 'jj'
syn match jjAdded "A .*" contained
syn match jjRemoved "D .*" contained
@@ -14,7 +13,12 @@ syn match jjChanged "M .*" contained
syn region jjComment start="^JJ: " end="$" contains=jjAdded,jjRemoved,jjChanged
+syn include @jjCommitDiff syntax/diff.vim
+syn region jjCommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@jjCommitDiff
+
hi def link jjComment Comment
hi def link jjAdded Added
hi def link jjRemoved Removed
hi def link jjChanged Changed
+
+let b:current_syntax = 'jjdescription'
diff --git a/runtime/syntax/just.vim b/runtime/syntax/just.vim
new file mode 100644
index 0000000000..79c81d0f9c
--- /dev/null
+++ b/runtime/syntax/just.vim
@@ -0,0 +1,406 @@
+" Vim syntax file
+" Language: Justfile
+" Maintainer: Peter Benjamin <@pbnj>
+" Last Change: 2025 Jan 25
+" Credits: The original author, Noah Bogart <https://github.com/NoahTheDuke/vim-just/>
+
+if exists('b:current_syntax')
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+let b:current_syntax = 'just'
+
+" syncing fromstart prevents mismatched highlighting when jumping around in a justfile
+" linebreaks= keeps multi-line constructs highlighted correctly while typing
+syn sync fromstart linebreaks=10
+
+" a-zA-Z0-9_-
+syn iskeyword @,48-57,_,-
+
+syn match justComment "#.*$" contains=@Spell,justCommentTodo
+syn match justCommentInBody '#.*$' contained contains=justCommentTodo,justInterpolation,@justOtherCurlyBraces
+syn keyword justCommentTodo TODO FIXME XXX contained
+syn match justShebang "^\s*#!.*$" contains=justInterpolation,@justOtherCurlyBraces
+syn match justName "\h\k*" contained
+syn match justFunction "\h\k*" contained
+
+syn match justPreBodyComment "\v%(\s|\\\n)*%([^\\]\n)@3<!#%([^!].*)?\n%(\t+| +)@=" transparent contained contains=justComment
+ \ nextgroup=@justBodies skipnl
+
+syn region justBacktick start=/`/ end=/`/
+syn region justBacktick start=/```/ end=/```/
+syn region justRawString start=/'/ end=/'/
+syn region justRawString start=/'''/ end=/'''/
+syn region justString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError
+syn region justString start=/"""/ skip=/\\\\\|\\"/ end=/"""/ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError
+
+syn region justShellExpandRawString start=/\v\k@1<!x'/ end=/'/
+ \ contains=justShellExpandVarRaw,justDollarEscape
+syn region justShellExpandRawString start=/\v\k@1<!x'''/ end=/'''/
+ \ contains=justShellExpandVarRaw,justDollarEscape
+syn region justShellExpandString
+ \ start=/\v\k@1<!x"/ skip=/\\\\\|\\"/ end=/"/
+ \ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justShellExpandVar,justDollarEscape,justDollarEscapeSplit
+syn region justShellExpandString
+ \ start=/\v\k@1<!x"""/ skip=/\\\\\|\\"/ end=/"""/
+ \ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justShellExpandVar,justDollarEscape,justDollarEscapeSplit
+
+syn cluster justStringLiterals
+ \ contains=justRawString,justString,justShellExpandRawString,justShellExpandString
+syn cluster justAllStrings contains=justBacktick,@justStringLiterals
+
+syn match justRegexReplacement
+ \ /\v,%(\_s|\\\n)*%('\_[^']*'|'''%(\_.%(''')@!)*\_.?''')%(\_s|\\\n)*%(,%(\_s|\\\n)*)?\)/me=e-1
+ \ transparent contained contains=@justExpr,@justStringsWithRegexCapture
+syn match justRegexReplacement
+ \ /\v,%(\_s|\\\n)*%("%(\_[^"]|\\")*"|"""%(\_.%(""")@!)*\_.?""")%(\_s|\\\n)*%(,%(\_s|\\\n)*)?\)/me=e-1
+ \ transparent contained contains=@justExpr,@justStringsWithRegexCapture
+
+syn region justRawStrRegexRepl start=/\v'/ end=/'/ contained contains=justRegexCapture,justDollarEscape
+syn region justRawStrRegexRepl start=/\v'''/ end=/'''/ contained contains=justRegexCapture,justDollarEscape
+syn region justStringRegexRepl start=/\v"/ skip=/\\\\\|\\"/ end=/"/ contained contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justRegexCapture,justDollarEscape,justDollarEscapeSplit
+syn region justStringRegexRepl start=/\v"""/ skip=/\\\\\|\\"/ end=/"""/ contained contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justRegexCapture,justDollarEscape,justDollarEscapeSplit
+syn match justRegexCapture '\v\$%(\w+|\{\w+\})' contained
+syn cluster justStringsWithRegexCapture contains=justRawStrRegexRepl,justStringRegexRepl
+
+syn cluster justRawStrings contains=justRawString,justRawStrRegexRepl
+
+syn region justStringInsideBody start=/\v\\@1<!'/ end=/'/ contained contains=justInterpolation,@justOtherCurlyBraces,justIndentError
+syn region justStringInsideBody start=/\v\\@1<!"/ skip=/\v\\@1<!\\"/ end=/"/ contained contains=justInterpolation,@justOtherCurlyBraces,justIndentError
+syn region justStringInShebangBody start=/\v\\@1<!'/ end=/'/ contained contains=justInterpolation,@justOtherCurlyBraces,justShebangIndentError
+syn region justStringInShebangBody start=/\v\\@1<!"/ skip=/\v\\@1<!\\"/ end=/"/ contained contains=justInterpolation,@justOtherCurlyBraces,justShebangIndentError
+
+syn match justStringEscapeError '\\.' contained
+syn match justStringEscapeSequence '\v\\[tnr"\\]' contained
+syn match justStringUEscapeSequence '\v\\u\{[0-9A-Fa-f]{1,6}\}' contained
+
+syn match justAssignmentOperator "\V:=" contained
+
+syn region justExprParen start='\V(' end='\V)' transparent contains=@justExpr
+syn region justExprParenInInterp start='\V(' end='\V)' transparent contained contains=@justExprInInterp
+
+syn match justRecipeAt "^@" contained
+syn match justRecipeColon ":" contained
+
+syn region justRecipeAttributes
+ \ matchgroup=justRecipeAttr start='\v^%(\\\n)@3<!\[' end='\V]'
+ \ contains=justRecipeAttr,justRecipeAttrSep,justRecipeAttrArgs,justRecipeAttrArgError,justRecipeAttrValueShort
+
+syn keyword justRecipeAttr
+ \ confirm doc extension group linux macos no-cd no-exit-message no-quiet openbsd positional-arguments private script unix windows working-directory
+ \ contained
+syn match justRecipeAttrSep ',' contained
+syn match justRecipeAttrValueShort '\v:%(\_s|\\\n)*' transparent contained
+ \ contains=justRecipeAttrValueColon nextgroup=@justStringLiterals,justInvalidAttrValue
+syn match justRecipeAttrValueColon '\V:' contained
+syn region justRecipeAttrArgs matchgroup=justRecipeAttr start='\V(' end='\V)' contained
+ \ contains=@justStringLiterals
+syn match justRecipeAttrArgError '\v\(%(\s|\\?\n)*\)' contained
+
+syn match justInvalidAttrValue '\v[^"',]["']@![^,\]]*' contained
+
+syn match justRecipeDeclSimple "\v^\@?\h\k*%(%(\s|\\\n)*:\=@!)@="
+ \ transparent contains=justRecipeName
+ \ nextgroup=justRecipeNoDeps,justRecipeDeps
+
+syn region justRecipeDeclComplex start="\v^\@?\h\k*%(\s|\\\n)+%([+*$]+%(\s|\\\n)*)*\h" end="\v%(:\=@!)@=|$"
+ \ transparent
+ \ contains=justRecipeName,justParameter
+ \ nextgroup=justRecipeNoDeps,justRecipeDeps
+
+syn match justRecipeName "\v^\@?\h\k*" transparent contained contains=justRecipeAt,justFunction
+
+syn match justParameter "\v%(\s|\\\n)@3<=%(%([*+]%(\s|\\\n)*)?%(\$%(\s|\\\n)*)?|\$%(\s|\\\n)*[*+]%(\s|\\\n)*)\h\k*"
+ \ transparent contained
+ \ contains=justName,justVariadicPrefix,justParamExport,justVariadicPrefixError
+ \ nextgroup=justPreParamValue
+
+syn match justPreParamValue '\v%(\s|\\\n)*\=%(\s|\\\n)*'
+ \ contained transparent
+ \ contains=justParameterOperator
+ \ nextgroup=justParamValue
+
+syn region justParamValue contained transparent
+ \ start="\v\S"
+ \ skip="\\\n"
+ \ end="\v%(\s|^)%([*+$:]|\h)@=|:@=|$"
+ \ contains=@justAllStrings,justRecipeParenDefault,@justExprFunc
+ \ nextgroup=justParameterError
+syn match justParameterOperator "\V=" contained
+
+syn match justVariadicPrefix "\v%(\s|\\\n)@3<=[*+]%(%(\s|\\\n)*\$?%(\s|\\\n)*\h)@=" contained
+syn match justParamExport '\V$' contained
+syn match justVariadicPrefixError "\v\$%(\s|\\\n)*[*+]" contained
+
+syn match justParameterError "\v%(%([+*$]+%(\s|\\\n)*)*\h\k*)@>%(%(\s|\\\n)*\=)@!" contained
+
+syn region justRecipeParenDefault
+ \ matchgroup=justRecipeDepParamsParen start='\v%(\=%(\s|\\\n)*)@<=\(' end='\V)'
+ \ contained
+ \ contains=@justExpr
+syn match justRecipeSubsequentDeps '\V&&' contained
+
+syn match justRecipeNoDeps '\v:%(\s|\\\n)*\n|:#@=|:%(\s|\\\n)+#@='
+ \ transparent contained
+ \ contains=justRecipeColon
+ \ nextgroup=justPreBodyComment,@justBodies
+syn region justRecipeDeps start="\v:%(\s|\\\n)*%([a-zA-Z_(]|\&\&)" skip='\\\n' end="\v#@=|\\@1<!\n"
+ \ transparent contained
+ \ contains=justFunction,justRecipeColon,justRecipeSubsequentDeps,justRecipeParamDep
+ \ nextgroup=justPreBodyComment,@justBodies
+
+syn region justRecipeParamDep contained transparent
+ \ matchgroup=justRecipeDepParamsParen
+ \ start="\V("
+ \ end="\V)"
+ \ contains=justRecipeDepParenName,@justExpr
+
+syn keyword justBoolean true false contained
+
+syn match justAssignment "\v^\h\k*%(\s|\\\n)*:\=" transparent contains=justAssignmentOperator
+
+syn match justSet '\v^set' contained
+syn keyword justSetKeywords
+ \ allow-duplicate-recipes allow-duplicate-variables dotenv-load dotenv-filename dotenv-path dotenv-required export fallback ignore-comments positional-arguments quiet script-interpreter shell tempdir unstable windows-shell working-directory
+ \ contained
+syn keyword justSetDeprecatedKeywords windows-powershell contained
+syn match justBooleanSet "\v^set%(\s|\\\n)+%(allow-duplicate-%(recip|variabl)es|dotenv-%(loa|require)d|export|fallback|ignore-comments|positional-arguments|quiet|unstable|windows-powershell)%(%(\s|\\\n)*:\=%(\s|\\\n)*%(true|false))?%(\s|\\\n)*%($|#@=)"
+ \ contains=justSet,justSetKeywords,justSetDeprecatedKeywords,justAssignmentOperator,justBoolean
+ \ transparent
+
+syn match justStringSet '\v^set%(\s|\\\n)+\k+%(\s|\\\n)*:\=%(\s|\\\n)*%(x?['"])@=' transparent contains=justSet,justSetKeywords,justAssignmentOperator
+
+syn match justShellSet
+ \ "\v^set%(\s|\\\n)+%(s%(hell|cript-interpreter)|windows-shell)%(\s|\\\n)*:\=%(\s|\\\n)*\[@="
+ \ contains=justSet,justSetKeywords,justAssignmentOperator
+ \ transparent skipwhite
+ \ nextgroup=justShellSetValue
+syn region justShellSetValue
+ \ start='\V[' end='\V]'
+ \ contained
+ \ contains=@justStringLiterals,justShellSetError
+
+syn match justShellSetError '\v\k+['"]@!' contained
+
+syn match justAlias '\v^alias' contained
+syn match justAliasDecl "\v^alias%(\s|\\\n)+\h\k*%(\s|\\\n)*:\=%(\s|\\\n)*"
+ \ transparent
+ \ contains=justAlias,justFunction,justAssignmentOperator
+ \ nextgroup=justAliasRes
+syn match justAliasRes '\v\h\k*%(\s|\\\n)*%(#@=|$)' contained transparent contains=justFunction
+
+syn match justExportedAssignment "\v^export%(\s|\\\n)+\h\k*%(\s|\\\n)*:\=" transparent
+ \ contains=justExport,justAssignmentOperator
+
+syn match justExport '\v^export' contained
+
+syn match justUnexportStatement '\v^unexport%(\s|\\\n)+\w+\s*$' contains=justUnexport
+syn match justUnexport '\v^unexport' contained
+
+syn keyword justConditional if else
+syn region justConditionalBraces start="\v\{\{@!" end="\v\}@=" transparent contains=@justExpr
+syn region justConditionalBracesInInterp start="\v\{\{@!" end="\v\}@=" transparent contained contains=@justExprInInterp
+
+syn match justLineLeadingSymbol "\v^%(\\\n)@3<!\s+\zs%(\@-|-\@|\@|-)"
+
+syn match justLineContinuation "\\$"
+ \ containedin=ALLBUT,justComment,justCommentInBody,justShebang,@justRawStrings,justRecipeAttrArgError,justShellExpandRawDefaultValue
+
+syn region justBody
+ \ start=/\v^\z( +|\t+)%(#!)@!\S/
+ \ skip='\v\\\n|\n\s*$'
+ \ end="\v\n\z1@!|%(^\S)@2<=\_.@="
+ \ contains=justInterpolation,@justOtherCurlyBraces,justLineLeadingSymbol,justCommentInBody,justStringInsideBody,justIndentError
+ \ contained
+
+syn region justShebangBody
+ \ start="\v^\z( +|\t+)#!"
+ \ skip='\v\\\n|\n\s*$'
+ \ end="\v\n\z1@!|%(^\S)@2<=\_.@="
+ \ contains=justInterpolation,@justOtherCurlyBraces,justCommentInBody,justShebang,justStringInShebangBody,justShebangIndentError
+ \ contained
+
+syn cluster justBodies contains=justBody,justShebangBody
+
+syn match justIndentError '\v^%(\\\n)@3<!%( +\zs\t|\t+\zs )\s*\S@='
+syn match justShebangIndentError '\v^ +\zs\t\s*\S@='
+
+syn region justInterpolation
+ \ matchgroup=justInterpolationDelim
+ \ start="\v\{\{\{@!" end="\v%(%(\\\n\s|\S)\s*)@<=\}\}|$"
+ \ matchgroup=justInterpError end='^\S'
+ \ contained
+ \ contains=@justExprInInterp
+
+syn match justBadCurlyBraces '\v\{{3}\ze[^{]' contained
+syn match justCurlyBraces '\v\{{4}' contained
+syn match justBadCurlyBraces '\v\{{5}\ze[^{]' contained
+syn cluster justOtherCurlyBraces contains=justCurlyBraces,justBadCurlyBraces
+
+syn match justFunctionCall "\v\w+%(\s|\\\n)*\(@=" transparent contains=justBuiltInFunction
+
+" error() is intentionally not included in this list
+syn keyword justBuiltInFunction
+ \ absolute_path append arch blake3 blake3_file cache_dir cache_directory canonicalize capitalize choose clean config_dir config_directory config_local_dir config_local_directory data_dir data_directory data_local_dir data_local_directory datetime datetime_utc encode_uri_component env env_var env_var_or_default executable_dir executable_directory extension file_name file_stem home_dir home_directory invocation_dir invocation_dir_native invocation_directory invocation_directory_native is_dependency join just_executable just_pid justfile justfile_dir justfile_directory kebabcase lowercamelcase lowercase module_dir module_directory module_file num_cpus os os_family parent_dir parent_directory path_exists prepend quote replace replace_regex semver_matches sha256 sha256_file shell shoutykebabcase shoutysnakecase snakecase source_dir source_directory source_file style titlecase trim trim_end trim_end_match trim_end_matches trim_start trim_start_match trim_start_matches uppercamelcase uppercase uuid without_extension
+ \ contained
+
+syn match justUserDefinedError "\v%(assert|error)%(%(\s|\\\n)*\()@="
+
+syn match justReplaceRegex '\vreplace_regex%(\s|\\\n)*\(@=' transparent contains=justBuiltInFunction nextgroup=justReplaceRegexCall
+syn match justReplaceRegexInInterp '\vreplace_regex%(\s|\\\n)*\(@=' transparent contained contains=justBuiltInFunction nextgroup=justReplaceRegexCallInInterp
+
+syn region justReplaceRegexCall
+ \ matchgroup=justReplaceRegexCall
+ \ start='\V(' end='\V)'
+ \ transparent contained
+ \ contains=@justExpr,justRegexReplacement
+syn region justReplaceRegexCallInInterp
+ \ matchgroup=justReplaceRegexCall
+ \ start='\V(' end='\V)'
+ \ transparent contained
+ \ contains=@justExprInInterp,justRegexReplacement
+
+syn match justParameterLineContinuation '\v%(\s|\\\n)*' contained nextgroup=justParameterError
+
+syn match justRecipeDepParenName '\v%(\(\n?)@3<=%(\_s|\\\n)*\h\k*'
+ \ transparent contained
+ \ contains=justFunction
+
+syn cluster justBuiltInFunctions contains=justFunctionCall,justUserDefinedError
+
+syn match justConditionalOperator "\V=="
+syn match justConditionalOperator "\V!="
+syn match justConditionalOperator "\V=~"
+
+syn match justOperator "\V+"
+syn match justOperator "\V/"
+syn match justOperator "\V&&"
+syn match justOperator "\V||"
+
+syn keyword justConstant
+ \ HEX HEXLOWER HEXUPPER
+ \ CLEAR NORMAL BOLD ITALIC UNDERLINE INVERT HIDE STRIKETHROUGH
+ \ BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE
+ \ BG_BLACK BG_RED BG_GREEN BG_YELLOW BG_BLUE BG_MAGENTA BG_CYAN BG_WHITE
+
+syn match justShellExpandVarRaw '\v\$%(\{\_[^}]*\}|\w+)' contained contains=justShellExpandRawDefaultDelimiter
+syn match justShellExpandRawDefaultDelimiter '\V:-' contained nextgroup=justShellExpandRawDefaultValue
+syn match justShellExpandRawDefaultValue '\v\_[^}]*' contained
+syn match justShellExpandVar '\v\$%(\w|\\\n\s*)+' contained
+syn region justShellExpandVar start='\v\$%(\\\n\s*)*\{' end='\V}' contains=justShellExpandDefaultDelimiter,justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError
+syn match justShellExpandDefaultDelimiter '\v:%(\\\n\s*)*-@=' contained nextgroup=justShellExpandDefault
+syn region justShellExpandDefault
+ \ matchgroup=justShellExpandDefaultDelimiter start='\V-' end='\v\}@='
+ \ contained
+ \ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError
+
+syn match justDollarEscape '\V$$' contained
+syn match justDollarEscapeSplit '\v\$%(\\\n\s*)*\$' contained
+
+syn cluster justExprBase contains=@justAllStrings,@justBuiltInFunctions,justConditional,justConditionalOperator,justOperator,justConstant
+syn cluster justExpr contains=@justExprBase,justExprParen,justConditionalBraces,justReplaceRegex
+syn cluster justExprInInterp contains=@justExprBase,justName,justExprParenInInterp,justConditionalBracesInInterp,justReplaceRegexInInterp
+
+syn cluster justExprFunc contains=@justBuiltInFunctions,justReplaceRegex,justExprParen
+
+syn match justImport /\v^import%(%(\s|\\\n)*\?|%(\s|\\\n)+%(x?['"])@=)/ transparent
+ \ contains=justImportStatement,justOptionalFile
+syn match justImportStatement '^import' contained
+
+syn match justOldInclude "^!include"
+
+syn match justModule /\v^mod%(%(\s|\\\n)*\?)?%(\s|\\\n)+\h\k*\s*%($|%(\s|\\\n)*%(x?['"]|#)@=)/
+ \ transparent contains=justModStatement,justName,justOptionalFile
+syn match justModStatement '^mod' contained
+
+syn match justOptionalFile '\V?' contained
+
+" Most linked colorscheme colors are chosen based on semantics of the color name.
+" Some are for parity with other syntax files (for example, Number for recipe body highlighting
+" is to align with the make.vim distributed with Vim).
+" Deprecated `just` syntaxes are highlighted as Underlined.
+"
+" Colors are linked 'def'(ault) so that users who prefer other colors
+" can override them, e.g. in ~/.vim/after/syntax/just.vim
+"
+" Note that vim-just's highlight groups are an implementation detail and may be subject to change.
+
+" The list of highlight links is sorted alphabetically.
+
+hi def link justAlias Statement
+hi def link justAssignmentOperator Operator
+hi def link justBacktick Special
+hi def link justBadCurlyBraces Error
+hi def link justBody Number
+hi def link justBoolean Boolean
+hi def link justBuiltInFunction Function
+hi def link justComment Comment
+hi def link justCommentInBody Comment
+hi def link justCommentTodo Todo
+hi def link justConditional Conditional
+hi def link justConditionalOperator Conditional
+hi def link justConstant Constant
+hi def link justCurlyBraces Special
+hi def link justDollarEscape Special
+hi def link justDollarEscapeSplit Special
+hi def link justExport Statement
+hi def link justFunction Function
+hi def link justImportStatement Include
+hi def link justIndentError Error
+hi def link justInterpError Error
+hi def link justInterpolation Normal
+hi def link justInterpolationDelim Delimiter
+hi def link justInvalidAttrValue Error
+hi def link justLineContinuation Special
+hi def link justLineLeadingSymbol Special
+hi def link justModStatement Keyword
+hi def link justName Identifier
+hi def link justOldInclude Error
+hi def link justOperator Operator
+hi def link justOptionalFile Conditional
+hi def link justParameterError Error
+hi def link justParameterOperator Operator
+hi def link justParamExport Statement
+hi def link justRawString String
+hi def link justRawStrRegexRepl String
+hi def link justRecipeAt Special
+hi def link justRecipeAttr Type
+hi def link justRecipeAttrArgError Error
+hi def link justRecipeAttrSep Operator
+hi def link justRecipeAttrValueColon Operator
+hi def link justRecipeColon Operator
+hi def link justRecipeDepParamsParen Delimiter
+hi def link justRecipeSubsequentDeps Delimiter
+hi def link justRegexCapture Identifier
+hi def link justSet Statement
+hi def link justSetDeprecatedKeywords Underlined
+hi def link justSetKeywords Keyword
+hi def link justShebang SpecialComment
+hi def link justShebangBody Number
+hi def link justShebangIndentError Error
+hi def link justShellExpandDefault Character
+hi def link justShellExpandDefaultDelimiter Operator
+hi def link justShellExpandRawDefaultDelimiter Operator
+hi def link justShellExpandRawDefaultValue Character
+hi def link justShellExpandRawString String
+hi def link justShellExpandString String
+hi def link justShellExpandVar PreProc
+hi def link justShellExpandVarRaw PreProc
+hi def link justShellSetError Error
+hi def link justString String
+hi def link justStringEscapeError Error
+hi def link justStringEscapeSequence Special
+hi def link justStringInShebangBody String
+hi def link justStringInsideBody String
+hi def link justStringRegexRepl String
+hi def link justStringUEscapeSequence Special
+hi def link justUnexport Statement
+hi def link justUserDefinedError Exception
+hi def link justVariadicPrefix Statement
+hi def link justVariadicPrefixError Error
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/karel.vim b/runtime/syntax/karel.vim
new file mode 100644
index 0000000000..85c78529e6
--- /dev/null
+++ b/runtime/syntax/karel.vim
@@ -0,0 +1,112 @@
+" Vim syntax file
+" Language: KAREL
+" Last Change: 2024-11-17
+" Maintainer: Kirill Morozov <kirill@robotix.pro>
+" Credits: Jay Strybis for the initial implementation and Patrick Knosowski
+" for a couple of fixes.
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" KAREL is case-insensitive
+syntax case ignore
+
+" Identifiers
+syn match karelIdentifier /[a-zA-Z0-9_]\+/
+hi def link karelIdentifier Identifier
+
+" Constants
+syn keyword karelConstant CR
+syn region karelString start="'" end="'"
+syn match karelReal /\d\+\.\d\+/
+syn match karelInteger /\d\+/
+syn keyword karelBoolean true false
+hi def link karelConstant Constant
+hi def link karelString String
+hi def link karelInteger Number
+hi def link karelReal Float
+hi def link karelBoolean Boolean
+
+" Directives
+syn match karelDirective /%[a-zA-Z]\+/
+hi def link karelDirective PreProc
+
+" Operators
+syn keyword karelOperator AND OR NOT DIV MOD
+syn match karelOperator /[\+\-\*\/\<\=\>\:\#\@]/
+syn match karelOperator /<=/
+syn match karelOperator />=/
+syn match karelOperator /<>/
+syn match karelOperator />=</
+hi def link karelOperator Operator
+
+" Types
+syn keyword karelType ARRAY BOOLEAN BYTE CONFIG DISP_DAT_T FILE INTEGER JOINTPOS PATH POSITION QUEUE_TYPE REAL SHORT STD_PTH_NODE STRING VECTOR XYZWPR XYZWPREXT
+syn keyword karelStructure STRUCTURE ENDSTRUCTURE
+hi def link karelType Type
+hi def link karelStructure Typedef
+
+syn keyword karelAction NOABORT NOMESSAGE NOPAUSE PAUSE PULSE RESUME STOP UNHOLD UNPAUSE
+syn match karelAction /SIGNAL EVENT/
+syn match karelAction /SIGNAL SEMAPHORE/
+hi def link karelAction Keyword
+
+syn keyword karelFunction ABS ACOS APPROACH ARRAY_LEN ASIN ATAN2 ATTACH BYNAME BYTES_LEFT CHR COS CURJPOS CURPOS CURR_PROG EXP
+syn keyword karelFunction FRAME GET_FILE_POS GET_JPOS_REG GET_JPOS_TPE GET_PORT_ATR GET_POS_REG GET_POS_TPE GET_USEC_TIM INDEX
+syn keyword karelFunction IN_RANGE INV IO_STATUS J_IN_RANGE JOINT2POS LN MIRROR MOTION_CTL NODE_SIZE ORD ORIENT PATH_LEN POS POS2JOINT
+syn keyword karelFunction ROUND SEMA_COUNT SIN SQRT STR_LEN SUB_STR TAN TRUNC UNINIT
+hi def link karelFunction Function
+
+syn keyword karelClause EVAL FROM IN WHEN WITH
+hi def link karelClause Keyword
+
+syn keyword karelConditional IF THEN ELSE ENDIF SELECT ENDSELECT CASE
+hi def link karelConditional Conditional
+
+syn keyword karelRepeat WHILE DO ENDWHILE FOR
+hi def link karelRepeat Repeat
+
+syn keyword karelProcedure ABORT_TASK ACT_SCREEN ACT_TBL ADD_BYNAMEPC ADD_DICT ADD_INTPC ADD_REALPC ADD_STRINGPC APPEND_NODE APPEND_QUEUE
+syn keyword karelProcedure ATT_WINDOW_D ATT_WINDOW_S AVL_POS_NUM
+syn keyword karelProcedure BYTES_AHEAD
+syn keyword karelProcedure CALL_PROG CALL_PROGLIN CHECK_DICT CHECK_EPOS CHECK_NAME CLEAR CLEAR_SEMA CLOSE_TEP CLR_IO_STAT CLR_PORT_SIM CLR_POS_REG
+syn keyword karelProcedure CNC_DYN_DISB CNC_DYN_DISE CNC_DYN_DISI CNC_DYN_DISP CNC_DYN_DISR CNC_DYN_DISS CNCL_STP_MTN CNV_CNF_STRG CNV_CONF_STR CNV_INT_STR CNV_JPOS_REL CNV_REAL_STR CNV_REL_JPOS CNV_STR_CONF CNV_STR_INT CNV_STR_REAL CNV_STR_TIME CNV_TIME_STR
+syn keyword karelProcedure COMPARE_FILE CONT_TASK COPY_FILE COPY_PATH COPY_QUEUE COPY_TPE CREATE_TPE CREATE_VAR
+syn keyword karelProcedure DAQ_CHECKP DAQ_REGPIPE DAQ_START DAQ_STOP DAQ_UNREG DAQ_WRITE DEF_SCREEN DEF_WINDOW
+syn keyword karelProcedure DELETE_FILE DELETE_NODE DELETE_QUEUE DEL_INST_TPE DET_WINDOW DISCTRL_ALPH DISCTRL_FORM DISCTRL_LIST DISCTRL_PLMN DISCTRL_SBMN DISCTRL_TBL DISMOUNT_DEV DOSFILE_INF
+syn keyword karelProcedure ERR_DATA FILE_LIST FORCE_SPMENU FORMAT_DEV GET_ATTR_PRG GET_PORT_ASG GET_PORT_CMT GET_PORT_MOD GET_PORT_SIM GET_PORT_VAL GET_POS_FRM GET_POS_TYP GET_PREG_CMT GET_QUEUE
+syn keyword karelProcedure GET_REG GET_REG_CMT GET_SREG_CMT GET_STR_REG GET_TIME GET_TPE_CMT GET_TPE_PRM GET_TSK_INFO GET_USEC_SUB GET_VAR
+syn keyword karelProcedure INI_DYN_DISB INI_DYN_DISE INI_DYN_DISI INI_DYN_DISP INI_DYN_DISR INI_DYN_DISS INIT_QUEUE INIT_TBL INSERT_NODE INSERT_QUEUE IO_MOD_TYPE
+syn keyword karelProcedure KCL KCL_NO_WAIT KCL_STATUS LOAD LOAD_STATUS LOCK_GROUP MODIFY_QUEUE MOUNT_DEV MOVE_FILE MSG_CONNECT MSG_DISO MSG_PING
+syn keyword karelProcedure OPEN_TPE PAUSE_TASK PEND_SEMA PIPE_CONFIG POP_KEY_RD POS_REG_TYPE POST_ERR POST_ERR_L POST_SEMA PRINT_FILE PROG_BACKUP PROG_CLEAR PROG_RESTORE PROG_LIST
+syn keyword karelProcedure PURGE_DEV PUSH_KEY_RD READ_DICT READ_DICT_V READ_KB REMOVE_DICT RENAME_FILE RENAME_VAR RENAME_VARS RESET RUN_TASK SAVE SAVE_DRAM SELECT_TPE SEND_DATAPC SEND_EVENTPC SET_ATTR_PRG SET_CURSOR SET_EPOS_REG SET_EPOS_TPE
+syn keyword karelProcedure SET_FILE_ATR SET_FILE_POS SET_INT_REG SET_JPOS_REG SET_JPOS_TPE SET_LANG SET_PERCH SET_PORT_ASG SET_PORT_ATR SET_PORT_CMT SET_PORT_MOD SET_PORT_SIM SET_PORT_VAL SET_POS_REG SET_POS_TPE SET_PREG_CMT SET_REAL_REG SET_REG CMT SET_SREG_CMT SET_STR_REG SET_TIME SET_TPE_CMT SET_TRNS_TPE SET_TSK_ATTR SET_TSK_NAME SET_VAR
+syn keyword karelProcedure TRANSLATE UNLOCK_GROUP UNPOS V_CAM_CALIB V_GET_OFFSET V_GET_PASSFL V_GET_QUEUE V_INIT_QUEUE V_RALC_QUEUE V_RUN_FIND V_SET_REF V_START_VTRK V_STOP_VTRK VAR_INFO VAR_LIST VOL_SPACE VREG_FND_POS VREG_OFFSET
+syn keyword karelProcedure WRITE_DICT WRITE_DICT_V XML_ADDTAG XML_GETDATA XML_REMTAG XML_SCAN XML_SETVAR
+hi def link karelProcedure Function
+
+syn keyword karelStatement ABORT CONDITION ENDCONDITION CONTINUE DELAY ERROR EVENT FOR ENDFOR HOLD READ RELEASE REPEAT RETURN SEMAPHORE UNTIL USING ENDUSING WRITE
+syn match karelStatement /CANCEL FILE/
+syn match karelStatement /CLOSE FILE/
+syn match karelStatement /CLOSE HAND/
+syn match karelStatement /CONNECT TIMER/
+syn match karelStatement /DISABLE CONDITION/
+syn match karelStatement /DISCONNECT TIMER/
+syn match karelStatement /ENABLE CONDITION/
+syn match karelStatement /GO TO/
+syn match karelStatement /OPEN FILE/
+syn match karelStatement /OPEN HAND/
+syn match karelStatement /PURGE CONDITION/
+syn match karelStatement /RELAX HAND/
+syn match karelStatement /WAIT FOR/
+hi def link karelStatement Statement
+
+syn keyword karelKeyword BEGIN CONST END PROGRAM ROUTINE STRUCT TYPE VAR
+hi def link karelKeyword Keyword
+
+" Comments
+syn region karelComment start="--" end="$"
+hi def link karelComment Comment
+
+let b:current_syntax = "karel"
diff --git a/runtime/syntax/kconfig.vim b/runtime/syntax/kconfig.vim
index 0aecc00060..64a47a5995 100644
--- a/runtime/syntax/kconfig.vim
+++ b/runtime/syntax/kconfig.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Maintainer: Christian Brabandt <cb@256bit.org>
" Previous Maintainer: Nikolai Weibull <now@bitwi.se>
-" Latest Revision: 2024-07-19
+" Latest Revision: 2025 Jan 20
" License: Vim (see :h license)
" Repository: https://github.com/chrisbra/vim-kconfig
@@ -12,722 +12,722 @@ endif
let s:cpo_save = &cpo
set cpo&vim
+exe "syn sync minlines=" . get(g:, 'kconfig_minlines', 50)
+
if exists("g:kconfig_syntax_heavy")
-syn match kconfigBegin '^' nextgroup=kconfigKeyword
- \ skipwhite
-
-syn keyword kconfigTodo contained TODO FIXME XXX NOTE
-
-syn match kconfigComment display '#.*$' contains=kconfigTodo
-
-syn keyword kconfigKeyword config nextgroup=kconfigSymbol
- \ skipwhite
-
-syn keyword kconfigKeyword menuconfig nextgroup=kconfigSymbol
- \ skipwhite
-
-syn keyword kconfigKeyword comment menu mainmenu
- \ nextgroup=kconfigKeywordPrompt
- \ skipwhite
-
-syn keyword kconfigKeyword choice
- \ nextgroup=@kconfigConfigOptions
- \ skipwhite skipnl
-
-syn keyword kconfigKeyword endmenu endchoice
-
-syn keyword kconfigPreProc source
- \ nextgroup=kconfigPath
- \ skipwhite
-
-" TODO: This is a hack. The who .*Expr stuff should really be generated so
-" that we can reuse it for various nextgroups.
-syn keyword kconfigConditional if endif
- \ nextgroup=@kconfigConfigOptionIfExpr
- \ skipwhite
-
-syn match kconfigKeywordPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=@kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigPath '"[^"\\]*\%(\\.[^"\\]*\)*"\|\S\+'
- \ contained
-
-syn match kconfigSymbol '\<\k\+\>'
- \ contained
- \ nextgroup=@kconfigConfigOptions
- \ skipwhite skipnl
-
-" FIXME: There is – probably – no reason to cluster these instead of just
-" defining them in the same group.
-syn cluster kconfigConfigOptions contains=kconfigTypeDefinition,
- \ kconfigInputPrompt,
- \ kconfigDefaultValue,
- \ kconfigDependencies,
- \ kconfigReverseDependencies,
- \ kconfigNumericalRanges,
- \ kconfigHelpText,
- \ kconfigDefBool,
- \ kconfigOptional
-
-syn keyword kconfigTypeDefinition bool boolean tristate string hex int
- \ contained
- \ nextgroup=kconfigTypeDefPrompt,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigTypeDefPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigTypeDefPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn keyword kconfigInputPrompt prompt
- \ contained
- \ nextgroup=kconfigPromptPrompt
- \ skipwhite
-
-syn match kconfigPromptPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigPromptPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn keyword kconfigDefaultValue default
- \ contained
- \ nextgroup=@kconfigConfigOptionExpr
- \ skipwhite
-
-syn match kconfigDependencies 'depends on\|requires'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfExpr
- \ skipwhite
-
-syn keyword kconfigReverseDependencies select
- \ contained
- \ nextgroup=@kconfigRevDepSymbol
- \ skipwhite
-
-syn cluster kconfigRevDepSymbol contains=kconfigRevDepCSymbol,
- \ kconfigRevDepNCSymbol
-
-syn match kconfigRevDepCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigRevDepCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigRevDepNCSymbol '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn keyword kconfigNumericalRanges range
- \ contained
- \ nextgroup=@kconfigRangeSymbol
- \ skipwhite
-
-syn cluster kconfigRangeSymbol contains=kconfigRangeCSymbol,
- \ kconfigRangeNCSymbol
-
-syn match kconfigRangeCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=@kconfigRangeSymbol2
- \ skipwhite skipnl
-
-syn match kconfigRangeCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=@kconfigRangeSymbol2
- \ skipwhite skipnl
-
-syn match kconfigRangeNCSymbol '\<\k\+\>'
- \ contained
- \ nextgroup=@kconfigRangeSymbol2
- \ skipwhite skipnl
-
-syn cluster kconfigRangeSymbol2 contains=kconfigRangeCSymbol2,
- \ kconfigRangeNCSymbol2
-
-syn match kconfigRangeCSymbol2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigRangeNCSymbol2 '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn region kconfigHelpText contained
- \ matchgroup=kconfigConfigOption
- \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)'
- \ skip='^$'
- \ end='^\z1\@!'
- \ nextgroup=@kconfigConfigOptions
- \ skipwhite skipnl
-
-" XXX: Undocumented
-syn keyword kconfigDefBool def_bool
- \ contained
- \ nextgroup=@kconfigDefBoolSymbol
- \ skipwhite
-
-syn cluster kconfigDefBoolSymbol contains=kconfigDefBoolCSymbol,
- \ kconfigDefBoolNCSymbol
-
-syn match kconfigDefBoolCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigDefBoolCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigDefBoolNCSymbol '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-" XXX: This is actually only a valid option for “choice”, but treating it
-" specially would require a lot of extra groups.
-syn keyword kconfigOptional optional
- \ contained
- \ nextgroup=@kconfigConfigOptions
- \ skipwhite skipnl
-
-syn keyword kconfigConfigOptionIf if
- \ contained
- \ nextgroup=@kconfigConfigOptionIfExpr
- \ skipwhite
-
-syn cluster kconfigConfigOptionIfExpr contains=@kconfigConfOptIfExprSym,
- \ kconfigConfOptIfExprNeg,
- \ kconfigConfOptIfExprGroup
-
-syn cluster kconfigConfOptIfExprSym contains=kconfigConfOptIfExprCSym,
- \ kconfigConfOptIfExprNCSym
-
-syn match kconfigConfOptIfExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=@kconfigConfigOptions,
- \ kconfigConfOptIfExprAnd,
- \ kconfigConfOptIfExprOr,
- \ kconfigConfOptIfExprEq,
- \ kconfigConfOptIfExprNEq
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=@kconfigConfigOptions,
- \ kconfigConfOptIfExprAnd,
- \ kconfigConfOptIfExprOr,
- \ kconfigConfOptIfExprEq,
- \ kconfigConfOptIfExprNEq
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfExprNCSym '\<\k\+\>'
- \ contained
- \ nextgroup=@kconfigConfigOptions,
- \ kconfigConfOptIfExprAnd,
- \ kconfigConfOptIfExprOr,
- \ kconfigConfOptIfExprEq,
- \ kconfigConfOptIfExprNEq
- \ skipwhite skipnl
-
-syn cluster kconfigConfOptIfExprSym2 contains=kconfigConfOptIfExprCSym2,
- \ kconfigConfOptIfExprNCSym2
-
-syn match kconfigConfOptIfExprEq '='
- \ contained
- \ nextgroup=@kconfigConfOptIfExprSym2
- \ skipwhite
-
-syn match kconfigConfOptIfExprNEq '!='
- \ contained
- \ nextgroup=@kconfigConfOptIfExprSym2
- \ skipwhite
-
-syn match kconfigConfOptIfExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=@kconfigConfigOptions,
- \ kconfigConfOptIfExprAnd,
- \ kconfigConfOptIfExprOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfExprNCSym2 '\<\k\+\>'
- \ contained
- \ nextgroup=@kconfigConfigOptions,
- \ kconfigConfOptIfExprAnd,
- \ kconfigConfOptIfExprOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfExprNeg '!'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfExpr
- \ skipwhite
-
-syn match kconfigConfOptIfExprAnd '&&'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfExpr
- \ skipwhite
-
-syn match kconfigConfOptIfExprOr '||'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfExpr
- \ skipwhite
-
-syn match kconfigConfOptIfExprGroup '('
- \ contained
- \ nextgroup=@kconfigConfigOptionIfGExp
- \ skipwhite
-
-" TODO: hm, this kind of recursion doesn't work right. We need another set of
-" expressions that have kconfigConfigOPtionIfGExp as nextgroup and a matcher
-" for '(' that sets it all off.
-syn cluster kconfigConfigOptionIfGExp contains=@kconfigConfOptIfGExpSym,
- \ kconfigConfOptIfGExpNeg,
- \ kconfigConfOptIfExprGroup
-
-syn cluster kconfigConfOptIfGExpSym contains=kconfigConfOptIfGExpCSym,
- \ kconfigConfOptIfGExpNCSym
-
-syn match kconfigConfOptIfGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=@kconfigConfigIf,
- \ kconfigConfOptIfGExpAnd,
- \ kconfigConfOptIfGExpOr,
- \ kconfigConfOptIfGExpEq,
- \ kconfigConfOptIfGExpNEq
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=@kconfigConfigIf,
- \ kconfigConfOptIfGExpAnd,
- \ kconfigConfOptIfGExpOr,
- \ kconfigConfOptIfGExpEq,
- \ kconfigConfOptIfGExpNEq
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfGExpNCSym '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfOptIfExprGrpE,
- \ kconfigConfOptIfGExpAnd,
- \ kconfigConfOptIfGExpOr,
- \ kconfigConfOptIfGExpEq,
- \ kconfigConfOptIfGExpNEq
- \ skipwhite skipnl
-
-syn cluster kconfigConfOptIfGExpSym2 contains=kconfigConfOptIfGExpCSym2,
- \ kconfigConfOptIfGExpNCSym2
-
-syn match kconfigConfOptIfGExpEq '='
- \ contained
- \ nextgroup=@kconfigConfOptIfGExpSym2
- \ skipwhite
-
-syn match kconfigConfOptIfGExpNEq '!='
- \ contained
- \ nextgroup=@kconfigConfOptIfGExpSym2
- \ skipwhite
-
-syn match kconfigConfOptIfGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfOptIfExprGrpE,
- \ kconfigConfOptIfGExpAnd,
- \ kconfigConfOptIfGExpOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfOptIfExprGrpE,
- \ kconfigConfOptIfGExpAnd,
- \ kconfigConfOptIfGExpOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfGExpNCSym2 '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfOptIfExprGrpE,
- \ kconfigConfOptIfGExpAnd,
- \ kconfigConfOptIfGExpOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptIfGExpNeg '!'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfGExp
- \ skipwhite
-
-syn match kconfigConfOptIfGExpAnd '&&'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfGExp
- \ skipwhite
-
-syn match kconfigConfOptIfGExpOr '||'
- \ contained
- \ nextgroup=@kconfigConfigOptionIfGExp
- \ skipwhite
-
-syn match kconfigConfOptIfExprGrpE ')'
- \ contained
- \ nextgroup=@kconfigConfigOptions,
- \ kconfigConfOptIfExprAnd,
- \ kconfigConfOptIfExprOr
- \ skipwhite skipnl
-
-
-syn cluster kconfigConfigOptionExpr contains=@kconfigConfOptExprSym,
- \ kconfigConfOptExprNeg,
- \ kconfigConfOptExprGroup
-
-syn cluster kconfigConfOptExprSym contains=kconfigConfOptExprCSym,
- \ kconfigConfOptExprNCSym
-
-syn match kconfigConfOptExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr,
- \ kconfigConfOptExprEq,
- \ kconfigConfOptExprNEq,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigConfOptExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr,
- \ kconfigConfOptExprEq,
- \ kconfigConfOptExprNEq,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigConfOptExprNCSym '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr,
- \ kconfigConfOptExprEq,
- \ kconfigConfOptExprNEq,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn cluster kconfigConfOptExprSym2 contains=kconfigConfOptExprCSym2,
- \ kconfigConfOptExprNCSym2
-
-syn match kconfigConfOptExprEq '='
- \ contained
- \ nextgroup=@kconfigConfOptExprSym2
- \ skipwhite
-
-syn match kconfigConfOptExprNEq '!='
- \ contained
- \ nextgroup=@kconfigConfOptExprSym2
- \ skipwhite
-
-syn match kconfigConfOptExprCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigConfOptExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigConfOptExprNCSym2 '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr,
- \ @kconfigConfigOptions
- \ skipwhite skipnl
-
-syn match kconfigConfOptExprNeg '!'
- \ contained
- \ nextgroup=@kconfigConfigOptionExpr
- \ skipwhite
-
-syn match kconfigConfOptExprAnd '&&'
- \ contained
- \ nextgroup=@kconfigConfigOptionExpr
- \ skipwhite
-
-syn match kconfigConfOptExprOr '||'
- \ contained
- \ nextgroup=@kconfigConfigOptionExpr
- \ skipwhite
-
-syn match kconfigConfOptExprGroup '('
- \ contained
- \ nextgroup=@kconfigConfigOptionGExp
- \ skipwhite
-
-syn cluster kconfigConfigOptionGExp contains=@kconfigConfOptGExpSym,
- \ kconfigConfOptGExpNeg,
- \ kconfigConfOptGExpGroup
-
-syn cluster kconfigConfOptGExpSym contains=kconfigConfOptGExpCSym,
- \ kconfigConfOptGExpNCSym
-
-syn match kconfigConfOptGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfOptExprGrpE,
- \ kconfigConfOptGExpAnd,
- \ kconfigConfOptGExpOr,
- \ kconfigConfOptGExpEq,
- \ kconfigConfOptGExpNEq
- \ skipwhite skipnl
-
-syn match kconfigConfOptGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfOptExprGrpE,
- \ kconfigConfOptGExpAnd,
- \ kconfigConfOptGExpOr,
- \ kconfigConfOptGExpEq,
- \ kconfigConfOptGExpNEq
- \ skipwhite skipnl
-
-syn match kconfigConfOptGExpNCSym '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfOptExprGrpE,
- \ kconfigConfOptGExpAnd,
- \ kconfigConfOptGExpOr,
- \ kconfigConfOptGExpEq,
- \ kconfigConfOptGExpNEq
- \ skipwhite skipnl
-
-syn cluster kconfigConfOptGExpSym2 contains=kconfigConfOptGExpCSym2,
- \ kconfigConfOptGExpNCSym2
-
-syn match kconfigConfOptGExpEq '='
- \ contained
- \ nextgroup=@kconfigConfOptGExpSym2
- \ skipwhite
-
-syn match kconfigConfOptGExpNEq '!='
- \ contained
- \ nextgroup=@kconfigConfOptGExpSym2
- \ skipwhite
-
-syn match kconfigConfOptGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"'
- \ contained
- \ nextgroup=kconfigConfOptExprGrpE,
- \ kconfigConfOptGExpAnd,
- \ kconfigConfOptGExpOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
- \ contained
- \ nextgroup=kconfigConfOptExprGrpE,
- \ kconfigConfOptGExpAnd,
- \ kconfigConfOptGExpOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptGExpNCSym2 '\<\k\+\>'
- \ contained
- \ nextgroup=kconfigConfOptExprGrpE,
- \ kconfigConfOptGExpAnd,
- \ kconfigConfOptGExpOr
- \ skipwhite skipnl
-
-syn match kconfigConfOptGExpNeg '!'
- \ contained
- \ nextgroup=@kconfigConfigOptionGExp
- \ skipwhite
-
-syn match kconfigConfOptGExpAnd '&&'
- \ contained
- \ nextgroup=@kconfigConfigOptionGExp
- \ skipwhite
-
-syn match kconfigConfOptGExpOr '||'
- \ contained
- \ nextgroup=@kconfigConfigOptionGExp
- \ skipwhite
-
-syn match kconfigConfOptExprGrpE ')'
- \ contained
- \ nextgroup=kconfigConfigOptionIf,
- \ kconfigConfOptExprAnd,
- \ kconfigConfOptExprOr
- \ skipwhite skipnl
-
-syn sync minlines=50
-
-hi def link kconfigTodo Todo
-hi def link kconfigComment Comment
-hi def link kconfigKeyword Keyword
-hi def link kconfigPreProc PreProc
-hi def link kconfigConditional Conditional
-hi def link kconfigPrompt String
-hi def link kconfigKeywordPrompt kconfigPrompt
-hi def link kconfigPath String
-hi def link kconfigSymbol String
-hi def link kconfigConstantSymbol Constant
-hi def link kconfigConfigOption Type
-hi def link kconfigTypeDefinition kconfigConfigOption
-hi def link kconfigTypeDefPrompt kconfigPrompt
-hi def link kconfigInputPrompt kconfigConfigOption
-hi def link kconfigPromptPrompt kconfigPrompt
-hi def link kconfigDefaultValue kconfigConfigOption
-hi def link kconfigDependencies kconfigConfigOption
-hi def link kconfigReverseDependencies kconfigConfigOption
-hi def link kconfigRevDepCSymbol kconfigConstantSymbol
-hi def link kconfigRevDepNCSymbol kconfigSymbol
-hi def link kconfigNumericalRanges kconfigConfigOption
-hi def link kconfigRangeCSymbol kconfigConstantSymbol
-hi def link kconfigRangeNCSymbol kconfigSymbol
-hi def link kconfigRangeCSymbol2 kconfigConstantSymbol
-hi def link kconfigRangeNCSymbol2 kconfigSymbol
-hi def link kconfigHelpText Normal
-hi def link kconfigDefBool kconfigConfigOption
-hi def link kconfigDefBoolCSymbol kconfigConstantSymbol
-hi def link kconfigDefBoolNCSymbol kconfigSymbol
-hi def link kconfigOptional kconfigConfigOption
-hi def link kconfigConfigOptionIf Conditional
-hi def link kconfigConfOptIfExprCSym kconfigConstantSymbol
-hi def link kconfigConfOptIfExprNCSym kconfigSymbol
-hi def link kconfigOperator Operator
-hi def link kconfigConfOptIfExprEq kconfigOperator
-hi def link kconfigConfOptIfExprNEq kconfigOperator
-hi def link kconfigConfOptIfExprCSym2 kconfigConstantSymbol
-hi def link kconfigConfOptIfExprNCSym2 kconfigSymbol
-hi def link kconfigConfOptIfExprNeg kconfigOperator
-hi def link kconfigConfOptIfExprAnd kconfigOperator
-hi def link kconfigConfOptIfExprOr kconfigOperator
-hi def link kconfigDelimiter Delimiter
-hi def link kconfigConfOptIfExprGroup kconfigDelimiter
-hi def link kconfigConfOptIfGExpCSym kconfigConstantSymbol
-hi def link kconfigConfOptIfGExpNCSym kconfigSymbol
-hi def link kconfigConfOptIfGExpEq kconfigOperator
-hi def link kconfigConfOptIfGExpNEq kconfigOperator
-hi def link kconfigConfOptIfGExpCSym2 kconfigConstantSymbol
-hi def link kconfigConfOptIfGExpNCSym2 kconfigSymbol
-hi def link kconfigConfOptIfGExpNeg kconfigOperator
-hi def link kconfigConfOptIfGExpAnd kconfigOperator
-hi def link kconfigConfOptIfGExpOr kconfigOperator
-hi def link kconfigConfOptIfExprGrpE kconfigDelimiter
-hi def link kconfigConfOptExprCSym kconfigConstantSymbol
-hi def link kconfigConfOptExprNCSym kconfigSymbol
-hi def link kconfigConfOptExprEq kconfigOperator
-hi def link kconfigConfOptExprNEq kconfigOperator
-hi def link kconfigConfOptExprCSym2 kconfigConstantSymbol
-hi def link kconfigConfOptExprNCSym2 kconfigSymbol
-hi def link kconfigConfOptExprNeg kconfigOperator
-hi def link kconfigConfOptExprAnd kconfigOperator
-hi def link kconfigConfOptExprOr kconfigOperator
-hi def link kconfigConfOptExprGroup kconfigDelimiter
-hi def link kconfigConfOptGExpCSym kconfigConstantSymbol
-hi def link kconfigConfOptGExpNCSym kconfigSymbol
-hi def link kconfigConfOptGExpEq kconfigOperator
-hi def link kconfigConfOptGExpNEq kconfigOperator
-hi def link kconfigConfOptGExpCSym2 kconfigConstantSymbol
-hi def link kconfigConfOptGExpNCSym2 kconfigSymbol
-hi def link kconfigConfOptGExpNeg kconfigOperator
-hi def link kconfigConfOptGExpAnd kconfigOperator
-hi def link kconfigConfOptGExpOr kconfigOperator
-hi def link kconfigConfOptExprGrpE kconfigConfOptIfExprGroup
+ syn match kconfigBegin '^' nextgroup=kconfigKeyword
+ \ skipwhite
+
+ syn keyword kconfigTodo contained TODO FIXME XXX NOTE
+
+ syn match kconfigComment display '#.*$' contains=kconfigTodo
+
+ syn keyword kconfigKeyword config nextgroup=kconfigSymbol
+ \ skipwhite
+
+ syn keyword kconfigKeyword menuconfig nextgroup=kconfigSymbol
+ \ skipwhite
+
+ syn keyword kconfigKeyword comment menu mainmenu
+ \ nextgroup=kconfigKeywordPrompt
+ \ skipwhite
+
+ syn keyword kconfigKeyword choice
+ \ nextgroup=@kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn keyword kconfigKeyword endmenu endchoice
+
+ syn keyword kconfigPreProc source
+ \ nextgroup=kconfigPath
+ \ skipwhite
+
+ " TODO: This is a hack. The who .*Expr stuff should really be generated so
+ " that we can reuse it for various nextgroups.
+ syn keyword kconfigConditional if endif
+ \ nextgroup=@kconfigConfigOptionIfExpr
+ \ skipwhite
+
+ syn match kconfigKeywordPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=@kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigPath '"[^"\\]*\%(\\.[^"\\]*\)*"\|\S\+'
+ \ contained
+
+ syn match kconfigSymbol '\<\k\+\>'
+ \ contained
+ \ nextgroup=@kconfigConfigOptions
+ \ skipwhite skipnl
+
+ " FIXME: There is – probably – no reason to cluster these instead of just
+ " defining them in the same group.
+ syn cluster kconfigConfigOptions contains=kconfigTypeDefinition,
+ \ kconfigInputPrompt,
+ \ kconfigDefaultValue,
+ \ kconfigDependencies,
+ \ kconfigReverseDependencies,
+ \ kconfigNumericalRanges,
+ \ kconfigHelpText,
+ \ kconfigDefBool,
+ \ kconfigOptional
+
+ syn keyword kconfigTypeDefinition bool boolean tristate string hex int
+ \ contained
+ \ nextgroup=kconfigTypeDefPrompt,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigTypeDefPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigTypeDefPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn keyword kconfigInputPrompt prompt
+ \ contained
+ \ nextgroup=kconfigPromptPrompt
+ \ skipwhite
+
+ syn match kconfigPromptPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigPromptPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn keyword kconfigDefaultValue default
+ \ contained
+ \ nextgroup=@kconfigConfigOptionExpr
+ \ skipwhite
+
+ syn match kconfigDependencies 'depends on\|requires'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfExpr
+ \ skipwhite
+
+ syn keyword kconfigReverseDependencies select
+ \ contained
+ \ nextgroup=@kconfigRevDepSymbol
+ \ skipwhite
+
+ syn cluster kconfigRevDepSymbol contains=kconfigRevDepCSymbol,
+ \ kconfigRevDepNCSymbol
+
+ syn match kconfigRevDepCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigRevDepCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigRevDepNCSymbol '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn keyword kconfigNumericalRanges range
+ \ contained
+ \ nextgroup=@kconfigRangeSymbol
+ \ skipwhite
+
+ syn cluster kconfigRangeSymbol contains=kconfigRangeCSymbol,
+ \ kconfigRangeNCSymbol
+
+ syn match kconfigRangeCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=@kconfigRangeSymbol2
+ \ skipwhite skipnl
+
+ syn match kconfigRangeCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=@kconfigRangeSymbol2
+ \ skipwhite skipnl
+
+ syn match kconfigRangeNCSymbol '\<\k\+\>'
+ \ contained
+ \ nextgroup=@kconfigRangeSymbol2
+ \ skipwhite skipnl
+
+ syn cluster kconfigRangeSymbol2 contains=kconfigRangeCSymbol2,
+ \ kconfigRangeNCSymbol2
+
+ syn match kconfigRangeCSymbol2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigRangeNCSymbol2 '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn region kconfigHelpText contained
+ \ matchgroup=kconfigConfigOption
+ \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)'
+ \ skip='^$'
+ \ end='^\z1\@!'
+ \ nextgroup=@kconfigConfigOptions
+ \ skipwhite skipnl
+
+ " XXX: Undocumented
+ syn keyword kconfigDefBool def_bool
+ \ contained
+ \ nextgroup=@kconfigDefBoolSymbol
+ \ skipwhite
+
+ syn cluster kconfigDefBoolSymbol contains=kconfigDefBoolCSymbol,
+ \ kconfigDefBoolNCSymbol
+
+ syn match kconfigDefBoolCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigDefBoolCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigDefBoolNCSymbol '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ " XXX: This is actually only a valid option for “choice”, but treating it
+ " specially would require a lot of extra groups.
+ syn keyword kconfigOptional optional
+ \ contained
+ \ nextgroup=@kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn keyword kconfigConfigOptionIf if
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfExpr
+ \ skipwhite
+
+ syn cluster kconfigConfigOptionIfExpr contains=@kconfigConfOptIfExprSym,
+ \ kconfigConfOptIfExprNeg,
+ \ kconfigConfOptIfExprGroup
+
+ syn cluster kconfigConfOptIfExprSym contains=kconfigConfOptIfExprCSym,
+ \ kconfigConfOptIfExprNCSym
+
+ syn match kconfigConfOptIfExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=@kconfigConfigOptions,
+ \ kconfigConfOptIfExprAnd,
+ \ kconfigConfOptIfExprOr,
+ \ kconfigConfOptIfExprEq,
+ \ kconfigConfOptIfExprNEq
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=@kconfigConfigOptions,
+ \ kconfigConfOptIfExprAnd,
+ \ kconfigConfOptIfExprOr,
+ \ kconfigConfOptIfExprEq,
+ \ kconfigConfOptIfExprNEq
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfExprNCSym '\<\k\+\>'
+ \ contained
+ \ nextgroup=@kconfigConfigOptions,
+ \ kconfigConfOptIfExprAnd,
+ \ kconfigConfOptIfExprOr,
+ \ kconfigConfOptIfExprEq,
+ \ kconfigConfOptIfExprNEq
+ \ skipwhite skipnl
+
+ syn cluster kconfigConfOptIfExprSym2 contains=kconfigConfOptIfExprCSym2,
+ \ kconfigConfOptIfExprNCSym2
+
+ syn match kconfigConfOptIfExprEq '='
+ \ contained
+ \ nextgroup=@kconfigConfOptIfExprSym2
+ \ skipwhite
+
+ syn match kconfigConfOptIfExprNEq '!='
+ \ contained
+ \ nextgroup=@kconfigConfOptIfExprSym2
+ \ skipwhite
+
+ syn match kconfigConfOptIfExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=@kconfigConfigOptions,
+ \ kconfigConfOptIfExprAnd,
+ \ kconfigConfOptIfExprOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfExprNCSym2 '\<\k\+\>'
+ \ contained
+ \ nextgroup=@kconfigConfigOptions,
+ \ kconfigConfOptIfExprAnd,
+ \ kconfigConfOptIfExprOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfExprNeg '!'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfExpr
+ \ skipwhite
+
+ syn match kconfigConfOptIfExprAnd '&&'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfExpr
+ \ skipwhite
+
+ syn match kconfigConfOptIfExprOr '||'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfExpr
+ \ skipwhite
+
+ syn match kconfigConfOptIfExprGroup '('
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfGExp
+ \ skipwhite
+
+ " TODO: hm, this kind of recursion doesn't work right. We need another set of
+ " expressions that have kconfigConfigOPtionIfGExp as nextgroup and a matcher
+ " for '(' that sets it all off.
+ syn cluster kconfigConfigOptionIfGExp contains=@kconfigConfOptIfGExpSym,
+ \ kconfigConfOptIfGExpNeg,
+ \ kconfigConfOptIfExprGroup
+
+ syn cluster kconfigConfOptIfGExpSym contains=kconfigConfOptIfGExpCSym,
+ \ kconfigConfOptIfGExpNCSym
+
+ syn match kconfigConfOptIfGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=@kconfigConfigIf,
+ \ kconfigConfOptIfGExpAnd,
+ \ kconfigConfOptIfGExpOr,
+ \ kconfigConfOptIfGExpEq,
+ \ kconfigConfOptIfGExpNEq
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=@kconfigConfigIf,
+ \ kconfigConfOptIfGExpAnd,
+ \ kconfigConfOptIfGExpOr,
+ \ kconfigConfOptIfGExpEq,
+ \ kconfigConfOptIfGExpNEq
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfGExpNCSym '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfOptIfExprGrpE,
+ \ kconfigConfOptIfGExpAnd,
+ \ kconfigConfOptIfGExpOr,
+ \ kconfigConfOptIfGExpEq,
+ \ kconfigConfOptIfGExpNEq
+ \ skipwhite skipnl
+
+ syn cluster kconfigConfOptIfGExpSym2 contains=kconfigConfOptIfGExpCSym2,
+ \ kconfigConfOptIfGExpNCSym2
+
+ syn match kconfigConfOptIfGExpEq '='
+ \ contained
+ \ nextgroup=@kconfigConfOptIfGExpSym2
+ \ skipwhite
+
+ syn match kconfigConfOptIfGExpNEq '!='
+ \ contained
+ \ nextgroup=@kconfigConfOptIfGExpSym2
+ \ skipwhite
+
+ syn match kconfigConfOptIfGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfOptIfExprGrpE,
+ \ kconfigConfOptIfGExpAnd,
+ \ kconfigConfOptIfGExpOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfOptIfExprGrpE,
+ \ kconfigConfOptIfGExpAnd,
+ \ kconfigConfOptIfGExpOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfGExpNCSym2 '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfOptIfExprGrpE,
+ \ kconfigConfOptIfGExpAnd,
+ \ kconfigConfOptIfGExpOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptIfGExpNeg '!'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfGExp
+ \ skipwhite
+
+ syn match kconfigConfOptIfGExpAnd '&&'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfGExp
+ \ skipwhite
+
+ syn match kconfigConfOptIfGExpOr '||'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionIfGExp
+ \ skipwhite
+
+ syn match kconfigConfOptIfExprGrpE ')'
+ \ contained
+ \ nextgroup=@kconfigConfigOptions,
+ \ kconfigConfOptIfExprAnd,
+ \ kconfigConfOptIfExprOr
+ \ skipwhite skipnl
+
+
+ syn cluster kconfigConfigOptionExpr contains=@kconfigConfOptExprSym,
+ \ kconfigConfOptExprNeg,
+ \ kconfigConfOptExprGroup
+
+ syn cluster kconfigConfOptExprSym contains=kconfigConfOptExprCSym,
+ \ kconfigConfOptExprNCSym
+
+ syn match kconfigConfOptExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr,
+ \ kconfigConfOptExprEq,
+ \ kconfigConfOptExprNEq,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr,
+ \ kconfigConfOptExprEq,
+ \ kconfigConfOptExprNEq,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptExprNCSym '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr,
+ \ kconfigConfOptExprEq,
+ \ kconfigConfOptExprNEq,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn cluster kconfigConfOptExprSym2 contains=kconfigConfOptExprCSym2,
+ \ kconfigConfOptExprNCSym2
+
+ syn match kconfigConfOptExprEq '='
+ \ contained
+ \ nextgroup=@kconfigConfOptExprSym2
+ \ skipwhite
+
+ syn match kconfigConfOptExprNEq '!='
+ \ contained
+ \ nextgroup=@kconfigConfOptExprSym2
+ \ skipwhite
+
+ syn match kconfigConfOptExprCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptExprNCSym2 '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr,
+ \ @kconfigConfigOptions
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptExprNeg '!'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionExpr
+ \ skipwhite
+
+ syn match kconfigConfOptExprAnd '&&'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionExpr
+ \ skipwhite
+
+ syn match kconfigConfOptExprOr '||'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionExpr
+ \ skipwhite
+
+ syn match kconfigConfOptExprGroup '('
+ \ contained
+ \ nextgroup=@kconfigConfigOptionGExp
+ \ skipwhite
+
+ syn cluster kconfigConfigOptionGExp contains=@kconfigConfOptGExpSym,
+ \ kconfigConfOptGExpNeg,
+ \ kconfigConfOptGExpGroup
+
+ syn cluster kconfigConfOptGExpSym contains=kconfigConfOptGExpCSym,
+ \ kconfigConfOptGExpNCSym
+
+ syn match kconfigConfOptGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfOptExprGrpE,
+ \ kconfigConfOptGExpAnd,
+ \ kconfigConfOptGExpOr,
+ \ kconfigConfOptGExpEq,
+ \ kconfigConfOptGExpNEq
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfOptExprGrpE,
+ \ kconfigConfOptGExpAnd,
+ \ kconfigConfOptGExpOr,
+ \ kconfigConfOptGExpEq,
+ \ kconfigConfOptGExpNEq
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptGExpNCSym '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfOptExprGrpE,
+ \ kconfigConfOptGExpAnd,
+ \ kconfigConfOptGExpOr,
+ \ kconfigConfOptGExpEq,
+ \ kconfigConfOptGExpNEq
+ \ skipwhite skipnl
+
+ syn cluster kconfigConfOptGExpSym2 contains=kconfigConfOptGExpCSym2,
+ \ kconfigConfOptGExpNCSym2
+
+ syn match kconfigConfOptGExpEq '='
+ \ contained
+ \ nextgroup=@kconfigConfOptGExpSym2
+ \ skipwhite
+
+ syn match kconfigConfOptGExpNEq '!='
+ \ contained
+ \ nextgroup=@kconfigConfOptGExpSym2
+ \ skipwhite
+
+ syn match kconfigConfOptGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"'
+ \ contained
+ \ nextgroup=kconfigConfOptExprGrpE,
+ \ kconfigConfOptGExpAnd,
+ \ kconfigConfOptGExpOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'"
+ \ contained
+ \ nextgroup=kconfigConfOptExprGrpE,
+ \ kconfigConfOptGExpAnd,
+ \ kconfigConfOptGExpOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptGExpNCSym2 '\<\k\+\>'
+ \ contained
+ \ nextgroup=kconfigConfOptExprGrpE,
+ \ kconfigConfOptGExpAnd,
+ \ kconfigConfOptGExpOr
+ \ skipwhite skipnl
+
+ syn match kconfigConfOptGExpNeg '!'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionGExp
+ \ skipwhite
+
+ syn match kconfigConfOptGExpAnd '&&'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionGExp
+ \ skipwhite
+
+ syn match kconfigConfOptGExpOr '||'
+ \ contained
+ \ nextgroup=@kconfigConfigOptionGExp
+ \ skipwhite
+
+ syn match kconfigConfOptExprGrpE ')'
+ \ contained
+ \ nextgroup=kconfigConfigOptionIf,
+ \ kconfigConfOptExprAnd,
+ \ kconfigConfOptExprOr
+ \ skipwhite skipnl
+
+ hi def link kconfigTodo Todo
+ hi def link kconfigComment Comment
+ hi def link kconfigKeyword Keyword
+ hi def link kconfigPreProc PreProc
+ hi def link kconfigConditional Conditional
+ hi def link kconfigPrompt String
+ hi def link kconfigKeywordPrompt kconfigPrompt
+ hi def link kconfigPath String
+ hi def link kconfigSymbol String
+ hi def link kconfigConstantSymbol Constant
+ hi def link kconfigConfigOption Type
+ hi def link kconfigTypeDefinition kconfigConfigOption
+ hi def link kconfigTypeDefPrompt kconfigPrompt
+ hi def link kconfigInputPrompt kconfigConfigOption
+ hi def link kconfigPromptPrompt kconfigPrompt
+ hi def link kconfigDefaultValue kconfigConfigOption
+ hi def link kconfigDependencies kconfigConfigOption
+ hi def link kconfigReverseDependencies kconfigConfigOption
+ hi def link kconfigRevDepCSymbol kconfigConstantSymbol
+ hi def link kconfigRevDepNCSymbol kconfigSymbol
+ hi def link kconfigNumericalRanges kconfigConfigOption
+ hi def link kconfigRangeCSymbol kconfigConstantSymbol
+ hi def link kconfigRangeNCSymbol kconfigSymbol
+ hi def link kconfigRangeCSymbol2 kconfigConstantSymbol
+ hi def link kconfigRangeNCSymbol2 kconfigSymbol
+ hi def link kconfigHelpText Normal
+ hi def link kconfigDefBool kconfigConfigOption
+ hi def link kconfigDefBoolCSymbol kconfigConstantSymbol
+ hi def link kconfigDefBoolNCSymbol kconfigSymbol
+ hi def link kconfigOptional kconfigConfigOption
+ hi def link kconfigConfigOptionIf Conditional
+ hi def link kconfigConfOptIfExprCSym kconfigConstantSymbol
+ hi def link kconfigConfOptIfExprNCSym kconfigSymbol
+ hi def link kconfigOperator Operator
+ hi def link kconfigConfOptIfExprEq kconfigOperator
+ hi def link kconfigConfOptIfExprNEq kconfigOperator
+ hi def link kconfigConfOptIfExprCSym2 kconfigConstantSymbol
+ hi def link kconfigConfOptIfExprNCSym2 kconfigSymbol
+ hi def link kconfigConfOptIfExprNeg kconfigOperator
+ hi def link kconfigConfOptIfExprAnd kconfigOperator
+ hi def link kconfigConfOptIfExprOr kconfigOperator
+ hi def link kconfigDelimiter Delimiter
+ hi def link kconfigConfOptIfExprGroup kconfigDelimiter
+ hi def link kconfigConfOptIfGExpCSym kconfigConstantSymbol
+ hi def link kconfigConfOptIfGExpNCSym kconfigSymbol
+ hi def link kconfigConfOptIfGExpEq kconfigOperator
+ hi def link kconfigConfOptIfGExpNEq kconfigOperator
+ hi def link kconfigConfOptIfGExpCSym2 kconfigConstantSymbol
+ hi def link kconfigConfOptIfGExpNCSym2 kconfigSymbol
+ hi def link kconfigConfOptIfGExpNeg kconfigOperator
+ hi def link kconfigConfOptIfGExpAnd kconfigOperator
+ hi def link kconfigConfOptIfGExpOr kconfigOperator
+ hi def link kconfigConfOptIfExprGrpE kconfigDelimiter
+ hi def link kconfigConfOptExprCSym kconfigConstantSymbol
+ hi def link kconfigConfOptExprNCSym kconfigSymbol
+ hi def link kconfigConfOptExprEq kconfigOperator
+ hi def link kconfigConfOptExprNEq kconfigOperator
+ hi def link kconfigConfOptExprCSym2 kconfigConstantSymbol
+ hi def link kconfigConfOptExprNCSym2 kconfigSymbol
+ hi def link kconfigConfOptExprNeg kconfigOperator
+ hi def link kconfigConfOptExprAnd kconfigOperator
+ hi def link kconfigConfOptExprOr kconfigOperator
+ hi def link kconfigConfOptExprGroup kconfigDelimiter
+ hi def link kconfigConfOptGExpCSym kconfigConstantSymbol
+ hi def link kconfigConfOptGExpNCSym kconfigSymbol
+ hi def link kconfigConfOptGExpEq kconfigOperator
+ hi def link kconfigConfOptGExpNEq kconfigOperator
+ hi def link kconfigConfOptGExpCSym2 kconfigConstantSymbol
+ hi def link kconfigConfOptGExpNCSym2 kconfigSymbol
+ hi def link kconfigConfOptGExpNeg kconfigOperator
+ hi def link kconfigConfOptGExpAnd kconfigOperator
+ hi def link kconfigConfOptGExpOr kconfigOperator
+ hi def link kconfigConfOptExprGrpE kconfigConfOptIfExprGroup
else
-syn keyword kconfigTodo contained TODO FIXME XXX NOTE
+ syn keyword kconfigTodo contained TODO FIXME XXX NOTE
-syn match kconfigComment display '#.*$' contains=kconfigTodo
+ syn match kconfigComment display '#.*$' contains=kconfigTodo
-syn keyword kconfigKeyword config menuconfig comment mainmenu
+ syn keyword kconfigKeyword config menuconfig comment mainmenu
-syn keyword kconfigConditional menu endmenu choice endchoice if endif
+ syn keyword kconfigConditional menu endmenu choice endchoice if endif
-syn keyword kconfigPreProc source
- \ nextgroup=kconfigPath
- \ skipwhite
+ syn keyword kconfigPreProc source
+ \ nextgroup=kconfigPath
+ \ skipwhite
-syn keyword kconfigTriState y m n
+ syn keyword kconfigTriState y m n
-syn match kconfigSpecialChar contained '\\.'
-syn match kconfigSpecialChar '\\$'
+ syn match kconfigSpecialChar contained '\\.'
+ syn match kconfigSpecialChar '\\$'
-syn region kconfigPath matchgroup=kconfigPath
- \ start=+"+ skip=+\\\\\|\\\"+ end=+"+
- \ contains=kconfigSpecialChar
+ syn region kconfigPath matchgroup=kconfigPath
+ \ start=+"+ skip=+\\\\\|\\\"+ end=+"+
+ \ contains=kconfigSpecialChar
-syn region kconfigPath matchgroup=kconfigPath
- \ start=+'+ skip=+\\\\\|\\\'+ end=+'+
- \ contains=kconfigSpecialChar
+ syn region kconfigPath matchgroup=kconfigPath
+ \ start=+'+ skip=+\\\\\|\\\'+ end=+'+
+ \ contains=kconfigSpecialChar
-syn match kconfigPath '\S\+'
- \ contained
+ syn match kconfigPath '\S\+'
+ \ contained
-syn region kconfigString matchgroup=kconfigString
- \ start=+"+ skip=+\\\\\|\\\"+ end=+"+
- \ contains=kconfigSpecialChar
+ syn region kconfigString matchgroup=kconfigString
+ \ start=+"+ skip=+\\\\\|\\\"+ end=+"+
+ \ contains=kconfigSpecialChar
-syn region kconfigString matchgroup=kconfigString
- \ start=+'+ skip=+\\\\\|\\\'+ end=+'+
- \ contains=kconfigSpecialChar
+ syn region kconfigString matchgroup=kconfigString
+ \ start=+'+ skip=+\\\\\|\\\'+ end=+'+
+ \ contains=kconfigSpecialChar
-syn keyword kconfigType bool boolean tristate string hex int
+ syn keyword kconfigType bool boolean tristate string hex int
-syn keyword kconfigOption prompt default requires select range
- \ optional
-syn match kconfigOption 'depends\%( on\)\='
+ syn keyword kconfigOption prompt default requires select range
+ \ optional
+ syn match kconfigOption 'depends\%( on\)\='
-syn keyword kconfigMacro def_bool def_tristate
+ syn keyword kconfigMacro def_bool def_tristate
-syn region kconfigHelpText
- \ matchgroup=kconfigOption
- \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)'
- \ skip='^$'
- \ end='^\z1\@!'
+ syn region kconfigHelpText
+ \ matchgroup=kconfigOption
+ \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)'
+ \ skip='^$'
+ \ end='^\z1\@!'
-hi def link kconfigTodo Todo
-hi def link kconfigComment Comment
-hi def link kconfigKeyword Keyword
-hi def link kconfigConditional Conditional
-hi def link kconfigPreProc PreProc
-hi def link kconfigTriState Boolean
-hi def link kconfigSpecialChar SpecialChar
-hi def link kconfigPath String
-hi def link kconfigString String
-hi def link kconfigType Type
-hi def link kconfigOption Identifier
-hi def link kconfigHelpText Normal
-hi def link kconfigmacro Macro
+ hi def link kconfigTodo Todo
+ hi def link kconfigComment Comment
+ hi def link kconfigKeyword Keyword
+ hi def link kconfigConditional Conditional
+ hi def link kconfigPreProc PreProc
+ hi def link kconfigTriState Boolean
+ hi def link kconfigSpecialChar SpecialChar
+ hi def link kconfigPath String
+ hi def link kconfigString String
+ hi def link kconfigType Type
+ hi def link kconfigOption Identifier
+ hi def link kconfigHelpText Normal
+ hi def link kconfigmacro Macro
endif
diff --git a/runtime/syntax/lnk.vim b/runtime/syntax/lnk.vim
new file mode 100644
index 0000000000..45ca382aca
--- /dev/null
+++ b/runtime/syntax/lnk.vim
@@ -0,0 +1,40 @@
+" Vim syntax file
+" Language: TI linker command file
+" Document: https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html
+" Document: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_Linker-Command-File-Primer.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 31
+
+if exists("b:current_syntax")
+ finish
+endif
+
+runtime! syntax/cmacro.vim
+
+syn case ignore
+syn match lnkNumber "0x[0-9a-f]\+"
+" Linker command files are ASCII files that contain one or more of the following:
+" Input filenames, which specify object files, archive libraries, or other command files.
+" Linker options, which can be used in the command file in the same manner that they are used on the command line
+syn match lnkOption "^[-+][-_a-zA-Z#@]\+"
+syn match lnkOption "^--[^ \t$=`'"|);]\+"
+syn match lnkFile '[^ =]\+\%(\.\S\+\)\+\>'
+syn match lnkLibFile '[^ =]\+\.lib\>'
+" The MEMORY and SECTIONS linker directives. The MEMORY directive defines the target memory configuration (see Section 8.5.4). The SECTIONS directive controls how sections are built and allocated (see Section 8.5.5.)
+syn keyword lnkKeyword ADDRESS_MASK f LOAD ORIGIN START ALGORITHM FILL LOAD_END PAGE TABLE ALIGN GROUP LOAD_SIZE PALIGN TYPE ATTR HAMMING_MASK LOAD_START PARITY_MASK UNION BLOCK HIGH MEMORY RUN UNORDERED COMPRESSION INPUT_PAGE MIRRORING RUN_END VFILL COPY INPUT_RANGE NOINIT RUN_SIZE DSECT l NOLOAD RUN_START ECC LEN o SECTIONS END LENGTH ORG SIZE
+syn region lnkLibrary start=+<+ end=+>+
+syn match lnkAttrib '\<[RWXI]\+\>'
+syn match lnkSections '\<\.\k\+'
+" Assignment statements, which define and assign values to global symbols
+syn case match
+
+hi def link lnkNumber Number
+hi def link lnkOption Special
+hi def link lnkKeyword Keyword
+hi def link lnkLibrary String
+hi def link lnkFile String
+hi def link lnkLibFile Special
+hi def link lnkAttrib Type
+hi def link lnkSections Macro
+
+let b:current_syntax = "lnk"
diff --git a/runtime/syntax/lnkmap.vim b/runtime/syntax/lnkmap.vim
new file mode 100644
index 0000000000..dc097f412f
--- /dev/null
+++ b/runtime/syntax/lnkmap.vim
@@ -0,0 +1,35 @@
+" Vim syntax file
+" Language: TI Linker map
+" Document: https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2024 Dec 30
+
+if exists("b:current_syntax")
+ finish
+endif
+
+syn match lnkmapTime ">> .*$"
+syn region lnkmapHeadline start="^\*\+$" end="^\*\+$"
+syn match lnkmapHeadline "^[A-Z][-A-Z0-9 ']*\ze\%(:\|$\)"
+syn match lnkmapSectionDelim "^=\+$"
+syn match lnkmapTableDelim "\%(^\|\s\)\zs---*\ze\%($\|\s\)"
+syn match lnkmapNumber "\%(^\|\s\)\zs[0-9a-f]\+\ze\%($\|\s\)"
+syn match lnkmapSections '\<\.\k\+\>'
+syn match lnkmapFile '[^ =]\+\%(\.\S\+\)\+\>'
+syn match lnkmapLibFile '[^ =]\+\.lib\>'
+syn match lnkmapAttrib '\<[RWIX]\+\>'
+syn match lnkmapAttrib '\s\zs--HOLE--\ze\%\(\s\|$\)'
+syn keyword lnkmapAttrib UNINITIALIZED DESCT
+
+
+hi def link lnkmapTime Comment
+hi def link lnkmapHeadline Title
+hi def link lnkmapSectionDelim PreProc
+hi def link lnkmapTableDelim PreProc
+hi def link lnkmapNumber Number
+hi def link lnkmapSections Macro
+hi def link lnkmapFile String
+hi def link lnkmapLibFile Special
+hi def link lnkmapAttrib Type
+
+let b:current_syntax = "lnkmap"
diff --git a/runtime/syntax/lyrics.vim b/runtime/syntax/lyrics.vim
index fd127988f2..48a5b1171c 100644
--- a/runtime/syntax/lyrics.vim
+++ b/runtime/syntax/lyrics.vim
@@ -2,7 +2,7 @@
" Language: LyRiCs
" Maintainer: ObserverOfTime <chronobserver@disroot.org>
" Filenames: *.lrc
-" Last Change: 2024 Sep 20
+" Last Change: 2025 Jan 13
if exists('b:current_syntax')
finish
@@ -23,7 +23,7 @@ syn match lrcTagName contained nextgroup=lrcTagValue
syn match lrcTagValue /:\zs.\+\ze\]/ contained
" Lyrics
-syn match lrcLyricTime /^\s*\(\[\d\d:\d\d\.\d\d\]\)\+/
+syn match lrcLyricTime /^\s*\(\[\d\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
diff --git a/runtime/syntax/netrw.vim b/runtime/syntax/netrw.vim
deleted file mode 100644
index f5b7fdc2c6..0000000000
--- a/runtime/syntax/netrw.vim
+++ /dev/null
@@ -1,148 +0,0 @@
-" Language : Netrw Listing Syntax
-" Maintainer: This runtime file is looking for a new maintainer.
-" Former Maintainer: Charles E. Campbell
-" Last Change: Nov 07, 2019
-" 2024 Feb 19 by Vim Project (announce adoption)
-" Version : 20
-" ---------------------------------------------------------------------
-if exists("b:current_syntax")
- finish
-endif
-
-" ---------------------------------------------------------------------
-" Directory List Syntax Highlighting: {{{1
-syn cluster NetrwGroup contains=netrwHide,netrwSortBy,netrwSortSeq,netrwQuickHelp,netrwVersion,netrwCopyTgt
-syn cluster NetrwTreeGroup contains=netrwDir,netrwSymLink,netrwExe
-
-syn match netrwPlain "\(\S\+ \)*\S\+" contains=netrwLink,@NoSpell
-syn match netrwSpecial "\%(\S\+ \)*\S\+[*|=]\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
-syn match netrwDir "\.\{1,2}/" contains=netrwClassify,@NoSpell
-syn match netrwDir "\%(\S\+ \)*\S\+/\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
-syn match netrwSizeDate "\<\d\+\s\d\{1,2}/\d\{1,2}/\d\{4}\s" skipwhite contains=netrwDateSep,@NoSpell nextgroup=netrwTime
-syn match netrwSymLink "\%(\S\+ \)*\S\+@\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
-syn match netrwExe "\%(\S\+ \)*\S*[^~]\*\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell
-if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4')
-syn match netrwTreeBar "^\%([-+|│] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup
-else
-syn match netrwTreeBar "^\%([-+|] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup
-endif
-syn match netrwTreeBarSpace " " contained
-
-syn match netrwClassify "[*=|@/]\ze\%(\s\{2,}\|$\)" contained
-syn match netrwDateSep "/" contained
-syn match netrwTime "\d\{1,2}:\d\{2}:\d\{2}" contained contains=netrwTimeSep
-syn match netrwTimeSep ":"
-
-syn match netrwComment '".*\%(\t\|$\)' contains=@NetrwGroup,@NoSpell
-syn match netrwHide '^"\s*\(Hid\|Show\)ing:' skipwhite contains=@NoSpell nextgroup=netrwHidePat
-syn match netrwSlash "/" contained
-syn match netrwHidePat "[^,]\+" contained skipwhite contains=@NoSpell nextgroup=netrwHideSep
-syn match netrwHideSep "," contained skipwhite nextgroup=netrwHidePat
-syn match netrwSortBy "Sorted by" contained transparent skipwhite nextgroup=netrwList
-syn match netrwSortSeq "Sort sequence:" contained transparent skipwhite nextgroup=netrwList
-syn match netrwCopyTgt "Copy/Move Tgt:" contained transparent skipwhite nextgroup=netrwList
-syn match netrwList ".*$" contained contains=netrwComma,@NoSpell
-syn match netrwComma "," contained
-syn region netrwQuickHelp matchgroup=Comment start="Quick Help:\s\+" end="$" contains=netrwHelpCmd,netrwQHTopic,@NoSpell keepend contained
-syn match netrwHelpCmd "\S\+\ze:" contained skipwhite contains=@NoSpell nextgroup=netrwCmdSep
-syn match netrwQHTopic "([a-zA-Z &]\+)" contained skipwhite
-syn match netrwCmdSep ":" contained nextgroup=netrwCmdNote
-syn match netrwCmdNote ".\{-}\ze " contained contains=@NoSpell
-syn match netrwVersion "(netrw.*)" contained contains=@NoSpell
-syn match netrwLink "-->" contained skipwhite
-
-" -----------------------------
-" Special filetype highlighting {{{1
-" -----------------------------
-if exists("g:netrw_special_syntax") && g:netrw_special_syntax
- if exists("+suffixes") && &suffixes != ""
- let suflist= join(split(&suffixes,','))
- let suflist= escape(substitute(suflist," ",'\\|','g'),'.~')
- exe "syn match netrwSpecFile '\\(\\S\\+ \\)*\\S*\\(".suflist."\\)\\>' contains=netrwTreeBar,@NoSpell"
- endif
- syn match netrwBak "\(\S\+ \)*\S\+\.bak\>" contains=netrwTreeBar,@NoSpell
- syn match netrwCompress "\(\S\+ \)*\S\+\.\%(gz\|bz2\|Z\|zip\)\>" contains=netrwTreeBar,@NoSpell
- if has("unix")
- syn match netrwCoreDump "\<core\%(\.\d\+\)\=\>" contains=netrwTreeBar,@NoSpell
- endif
- syn match netrwLex "\(\S\+ \)*\S\+\.\%(l\|lex\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwYacc "\(\S\+ \)*\S\+\.y\>" contains=netrwTreeBar,@NoSpell
- syn match netrwData "\(\S\+ \)*\S\+\.dat\>" contains=netrwTreeBar,@NoSpell
- syn match netrwDoc "\(\S\+ \)*\S\+\.\%(doc\|txt\|pdf\|ps\|docx\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwHdr "\(\S\+ \)*\S\+\.\%(h\|hpp\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwLib "\(\S\+ \)*\S*\.\%(a\|so\|lib\|dll\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwMakeFile "\<[mM]akefile\>\|\(\S\+ \)*\S\+\.mak\>" contains=netrwTreeBar,@NoSpell
- syn match netrwObj "\(\S\+ \)*\S*\.\%(o\|obj\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwPix "\c\(\S\+ \)*\S*\.\%(bmp\|fits\=\|gif\|je\=pg\|pcx\|ppc\|pgm\|png\|ppm\|psd\|rgb\|tif\|xbm\|xcf\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwTags "\<\(ANmenu\|ANtags\)\>" contains=netrwTreeBar,@NoSpell
- syn match netrwTags "\<tags\>" contains=netrwTreeBar,@NoSpell
- syn match netrwTilde "\(\S\+ \)*\S\+\~\*\=\>" contains=netrwTreeBar,@NoSpell
- syn match netrwTmp "\<tmp\(\S\+ \)*\S\+\>\|\(\S\+ \)*\S*tmp\>" contains=netrwTreeBar,@NoSpell
-endif
-
-" ---------------------------------------------------------------------
-" Highlighting Links: {{{1
-if !exists("did_drchip_netrwlist_syntax")
- let did_drchip_netrwlist_syntax= 1
- hi default link netrwClassify Function
- hi default link netrwCmdSep Delimiter
- hi default link netrwComment Comment
- hi default link netrwDir Directory
- hi default link netrwHelpCmd Function
- hi default link netrwQHTopic Number
- hi default link netrwHidePat Statement
- hi default link netrwHideSep netrwComment
- hi default link netrwList Statement
- hi default link netrwVersion Identifier
- hi default link netrwSymLink Question
- hi default link netrwExe PreProc
- hi default link netrwDateSep Delimiter
-
- hi default link netrwTreeBar Special
- hi default link netrwTimeSep netrwDateSep
- hi default link netrwComma netrwComment
- hi default link netrwHide netrwComment
- hi default link netrwMarkFile TabLineSel
- hi default link netrwLink Special
-
- " special syntax highlighting (see :he g:netrw_special_syntax)
- hi default link netrwCoreDump WarningMsg
- hi default link netrwData Folded
- hi default link netrwHdr netrwPlain
- hi default link netrwLex netrwPlain
- hi default link netrwLib DiffChange
- hi default link netrwMakefile DiffChange
- hi default link netrwYacc netrwPlain
- hi default link netrwPix Special
-
- hi default link netrwBak netrwGray
- hi default link netrwCompress netrwGray
- hi default link netrwSpecFile netrwGray
- hi default link netrwObj netrwGray
- hi default link netrwTags netrwGray
- hi default link netrwTilde netrwGray
- hi default link netrwTmp netrwGray
-endif
-
- " set up netrwGray to be understated (but not Ignore'd or Conceal'd, as those
- " can be hard/impossible to read). Users may override this in a colorscheme by
- " specifying netrwGray highlighting.
- redir => s:netrwgray
- sil hi netrwGray
- redir END
- if s:netrwgray !~ 'guifg'
- if has("gui") && has("gui_running")
- if &bg == "dark"
- exe "hi netrwGray gui=NONE guifg=gray30"
- else
- exe "hi netrwGray gui=NONE guifg=gray70"
- endif
- else
- hi link netrwGray Folded
- endif
- endif
-
-" Current Syntax: {{{1
-let b:current_syntax = "netrwlist"
-" ---------------------------------------------------------------------
-" vim: ts=8 fdm=marker
diff --git a/runtime/syntax/opencl.vim b/runtime/syntax/opencl.vim
new file mode 100644
index 0000000000..c237aa30f9
--- /dev/null
+++ b/runtime/syntax/opencl.vim
@@ -0,0 +1,13 @@
+" Vim syntax file
+" Language: OpenCL
+" Last Change: 2024 Nov 19
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" TODO: support openCL specific keywords
+runtime! syntax/c.vim
+
+let current_syntax = "opencl"
diff --git a/runtime/syntax/po.vim b/runtime/syntax/po.vim
index 08d6baec27..6da27f639d 100644
--- a/runtime/syntax/po.vim
+++ b/runtime/syntax/po.vim
@@ -1,10 +1,11 @@
" Vim syntax file
" Language: po (gettext)
" Maintainer: Dwayne Bailey <dwayne@translate.org.za>
-" Last Change: 2015 Jun 07
+" Last Change: 2024 Nov 28
" Contributors: Dwayne Bailey (Most advanced syntax highlighting)
" Leonardo Fontenelle (Spell checking)
" Nam SungHyun <namsh@kldp.org> (Original maintainer)
+" Eisuke Kawashima (add format-flags: #16132)
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -32,9 +33,9 @@ syn region poMsgCTxt matchgroup=poStatementMsgCTxt start=+^msgctxt "+rs=e-1
syn region poMsgID matchgroup=poStatementMsgid start=+^msgid "+rs=e-1 matchgroup=poStringID end=+^msgstr\(\|\[[\]0\[]\]\) "+me=s-1 contains=poStringID,poStatementMsgidplural,poStatementMsgid
syn region poMsgSTR matchgroup=poStatementMsgstr start=+^msgstr\(\|\[[\]0\[]\]\) "+rs=e-1 matchgroup=poStringSTR end=+\n\n+me=s-1 contains=poStringSTR,poStatementMsgstr
syn region poStringCTxt start=+"+ skip=+\\\\\|\\"+ end=+"+
-syn region poStringID start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
+syn region poStringID start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
\ contains=poSpecial,poFormat,poCommentKDE,poPluralKDE,poKDEdesktopFile,poHtml,poAcceleratorId,poHtmlNot,poVariable
-syn region poStringSTR start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
+syn region poStringSTR start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
\ contains=@Spell,poSpecial,poFormat,poHeaderItem,poCommentKDEError,poHeaderUndefined,poPluralKDEError,poMsguniqError,poKDEdesktopFile,poHtml,poAcceleratorStr,poHtmlNot,poVariable
" Header and Copyright
@@ -45,13 +46,43 @@ syn match poCopyrightUnset "SOME DESCRIPTIVE TITLE\|FIRST AUTHOR <EMAIL@ADDR
" Translation comment block including: translator comment, automatic comments, flags and locations
syn match poComment "^#.*$"
syn keyword poFlagFuzzy fuzzy contained
+
+syn match poFlagFormat /\<\%(no-\)\?awk-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?boost-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?c++-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?c-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?csharp-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?elisp-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?gcc-internal-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?gfc-internal-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?java-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?java-printf-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?javascript-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?kde-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?librep-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?lisp-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?lua-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?objc-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?object-pascal-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?perl-brace-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?perl-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?php-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?python-brace-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?python-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?qt-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?qt-plural-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?ruby-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?scheme-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?sh-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?smalltalk-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?tcl-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?ycp-format\>/ contained
+
syn match poCommentTranslator "^# .*$" contains=poCopyrightUnset
-syn match poCommentAutomatic "^#\..*$"
+syn match poCommentAutomatic "^#\..*$"
syn match poCommentSources "^#:.*$"
-syn match poCommentFlags "^#,.*$" contains=poFlagFuzzy
-syn match poDiffOld '\(^#| "[^{]*+}\|{+[^}]*+}\|{+[^}]*\|"$\)' contained
-syn match poDiffNew '\(^#| "[^{]*-}\|{-[^}]*-}\|{-[^}]*\|"$\)' contained
-syn match poCommentDiff "^#|.*$" contains=poDiffOld,poDiffNew
+syn match poCommentFlags "^#,.*$" contains=poFlagFuzzy,poFlagFormat
+syn match poCommentPrevious "^#|.*$"
" Translations (also includes header fields as they appear in a translation msgstr)
syn region poCommentKDE start=+"_: +ms=s+1 end="\\n" end="\"\n^msgstr"me=s-1 contained
@@ -66,13 +97,13 @@ syn match poFormat "%%" contained
syn region poMsguniqError matchgroup=poMsguniqErrorMarkers start="#-#-#-#-#" end='#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)\\n' contained
" Obsolete messages
-syn match poObsolete "^#\~.*$"
+syn match poObsolete "^#\~.*$"
" KDE Name= handling
syn match poKDEdesktopFile "\"\(Name\|Comment\|GenericName\|Description\|Keywords\|About\)="ms=s+1,me=e-1
" Accelerator keys - this messes up if the preceding or following char is a multibyte unicode char
-syn match poAcceleratorId contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1
+syn match poAcceleratorId contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1
syn match poAcceleratorStr contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1 contains=@Spell
" Variables simple
@@ -86,11 +117,10 @@ hi def link poComment Comment
hi def link poCommentAutomatic Comment
hi def link poCommentTranslator Comment
hi def link poCommentFlags Special
-hi def link poCommentDiff Comment
+hi def link poCommentPrevious Comment
hi def link poCopyrightUnset Todo
hi def link poFlagFuzzy Todo
-hi def link poDiffOld Todo
-hi def link poDiffNew Special
+hi def link poFlagFormat Todo
hi def link poObsolete Comment
hi def link poStatementMsgid Statement
diff --git a/runtime/syntax/ptx.vim b/runtime/syntax/ptx.vim
new file mode 100644
index 0000000000..98de4ff6d3
--- /dev/null
+++ b/runtime/syntax/ptx.vim
@@ -0,0 +1,52 @@
+" Vim syntax file
+" Language: Nvidia PTX (Parallel Thread Execution)
+" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com>
+" Latest Revision: 2024-12-05
+
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+syntax iskeyword .,_,a-z,48-57
+
+" https://docs.nvidia.com/cuda/parallel-thread-execution/#directives
+syntax keyword ptxFunction .entry .func
+syntax keyword ptxDirective .branchtargets .file .loc .secion .maxnctapersm .maxnreg .minnctapersm .noreturn .pragma .reqntid .target .version .weak
+syntax keyword ptxOperator .address_size .alias .align .callprototype .calltargets
+syntax keyword ptxStorageClass .common .const .extern .global .local .param .reg .sreg .shared .tex .visible
+syntax keyword ptxType .explicitcluster .maxclusterrank .reqnctapercluster
+
+" https://docs.nvidia.com/cuda/parallel-thread-execution/#fundamental-types
+" signed integer
+syntax keyword ptxType .s8 .s16 .s32 .s64
+" unsigned integer
+syntax keyword ptxType .u8 .u16 .u32 .u64
+" floating-point
+syntax keyword ptxType .f16 .f16x2 .f32 .f64
+" bits (untyped)
+syntax keyword ptxType .b8 .b16 .b32 .b64 .b128
+" predicate
+syntax keyword ptxType .pred
+
+" https://docs.nvidia.com/cuda/parallel-thread-execution/#instruction-statements
+syntax keyword ptxStatement ret
+
+syntax region ptxCommentL start="//" skip="\\$" end="$" keepend
+syntax region ptxComment matchgroup=ptxCommentStart start="/\*" end="\*/" extend
+
+hi def link ptxFunction Function
+hi def link ptxDirective Keyword
+hi def link ptxOperator Operator
+hi def link ptxStorageClass StorageClass
+hi def link ptxType Type
+hi def link ptxStatement Statement
+
+hi def link ptxCommentL ptxComment
+hi def link ptxCommentStart ptxComment
+hi def link ptxComment Comment
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/syntax/query.lua b/runtime/syntax/query.lua
index 2dfe29f69b..0de08b4dfb 100644
--- a/runtime/syntax/query.lua
+++ b/runtime/syntax/query.lua
@@ -1,6 +1,5 @@
-- Neovim syntax file
-- Language: Treesitter query
--- Last Change: 2024 Jul 03
-- it's a lisp!
vim.cmd([[runtime! syntax/lisp.vim]])
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index 97e74d205f..67268cdfe3 100644
--- a/runtime/syntax/sh.vim
+++ b/runtime/syntax/sh.vim
@@ -4,6 +4,9 @@
" Previous Maintainers: Charles E. Campbell
" Lennart Schultz <Lennart.Schultz@ecmwf.int>
" Last Change: 2024 Mar 04 by Vim Project
+" 2024 Nov 03 by Aliaksei Budavei <0x000c70 AT gmail DOT com> (improved bracket expressions, #15941)
+" 2025 Jan 06 add $PS0 to bashSpecialVariables (#16394)
+" 2025 Jan 18 add bash coproc, remove duplicate syn keywords (#16467)
" Version: 208
" Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH
" For options and settings, please use: :help ft-sh-syntax
@@ -22,7 +25,7 @@ elseif getline(1) =~ '\<bash\>'
elseif getline(1) =~ '\<dash\>'
let b:is_dash = 1
elseif !exists("g:is_kornshell") && !exists("g:is_bash") && !exists("g:is_posix") && !exists("g:is_sh") && !exists("g:is_dash")
- " user did not specify which shell to use, and
+ " user did not specify which shell to use, and
" the script itself does not specify which shell to use. FYI: /bin/sh is ambiguous.
" Assuming /bin/sh is executable, and if its a link, find out what it links to.
let s:shell = ""
@@ -127,6 +130,50 @@ else
com! -nargs=* ShFoldIfDoFor <args>
endif
+" Generate bracket expression items {{{1
+" =================================
+" Note that the following function can be invoked as many times as necessary
+" provided that these constraints hold for the passed dictionary argument:
+" - every time a unique group-name value is assigned to the "itemGroup" key;
+" - only ONCE either the "extraArgs" key is not entered or it is entered and
+" its value does not have "contained" among other optional arguments (":help
+" :syn-arguments").
+fun! s:GenerateBracketExpressionItems(dict) abort
+ let itemGroup = a:dict.itemGroup
+ let bracketGroup = a:dict.bracketGroup
+ let invGroup = itemGroup . 'Inv'
+ let skipLeftBracketGroup = itemGroup . 'SkipLeftBracket'
+ let skipRightBracketGroup = itemGroup . 'SkipRightBracket'
+ let extraArgs = has_key(a:dict, 'extraArgs') ? a:dict.extraArgs : ''
+
+ " Make the leading "[!^]" stand out in a NON-matching expression.
+ exec 'syn match ' . invGroup . ' contained "\[\@<=[!^]"'
+
+ " Set up indirections for unbalanced-bracket highlighting.
+ exec 'syn region ' . skipRightBracketGroup . ' contained matchgroup=' . bracketGroup . ' start="\[\%([!^]\=\\\=\]\)\@=" matchgroup=shCollSymb end="\[\.[^]]\{-}\][^]]\{-}\.\]" matchgroup=' . itemGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . invGroup
+ exec 'syn region ' . skipLeftBracketGroup . ' contained matchgroup=' . bracketGroup . ' start="\[\%([!^]\=\\\=\]\)\@=" skip="[!^]\=\\\=\]\%(\[[^]]\+\]\|[^]]\)\{-}\%(\[[:.=]\@!\)\@=" matchgroup=' . itemGroup . ' end="\[[:.=]\@!" contains=@shBracketExprList,shDoubleQuote,' . invGroup
+
+ " Look for a general matching expression.
+ exec 'syn region ' . itemGroup . ' matchgroup=' . bracketGroup . ' start="\[\S\@=" end="\]" contains=@shBracketExprList,shDoubleQuote ' . extraArgs
+ " Look for a general NON-matching expression.
+ exec 'syn region ' . itemGroup . ' matchgroup=' . bracketGroup . ' start="\[[!^]\@=" end="\]" contains=@shBracketExprList,shDoubleQuote,' . invGroup . ' ' . extraArgs
+
+ " Accommodate unbalanced brackets in bracket expressions. The supported
+ " syntax for a plain "]" can be: "[]ws]" and "[^]ws]"; or, "[ws[.xs]ys.]zs]"
+ " and "[^ws[.xs]ys.]zs]"; see §9.3.5 RE Bracket Expression (in XBD).
+ exec 'syn region ' . itemGroup . ' matchgroup=NONE start="\[[!^]\=\\\=\]" matchgroup=' . bracketGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . skipRightBracketGroup . ' ' . extraArgs
+ " Strive to handle "[]...[]" etc.
+ exec 'syn region ' . itemGroup . ' matchgroup=NONE start="\[[!^]\=\\\=\]\%(\[[^]]\+\]\|[^]]\)\{-}\[[:.=]\@!" matchgroup=' . bracketGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . skipLeftBracketGroup . ' ' . extraArgs
+
+ if !exists("g:skip_sh_syntax_inits")
+ exec 'hi def link ' . skipLeftBracketGroup . ' ' . itemGroup
+ exec 'hi def link ' . skipRightBracketGroup . ' ' . itemGroup
+ exec 'hi def link ' . invGroup . ' Underlined'
+ endif
+endfun
+
+call s:GenerateBracketExpressionItems({'itemGroup': 'shBracketExpr', 'bracketGroup': 'shBracketExprDelim'})
+
" sh syntax is case sensitive {{{1
syn case match
@@ -136,23 +183,24 @@ syn cluster shErrorList contains=shDoError,shIfError,shInError,shCaseError,shEsa
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster ErrorList add=shDTestError
endif
-syn cluster shArithParenList contains=shArithmetic,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 shArithParenList contains=shArithmetic,shArithParen,shCaseEsac,shComment,shDeref,shDerefVarArray,shDo,shDerefSimple,shEcho,shEscape,shExpr,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 shBracketExprList contains=shCharClassOther,shCharClass,shCollSymb,shEqClass
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,shSubshare,shValsub,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,shDblParen
endif
-syn cluster shCommandSubList contains=shAlias,shArithmetic,shCmdParenRegion,shCommandSub,shComment,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
+syn cluster shCommandSubList contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shComment,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
syn cluster shCurlyList contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial
" COMBAK: removing shEscape from shDblQuoteList fails ksh04:43 -- Jun 09, 2022: I don't see the problem with ksh04, so am reinstating shEscape
syn cluster shDblQuoteList contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shPosnParm,shCtrlSeq,shSpecial,shSpecialDQ
syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPSR,shDerefPPS
syn cluster shDerefVarList contains=shDerefOffset,shDerefOp,shDerefVarArray,shDerefOpError
-syn cluster shEchoList contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
-syn cluster shExprList1 contains=shCharClass,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
+syn cluster shEchoList contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
+syn cluster shExprList1 contains=shBracketExpr,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest
-syn cluster shFunctionList contains=@shCommandSubList,shCaseEsac,shColon,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
+syn cluster shFunctionList contains=shBracketExpr,@shCommandSubList,shCaseEsac,shColon,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster shFunctionList add=shRepeat,shDblBrace,shDblParen,shForPP
syn cluster shDerefList add=shCommandSubList,shEchoDeref
@@ -160,16 +208,16 @@ endif
syn cluster shHereBeginList contains=@shCommandSubList
syn cluster shHereList contains=shBeginHere,shHerePayload
syn cluster shHereListDQ contains=shBeginHere,@shDblQuoteList,shHerePayload
-syn cluster shIdList contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr
+syn cluster shIdList contains=shArithmetic,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr
syn cluster shIfList contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey,shFunctionOne,shFunctionTwo
syn cluster shLoopList contains=@shCaseList,@shErrorList,shCaseEsac,shConditional,shDblBrace,shExpr,shFor,shIf,shOption,shSet,shTest,shTestOpr,shTouch
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster shLoopList add=shForPP,shDblParen
endif
-syn cluster shPPSLeftList contains=shAlias,shArithmetic,shCmdParenRegion,shCommandSub,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
+syn cluster shPPSLeftList contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable
syn cluster shPPSRightList contains=shDeref,shDerefSimple,shEscape,shPosnParm
-syn cluster shSubShList contains=@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator
-syn cluster shTestList contains=shArithmetic,shCharClass,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shSpecialDQ,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr
+syn cluster shSubShList contains=shBracketExpr,@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator
+syn cluster shTestList contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shSpecialDQ,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr
syn cluster shNoZSList contains=shSpecialNoZS
syn cluster shForList contains=shTestOpr,shNumber,shDerefSimple,shDeref,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shArithmetic
@@ -190,7 +238,7 @@ endif
syn match shEchoQuote contained '\%(\\\\\)*\\["`'()]'
" This must be after the strings, so that ... \" will be correct
-syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="\s#"me=e-2 contains=shNumber,shExSingleQuote,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shOperator,shExDoubleQuote,shDoubleQuote,shCharClass,shCtrlSeq
+syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="\s#"me=e-2 contains=shBracketExpr,shNumber,shExSingleQuote,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shOperator,shExDoubleQuote,shDoubleQuote,shCtrlSeq
" Alias: {{{1
" =====
@@ -240,7 +288,6 @@ syn match shRedir "\d<<-\="
" ==========
syn match shOperator "<<\|>>" contained
syn match shOperator "[!&;|]" contained
-syn match shOperator "\[[[^:]\|\]]" contained
syn match shOperator "[-=/*+%]\==" skipwhite nextgroup=shPattern
syn match shPattern "\<\S\+\())\)\@=" contained contains=shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shDeref
@@ -251,9 +298,9 @@ syn region shSubSh transparent matchgroup=shSubShRegion start="[^(]\zs(" end=")"
" Tests: {{{1
"=======
-syn region shExpr matchgroup=shRange start="\[" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial
+syn region shExpr matchgroup=shRange start="\[\s\@=" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial
syn region shTest transparent matchgroup=shStatement start="\<test\s" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1
-syn region shNoQuote start='\S' skip='\%(\\\\\)*\\.' end='\ze\s' end="\ze['"]" contained contains=shDerefSimple,shDeref
+syn region shNoQuote start='\S' skip='\%(\\\\\)*\\.' end='\ze\s' end="\ze['"]" contained contains=shBracketExpr,shDerefSimple,shDeref
syn match shAstQuote contained '\*\ze"' nextgroup=shString
syn match shTestOpr contained '[^-+/%]\zs=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern
syn match shTestOpr contained "<=\|>=\|!=\|==\|=\~\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]"
@@ -262,13 +309,16 @@ syn region shTestDoubleQuote contained start='\%(\%(\\\\\)*\\\)\@<!"' skip=+\\\\
syn match shTestSingleQuote contained '\\.' nextgroup=shTestSingleQuote
syn match shTestSingleQuote contained "'[^']*'"
if exists("b:is_kornshell") || exists("b:is_bash")
- syn region shDblBrace matchgroup=Delimiter start="\[\[" skip=+\%(\\\\\)*\\$+ end="\]\]" contains=@shTestList,shAstQuote,shNoQuote,shComment
+ syn region shDblBrace matchgroup=Delimiter start="\[\[\s\@=" skip=+\%(\\\\\)*\\$+ end="\]\]" contains=@shTestList,shAstQuote,shNoQuote,shComment
syn region shDblParen matchgroup=Delimiter start="((" skip=+\%(\\\\\)*\\$+ end="))" contains=@shTestList,shComment
endif
" Character Class In Range: {{{1
" =========================
+syn match shCharClassOther contained "\[:\w\{-}:\]"
syn match shCharClass contained "\[:\(backspace\|escape\|return\|xdigit\|alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|tab\):\]"
+syn match shCollSymb contained "\[\..\{-}\.\]"
+syn match shEqClass contained "\[=.\{-}=\]"
" Loops: do, if, while, until {{{1
" ======
@@ -298,12 +348,12 @@ syn match shComma contained ","
" ====
syn match shCaseBar contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|" nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
syn match shCaseStart contained skipwhite skipnl "(" nextgroup=shCase,shCaseBar
-syn match shCaseLabel contained skipwhite "\%(\\.\|[-a-zA-Z0-9_*.]\)\+" contains=shCharClass
+syn match shCaseLabel contained skipwhite "\%(\\.\|[-a-zA-Z0-9_*.]\)\+" contains=shBracketExpr
if exists("b:is_bash")
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end=";;&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
elseif exists("b:is_kornshell")
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
-else
+else
ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
endif
ShFoldIfDoFor syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList
@@ -317,11 +367,9 @@ endif
syn region shCaseSingleQuote matchgroup=shQuote start=+'+ end=+'+ contains=shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained
syn region shCaseDoubleQuote matchgroup=shQuote start=+"+ skip=+\\\\\|\\.+ end=+"+ contains=@shDblQuoteList,shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained
syn region shCaseCommandSub start=+`+ skip=+\\\\\|\\.+ end=+`+ contains=@shCommandSubList skipwhite skipnl nextgroup=shCaseBar contained
+call s:GenerateBracketExpressionItems({'itemGroup': 'shCaseRange', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'skip=+\\\\+ contained'})
if exists("b:is_bash")
- syn region shCaseRange matchgroup=Delimiter start=+\[+ skip=+\\\\+ end=+\]+ contained contains=shCharClass
syn match shCharClass '\[:\%(alnum\|alpha\|ascii\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|word\|or\|xdigit\):\]' contained
-else
- syn region shCaseRange matchgroup=Delimiter start=+\[+ skip=+\\\\+ end=+\]+ contained
endif
" Misc: {{{1
"======
@@ -358,7 +406,7 @@ syn region shCmdParenRegion matchgroup=shCmdSubRegion start="((\@!" skip='\\\\\|
if exists("b:is_bash")
syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
syn cluster shCaseList add=bashAdminStatement,bashStatement
- syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ALIASES BASH_ARGC BASH_ARGC BASH_ARGV BASH_ARGV BASH_CMDS BASH_CMDS BASH_COMMAND BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_EXECUTION_STRING BASH_LINENO BASH_LINENO BASHOPTS BASHOPTS BASHPID BASHPID BASH_REMATCH BASH_REMATCH BASH_SOURCE BASH_SOURCE BASH_SUBSHELL BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD BASH_XTRACEFD CDPATH COLUMNS COLUMNS COMP_CWORD COMP_CWORD COMP_KEY COMP_KEY COMP_LINE COMP_LINE COMP_POINT COMP_POINT COMPREPLY COMPREPLY COMP_TYPE COMP_TYPE COMP_WORDBREAKS COMP_WORDBREAKS COMP_WORDS COMP_WORDS COPROC COPROC DIRSTACK EMACS EMACS ENV ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNAME FUNCNEST FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_NUMERIC LINENO LINES LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT POSIXLY_CORRECT PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_LINE READLINE_POINT READLINE_POINT REPLY SECONDS SHELL SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR TMPDIR UID
+ syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_LINENO BASHOPTS BASHPID BASH_REMATCH BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD CDPATH COLUMNS COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMPREPLY COMP_TYPE COMP_WORDBREAKS COMP_WORDS COPROC COPROC_PID DIRSTACK EMACS ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LINENO LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT PPID PROMPT_COMMAND PS0 PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_POINT REPLY SECONDS SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR UID
syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep head less ls mkdir mv rm rmdir rpm sed sleep sort strip tail
syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop
syn keyword bashStatement command compgen
@@ -453,7 +501,18 @@ endif
"=============
syn match shSetOption "\s\zs[-+][a-zA-Z0-9]\+\>" contained
syn match shVariable "\<\h\w*\ze=" nextgroup=shVarAssign
-syn match shVarAssign "=" contained nextgroup=shCmdParenRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar
+if exists("b:is_bash")
+ " The subscript form for array values, e.g. "foo=([2]=10 [4]=100)".
+ syn region shArrayValue contained start="\[\%(..\{-}\]=\)\@=" end="\]=\@=" contains=@shArrayValueList nextgroup=shVarAssign
+ syn cluster shArrayValueList contains=shArithmetic,shArithParen,shCommandSub,shDeref,shDerefSimple,shExpr,shNumber,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shSpecial,shParen,bashSpecialVariables,shParenError
+endif
+if exists("b:is_bash") || exists("b:is_kornshell")
+ syn match shVariable "\<\h\w*\%(\[..\{-}\]\)\=\ze\%([|^&*/%+-]\|[<^]<\|[>^]>\)\==" contains=shDerefVarArray nextgroup=shVarAssign
+ syn match shVarAssign contained "\%([|^&*/%+-]\|[<^]<\|[>^]>\)\==" nextgroup=shArrayRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar
+ syn region shArrayRegion contained matchgroup=shShellVariables start="(" skip='\\\\\|\\.' end=")" contains=@shArrayValueList,shArrayValue,shComment
+else
+ syn match shVarAssign contained "=" nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar
+endif
syn match shVar contained "\h\w*"
syn region shAtExpr contained start="@(" end=")" contains=@shIdList
if exists("b:is_bash")
@@ -482,6 +541,7 @@ if !exists("b:is_posix")
endif
if exists("b:is_bash")
+ syn keyword shFunctionKey coproc
ShFoldFunctions syn region shFunctionOne matchgroup=shFunction start="^\s*[A-Za-z_0-9:][-a-zA-Z_0-9:]*\s*()\_s*{" end="}" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
ShFoldFunctions syn region shFunctionTwo matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_0-9:][-a-zA-Z_0-9:]*\>\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
ShFoldFunctions syn region shFunctionThree matchgroup=shFunction start="^\s*[A-Za-z_0-9:][-a-zA-Z_0-9:]*\s*()\_s*(" end=")" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
@@ -571,8 +631,11 @@ syn match shDerefOp contained ":\=+" nextgroup=@shDerefPatternList
if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix")
syn match shDerefOp contained "#\{1,2}" nextgroup=@shDerefPatternList
syn match shDerefOp contained "%\{1,2}" nextgroup=@shDerefPatternList
- syn match shDerefPattern contained "[^{}]\+" contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern
+ syn match shDerefPattern contained "[^{}]\+" contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern skipnl
syn region shDerefPattern contained start="{" end="}" contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern
+ " Match parametric bracket expressions with a leading whitespace character.
+ syn region shDerefPattern contained matchgroup=shBracketExprDelim start="\[" end="\]" contains=@shBracketExprList,shDoubleQuote nextgroup=shDerefPattern
+ call s:GenerateBracketExpressionItems({'itemGroup': 'shDerefPattern', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'contained nextgroup=shDerefPattern'})
syn match shDerefEscape contained '\%(\\\\\)*\\.'
endif
if exists("b:is_bash")
@@ -592,15 +655,16 @@ if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix")
endif
if exists("b:is_bash")
+ " bash : ${parameter/pattern/string}
" bash : ${parameter//pattern/string}
- " bash : ${parameter//pattern}
syn match shDerefPPS contained '/\{1,2}' nextgroup=shDerefPPSleft
syn region shDerefPPSleft contained start='.' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' end='"' nextgroup=shDerefPPSright contains=@shPPSLeftList
syn region shDerefPPSright contained start='.' skip=@\%(\\\\\)\+@ end='\ze}' contains=@shPPSRightList
- " bash : ${parameter/#substring/replacement}
- syn match shDerefPSR contained '/#' nextgroup=shDerefPSRleft,shDoubleQuote,shSingleQuote
- syn region shDerefPSRleft contained start='[^"']' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPSRright
+ " bash : ${parameter/#pattern/string}
+ " bash : ${parameter/%pattern/string}
+ syn match shDerefPSR contained '/[#%]' nextgroup=shDerefPSRleft,shDoubleQuote,shSingleQuote
+ syn region shDerefPSRleft contained start='[^"']' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPSRright contains=shBracketExpr
syn region shDerefPSRright contained start='.' skip=@\%(\\\\\)\+@ end='\ze}'
endif
@@ -631,10 +695,10 @@ if exists("b:is_kornshell") || exists("b:is_posix")
" Additional bash Keywords: {{{1
" =====================
elseif exists("b:is_bash")
- syn keyword shStatement bg builtin disown export false fg getopts jobs let printf sleep true unalias
+ syn keyword shStatement bg builtin disown export false fg getopts jobs let printf true unalias
syn keyword shStatement typeset nextgroup=shSetOption
syn keyword shStatement fc hash history source suspend times type
- syn keyword shStatement bind builtin caller compopt declare dirs disown enable export help logout local mapfile popd pushd readarray shopt source typeset
+ syn keyword shStatement bind caller compopt declare dirs enable help logout local mapfile popd pushd readarray shopt typeset
else
syn keyword shStatement login newgrp
endif
@@ -670,6 +734,7 @@ syn sync match shWhileSync grouphere shRepeat "\<while\>"
" =====================
if !exists("skip_sh_syntax_inits")
hi def link shArithRegion shShellVariables
+ hi def link shArrayValue shDeref
hi def link shAstQuote shDoubleQuote
hi def link shAtExpr shSetList
hi def link shBkslshSnglQuote shSingleQuote
@@ -764,7 +829,10 @@ if !exists("skip_sh_syntax_inits")
endif
hi def link shArithmetic Special
+ hi def link shBracketExprDelim Delimiter
hi def link shCharClass Identifier
+ hi def link shCollSymb shCharClass
+ hi def link shEqClass shCharClass
hi def link shSnglCase Statement
hi def link shCommandSub Special
hi def link shCommandSubBQ shCommandSub
@@ -814,6 +882,10 @@ delc ShFoldFunctions
delc ShFoldHereDoc
delc ShFoldIfDoFor
+" Delete the bracket expression function {{{1
+" ======================================
+delfun s:GenerateBracketExpressionItems
+
" Set Current Syntax: {{{1
" ===================
if exists("b:is_bash")
diff --git a/runtime/syntax/shaderslang.vim b/runtime/syntax/shaderslang.vim
new file mode 100644
index 0000000000..1cae202b04
--- /dev/null
+++ b/runtime/syntax/shaderslang.vim
@@ -0,0 +1,360 @@
+" Vim syntax file
+" Language: Slang
+" Maintainer: Austin Shijo <epestr@proton.me>
+" Last Change: 2024 Jan 05
+
+if exists("b:current_syntax")
+ finish
+endif
+
+" Read the C syntax to start with
+runtime! syntax/c.vim
+unlet b:current_syntax
+
+" Annotations
+syn match shaderslangAnnotation /<.*;>/
+
+" Attributes
+syn match shaderslangAttribute /^\s*\[maxvertexcount(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[domain(\s*"\(tri\|quad\|isoline\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[earlydepthstencil\]/
+syn match shaderslangAttribute /^\s*\[instance(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[maxtessfactor(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[numthreads(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[outputcontrolpoints(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[outputtopology(\s*"\(point\|line\|triangle_cw\|triangle_ccw\|triangle\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[partitioning(\s*"\(integer\|fractional_even\|fractional_odd\|pow2\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[patchconstantfunc(\s*"\(\d\|\w\|_\)\+"\s*)\]/
+syn match shaderslangAttribute /^\s*\[WaveSize(\s*\w\+\(\s*,\s*\w\+\(\s*,\s*\w\+\)\?\)\?\s*)\]/
+syn match shaderslangAttribute /^\s*\[shader(\s*"\(anyhit\|callable\|closesthit\|intersection\|miss\|raygeneration\)"\s*)\]/
+
+syn match shaderslangAttribute /^\s*\[fastopt\]/
+syn match shaderslangAttribute /^\s*\[loop\]/
+syn match shaderslangAttribute /^\s*\[unroll\]/
+syn match shaderslangAttribute /^\s*\[allow_uav_condition\]/
+syn match shaderslangAttribute /^\s*\[branch\]/
+syn match shaderslangAttribute /^\s*\[flatten\]/
+syn match shaderslangAttribute /^\s*\[forcecase\]/
+syn match shaderslangAttribute /^\s*\[call\]/
+syn match shaderslangAttribute /^\s*\[WaveOpsIncludeHelperLanes\]/
+
+syn match shaderslangAttribute /\[raypayload\]/
+
+" Work graph shader target attributes
+syn match shaderslangAttribute /^\s*\[Shader(\s*"\(\d\|\w\|_\)\+"\s*)\]/
+
+" Work graph shader function attributes
+syn match shaderslangAttribute /^\s*\[NodeLaunch(\s*"\(broadcasting\|coalescing\|thread\)"\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeIsProgramEntry\]/
+syn match shaderslangAttribute /^\s*\[NodeLocalRootArgumentsTableIndex(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NumThreads(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeShareInputOf(\s*"\w\+"\(\s*,\s*\w\+\)\?\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeDispatchGrid(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeMaxDispatchGrid(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeMaxRecursionDepth(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /^\s*\[NodeMaxInputRecordsPerGraphEntryRecord(\s*\w\+\s*,\s*\(true\|false\)\s*)\]/
+
+" Work graph record attributes
+syn match shaderslangAttribute /\[NodeTrackRWInputSharing\]/
+syn match shaderslangAttribute /\[MaxRecords(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /\[NodeID(\s*"\w\+"\(\s*,\s*\w\+\)\?\s*)\]/
+syn match shaderslangAttribute /\[MaxRecordsSharedWith(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /\[AllowSparseNodes\]/
+syn match shaderslangAttribute /\[NodeArraySize(\s*\w\+\s*)\]/
+syn match shaderslangAttribute /\[UnboundedSparseNodes\]/
+
+" Intrinsic functions
+syn keyword shaderslangFunc abs acos acosh asin asinh atan atanh cos cosh exp exp2 floor log log10 log2 round rsqrt sin sincos sinh sqrt tan tanh trunc
+syn keyword shaderslangFunc AllMemoryBarrier AllMemoryBarrierWithGroupSync DeviceMemoryBarrier DeviceMemoryBarrierWithGroupSync GroupMemoryBarrier GroupMemoryBarrierWithGroupSync
+syn keyword shaderslangFunc abort clip errorf printf
+syn keyword shaderslangFunc all any countbits faceforward firstbithigh firstbitlow isfinite isinf isnan max min noise pow reversebits sign
+syn keyword shaderslangFunc asdouble asfloat asint asuint D3DCOLORtoUBYTE4 f16tof32 f32tof16
+syn keyword shaderslangFunc ceil clamp degrees fma fmod frac frexp ldexp lerp mad modf radiants saturate smoothstep step
+syn keyword shaderslangFunc cross determinant distance dot dst length lit msad4 mul normalize rcp reflect refract transpose
+syn keyword shaderslangFunc ddx ddx_coarse ddx_fine ddy ddy_coarse ddy_fine fwidth
+syn keyword shaderslangFunc EvaluateAttributeAtCentroid EvaluateAttributeAtSample EvaluateAttributeSnapped
+syn keyword shaderslangFunc GetRenderTargetSampleCount GetRenderTargetSamplePosition
+syn keyword shaderslangFunc InterlockedAdd InterlockedAnd InterlockedCompareExchange InterlockedCompareStore InterlockedExchange InterlockedMax InterlockedMin InterlockedOr InterlockedXor
+syn keyword shaderslangFunc InterlockedCompareStoreFloatBitwise InterlockedCompareExchangeFloatBitwise
+syn keyword shaderslangFunc Process2DQuadTessFactorsAvg Process2DQuadTessFactorsMax Process2DQuadTessFactorsMin ProcessIsolineTessFactors
+syn keyword shaderslangFunc ProcessQuadTessFactorsAvg ProcessQuadTessFactorsMax ProcessQuadTessFactorsMin ProcessTriTessFactorsAvg ProcessTriTessFactorsMax ProcessTriTessFactorsMin
+syn keyword shaderslangFunc tex1D tex1Dbias tex1Dgrad tex1Dlod tex1Dproj
+syn keyword shaderslangFunc tex2D tex2Dbias tex2Dgrad tex2Dlod tex2Dproj
+syn keyword shaderslangFunc tex3D tex3Dbias tex3Dgrad tex3Dlod tex3Dproj
+syn keyword shaderslangFunc texCUBE texCUBEbias texCUBEgrad texCUBElod texCUBEproj
+syn keyword shaderslangFunc WaveIsFirstLane WaveGetLaneCount WaveGetLaneIndex
+syn keyword shaderslangFunc IsHelperLane
+syn keyword shaderslangFunc WaveActiveAnyTrue WaveActiveAllTrue WaveActiveBallot
+syn keyword shaderslangFunc WaveReadLaneFirst WaveReadLaneAt
+syn keyword shaderslangFunc WaveActiveAllEqual WaveActiveAllEqualBool WaveActiveCountBits
+syn keyword shaderslangFunc WaveActiveSum WaveActiveProduct WaveActiveBitAnd WaveActiveBitOr WaveActiveBitXor WaveActiveMin WaveActiveMax
+syn keyword shaderslangFunc WavePrefixCountBits WavePrefixProduct WavePrefixSum
+syn keyword shaderslangFunc QuadReadAcrossX QuadReadAcrossY QuadReadAcrossDiagonal QuadReadLaneAt
+syn keyword shaderslangFunc QuadAny QuadAll
+syn keyword shaderslangFunc WaveMatch WaveMultiPrefixSum WaveMultiPrefixProduct WaveMultiPrefixCountBits WaveMultiPrefixAnd WaveMultiPrefixOr WaveMultiPrefixXor
+syn keyword shaderslangFunc NonUniformResourceIndex
+syn keyword shaderslangFunc DispatchMesh SetMeshOutputCounts
+syn keyword shaderslangFunc dot4add_u8packed dot4add_i8packed dot2add
+
+syn keyword shaderslangFunc RestartStrip
+syn keyword shaderslangFunc CalculateLevelOfDetail CalculateLevelOfDetailUnclamped Gather GetDimensions GetSamplePosition Load Sample SampleBias SampleCmp SampleCmpLevelZero SampleGrad SampleLevel GatherRaw SampleCmpLevel
+syn keyword shaderslangFunc SampleCmpBias SampleCmpGrad
+syn keyword shaderslangFunc WriteSamplerFeedback WriteSamplerFeedbackBias WriteSamplerFeedbackGrad WriteSamplerFeedbackLevel
+syn keyword shaderslangFunc Append Consume DecrementCounter IncrementCounter
+syn keyword shaderslangFunc Load2 Load3 Load4 Store Store2 Store3 Store4
+syn keyword shaderslangFunc GatherRed GatherGreen GatherBlue GatherAlpha GatherCmp GatherCmpRed GatherCmpGreen GatherCmpBlue GatherCmpAlpha
+syn match shaderslangFunc /\.mips\[\d\+\]\[\d\+\]/
+syn match shaderslangFunc /\.sample\[\d\+\]\[\d\+\]/
+
+" Ray intrinsics
+syn keyword shaderslangFunc AcceptHitAndEndSearch CallShader IgnoreHit ReportHit TraceRay
+syn keyword shaderslangFunc DispatchRaysIndex DispatchRaysDimensions
+syn keyword shaderslangFunc WorldRayOrigin WorldRayDirection RayTMin RayTCurrent RayFlags
+syn keyword shaderslangFunc InstanceIndex InstanceID GeometryIndex PrimitiveIndex ObjectRayOrigin ObjectRayDirection ObjectToWorld3x4 ObjectToWorld4x3 WorldToObject3x4 WorldToObject4x3
+syn keyword shaderslangFunc HitKind
+
+" RayQuery intrinsics
+syn keyword shaderslangFunc TraceRayInline Proceed Abort CommittedStatus
+syn keyword shaderslangFunc CandidateType CandidateProceduralPrimitiveNonOpaque CandidateTriangleRayT CandidateInstanceIndex CandidateInstanceID CandidateInstanceContributionToHitGroupIndex CandidateGeometryIndex
+syn keyword shaderslangFunc CandidatePrimitiveIndex CandidateObjectRayOrigin CandidateObjectRayDirection CandidateObjectToWorld3x4 CandidateObjectToWorld4x3 CandidateWorldToObject3x4 CandidateWorldToObject4x3
+syn keyword shaderslangFunc CommitNonOpaqueTriangleHit CommitProceduralPrimitiveHit CommittedStatus CommittedRayT CommittedInstanceIndex CommittedInstanceID CommittedInstanceContributionToHitGroupIndex
+syn keyword shaderslangFunc CommittedGeometryIndex CommittedPrimitiveIndex CommittedObjectRayOrigin CommittedObjectRayDirection CommittedObjectToWorld3x4 CommittedObjectToWorld4x3 CommittedWorldToObject3x4
+syn keyword shaderslangFunc CommittedWorldToObject4x3 CandidateTriangleBarycentrics CandidateTriangleFrontFace CommittedTriangleBarycentrics CommittedTriangleFrontFace
+
+" Pack/unpack math intrinsics
+syn keyword shaderslangFunc unpack_s8s16 unpack_u8u16 unpack_s8s32 unpack_u8u32
+syn keyword shaderslangFunc pack_u8 pack_s8 pack_clamp_u8 pack_clamp_s8
+
+" Work graph object methods
+syn keyword shaderslangFunc Get FinishedCrossGroupSharing Count GetThreadNodeOutputRecords GetGroupNodeOutputRecords IsValid GroupIncrementOutputCount ThreadIncrementOutputCount OutputComplete
+
+" Work graph free intrinsics
+syn keyword shaderslangFunc GetRemainingRecursionLevels Barrier
+
+" Layout Qualifiers
+syn keyword shaderslangLayoutQual const row_major column_major
+syn keyword shaderslangLayoutQual point line triangle lineadj triangleadj
+syn keyword shaderslangLayoutQual InputPatch OutputPatch
+syn match shaderslangLayoutQual /PointStream<\s*\w\+\s*>/
+syn match shaderslangLayoutQual /LineStream<\s*\w\+\s*>/
+syn match shaderslangLayoutQual /TriangleStream<\s*\w\+\s*>/
+
+" User defined Semantics
+syn match shaderslangSemantic /:\s*[A-Z]\w*/
+syn match shaderslangSemantic /:\s*packoffset(\s*c\d\+\(\.[xyzw]\)\?\s*)/ " packoffset
+syn match shaderslangSemantic /:\s*register(\s*\(r\|x\|v\|t\|s\|cb\|icb\|b\|c\|u\)\d\+\s*)/ " register
+syn match shaderslangSemantic /:\s*read(\s*\(\(anyhit\|closesthit\|miss\|caller\)\s*,\s*\)*\(anyhit\|closesthit\|miss\|caller\)\?\s*)/ " read
+syn match shaderslangSemantic /:\s*write(\s*\(\(anyhit\|closesthit\|miss\|caller\)\s*,\s*\)*\(anyhit\|closesthit\|miss\|caller\)\?\s*)/ " write
+
+" System-Value Semantics
+" Vertex Shader
+syn match shaderslangSemantic /SV_ClipDistance\d\+/
+syn match shaderslangSemantic /SV_CullDistance\d\+/
+syn keyword shaderslangSemantic SV_Position SV_InstanceID SV_PrimitiveID SV_VertexID
+syn keyword shaderslangSemantic SV_StartVertexLocation SV_StartInstanceLocation
+" Tessellation pipeline
+syn keyword shaderslangSemantic SV_DomainLocation SV_InsideTessFactor SV_OutputControlPointID SV_TessFactor
+" Geometry Shader
+syn keyword shaderslangSemantic SV_GSInstanceID SV_RenderTargetArrayIndex
+" Pixel Shader - MSAA
+syn keyword shaderslangSemantic SV_Coverage SV_Depth SV_IsFrontFace SV_SampleIndex
+syn match shaderslangSemantic /SV_Target[0-7]/
+syn keyword shaderslangSemantic SV_ShadingRate SV_ViewID
+syn match shaderslangSemantic /SV_Barycentrics[0-1]/
+" Compute Shader
+syn keyword shaderslangSemantic SV_DispatchThreadID SV_GroupID SV_GroupIndex SV_GroupThreadID
+" Mesh shading pipeline
+syn keyword shaderslangSemantic SV_CullPrimitive
+" Work graph record system values
+syn keyword shaderslangSemantic SV_DispatchGrid
+
+" slang structures
+syn keyword shaderslangStructure cbuffer
+
+" Shader profiles
+" Cg profiles
+syn keyword shaderslangProfile arbfp1 arbvp1 fp20 vp20 fp30 vp30 ps_1_1 ps_1_2 ps_1_3
+" Shader Model 1
+syn keyword shaderslangProfile vs_1_1
+" Shader Model 2
+syn keyword shaderslangProfile ps_2_0 ps_2_x vs_2_0 vs_2_x
+" Shader Model 3
+syn keyword shaderslangProfile ps_3_0 vs_3_0
+" Shader Model 4
+syn keyword shaderslangProfile gs_4_0 ps_4_0 vs_4_0 gs_4_1 ps_4_1 vs_4_1
+" Shader Model 5
+syn keyword shaderslangProfile cs_4_0 cs_4_1 cs_5_0 ds_5_0 gs_5_0 hs_5_0 ps_5_0 vs_5_0
+" Shader Model 6
+syn keyword shaderslangProfile cs_6_0 ds_6_0 gs_6_0 hs_6_0 ps_6_0 vs_6_0 lib_6_0
+
+" Swizzling
+syn match shaderslangSwizzle /\.[xyzw]\{1,4\}\>/
+syn match shaderslangSwizzle /\.[rgba]\{1,4\}\>/
+syn match shaderslangSwizzle /\.\(_m[0-3]\{2}\)\{1,4\}/
+syn match shaderslangSwizzle /\.\(_[1-4]\{2}\)\{1,4\}/
+
+" Other Statements
+syn keyword shaderslangStatement discard
+
+" Storage class
+syn match shaderslangStorageClass /\<in\(\s+pipeline\)\?\>/
+syn match shaderslangStorageClass /\<out\(\s\+indices\|\s\+vertices\|\s\+primitives\)\?\>/
+syn keyword shaderslangStorageClass inout
+syn keyword shaderslangStorageClass extern nointerpolation precise shared groupshared static uniform volatile
+syn keyword shaderslangStorageClass snorm unorm
+syn keyword shaderslangStorageClass linear centroid nointerpolation noperspective sample
+syn keyword shaderslangStorageClass globallycoherent
+
+" Types
+" Buffer types
+syn keyword shaderslangType ConstantBuffer Buffer ByteAddressBuffer ConsumeStructuredBuffer StructuredBuffer
+syn keyword shaderslangType AppendStructuredBuffer RWBuffer RWByteAddressBuffer RWStructuredBuffer
+syn keyword shaderslangType RasterizerOrderedBuffer RasterizerOrderedByteAddressBuffer RasterizerOrderedStructuredBuffer
+
+" Scalar types
+syn keyword shaderslangType bool int uint dword half float double
+syn keyword shaderslangType min16float min10float min16int min12int min16uint
+syn keyword shaderslangType float16_t float32_t float64_t
+
+" Vector types
+syn match shaderslangType /vector<\s*\w\+,\s*[1-4]\s*>/
+syn keyword shaderslangType bool1 bool2 bool3 bool4
+syn keyword shaderslangType int1 int2 int3 int4
+syn keyword shaderslangType uint1 uint2 uint3 uint4
+syn keyword shaderslangType dword1 dword2 dword3 dword4
+syn keyword shaderslangType half1 half2 half3 half4
+syn keyword shaderslangType float1 float2 float3 float4
+syn keyword shaderslangType double1 double2 double3 double4
+syn keyword shaderslangType min16float1 min16float2 min16float3 min16float4
+syn keyword shaderslangType min10float1 min10float2 min10float3 min10float4
+syn keyword shaderslangType min16int1 min16int2 min16int3 min16int4
+syn keyword shaderslangType min12int1 min12int2 min12int3 min12int4
+syn keyword shaderslangType min16uint1 min16uint2 min16uint3 min16uint4
+syn keyword shaderslangType float16_t1 float16_t2 float16_t3 float16_t4
+syn keyword shaderslangType float32_t1 float32_t2 float32_t3 float32_t4
+syn keyword shaderslangType float64_t1 float64_t2 float64_t3 float64_t4
+syn keyword shaderslangType int16_t1 int16_t2 int16_t3 int16_t4
+syn keyword shaderslangType int32_t1 int32_t2 int32_t3 int32_t4
+syn keyword shaderslangType int64_t1 int64_t2 int64_t3 int64_t4
+syn keyword shaderslangType uint16_t1 uint16_t2 uint16_t3 uint16_t4
+syn keyword shaderslangType uint32_t1 uint32_t2 uint32_t3 uint32_t4
+syn keyword shaderslangType uint64_t1 uint64_t2 uint64_t3 uint64_t4
+
+" Packed types
+syn keyword shaderslangType uint8_t4_packed int8_t4_packed
+
+" Matrix types
+syn match shaderslangType /matrix<\s*\w\+\s*,\s*[1-4]\s*,\s*[1-4]\s*>/
+syn keyword shaderslangType bool1x1 bool2x1 bool3x1 bool4x1 bool1x2 bool2x2 bool3x2 bool4x2 bool1x3 bool2x3 bool3x3 bool4x3 bool1x4 bool2x4 bool3x4 bool4x4
+syn keyword shaderslangType int1x1 int2x1 int3x1 int4x1 int1x2 int2x2 int3x2 int4x2 int1x3 int2x3 int3x3 int4x3 int1x4 int2x4 int3x4 int4x4
+syn keyword shaderslangType uint1x1 uint2x1 uint3x1 uint4x1 uint1x2 uint2x2 uint3x2 uint4x2 uint1x3 uint2x3 uint3x3 uint4x3 uint1x4 uint2x4 uint3x4 uint4x4
+syn keyword shaderslangType dword1x1 dword2x1 dword3x1 dword4x1 dword1x2 dword2x2 dword3x2 dword4x2 dword1x3 dword2x3 dword3x3 dword4x3 dword1x4 dword2x4 dword3x4 dword4x4
+syn keyword shaderslangType half1x1 half2x1 half3x1 half4x1 half1x2 half2x2 half3x2 half4x2 half1x3 half2x3 half3x3 half4x3 half1x4 half2x4 half3x4 half4x4
+syn keyword shaderslangType float1x1 float2x1 float3x1 float4x1 float1x2 float2x2 float3x2 float4x2 float1x3 float2x3 float3x3 float4x3 float1x4 float2x4 float3x4 float4x4
+syn keyword shaderslangType double1x1 double2x1 double3x1 double4x1 double1x2 double2x2 double3x2 double4x2 double1x3 double2x3 double3x3 double4x3 double1x4 double2x4 double3x4 double4x4
+syn keyword shaderslangType min16float1x1 min16float2x1 min16float3x1 min16float4x1 min16float1x2 min16float2x2 min16float3x2 min16float4x2 min16float1x3 min16float2x3 min16float3x3 min16float4x3 min16float1x4 min16float2x4 min16float3x4 min16float4x4
+syn keyword shaderslangType min10float1x1 min10float2x1 min10float3x1 min10float4x1 min10float1x2 min10float2x2 min10float3x2 min10float4x2 min10float1x3 min10float2x3 min10float3x3 min10float4x3 min10float1x4 min10float2x4 min10float3x4 min10float4x4
+syn keyword shaderslangType min16int1x1 min16int2x1 min16int3x1 min16int4x1 min16int1x2 min16int2x2 min16int3x2 min16int4x2 min16int1x3 min16int2x3 min16int3x3 min16int4x3 min16int1x4 min16int2x4 min16int3x4 min16int4x4
+syn keyword shaderslangType min12int1x1 min12int2x1 min12int3x1 min12int4x1 min12int1x2 min12int2x2 min12int3x2 min12int4x2 min12int1x3 min12int2x3 min12int3x3 min12int4x3 min12int1x4 min12int2x4 min12int3x4 min12int4x4
+syn keyword shaderslangType min16uint1x1 min16uint2x1 min16uint3x1 min16uint4x1 min16uint1x2 min16uint2x2 min16uint3x2 min16uint4x2 min16uint1x3 min16uint2x3 min16uint3x3 min16uint4x3 min16uint1x4 min16uint2x4 min16uint3x4 min16uint4x4
+syn keyword shaderslangType float16_t1x1 float16_t2x1 float16_t3x1 float16_t4x1 float16_t1x2 float16_t2x2 float16_t3x2 float16_t4x2 float16_t1x3 float16_t2x3 float16_t3x3 float16_t4x3 float16_t1x4 float16_t2x4 float16_t3x4 float16_t4x4
+syn keyword shaderslangType float32_t1x1 float32_t2x1 float32_t3x1 float32_t4x1 float32_t1x2 float32_t2x2 float32_t3x2 float32_t4x2 float32_t1x3 float32_t2x3 float32_t3x3 float32_t4x3 float32_t1x4 float32_t2x4 float32_t3x4 float32_t4x4
+syn keyword shaderslangType float64_t1x1 float64_t2x1 float64_t3x1 float64_t4x1 float64_t1x2 float64_t2x2 float64_t3x2 float64_t4x2 float64_t1x3 float64_t2x3 float64_t3x3 float64_t4x3 float64_t1x4 float64_t2x4 float64_t3x4 float64_t4x4
+syn keyword shaderslangType int16_t1x1 int16_t2x1 int16_t3x1 int16_t4x1 int16_t1x2 int16_t2x2 int16_t3x2 int16_t4x2 int16_t1x3 int16_t2x3 int16_t3x3 int16_t4x3 int16_t1x4 int16_t2x4 int16_t3x4 int16_t4x4
+syn keyword shaderslangType int32_t1x1 int32_t2x1 int32_t3x1 int32_t4x1 int32_t1x2 int32_t2x2 int32_t3x2 int32_t4x2 int32_t1x3 int32_t2x3 int32_t3x3 int32_t4x3 int32_t1x4 int32_t2x4 int32_t3x4 int32_t4x4
+syn keyword shaderslangType int64_t1x1 int64_t2x1 int64_t3x1 int64_t4x1 int64_t1x2 int64_t2x2 int64_t3x2 int64_t4x2 int64_t1x3 int64_t2x3 int64_t3x3 int64_t4x3 int64_t1x4 int64_t2x4 int64_t3x4 int64_t4x4
+syn keyword shaderslangType uint16_t1x1 uint16_t2x1 uint16_t3x1 uint16_t4x1 uint16_t1x2 uint16_t2x2 uint16_t3x2 uint16_t4x2 uint16_t1x3 uint16_t2x3 uint16_t3x3 uint16_t4x3 uint16_t1x4 uint16_t2x4 uint16_t3x4 uint16_t4x4
+syn keyword shaderslangType uint32_t1x1 uint32_t2x1 uint32_t3x1 uint32_t4x1 uint32_t1x2 uint32_t2x2 uint32_t3x2 uint32_t4x2 uint32_t1x3 uint32_t2x3 uint32_t3x3 uint32_t4x3 uint32_t1x4 uint32_t2x4 uint32_t3x4 uint32_t4x4
+syn keyword shaderslangType uint64_t1x1 uint64_t2x1 uint64_t3x1 uint64_t4x1 uint64_t1x2 uint64_t2x2 uint64_t3x2 uint64_t4x2 uint64_t1x3 uint64_t2x3 uint64_t3x3 uint64_t4x3 uint64_t1x4 uint64_t2x4 uint64_t3x4 uint64_t4x4
+
+" Sampler types
+syn keyword shaderslangType SamplerState SamplerComparisonState
+syn keyword shaderslangType sampler sampler1D sampler2D sampler3D samplerCUBE sampler_state
+
+" Texture types
+syn keyword shaderslangType Texture1D Texture1DArray Texture2D Texture2DArray Texture2DMS Texture2DMSArray Texture3D TextureCube TextureCubeArray
+syn keyword shaderslangType RWTexture1D RWTexture2D RWTexture2DArray RWTexture3D RWTextureCubeArray RWTexture2DMS RWTexture2DMSArray
+syn keyword shaderslangType FeedbackTexture2D FeedbackTexture2DArray
+syn keyword shaderslangType RasterizerOrderedTexture1D RasterizerOrderedTexture1DArray RasterizerOrderedTexture2D RasterizerOrderedTexture2DArray RasterizerOrderedTexture3D
+syn keyword shaderslangTypeDeprec texture texture1D texture2D texture3D
+
+" Raytracing types
+syn keyword shaderslangType RaytracingAccelerationStructure RayDesc RayQuery BuiltInTriangleIntersectionAttributes
+
+" Work graph input record objects
+syn keyword shaderslangType DispatchNodeInputRecord RWDispatchNodeInputRecord GroupNodeInputRecords RWGroupNodeInputRecords ThreadNodeInputRecord RWThreadNodeInputRecord EmptyNodeInput
+
+" Work graph output node objects
+syn keyword shaderslangType NodeOutput NodeOutputArray EmptyNodeOutput EmptyNodeOutputArray
+
+" Work graph output record objects
+syn keyword shaderslangType ThreadNodeOutputRecords GroupNodeOutputRecords
+
+" State Groups args
+syn case ignore " This section case insensitive
+
+" Blend state group
+syn keyword shaderslangStateGroupArg AlphaToCoverageEnable BlendEnable SrcBlend DestBlend BlendOp SrcBlendAlpha DestBlendAlpha BlendOpAlpha RenderTargetWriteMask
+syn keyword shaderslangStateGroupVal ZERO ONE SRC_COLOR INV_SRC_COLOR SRC_ALPHA INV_SRC_ALPHA DEST_ALPHA INV_DEST_ALPHA DEST_COLOR INV_DEST_COLOR SRC_ALPHA_SAT BLEND_FACTOR INV_BLEND_FACTOR SRC1_COLOR INV_SRC1_COLOR SRC1_ALPHA INV_SRC1_ALPHA
+syn keyword shaderslangStateGroupVal ADD SUBSTRACT REV_SUBSTRACT MIN MAX
+
+" Rasterizer state group
+syn keyword shaderslangStateGroupArg FillMode CullMode FrontCounterClockwise DepthBias DepthBiasClamp SlopeScaledDepthBias ZClipEnable DepthClipEnable ScissorEnable MultisampleEnable AntialiasedLineEnable
+syn keyword shaderslangStateGroupVal SOLID WIREFRAME
+syn keyword shaderslangStateGroupVal NONE FRONT BACK
+
+" Sampler state group
+syn keyword shaderslangStateGroupArg Filter AddressU AddressV AddressW MipLODBias MaxAnisotropy ComparisonFunc BorderColor MinLOD MaxLOD ComparisonFilter
+syn keyword shaderslangStateGroupVal MIN_MAG_MIP_POINT MIN_MAG_POINT_MIP_LINEAR MIN_POINT_MAG_LINEAR_MIP_POINT MIN_POINT_MAG_MIP_LINEAR MIN_LINEAR_MAG_MIP_POINT MIN_LINEAR_MAG_POINT_MIP_LINEAR MIN_MAG_LINEAR_MIP_POINT MIN_MAG_MIP_LINEAR ANISOTROPIC
+syn keyword shaderslangStateGroupVal COMPARISON_MIN_MAG_MIP_POINT COMPARISON_MIN_MAG_POINT_MIP_LINEAR COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT COMPARISON_MIN_POINT_MAG_MIP_LINEAR COMPARISON_MIN_LINEAR_MAG_MIP_POINT
+syn keyword shaderslangStateGroupVal COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR COMPARISON_MIN_MAG_LINEAR_MIP_POINT COMPARISON_MIN_MAG_MIP_LINEAR COMPARISON_ANISOTROPIC
+syn keyword shaderslangStateGroupVal COMPARISON_NEVER COMPARISON_LESS COMPARISON_EQUAL COMPARISON_LESS_EQUAL COMPARISON_GREATER COMPARISON_NOT_EQUAL COMPARISON_GREATER_EQUAL COMPARISON_ALWAYS
+syn keyword shaderslangStateGroupVal WRAP MIRROR CLAMP BORDER MIRROR_ONCE
+syn keyword shaderslangStateGroupVal SAMPLER_FEEDBACK_MIN_MIP SAMPLER_FEEDBACK_MIP_REGION_USED
+
+" Ray flags
+syn keyword shaderslangStateGroupVal RAY_FLAG_NONE RAY_FLAG_FORCE_OPAQUE RAY_FLAG_FORCE_NON_OPAQUE RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH RAY_FLAG_SKIP_CLOSEST_HIT_SHADER
+syn keyword shaderslangStateGroupVal RAY_FLAG_CULL_BACK_FACING_TRIANGLES RAY_FLAG_CULL_FRONT_FACING_TRIANGLES RAY_FLAG_CULL_OPAQUE RAY_FLAG_CULL_NON_OPAQUE
+syn keyword shaderslangStateGroupVal RAY_FLAG_SKIP_TRIANGLES RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES
+
+" HitKind enum
+syn keyword shaderslangStateGroupVal HIT_KIND_TRIANGLE_FRONT_FACE HIT_KIND_TRIANGLE_BACK_FACE
+
+" RayQuery enums
+syn keyword shaderslangStateGroupVal COMMITTED_NOTHING COMMITTED_TRIANGLE_HIT COMMITTED_PROCEDURAL_PRIMITIVE_HIT
+syn keyword shaderslangStateGroupVal CANDIDATE_NON_OPAQUE_TRIANGLE CANDIDATE_PROCEDURAL_PRIMITIVE
+
+" Heap objects
+syn keyword shaderslangStateGroupVal ResourceDescriptorHeap SamplerDescriptorHeap
+
+" Work graph constants
+syn keyword shaderslangStateGroupVal UAV_MEMORY GROUP_SHARED_MEMORY NODE_INPUT_MEMORY NODE_OUTPUT_MEMORY ALL_MEMORY GROUP_SYNC GROUP_SCOPE DEVICE_SCOPE
+
+syn case match " Case sensitive from now on
+
+" Effect files declarations and functions
+" Effect groups, techniques passes
+syn keyword shaderslangEffectGroup fxgroup technique11 pass
+" Effect functions
+syn keyword shaderslangEffectFunc SetBlendState SetDepthStencilState SetRasterizerState SetVertexShader SetHullShader SetDomainShader SetGeometryShader SetPixelShader SetComputeShader CompileShader ConstructGSWithSO SetRenderTargets
+
+" Default highlighting
+hi def link shaderslangProfile shaderslangStatement
+hi def link shaderslangStateGroupArg shaderslangStatement
+hi def link shaderslangStateGroupVal Number
+hi def link shaderslangStatement Statement
+hi def link shaderslangType Type
+hi def link shaderslangTypeDeprec WarningMsg
+hi def link shaderslangStorageClass StorageClass
+hi def link shaderslangSemantic PreProc
+hi def link shaderslangFunc shaderslangStatement
+hi def link shaderslangLayoutQual shaderslangFunc
+hi def link shaderslangAnnotation PreProc
+hi def link shaderslangStructure Structure
+hi def link shaderslangSwizzle SpecialChar
+hi def link shaderslangAttribute Statement
+
+hi def link shaderslangEffectGroup Type
+hi def link shaderslangEffectFunc Statement
+
+let b:current_syntax = "shaderslang"
diff --git a/runtime/syntax/tex.vim b/runtime/syntax/tex.vim
index 77a40e11d3..4f35bba939 100644
--- a/runtime/syntax/tex.vim
+++ b/runtime/syntax/tex.vim
@@ -3,7 +3,8 @@
" Maintainer: This runtime file is looking for a new maintainer.
" Former Maintainer: Charles E. Campbell
" Last Change: Apr 22, 2022
-" 2024 Feb 19 by Vim Project (announce adoption)
+" 2024 Feb 19 by Vim Project: announce adoption
+" 2025 Jan 18 by Vim Project: add texEmphStyle to texMatchGroup, #16228
" Version: 121
" Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TEX
"
@@ -176,11 +177,11 @@ if !s:tex_excludematcher
endif
if !s:tex_nospell
if !s:tex_no_error
- syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
+ syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texEmphStyle,texZone,texInputFile,texOption,@Spell
syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell
else
- syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
+ syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texEmphStyle,texZone,texInputFile,texOption,@Spell
syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell
syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell
endif
diff --git a/runtime/syntax/tiasm.vim b/runtime/syntax/tiasm.vim
new file mode 100644
index 0000000000..c79596bdfe
--- /dev/null
+++ b/runtime/syntax/tiasm.vim
@@ -0,0 +1,102 @@
+" Vim syntax file
+" Language: TI linear assembly language
+" Document: https://downloads.ti.com/docs/esd/SPRUI03B/#SPRUI03B_HTML/assembler-description.html
+" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu>
+" Last Change: 2025 Jan 08
+
+if exists("b:current_syntax")
+ finish
+endif
+
+syn case ignore
+
+" storage types
+syn match tiasmType "\.bits"
+syn match tiasmType "\.byte"
+syn match tiasmType "\.char"
+syn match tiasmType "\.cstring"
+syn match tiasmType "\.double"
+syn match tiasmType "\.field"
+syn match tiasmType "\.float"
+syn match tiasmType "\.half"
+syn match tiasmType "\.int"
+syn match tiasmType "\.long"
+syn match tiasmType "\.short"
+syn match tiasmType "\.string"
+syn match tiasmType "\.ubyte"
+syn match tiasmType "\.uchar"
+syn match tiasmType "\.uhalf"
+syn match tiasmType "\.uint"
+syn match tiasmType "\.ulong"
+syn match tiasmType "\.ushort"
+syn match tiasmType "\.uword"
+syn match tiasmType "\.word"
+
+syn match tiasmIdentifier "[a-z_][a-z0-9_]*"
+
+syn match tiasmDecimal "\<[1-9]\d*\>" display
+syn match tiasmOctal "\<0[0-7][0-7]\+\>\|\<[0-7]\+[oO]\>" display
+syn match tiasmHexadecimal "\<0[xX][0-9a-fA-F]\+\>\|\<[0-9][0-9a-fA-F]*[hH]\>" display
+syn match tiasmBinary "\<0[bB][0-1]\+\>\|\<[01]\+[bB]\>" display
+
+syn match tiasmFloat "\<\d\+\.\d*\%(e[+-]\=\d\+\)\=\>" display
+syn match tiasmFloat "\<\d\%(e[+-]\=\d\+\)\>" display
+
+syn match tiasmCharacter "'.'\|''\|'[^']'"
+
+syn region tiasmString start="\"" end="\"" skip="\"\""
+
+syn match tiasmFunction "\$[a-zA-Z_][a-zA-Z_0-9]*\ze("
+
+syn keyword tiasmTodo contained TODO FIXME XXX NOTE
+syn region tiasmComment start=";" end="$" keepend contains=tiasmTodo,@Spell
+syn match tiasmComment "^[*!].*" contains=tiasmTodo,@Spell
+syn match tiasmLabel "^[^ *!;][^ :]*"
+
+syn match tiasmInclude "\.include"
+syn match tiasmCond "\.if"
+syn match tiasmCond "\.else"
+syn match tiasmCond "\.endif"
+syn match tiasmMacro "\.macro"
+syn match tiasmMacro "\.endm"
+
+syn match tiasmDirective "\.[A-Za-z][0-9A-Za-z-_]*"
+
+syn case match
+
+hi def link tiasmLabel Label
+hi def link tiasmComment Comment
+hi def link tiasmTodo Todo
+hi def link tiasmDirective Statement
+
+hi def link tiasmInclude Include
+hi def link tiasmCond PreCondit
+hi def link tiasmMacro Macro
+
+if exists('g:tiasm_legacy_syntax_groups')
+ hi def link hexNumber Number
+ hi def link decNumber Number
+ hi def link octNumber Number
+ hi def link binNumber Number
+ hi def link tiasmHexadecimal hexNumber
+ hi def link tiasmDecimal decNumber
+ hi def link tiasmOctal octNumber
+ hi def link tiasmBinary binNumber
+else
+ hi def link tiasmHexadecimal Number
+ hi def link tiasmDecimal Number
+ hi def link tiasmOctal Number
+ hi def link tiasmBinary Number
+endif
+hi def link tiasmFloat Float
+
+hi def link tiasmString String
+hi def link tiasmStringEscape Special
+hi def link tiasmCharacter Character
+hi def link tiasmCharacterEscape Special
+
+hi def link tiasmIdentifier Identifier
+hi def link tiasmType Type
+hi def link tiasmFunction Function
+
+let b:current_syntax = "tiasm"
diff --git a/runtime/syntax/typst.vim b/runtime/syntax/typst.vim
index dae1424780..b1f05ec853 100644
--- a/runtime/syntax/typst.vim
+++ b/runtime/syntax/typst.vim
@@ -1,7 +1,8 @@
" Vim syntax file
" Language: Typst
-" Maintainer: Gregory Anders <greg@gpanders.com>
-" Last Change: 2024 Nov 02
+" Previous Maintainer: Gregory Anders
+" Maintainer: Luca Saccarola <github.e41mv@aleeas.com>
+" Last Change: 2024 Dec 09
" Based on: https://github.com/kaarmu/typst.vim
if exists('b:current_syntax')
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 3ad04e2957..c4e231d145 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -60,10 +60,10 @@ 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 ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC CursorIM LineNrAbove LineNrBelow
+syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel ComplMatchIns Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC CursorIM LineNrAbove LineNrBelow
syn match vimHLGroup contained "\<Conceal\>"
syn keyword vimOnlyHLGroup contained Menu Scrollbar ToolbarButton ToolbarLine Tooltip VisualNOS
-syn keyword nvimHLGroup contained FloatBorder FloatFooter FloatTitle MsgSeparator NormalFloat NormalNC Substitute TermCursor TermCursorNC VisualNC Whitespace WinBar WinBarNC WinSeparator
+syn keyword nvimHLGroup contained FloatBorder FloatFooter FloatTitle MsgSeparator NormalFloat NormalNC Substitute TermCursor VisualNC Whitespace WinBar WinBarNC WinSeparator
"}}}2
syn case match
@@ -185,20 +185,20 @@ Vim9 syn keyword vim9Boolean true false
" Numbers {{{2
" =======
syn case ignore
-syn match vimNumber '\<\d\+\%(\.\d\+\%(e[+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
-syn match vimNumber '\<0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
-syn match vimNumber '\<0o\=\o\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
-syn match vimNumber '\<0x\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
-syn match vimNumber '\<0z\>' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
-syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
-syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment
+syn match vimNumber '\<\d\+\%(\.\d\+\%(e[+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
+syn match vimNumber '\<0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
+syn match vimNumber '\<0o\=\o\+' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
+syn match vimNumber '\<0x\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
+syn match vimNumber '\<0z\>' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
+syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
+syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment
syn case match
" All vimCommands are contained by vimIsCommand. {{{2
syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimCall,vimCatch,vimConst,vimDef,vimDefFold,vimDelcommand,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimFuncFold,vimGlobal,vimHighlight,vimLet,vimLoadkeymap,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimSet,vimSleep,vimSyntax,vimThrow,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate,@vim9CmdList
-syn cluster vim9CmdList contains=vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
+syn cluster vim9CmdList contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var
syn match vimCmdSep "[:|]\+" skipwhite nextgroup=@vimCmdList,vimSubst1
-syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand
+syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" nextgroup=vimBang contains=vimCommand
syn match vimBang contained "!"
syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>"
syn match vimVar "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
@@ -207,7 +207,6 @@ syn match vimVar "\s\zs&t_\S[a-zA-Z0-9]\>"
syn match vimVar "\s\zs&t_k;"
syn match vimFBVar contained "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>"
syn keyword vimCommand contained in
-syn match vimBang contained "!"
syn cluster vimExprList contains=vimEnvvar,vimFunc,vimNumber,vimOper,vimOperParen,vimLetRegister,vimString,vimVar,@vim9ExprList
syn cluster vim9ExprList contains=vim9Boolean,vim9Null
@@ -275,9 +274,16 @@ syn keyword vimAugroupKey contained aug[roup] skipwhite nextgroup=vimAugroupBan
" Operators: {{{2
" =========
syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimRegister,@vimContinue,vim9Comment,vimVar,vimBoolean,vimNull
-syn match vimOper "||\|&&\|[-+*/%.!]" skipwhite nextgroup=vimString,vimSpecFile
-syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\|!\~#\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile
-syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "\a\@<!!" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "||\|&&\|[-+*/%.]" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "?" skipwhite nextgroup=@vimExprList
+" distinguish ternary : from ex-colon
+syn match vimOper "\s\@1<=:\ze\s\|\s\@1<=:$" skipwhite nextgroup=@vimExprList
+syn match vimOper "??" skipwhite nextgroup=@vimExprList
+syn match vimOper "=" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "\%#=1\%(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\)[?#]\=" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "\<is\%(not\)\=\>" skipwhite nextgroup=vimString,vimSpecFile
+syn match vimOper "\<is\%(not\)\=[?#]" skipwhite nextgroup=vimString,vimSpecFile
syn region vimOperParen matchgroup=vimParenSep start="(" end=")" contains=@vimOperGroup
syn region vimOperParen matchgroup=vimSep start="#\={" end="}" contains=@vimOperGroup nextgroup=vimVar,vimFuncVar
if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_noopererror")
@@ -289,9 +295,9 @@ endif
syn cluster vimFuncList contains=vimFuncBang,vimFunctionError,vimFuncKey,vimFuncSID,Tag
syn cluster vimDefList contains=vimFuncBang,vimFunctionError,vimDefKey,vimFuncSID,Tag
-syn cluster vimFuncBodyCommon contains=@vimCmdList,vimCmplxRepeat,vimContinue,vimCtrlChar,vimDef,vimEnvvar,vimFBVar,vimFunc,vimFunction,vimLetHereDoc,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegister,vimSearch,vimSpecFile,vimString,vimSubst,vimFuncFold,vimDefFold
-syn cluster vimFuncBodyList contains=@vimFuncBodyCommon,vimComment,vimLineComment,vimFuncVar,vimInsert,vimConst,vimLet
-syn cluster vimDefBodyList contains=@vimFuncBodyCommon,vim9Comment,vim9LineComment,vim9Const,vim9Final,vim9Var,vim9Null,vim9Boolean,vim9For
+syn cluster vimFuncBodyCommon contains=@vimCmdList,vimCmplxRepeat,vimContinue,vimCtrlChar,vimDef,vimEnvvar,vimFBVar,vimFunc,vimFunction,vimLetHereDoc,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegister,vimSpecFile,vimString,vimSubst,vimFuncFold,vimDefFold
+syn cluster vimFuncBodyList contains=@vimFuncBodyCommon,vimComment,vimLineComment,vimFuncVar,vimInsert,vimConst,vimLet,vimSearch
+syn cluster vimDefBodyList contains=@vimFuncBodyCommon,vim9Comment,vim9LineComment,vim9Const,vim9Final,vim9Var,vim9Null,vim9Boolean,vim9For,vim9Search
syn region vimFuncPattern contained matchgroup=vimOper start="/" end="$" contains=@vimSubstList
syn match vimFunction "\<fu\%[nction]\>" skipwhite nextgroup=vimCmdSep,vimComment,vimFuncPattern contains=vimFuncKey
@@ -309,8 +315,8 @@ syn match vimFuncSID contained "\<[sg]:"
syn keyword vimFuncKey contained fu[nction]
syn keyword vimDefKey contained def
-syn region vimFuncParams contained matchgroup=Delimiter start="(" skip=+\n\s*\\\|\n\s*"\\ + end=")" skipwhite skipempty nextgroup=vimFuncBody,vimFuncComment,vimEndfunction,vimFuncMod,vim9CommentError contains=vimFuncParam,@vimContinue
-syn region vimDefParams contained matchgroup=Delimiter start="(" end=")" skipwhite skipempty nextgroup=vimDefBody,vimDefComment,vimEnddef,vimReturnType,vimCommentError contains=vimDefParam,vim9Comment,vimFuncParamEquals
+syn region vimFuncParams contained matchgroup=Delimiter start="(" skip=+\n\s*\\\|\n\s*"\\ + end=")" skipwhite skipempty nextgroup=vimFuncBody,vimFuncComment,vimEndfunction,vimFuncMod,vim9CommentError contains=vimFuncParam,vimOperParen,@vimContinue
+syn region vimDefParams contained matchgroup=Delimiter start="(" end=")" skipwhite skipempty nextgroup=vimDefBody,vimDefComment,vimEnddef,vimReturnType,vimCommentError contains=vimDefParam,vim9Comment,vimFuncParamEquals,vimOperParen
syn match vimFuncParam contained "\<\h\w*\>\|\.\.\." skipwhite nextgroup=vimFuncParamEquals
syn match vimDefParam contained "\<\h\w*\>" skipwhite nextgroup=vimParamType,vimFuncParamEquals
@@ -553,19 +559,21 @@ syn region vimPatSepZone oneline contained matchgroup=vimPatSepZ start="\\%\
syn region vimPatRegion contained transparent matchgroup=vimPatSepR start="\\[z%]\=(" end="\\)" contains=@vimSubstList oneline
syn match vimNotPatSep contained "\\\\"
syn cluster vimStringGroup contains=vimEscape,vimEscapeBrace,vimPatSep,vimNotPatSep,vimPatSepErr,vimPatSepZone,@Spell
-syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend
-syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]'+lc=1 end=+'+ extend
+syn region vimString oneline keepend matchgroup=vimString start=+[^a-zA-Z>\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend
+syn region vimString oneline matchgroup=vimString start=+[^a-zA-Z>\\@]'+lc=1 end=+'+ contains=vimQuoteEscape extend
"syn region vimString oneline start="\s/\s*\A"lc=1 skip="\\\\\|\\+" end="/" contains=@vimStringGroup " see tst45.vim
syn match vimString contained +"[^"]*\\$+ skipnl nextgroup=vimStringCont
syn match vimStringCont contained +\(\\\\\|.\)\{-}[^\\]"+
+
syn match vimEscape contained "\\."
" syn match vimEscape contained +\\[befnrt\"]+
syn match vimEscape contained "\\\o\{1,3}\|\\[xX]\x\{1,2}\|\\u\x\{1,4}\|\\U\x\{1,8}"
syn match vimEscape contained "\\<" contains=vimNotation
syn match vimEscape contained "\\<\*[^>]*>\=>"
+syn match vimQuoteEscape contained "''"
-syn region vimString oneline start=+$'+ skip=+''+ end=+'+ contains=@vimStringInterpolation extend
-syn region vimString oneline start=+$"+ end=+"+ contains=@vimStringGroup,@vimStringInterpolation extend
+syn region vimString oneline matchgroup=vimString start=+$'+ skip=+''+ end=+'+ contains=vimQuoteEscape,@vimStringInterpolation extend
+syn region vimString oneline matchgroup=vimString start=+$"+ end=+"+ contains=@vimStringGroup,@vimStringInterpolation extend
syn region vimStringInterpolationExpr oneline contained matchgroup=vimSep start=+{+ end=+}+ contains=@vimExprList
syn match vimStringInterpolationBrace contained "{{"
syn match vimStringInterpolationBrace contained "}}"
@@ -678,10 +686,12 @@ syn keyword vimAbb abc[lear] cabc[lear] iabc[lear] skipwhite nextgroup=vimMapMod
" Autocmd: {{{2
" =======
-syn match vimAutoEventList contained "\(!\s\+\)\=\(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace
+syn match vimAutoCmdBang contained "\a\@1<=!" skipwhite nextgroup=vimAutoEventList
+syn match vimAutoEventList contained "\%(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace
syn match vimAutoCmdSpace contained "\s\+" nextgroup=vimAutoCmdSfxList
syn match vimAutoCmdSfxList contained "\S*" skipwhite nextgroup=vimAutoCmdMod,vimAutoCmdBlock
-syn keyword vimAutoCmd au[tocmd] do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList
+syn keyword vimAutoCmd au[tocmd] skipwhite nextgroup=vimAutoCmdBang,vimAutoEventList
+syn keyword vimAutoCmd do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList
syn match vimAutoCmdMod "\(++\)\=\(once\|nested\)" skipwhite nextgroup=vimAutoCmdBlock
syn region vimAutoCmdBlock contained matchgroup=vimSep start="{" end="}" contains=@vimDefBodyList
@@ -1013,8 +1023,10 @@ syn match vim9CommentTitleLeader '#\s\+'ms=s+1 contained
" Searches And Globals: {{{2
" ====================
-syn match vimSearch '^\s*[/?].*' contains=vimSearchDelim
+VimL syn match vimSearch '^\s*[/?].*' contains=vimSearchDelim
syn match vimSearchDelim '^\s*\zs[/?]\|[/?]$' contained
+Vim9 syn match vim9Search '^\s*:[/?].*' contains=vim9SearchDelim
+syn match vim9SearchDelim '^\s*\zs:[/?]\|[/?]$' contained contains=vimCmdSep
syn region vimGlobal matchgroup=Statement start='\<g\%[lobal]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1
syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1
@@ -1275,6 +1287,7 @@ if !exists("skip_vim_syntax_inits")
hi def link vimAugroupError vimError
hi def link vimAugroupKey vimCommand
hi def link vimAutoCmd vimCommand
+ hi def link vimAutoCmdBang vimBang
hi def link vimAutoEvent Type
hi def link vimAutoCmdMod Special
hi def link vimBang vimOper
@@ -1396,10 +1409,11 @@ if !exists("skip_vim_syntax_inits")
hi def link vimPattern Type
hi def link vimPlainMark vimMark
hi def link vimPlainRegister vimRegister
+ hi def link vimQuoteEscape vimEscape
hi def link vimRegister SpecialChar
hi def link vimScriptDelim Comment
- hi def link vimSearchDelim Statement
hi def link vimSearch vimString
+ hi def link vimSearchDelim Delimiter
hi def link vimSep Delimiter
hi def link vimSet vimCommand
hi def link vimSetAll vimOption
@@ -1492,6 +1506,8 @@ if !exists("skip_vim_syntax_inits")
hi def link vim9MethodNameError vimFunctionError
hi def link vim9Null Constant
hi def link vim9Public vimCommand
+ hi def link vim9Search vimString
+ hi def link vim9SearchDelim Delimiter
hi def link vim9Static vimCommand
hi def link vim9Super Identifier
hi def link vim9This Identifier
diff --git a/runtime/syntax/xf86conf.vim b/runtime/syntax/xf86conf.vim
index e8162f3a35..0f4e5036ff 100644
--- a/runtime/syntax/xf86conf.vim
+++ b/runtime/syntax/xf86conf.vim
@@ -1,9 +1,9 @@
" Vim syntax file
" Language: XF86Config (XFree86 configuration file)
+" Maintainer: This runtime file is looking for a new maintainer.
+" Last Change: 2025 Jan 06 by Jan-Arvid Harrach (#16397)
" Former Maintainer: David Ne\v{c}as (Yeti) <yeti@physics.muni.cz>
" Last Change By David: 2010 Nov 01
-" Last Change: 2023 Jan 23
-" Required Vim Version: 6.0
"
" Options: let xf86conf_xfree86_version = 3 or 4
" to force XFree86 3.x or 4.x XF86Config syntax
@@ -58,7 +58,7 @@ syn match xf86confModeLineValue "\"[^\"]\+\"\(\_s\+[0-9.]\+\)\{9}" nextgroup=xf8
" Sections and subsections
if b:xf86conf_xfree86_version >= 4
- syn region xf86confSection matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\)\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confComment,xf86confOption,xf86confKeyword,xf86confSectionError
+ syn region xf86confSection matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\|OutputClass\)\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confComment,xf86confOption,xf86confKeyword,xf86confSectionError
syn region xf86confSectionModule matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Module\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionAny,xf86confComment,xf86confOption,xf86confKeyword
syn region xf86confSectionMonitor matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Monitor\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionMode,xf86confModeLine,xf86confComment,xf86confOption,xf86confKeyword
syn region xf86confSectionModes matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Modes\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionMode,xf86confModeLine,xf86confComment
@@ -162,7 +162,7 @@ syn match xf86confSync "\(\s\+[+-][CHV]_*Sync\)\+" contained
" Synchronization
if b:xf86conf_xfree86_version >= 4
- syn sync match xf86confSyncSection grouphere xf86confSection "^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\)\""
+ syn sync match xf86confSyncSection grouphere xf86confSection "^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\|OutputClass\)\""
syn sync match xf86confSyncSectionModule grouphere xf86confSectionModule "^\s*Section\s\+\"Module\""
syn sync match xf86confSyncSectionModes groupthere xf86confSectionModes "^\s*Section\s\+\"Modes\""
else
diff --git a/runtime/syntax/zsh.vim b/runtime/syntax/zsh.vim
index 084f8cdb41..04b39aeac0 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: 2022-07-26
+" Latest Revision: 2024 Jan 04
" License: Vim (see :h license)
" Repository: https://github.com/chrisbra/vim-zsh
@@ -48,8 +48,9 @@ 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,@zshSubstQuoted fold
+ \ contains=@Spell,zshQuoted,@zshDerefs,@zshSubstQuoted fold
syn region zshString matchgroup=zshStringDelimiter start=+'+ end=+'+ fold
+ \ contains=@Spell
syn region zshPOSIXString matchgroup=zshStringDelimiter start=+\$'+
\ skip=+\\[\\']+ end=+'+ contains=zshPOSIXQuoted,zshQuoted
syn match zshJobSpec '%\(\d\+\|?\=\w\+\|[%+-]\)'
@@ -68,7 +69,7 @@ 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 keyword zshCaseIn in nextgroup=zshComment,zshCasePattern skipwhite skipnl contained
syn match zshCasePattern /\S[^)]*)/ contained
syn keyword zshRepeat while until repeat
@@ -94,22 +95,24 @@ syn match zshRedir '|\@1<!|&\=|\@!'
syn region zshHereDoc matchgroup=zshRedir
\ start='<\@<!<<\s*\z([^<]\S*\)'
- \ end='^\z1\>'
- \ contains=@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString
+ \ end='^\z1$'
+ \ contains=@Spell,@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString
syn region zshHereDoc matchgroup=zshRedir
\ start='<\@<!<<\s*\\\z(\S\+\)'
- \ end='^\z1\>'
- \ contains=@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString
+ \ end='^\z1$'
+ \ contains=@Spell
syn region zshHereDoc matchgroup=zshRedir
\ start='<\@<!<<-\s*\\\=\z(\S\+\)'
- \ end='^\s*\z1\>'
- \ contains=@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString
+ \ end='^\t*\z1$'
+ \ contains=@Spell
syn region zshHereDoc matchgroup=zshRedir
\ start=+<\@<!<<\s*\(["']\)\z(\S\+\)\1+
- \ end='^\z1\>'
+ \ end='^\z1$'
+ \ contains=@Spell
syn region zshHereDoc matchgroup=zshRedir
\ start=+<\@<!<<-\s*\(["']\)\z(\S\+\)\1+
- \ end='^\s*\z1\>'
+ \ end='^\t*\z1$'
+ \ contains=@Spell
syn match zshVariable '\<\h\w*' contained
diff --git a/scripts/bump_deps.lua b/scripts/bump_deps.lua
index ad71da5150..a44e4a2d5e 100755
--- a/scripts/bump_deps.lua
+++ b/scripts/bump_deps.lua
@@ -3,44 +3,50 @@
-- Usage:
-- ./scripts/bump_deps.lua -h
-local M = {}
+assert(vim.fn.executable('gh') == 1)
+assert(vim.fn.executable('sed') == 1)
-local _trace = false
local required_branch_prefix = 'bump-'
local commit_prefix = 'build(deps): '
--- Print message
-local function p(s)
- vim.cmd('set verbose=1')
- vim.api.nvim_echo({ { s, '' } }, false, {})
- vim.cmd('set verbose=0')
-end
-
-local function die()
- p('')
+local repos = {
+ 'luajit/luajit',
+ 'libuv/libuv',
+ 'luvit/luv',
+ 'neovim/unibilium',
+ 'juliastrings/utf8proc',
+ 'tree-sitter/tree-sitter',
+ 'tree-sitter/tree-sitter-c',
+ 'tree-sitter-grammars/tree-sitter-lua',
+ 'tree-sitter-grammars/tree-sitter-vim',
+ 'neovim/tree-sitter-vimdoc',
+ 'tree-sitter-grammars/tree-sitter-query',
+ 'tree-sitter-grammars/tree-sitter-markdown',
+ 'bytecodealliance/wasmtime',
+ 'uncrustify/uncrustify',
+}
+
+local dependency_table = {} --- @type table<string, string>
+for _, repo in pairs(repos) do
+ dependency_table[vim.fs.basename(repo)] = repo
+end
+
+local function die(msg)
+ print(msg)
vim.cmd('cquit 1')
end
-- Executes and returns the output of `cmd`, or nil on failure.
-- if die_on_fail is true, process dies with die_msg on failure
---
--- Prints `cmd` if `trace` is enabled.
local function _run(cmd, die_on_fail, die_msg)
- if _trace then
- p('run: ' .. vim.inspect(cmd))
- end
- local rv = vim.trim(vim.fn.system(cmd)) or ''
- if vim.v.shell_error ~= 0 then
+ local rv = vim.system(cmd):wait()
+ if rv.code ~= 0 then
if die_on_fail then
- if _trace then
- p(rv)
- end
- p(die_msg)
- die()
+ die(die_msg)
end
return nil
end
- return rv
+ return vim.trim(rv.stdout)
end
-- Run a command, return nil on failure
@@ -53,108 +59,20 @@ local function run_die(cmd, err_msg)
return _run(cmd, true, err_msg)
end
-local function require_executable(cmd)
- local cmd_path = run_die({ 'sh', '-c', 'command -v ' .. cmd }, cmd .. ' not found!')
- run_die({ 'test', '-x', cmd_path }, cmd .. ' is not executable')
-end
-
-local function rm_file_if_present(path_to_file)
- run({ 'rm', '-f', path_to_file })
-end
-
-local nvim_src_dir = vim.fn.getcwd()
+local nvim_src_dir = run({ 'git', 'rev-parse', '--show-toplevel' })
local deps_file = nvim_src_dir .. '/' .. 'cmake.deps/deps.txt'
-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',
- },
- ['unibilium'] = {
- repo = 'neovim/unibilium',
- symbol = 'UNIBILIUM',
- },
- ['utf8proc'] = {
- repo = 'JuliaStrings/utf8proc',
- symbol = 'UTF8PROC',
- },
- ['tree-sitter'] = {
- repo = 'tree-sitter/tree-sitter',
- symbol = 'TREESITTER',
- },
- ['tree-sitter-c'] = {
- repo = 'tree-sitter/tree-sitter-c',
- symbol = 'TREESITTER_C',
- },
- ['tree-sitter-lua'] = {
- repo = 'tree-sitter-grammars/tree-sitter-lua',
- symbol = 'TREESITTER_LUA',
- },
- ['tree-sitter-vim'] = {
- repo = 'tree-sitter-grammars/tree-sitter-vim',
- symbol = 'TREESITTER_VIM',
- },
- ['tree-sitter-vimdoc'] = {
- repo = 'neovim/tree-sitter-vimdoc',
- symbol = 'TREESITTER_VIMDOC',
- },
- ['tree-sitter-query'] = {
- repo = 'tree-sitter-grammars/tree-sitter-query',
- symbol = 'TREESITTER_QUERY',
- },
- ['tree-sitter-markdown'] = {
- repo = 'tree-sitter-grammars/tree-sitter-markdown',
- symbol = 'TREESITTER_MARKDOWN',
- },
- ['wasmtime'] = {
- repo = 'bytecodealliance/wasmtime',
- symbol = 'WASMTIME',
- },
- ['uncrustify'] = {
- repo = 'uncrustify/uncrustify',
- symbol = 'UNCRUSTIFY',
- },
- }
- local dependency = dependency_table[dependency_name]
- if dependency == nil then
- p('Not a dependency: ' .. dependency_name)
- die()
- end
- dependency.name = dependency_name
- return dependency
-end
-
-local function get_gh_commit_sha(repo, ref)
- require_executable('gh')
-
- local sha = run_die(
- { 'gh', 'api', 'repos/' .. repo .. '/commits/' .. ref, '--jq', '.sha' },
- 'Failed to get commit hash from GitHub. Not a valid ref?'
- )
- return sha
-end
+--- @param repo string
+--- @param ref string
local function get_archive_info(repo, ref)
- require_executable('curl')
+ local temp_dir = os.getenv('TMPDIR') or os.getenv('TEMP')
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 },
+ { 'curl', '-sfL', archive_url, '-o', archive_path },
'Failed to download archive from GitHub'
)
@@ -166,9 +84,24 @@ local function get_archive_info(repo, ref)
return { url = archive_url, sha = archive_sha }
end
-local function write_cmakelists_line(symbol, kind, value)
- require_executable('sed')
+local function get_gh_commit_sha(repo, ref)
+ local full_repo = string.format('https://github.com/%s.git', repo)
+ local tag_exists = run_die({ 'git', 'ls-remote', full_repo, 'refs/tags/' .. ref }) ~= ''
+ -- We'd rather use the git tag over commit sha if possible
+ if tag_exists then
+ return ref
+ end
+
+ local sha = assert(
+ 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 update_deps_file(symbol, kind, value)
run_die({
'sed',
'-i',
@@ -178,290 +111,103 @@ local function write_cmakelists_line(symbol, kind, value)
}, 'Failed to write ' .. deps_file)
end
-local function explicit_create_branch(dep)
- require_executable('git')
+local function ref(name, _ref)
+ local repo = dependency_table[name]
+ local symbol = string.gsub(name, 'tree%-sitter', 'treesitter'):gsub('%-', '_'):upper()
- 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
+ run_die(
+ { 'git', 'diff', '--quiet', 'HEAD', '--', deps_file },
+ deps_file .. ' has uncommitted changes'
+ )
-local function verify_branch(new_branch_suffix)
- require_executable('git')
+ _ref = get_gh_commit_sha(repo, _ref)
+
+ local archive = get_archive_info(repo, _ref)
+ local comment = string.sub(_ref, 1, 9)
local checked_out_branch = assert(run({ 'git', 'rev-parse', '--abbrev-ref', 'HEAD' }))
if not checked_out_branch:match('^' .. required_branch_prefix) then
- p(
+ print(
"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)
+ print('Checking out to bump-' .. name)
+ run_die({ 'git', 'checkout', '-b', 'bump-' .. name }, 'git failed to create branch')
end
-end
-
-local function update_cmakelists(dependency, archive, comment)
- require_executable('git')
- verify_branch(dependency.name)
-
- 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)
+ print('Updating ' .. name .. ' to ' .. archive.url .. '\n')
+ update_deps_file(symbol, 'URL', archive.url:gsub('/', '\\/'))
+ update_deps_file(symbol, 'SHA256', archive.sha)
run_die({
'git',
'commit',
deps_file,
'-m',
- commit_prefix .. 'bump ' .. dependency.name .. ' to ' .. comment,
+ commit_prefix .. 'bump ' .. name .. ' to ' .. comment,
}, 'git failed to commit')
end
-local function verify_cmakelists_committed()
- require_executable('git')
-
- run_die(
- { 'git', 'diff', '--quiet', 'HEAD', '--', deps_file },
- deps_file .. ' has uncommitted changes'
- )
-end
-
-local function warn_luv_symbol()
- p('warning: ' .. get_dependency('Luv').symbol .. '_VERSION will not be updated')
-end
-
--- return first 9 chars of commit
-local function short_commit(commit)
- return string.sub(commit, 1, 9)
-end
-
--- TODO: remove hardcoded fork
-local function gh_pr(pr_title, pr_body)
- require_executable('gh')
-
- local pr_url = run_die({
- 'gh',
- 'pr',
- 'create',
- '--title',
- pr_title,
- '--body',
- pr_body,
- }, 'Failed to create PR')
- return pr_url
-end
-
-local function find_git_remote(fork)
- require_executable('git')
-
- local remotes = 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')
-end
-
-function M.commit(dependency_name, 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)
- vim.validate('dependency_name', dependency_name, 'string')
- vim.validate('version', version, 'string')
- 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 = 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)
-end
-
-function M.submit_pr()
- 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(([=[
+ local this_script = tostring(vim.fs.basename(_G.arg[0]))
+ local script_exe = './' .. this_script
+ local help = ([=[
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
+ Usage: %s [options]
+ Bump to HEAD, tagged version or commit:
+ %s luv --head
+ %s luv --ref 1.43.0-0
+ %s luv --ref abc123
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.
+ -h, --help show this message and exit.
+ --list list all dependencies
Dependency Options:
- --version <tag> bump to a specific release or tag.
- --commit <hash> bump to a specific commit.
- --HEAD bump to a current head.
+ --ref <ref> bump to a specific commit or tag.
+ --head bump to a current head.
+ ]=]):format(script_exe, script_exe, script_exe, script_exe)
+ print(help)
+end
- <dependency> is one of:
- "LuaJIT", "libuv", "Luv", "tree-sitter"
- ]=]):format(this_script, this_script, this_script, this_script, this_script, this_script))
+local function list_deps()
+ local l = 'Dependencies:\n'
+ for k in vim.spairs(dependency_table) do
+ l = string.format('%s\n%s%s', l, string.rep(' ', 2), k)
+ end
+ print(l)
end
-local function parseargs()
+do
local args = {}
- for i = 1, #_G.arg do
- if _G.arg[i] == '-h' then
+ local i = 1
+ while i <= #_G.arg do
+ if _G.arg[i] == '-h' or _G.arg[i] == '--help' 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] == '--list' then
+ args.list = true
+ elseif _G.arg[i] == '--ref' then
+ args.ref = _G.arg[i + 1]
+ i = i + 1
elseif _G.arg[i] == '--head' then
- args.head = true
+ args.ref = 'HEAD'
+ elseif vim.startswith(_G.arg[i], '--') then
+ die(string.format('Invalid argument %s\n', _G.arg[i]))
+ else
+ args.dep = _G.arg[i]
end
+ i = i + 1
end
- return args
-end
-
-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()
+ elseif args.list then
+ list_deps()
+ elseif args.ref then
+ ref(args.dep, args.ref)
else
- print('missing required arg\n')
- os.exit(1)
+ die('missing required arg\n')
end
-else
- return M
end
diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua
index a9431ae2e5..aaf76a0411 100755
--- a/scripts/gen_eval_files.lua
+++ b/scripts/gen_eval_files.lua
@@ -26,11 +26,11 @@ local LUA_API_RETURN_OVERRIDES = {
nvim_buf_get_command = 'table<string,vim.api.keyset.command_info>',
nvim_buf_get_extmark_by_id = 'vim.api.keyset.get_extmark_item_by_id',
nvim_buf_get_extmarks = 'vim.api.keyset.get_extmark_item[]',
- nvim_buf_get_keymap = 'vim.api.keyset.keymap[]',
+ nvim_buf_get_keymap = 'vim.api.keyset.get_keymap[]',
nvim_get_autocmds = 'vim.api.keyset.get_autocmds.ret[]',
nvim_get_color_map = 'table<string,integer>',
nvim_get_command = 'table<string,vim.api.keyset.command_info>',
- nvim_get_keymap = 'vim.api.keyset.keymap[]',
+ nvim_get_keymap = 'vim.api.keyset.get_keymap[]',
nvim_get_mark = 'vim.api.keyset.get_mark',
-- Can also return table<string,vim.api.keyset.get_hl_info>, however we need to
@@ -47,6 +47,18 @@ local LUA_API_RETURN_OVERRIDES = {
nvim_win_get_config = 'vim.api.keyset.win_config',
}
+local LUA_API_KEYSET_OVERRIDES = {
+ create_autocmd = {
+ callback = 'string|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean?)',
+ },
+}
+
+local LUA_API_PARAM_OVERRIDES = {
+ nvim_create_user_command = {
+ command = 'string|fun(args: vim.api.keyset.create_user_command.command_args)',
+ },
+}
+
local LUA_META_HEADER = {
'--- @meta _',
'-- THIS FILE IS GENERATED',
@@ -60,6 +72,10 @@ local LUA_API_META_HEADER = {
'-- DO NOT EDIT',
"error('Cannot require a meta file')",
'',
+ '--- This file embeds vimdoc as the function descriptions',
+ '--- so ignore any doc related errors.',
+ '--- @diagnostic disable: undefined-doc-name,luadoc-miss-symbol',
+ '',
'vim.api = {}',
}
@@ -118,10 +134,19 @@ local API_TYPES = {
LuaRef = 'function',
Dict = 'table<string,any>',
Float = 'number',
- HLGroupID = 'number|string',
+ HLGroupID = 'integer|string',
void = '',
}
+--- @param s string
+--- @return string
+local function luaescape(s)
+ if LUA_KEYWORDS[s] then
+ return s .. '_'
+ end
+ return s
+end
+
--- @param x string
--- @param sep? string
--- @return string[]
@@ -133,6 +158,10 @@ end
--- @param t string
--- @return string
local function api_type(t)
+ if vim.startswith(t, '*') then
+ return api_type(t:sub(2)) .. '?'
+ end
+
local as0 = t:match('^ArrayOf%((.*)%)')
if as0 then
local as = split(as0, ', ')
@@ -149,6 +178,33 @@ local function api_type(t)
return 'table<string,' .. api_type(d0) .. '>'
end
+ local u = t:match('^Union%((.*)%)')
+ if u then
+ local us = vim.split(u, ',%s*')
+ return table.concat(vim.tbl_map(api_type, us), '|')
+ end
+
+ local l = t:match('^LuaRefOf%((.*)%)')
+ if l then
+ --- @type string
+ l = l:gsub('%s+', ' ')
+ --- @type string?, string?
+ local as, r = l:match('%((.*)%),%s*(.*)')
+ if not as then
+ --- @type string
+ as = assert(l:match('%((.*)%)'))
+ end
+
+ local as1 = {} --- @type string[]
+ for a in vim.gsplit(as, ',%s') do
+ local a1 = vim.split(a, '%s+', { trimempty = true })
+ local nm = a1[2]:gsub('%*(.*)$', '%1?')
+ as1[#as1 + 1] = nm .. ': ' .. api_type(a1[1])
+ end
+
+ return ('fun(%s)%s'):format(table.concat(as1, ', '), r and ': ' .. api_type(r) or '')
+ end
+
return API_TYPES[t] or t
end
@@ -165,7 +221,7 @@ local function render_fun_sig(f, params)
--- @param v [string,string]
--- @return string
function(v)
- return v[1]
+ return luaescape(v[1])
end,
params
),
@@ -181,7 +237,6 @@ local function render_fun_sig(f, params)
end
--- Uniquify names
---- Fix any names that are lua keywords
--- @param params [string,string,string][]
--- @return [string,string,string][]
local function process_params(params)
@@ -189,9 +244,6 @@ local function process_params(params)
local sfx = 1
for _, p in ipairs(params) do
- if LUA_KEYWORDS[p[1]] then
- p[1] = p[1] .. '_'
- end
if seen[p[1]] then
p[1] = p[1] .. sfx
sfx = sfx + 1
@@ -251,11 +303,13 @@ local function get_api_meta()
sees[#sees + 1] = see.desc
end
+ local pty_overrides = LUA_API_PARAM_OVERRIDES[fun.name] or {}
+
local params = {} --- @type [string,string][]
for _, p in ipairs(fun.params) do
params[#params + 1] = {
p.name,
- api_type(p.type),
+ api_type(pty_overrides[p.name] or p.type),
not deprecated and p.desc or nil,
}
end
@@ -344,10 +398,10 @@ local function render_api_meta(_f, fun, write)
local param_names = {} --- @type string[]
local params = process_params(fun.params)
for _, p in ipairs(params) do
- param_names[#param_names + 1] = p[1]
- local pdesc = p[3]
+ local pname, ptype, pdesc = luaescape(p[1]), p[2], p[3]
+ param_names[#param_names + 1] = pname
if pdesc then
- local s = '--- @param ' .. p[1] .. ' ' .. p[2] .. ' '
+ local s = '--- @param ' .. pname .. ' ' .. ptype .. ' '
local pdesc_a = split(vim.trim(norm_text(pdesc)))
write(s .. pdesc_a[1])
for i = 2, #pdesc_a do
@@ -357,7 +411,7 @@ local function render_api_meta(_f, fun, write)
write('--- ' .. pdesc_a[i])
end
else
- write('--- @param ' .. p[1] .. ' ' .. p[2])
+ write('--- @param ' .. pname .. ' ' .. ptype)
end
end
@@ -382,9 +436,11 @@ local function get_api_keysets_meta()
local keysets = metadata.keysets
for _, k in ipairs(keysets) do
+ local pty_overrides = LUA_API_KEYSET_OVERRIDES[k.name] or {}
local params = {}
for _, key in ipairs(k.keys) do
- table.insert(params, { key .. '?', api_type(k.types[key] or 'any') })
+ local pty = pty_overrides[key] or k.types[key] or 'any'
+ table.insert(params, { key .. '?', api_type(pty) })
end
ret[k.name] = {
signature = 'NA',
@@ -444,10 +500,14 @@ local function render_eval_meta(f, fun, write)
end
end
+ for _, text in ipairs(vim.fn.reverse(fun.generics or {})) do
+ write(fmt('--- @generic %s', text))
+ end
+
local req_args = type(fun.args) == 'table' and fun.args[1] or fun.args or 0
for i, param in ipairs(params) do
- local pname, ptype = param[1], param[2]
+ local pname, ptype = luaescape(param[1]), param[2]
local optional = (pname ~= '...' and i > req_args) and '?' or ''
write(fmt('--- @param %s%s %s', pname, optional, ptype))
end
@@ -610,7 +670,16 @@ local function render_option_meta(_f, opt, write)
write('--- ' .. l)
end
- write('--- @type ' .. OPTION_TYPES[opt.type])
+ if opt.type == 'string' and not opt.list and opt.values then
+ local values = {} --- @type string[]
+ for _, e in ipairs(opt.values) do
+ values[#values + 1] = fmt("'%s'", e)
+ end
+ write('--- @type ' .. table.concat(values, '|'))
+ else
+ write('--- @type ' .. OPTION_TYPES[opt.type])
+ end
+
write('vim.o.' .. opt.full_name .. ' = ' .. render_option_default(opt.defaults))
if opt.abbreviation then
write('vim.o.' .. opt.abbreviation .. ' = vim.o.' .. opt.full_name)
diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua
index b8f80e94be..53a65fd65f 100644
--- a/scripts/gen_help_html.lua
+++ b/scripts/gen_help_html.lua
@@ -48,7 +48,7 @@ local spell_dict = {
--- specify the list of keywords to ignore (i.e. allow), or true to disable spell check completely.
--- @type table<string, true|string[]>
local spell_ignore_files = {
- ['backers.txt'] = true,
+ ['credits.txt'] = { 'Neovim' },
['news.txt'] = { 'tree-sitter' }, -- in news, may refer to the upstream "tree-sitter" library
['news-0.10.txt'] = { 'tree-sitter' },
}
@@ -70,6 +70,8 @@ local new_layout = {
['dev_vimpatch.txt'] = true,
['editorconfig.txt'] = true,
['faq.txt'] = true,
+ ['gui.txt'] = true,
+ ['intro.txt'] = true,
['lua.txt'] = true,
['luaref.txt'] = true,
['news.txt'] = true,
@@ -84,6 +86,7 @@ local new_layout = {
-- Map of new:old pages, to redirect renamed pages.
local redirects = {
+ ['credits'] = 'backers',
['tui'] = 'term',
['terminal'] = 'nvim_terminal_emulator',
}
@@ -116,7 +119,7 @@ local exclude_invalid_urls = {
-- Deprecated, brain-damaged files that I don't care about.
local ignore_errors = {
['pi_netrw.txt'] = true,
- ['backers.txt'] = true,
+ ['credits.txt'] = true,
}
local function tofile(fname, text)
diff --git a/scripts/gen_vimdoc.lua b/scripts/gen_vimdoc.lua
index 9cd5b598da..d200050fe1 100755
--- a/scripts/gen_vimdoc.lua
+++ b/scripts/gen_vimdoc.lua
@@ -274,6 +274,7 @@ local config = {
'diagnostic.lua',
'codelens.lua',
'completion.lua',
+ 'folding_range.lua',
'inlay_hint.lua',
'tagfunc.lua',
'semantic_tokens.lua',
@@ -349,12 +350,14 @@ local config = {
helptag_fmt = function(name)
if name:lower() == 'treesitter' then
return 'lua-treesitter-core'
+ elseif name:lower() == 'query' then
+ return 'lua-treesitter-query'
elseif name:lower() == 'tstree' then
return { 'treesitter-tree', 'TSTree' }
elseif name:lower() == 'tsnode' then
return { 'treesitter-node', 'TSNode' }
end
- return 'lua-treesitter-' .. name:lower()
+ return 'treesitter-' .. name:lower()
end,
},
editorconfig = {
@@ -514,6 +517,8 @@ local function inline_type(obj, classes)
elseif desc == '' then
if ty_islist then
desc = desc .. 'A list of objects with the following fields:'
+ elseif cls.parent then
+ desc = desc .. fmt('Extends |%s| with the additional fields:', cls.parent)
else
desc = desc .. 'A table with the following fields:'
end
@@ -538,7 +543,8 @@ end
--- @param generics? table<string,string>
--- @param classes? table<string,nvim.luacats.parser.class>
--- @param exclude_types? true
-local function render_fields_or_params(xs, generics, classes, exclude_types)
+--- @param cfg nvim.gen_vimdoc.Config
+local function render_fields_or_params(xs, generics, classes, exclude_types, cfg)
local ret = {} --- @type string[]
xs = vim.tbl_filter(should_render_field_or_param, xs)
@@ -558,7 +564,9 @@ local function render_fields_or_params(xs, generics, classes, exclude_types)
p.desc = pdesc
inline_type(p, classes)
- local nm, ty, desc = p.name, p.type, p.desc
+ local nm, ty = p.name, p.type
+
+ local desc = p.classvar and string.format('See |%s|.', cfg.fn_helptag_fmt(p)) or p.desc
local fnm = p.kind == 'operator' and fmt('op(%s)', nm) or fmt_field_name(nm)
local pnm = fmt(' • %-' .. indent .. 's', fnm)
@@ -591,7 +599,8 @@ end
--- @param class nvim.luacats.parser.class
--- @param classes table<string,nvim.luacats.parser.class>
-local function render_class(class, classes)
+--- @param cfg nvim.gen_vimdoc.Config
+local function render_class(class, classes, cfg)
if class.access or class.nodoc or class.inlinedoc then
return
end
@@ -610,7 +619,7 @@ local function render_class(class, classes)
table.insert(ret, md_to_vimdoc(class.desc, INDENTATION, INDENTATION, TEXT_WIDTH))
end
- local fields_txt = render_fields_or_params(class.fields, nil, classes)
+ local fields_txt = render_fields_or_params(class.fields, nil, classes, nil, cfg)
if not fields_txt:match('^%s*$') then
table.insert(ret, '\n Fields: ~\n')
table.insert(ret, fields_txt)
@@ -621,11 +630,12 @@ local function render_class(class, classes)
end
--- @param classes table<string,nvim.luacats.parser.class>
-local function render_classes(classes)
+--- @param cfg nvim.gen_vimdoc.Config
+local function render_classes(classes, cfg)
local ret = {} --- @type string[]
for _, class in vim.spairs(classes) do
- ret[#ret + 1] = render_class(class, classes)
+ ret[#ret + 1] = render_class(class, classes, cfg)
end
return table.concat(ret)
@@ -656,10 +666,6 @@ local function render_fun_header(fun, cfg)
local proto = fun.table and nm or nm .. '(' .. table.concat(args, ', ') .. ')'
- if not cfg.fn_helptag_fmt then
- cfg.fn_helptag_fmt = fn_helptag_fmt_common
- end
-
local tag = '*' .. cfg.fn_helptag_fmt(fun) .. '*'
if #proto + #tag > TEXT_WIDTH - 8 then
@@ -774,7 +780,8 @@ local function render_fun(fun, classes, cfg)
end
if fun.params and #fun.params > 0 then
- local param_txt = render_fields_or_params(fun.params, fun.generics, classes, cfg.exclude_types)
+ local param_txt =
+ render_fields_or_params(fun.params, fun.generics, classes, cfg.exclude_types, cfg)
if not param_txt:match('^%s*$') then
table.insert(ret, '\n Parameters: ~\n')
ret[#ret + 1] = param_txt
@@ -957,6 +964,7 @@ end
--- @param cfg nvim.gen_vimdoc.Config
local function gen_target(cfg)
+ cfg.fn_helptag_fmt = cfg.fn_helptag_fmt or fn_helptag_fmt_common
print('Target:', cfg.filename)
local sections = {} --- @type table<string,nvim.gen_vimdoc.Section>
@@ -987,7 +995,7 @@ local function gen_target(cfg)
print(' Processing file:', f)
local funs_txt = render_funs(funs, all_classes, cfg)
if next(classes) then
- local classes_txt = render_classes(classes)
+ local classes_txt = render_classes(classes, cfg)
if vim.trim(classes_txt) ~= '' then
funs_txt = classes_txt .. '\n' .. funs_txt
end
diff --git a/scripts/genappimage.sh b/scripts/genappimage.sh
index e8aac42a9c..e683a9dcd1 100755
--- a/scripts/genappimage.sh
+++ b/scripts/genappimage.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/bash -e
########################################################################
# Package the binaries built as an AppImage
@@ -11,6 +11,7 @@ if [ -z "$ARCH" ]; then
ARCH="$(arch)"
export ARCH
fi
+ARCH_ORIGINAL=$ARCH
TAG=$1
@@ -40,16 +41,16 @@ export VERSION
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
- curl -Lo "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage \
- -z "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage \
- https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+if [ -e "$APP_BUILD_DIR"/linuxdeploy-"$ARCH".AppImage ]; then
+ curl -Lo "$APP_BUILD_DIR"/linuxdeploy-"$ARCH".AppImage \
+ -z "$APP_BUILD_DIR"/linuxdeploy-"$ARCH".AppImage \
+ https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-"$ARCH".AppImage
else
- curl -Lo "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage \
- https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+ curl -Lo "$APP_BUILD_DIR"/linuxdeploy-"$ARCH".AppImage \
+ https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-"$ARCH".AppImage
fi
-chmod +x "$APP_BUILD_DIR"/linuxdeploy-x86_64.AppImage
+chmod +x "$APP_BUILD_DIR"/linuxdeploy-"$ARCH".AppImage
# metainfo is not packaged automatically by linuxdeploy
mkdir -p "$APP_DIR/usr/share/metainfo/"
@@ -75,24 +76,30 @@ chmod 755 AppRun
cd "$APP_BUILD_DIR" || exit # Get out of AppImage directory.
+# We want to be consistent, so always use arm64 over aarch64
+if [[ "$ARCH" == 'aarch64' ]]; then
+ ARCH="arm64"
+ export ARCH
+fi
+
# Set the name of the file generated by appimage
-export OUTPUT=nvim.appimage
+export OUTPUT=nvim-linux-"$ARCH".appimage
# If it's a release generate the zsync file
if [ -n "$TAG" ]; then
- export UPDATE_INFORMATION="gh-releases-zsync|neovim|neovim|$TAG|nvim.appimage.zsync"
+ export UPDATE_INFORMATION="gh-releases-zsync|neovim|neovim|$TAG|nvim-linux-$ARCH.appimage.zsync"
fi
# Generate AppImage.
# - 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 \
+# - Produces: ./nvim-linux-$ARCH.appimage
+./linuxdeploy-"$ARCH_ORIGINAL".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
# way for a subsequent build.
-mv "$ROOT_DIR"/build/nvim.appimage* "$ROOT_DIR"/build/bin
+mv "$ROOT_DIR"/build/nvim-linux-"$ARCH".appimage* "$ROOT_DIR"/build/bin
echo 'genappimage.sh: finished'
diff --git a/scripts/luacats_grammar.lua b/scripts/luacats_grammar.lua
index 34c1470fea..b700bcf58f 100644
--- a/scripts/luacats_grammar.lua
+++ b/scripts/luacats_grammar.lua
@@ -160,9 +160,9 @@ local typedef = P({
return vim.trim(match):gsub('^%((.*)%)$', '%1'):gsub('%?+', '?')
end
-local opt_exact = opt(Cg(Pf('(exact)'), 'access'))
local access = P('private') + P('protected') + P('package')
local caccess = Cg(access, 'access')
+local cattr = Cg(comma(access + P('exact')), 'access')
local desc_delim = Sf '#:' + ws
local desc = Cg(rep(any), 'desc')
local opt_desc = opt(desc_delim * desc)
@@ -178,7 +178,7 @@ local grammar = P {
+ annot('type', comma1(Ct(v.ctype)) * opt_desc)
+ annot('cast', ty_name * ws * opt(Sf('+-')) * v.ctype)
+ annot('generic', ty_name * opt(colon * v.ctype))
- + annot('class', opt_exact * opt(paren(caccess)) * fill * ty_name * opt_parent)
+ + annot('class', opt(paren(cattr)) * fill * ty_name * opt_parent)
+ annot('field', opt(caccess * ws) * v.field_name * ws * v.ctype * opt_desc)
+ annot('operator', ty_name * opt(paren(Cg(v.ctype, 'argtype'))) * colon * v.ctype)
+ annot(access)
diff --git a/scripts/luacats_parser.lua b/scripts/luacats_parser.lua
index 9a763e4d7b..8a50077aa8 100644
--- a/scripts/luacats_parser.lua
+++ b/scripts/luacats_parser.lua
@@ -1,9 +1,6 @@
local luacats_grammar = require('scripts.luacats_grammar')
---- @class nvim.luacats.parser.param
---- @field name string
---- @field type string
---- @field desc string
+--- @class nvim.luacats.parser.param : nvim.luacats.Param
--- @class nvim.luacats.parser.return
--- @field name string
@@ -41,21 +38,14 @@ local luacats_grammar = require('scripts.luacats_grammar')
--- @field notes? nvim.luacats.parser.note[]
--- @field see? nvim.luacats.parser.note[]
---- @class nvim.luacats.parser.field
---- @field name string
---- @field type string
---- @field desc string
---- @field access? 'private'|'package'|'protected'
+--- @class nvim.luacats.parser.field : nvim.luacats.Field
+--- @field classvar? string
--- @field nodoc? true
---- @class nvim.luacats.parser.class
---- @field kind 'class'
---- @field parent? string
---- @field name string
---- @field desc string
+--- @class nvim.luacats.parser.class : nvim.luacats.Class
+--- @field desc? string
--- @field nodoc? true
--- @field inlinedoc? true
---- @field access? 'private'|'package'|'protected'
--- @field fields nvim.luacats.parser.field[]
--- @field notes? string[]
@@ -332,7 +322,10 @@ local function process_lua_line(line, state, classes, classvars, has_indent)
end
-- Add method as the field to the class
- table.insert(classes[class].fields, fun2field(cur_obj))
+ local cls = classes[class]
+ local field = fun2field(cur_obj)
+ field.classvar = cur_obj.classvar
+ table.insert(cls.fields, field)
return
end
diff --git a/scripts/vimpatch.lua b/scripts/vimpatch.lua
index cbec50fc17..5d8bea6a98 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 -V1 -es -i NONE +'luafile ./scripts/vimpatch.lua' +q
+-- VIM_SOURCE_DIR=~/neovim/.vim-src/ nvim -l ./scripts/vimpatch.lua
local nvim = vim.api
diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c
index 254355e5a2..e4bd0bcf6b 100644
--- a/src/cjson/lua_cjson.c
+++ b/src/cjson/lua_cjson.c
@@ -173,6 +173,16 @@ typedef struct {
} json_config_t;
typedef struct {
+ const char **char2escape[256];
+} json_encode_options_t;
+
+typedef struct {
+ json_config_t *cfg;
+ json_encode_options_t *options;
+ strbuf_t *json;
+} json_encode_t;
+
+typedef struct {
/* convert null in json objects to lua nil instead of vim.NIL */
bool luanil_object;
/* convert null in json arrays to lua nil instead of vim.NIL */
@@ -209,7 +219,7 @@ static const char *char2escape[256] = {
"\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f",
NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -555,11 +565,11 @@ static void json_create_config(lua_State *l)
/* ===== ENCODING ===== */
-static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex,
+static void json_encode_exception(lua_State *l, json_encode_t *ctx, int lindex,
const char *reason)
{
- if (!cfg->encode_keep_buffer)
- strbuf_free(json);
+ if (!ctx->cfg->encode_keep_buffer)
+ strbuf_free(ctx->json);
luaL_error(l, "Cannot serialise %s: %s",
lua_typename(l, lua_type(l, lindex)), reason);
}
@@ -570,12 +580,13 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *js
* - String (Lua stack index)
*
* Returns nothing. Doesn't remove string from Lua stack */
-static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
+static void json_append_string(lua_State *l, json_encode_t *ctx, int lindex)
{
const char *escstr;
unsigned i;
const char *str;
size_t len;
+ strbuf_t *json = ctx->json;
str = lua_tolstring(l, lindex, &len);
@@ -587,7 +598,7 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
strbuf_append_char_unsafe(json, '\"');
for (i = 0; i < len; i++) {
- escstr = char2escape[(unsigned char)str[i]];
+ escstr = (*ctx->options->char2escape)[(unsigned char)str[i]];
if (escstr)
strbuf_append_string(json, escstr);
else
@@ -600,11 +611,12 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
* -1 object (not a pure array)
* >=0 elements in array
*/
-static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json)
+static int lua_array_length(lua_State *l, json_encode_t *ctx)
{
double k;
int max;
int items;
+ json_config_t *cfg = ctx->cfg;
max = 0;
items = 0;
@@ -635,7 +647,7 @@ static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json)
max > items * cfg->encode_sparse_ratio &&
max > cfg->encode_sparse_safe) {
if (!cfg->encode_sparse_convert)
- json_encode_exception(l, cfg, json, -1, "excessively sparse array");
+ json_encode_exception(l, ctx, -1, "excessively sparse array");
return -1;
}
@@ -666,17 +678,18 @@ static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
current_depth);
}
-static void json_append_data(lua_State *l, json_config_t *cfg,
- int current_depth, strbuf_t *json);
+static void json_append_data(lua_State *l, json_encode_t *cfg,
+ int current_depth);
/* json_append_array args:
* - lua_State
* - JSON strbuf
* - Size of passwd Lua array (top of stack) */
-static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth,
- strbuf_t *json, int array_length)
+static void json_append_array(lua_State *l, json_encode_t *ctx, int current_depth,
+ int array_length)
{
int comma, i;
+ strbuf_t *json = ctx->json;
strbuf_append_char(json, '[');
@@ -688,23 +701,25 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
comma = 1;
lua_rawgeti(l, -1, i);
- json_append_data(l, cfg, current_depth, json);
+ json_append_data(l, ctx, current_depth);
lua_pop(l, 1);
}
strbuf_append_char(json, ']');
}
-static void json_append_number(lua_State *l, json_config_t *cfg,
- strbuf_t *json, int lindex)
+static void json_append_number(lua_State *l, json_encode_t *ctx,
+ int lindex)
{
double num = lua_tonumber(l, lindex);
int len;
+ json_config_t *cfg = ctx->cfg;
+ strbuf_t *json = ctx->json;
if (cfg->encode_invalid_numbers == 0) {
/* Prevent encoding invalid numbers */
if (isinf(num) || isnan(num))
- json_encode_exception(l, cfg, json, lindex,
+ json_encode_exception(l, ctx, lindex,
"must not be NaN or Infinity");
} else if (cfg->encode_invalid_numbers == 1) {
/* Encode NaN/Infinity separately to ensure Javascript compatible
@@ -733,10 +748,11 @@ static void json_append_number(lua_State *l, json_config_t *cfg,
strbuf_extend_length(json, len);
}
-static void json_append_object(lua_State *l, json_config_t *cfg,
- int current_depth, strbuf_t *json)
+static void json_append_object(lua_State *l, json_encode_t *ctx,
+ int current_depth)
{
int comma, keytype;
+ strbuf_t *json = ctx->json;
/* Object */
strbuf_append_char(json, '{');
@@ -754,19 +770,19 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
keytype = lua_type(l, -2);
if (keytype == LUA_TNUMBER) {
strbuf_append_char(json, '"');
- json_append_number(l, cfg, json, -2);
+ json_append_number(l, ctx, -2);
strbuf_append_mem(json, "\":", 2);
} else if (keytype == LUA_TSTRING) {
- json_append_string(l, json, -2);
+ json_append_string(l, ctx, -2);
strbuf_append_char(json, ':');
} else {
- json_encode_exception(l, cfg, json, -2,
+ json_encode_exception(l, ctx, -2,
"table key must be a number or string");
/* never returns */
}
/* table, key, value */
- json_append_data(l, cfg, current_depth, json);
+ json_append_data(l, ctx, current_depth);
lua_pop(l, 1);
/* table, key */
}
@@ -775,20 +791,22 @@ static void json_append_object(lua_State *l, json_config_t *cfg,
}
/* Serialise Lua data into JSON string. */
-static void json_append_data(lua_State *l, json_config_t *cfg,
- int current_depth, strbuf_t *json)
+static void json_append_data(lua_State *l, json_encode_t *ctx,
+ int current_depth)
{
int len;
int as_array = 0;
int as_empty_dict = 0;
int has_metatable;
+ json_config_t *cfg = ctx->cfg;
+ strbuf_t *json = ctx->json;
switch (lua_type(l, -1)) {
case LUA_TSTRING:
- json_append_string(l, json, -1);
+ json_append_string(l, ctx, -1);
break;
case LUA_TNUMBER:
- json_append_number(l, cfg, json, -1);
+ json_append_number(l, ctx, -1);
break;
case LUA_TBOOLEAN:
if (lua_toboolean(l, -1))
@@ -818,12 +836,12 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
if (as_array) {
len = lua_objlen(l, -1);
- json_append_array(l, cfg, current_depth, json, len);
+ json_append_array(l, ctx, current_depth, len);
} else {
- len = lua_array_length(l, cfg, json);
+ len = lua_array_length(l, ctx);
if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object && !as_empty_dict)) {
- json_append_array(l, cfg, current_depth, json, len);
+ json_append_array(l, ctx, current_depth, len);
} else {
if (has_metatable) {
lua_getmetatable(l, -1);
@@ -833,11 +851,11 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
as_array = lua_rawequal(l, -1, -2);
lua_pop(l, 2); /* pop pointer + metatable */
if (as_array) {
- json_append_array(l, cfg, current_depth, json, 0);
+ json_append_array(l, ctx, current_depth, 0);
break;
}
}
- json_append_object(l, cfg, current_depth, json);
+ json_append_object(l, ctx, current_depth);
}
}
break;
@@ -846,7 +864,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
break;
case LUA_TLIGHTUSERDATA:
if (lua_touserdata(l, -1) == &json_array) {
- json_append_array(l, cfg, current_depth, json, 0);
+ json_append_array(l, ctx, current_depth, 0);
}
break;
case LUA_TUSERDATA:
@@ -862,7 +880,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
default:
/* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
* and LUA_TLIGHTUSERDATA) cannot be serialised */
- json_encode_exception(l, cfg, json, -1, "type not supported");
+ json_encode_exception(l, ctx, -1, "type not supported");
/* never returns */
}
}
@@ -870,12 +888,44 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
static int json_encode(lua_State *l)
{
json_config_t *cfg = json_fetch_config(l);
+ json_encode_options_t options = { .char2escape = { char2escape } };
+ json_encode_t ctx = { .options = &options, .cfg = cfg };
strbuf_t local_encode_buf;
strbuf_t *encode_buf;
char *json;
int len;
+ const char *customChar2escape[256];
- luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
+ switch (lua_gettop(l)) {
+ case 1:
+ break;
+ case 2:
+ luaL_checktype(l, 2, LUA_TTABLE);
+ lua_getfield(l, 2, "escape_slash");
+
+ /* We only handle the escape_slash option for now */
+ if (lua_isnil(l, -1)) {
+ lua_pop(l, 2);
+ break;
+ }
+
+ luaL_checktype(l, -1, LUA_TBOOLEAN);
+
+ int escape_slash = lua_toboolean(l, -1);
+
+ if (escape_slash) {
+ /* This can be optimised by adding a new hard-coded escape table for this case,
+ * but this path will rarely if ever be used, so let's just memcpy.*/
+ memcpy(customChar2escape, char2escape, sizeof(char2escape));
+ customChar2escape['/'] = "\\/";
+ *ctx.options->char2escape = customChar2escape;
+ }
+
+ lua_pop(l, 2);
+ break;
+ default:
+ return luaL_error (l, "expected 1 or 2 arguments");
+ }
if (!cfg->encode_keep_buffer) {
/* Use private buffer */
@@ -887,7 +937,8 @@ static int json_encode(lua_State *l)
strbuf_reset(encode_buf);
}
- json_append_data(l, cfg, 0, encode_buf);
+ ctx.json = encode_buf;
+ json_append_data(l, &ctx, 0);
json = strbuf_string(encode_buf, &len);
lua_pushlstring(l, json, len);
diff --git a/src/clint.py b/src/clint.py
index b57bbe354b..8044607098 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -897,7 +897,7 @@ def CheckIncludes(filename, lines, error):
if (not name.endswith('.h.generated.h') and
not name.endswith('/defs.h') and
not name.endswith('_defs.h') and
- not name.endswith('h.inline.generated.h') and
+ not name.endswith('.h.inline.generated.h') and
not name.endswith('_defs.generated.h') and
not name.endswith('_enum.generated.h')):
error(filename, i, 'build/include_defs', 5,
@@ -2206,12 +2206,6 @@ def ProcessFileData(filename, file_extension, lines, error,
error = RecordedError
- if file_extension == 'h':
- CheckForHeaderGuard(filename, lines, error)
- CheckIncludes(filename, lines, error)
- if filename.endswith('/defs.h') or filename.endswith('_defs.h'):
- CheckNonSymbols(filename, lines, error)
-
RemoveMultiLineComments(filename, lines, error)
clean_lines = CleansedLines(lines, init_lines)
for line in range(clean_lines.NumLines()):
@@ -2219,6 +2213,12 @@ def ProcessFileData(filename, file_extension, lines, error,
nesting_state, error,
extra_check_functions)
+ if file_extension == 'h':
+ CheckForHeaderGuard(filename, lines, error)
+ CheckIncludes(filename, lines, error)
+ if filename.endswith('/defs.h') or filename.endswith('_defs.h'):
+ CheckNonSymbols(filename, lines, error)
+
# We check here rather than inside ProcessLine so that we see raw
# lines rather than "cleaned" lines.
CheckForBadCharacters(filename, lines, error)
diff --git a/src/klib/kvec.h b/src/klib/kvec.h
index 1b9e6fd9f8..ea8cbe48cf 100644
--- a/src/klib/kvec.h
+++ b/src/klib/kvec.h
@@ -135,6 +135,10 @@
: 0UL)), \
&(v).items[(i)]))
+#define kv_shift(v, i, n) ((v).size -= (n), (i) < (v).size \
+ && memmove(&kv_A(v, (i)), &kv_A(v, (i)+(n)), \
+ ((v).size-(i))*sizeof(kv_A(v, i))))
+
#define kv_printf(v, ...) kv_do_printf(&(v), __VA_ARGS__)
/// Type of a vector with a few first members allocated on stack
diff --git a/src/mpack/conv.h b/src/mpack/conv.h
index 71f14a067e..e46a2b16d7 100644
--- a/src/mpack/conv.h
+++ b/src/mpack/conv.h
@@ -14,10 +14,6 @@ typedef mpack_sint32_t mpack_sintmax_t;
typedef mpack_uint32_t mpack_uintmax_t;
#endif
-#ifndef bool
-# define bool unsigned
-#endif
-
MPACK_API mpack_token_t mpack_pack_nil(void) FUNUSED FPURE;
MPACK_API mpack_token_t mpack_pack_boolean(unsigned v) FUNUSED FPURE;
MPACK_API mpack_token_t mpack_pack_uint(mpack_uintmax_t v) FUNUSED FPURE;
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 344b4bef00..6e27e39f9a 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -31,7 +31,7 @@ target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY})
find_package(Iconv REQUIRED)
find_package(Libuv 1.28.0 REQUIRED)
find_package(Lpeg REQUIRED)
-find_package(Treesitter 0.24.0 REQUIRED)
+find_package(Treesitter 0.25.0 REQUIRED)
find_package(Unibilium 2.0 REQUIRED)
find_package(UTF8proc REQUIRED)
@@ -49,7 +49,7 @@ if(ENABLE_LIBINTL)
endif()
if(ENABLE_WASMTIME)
- find_package(Wasmtime 25.0.2 EXACT REQUIRED)
+ find_package(Wasmtime 29.0.1 EXACT REQUIRED)
target_link_libraries(main_lib INTERFACE wasmtime)
target_compile_definitions(nvim_bin PRIVATE HAVE_WASMTIME)
endif()
@@ -120,8 +120,8 @@ elseif(MINGW)
# Use POSIX compatible stdio in Mingw
target_compile_definitions(main_lib INTERFACE __USE_MINGW_ANSI_STDIO)
- # Enable wmain
- target_link_libraries(nvim_bin PRIVATE -municode)
+ # wrapper for nvim.manifest
+ target_sources(main_lib INTERFACE ${CMAKE_CURRENT_LIST_DIR}/os/nvim.rc)
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(main_lib INTERFACE
-Wno-conversion
@@ -322,6 +322,7 @@ set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h)
set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h)
set(GENERATED_OPTIONS_ENUM ${GENERATED_DIR}/options_enum.generated.h)
set(GENERATED_OPTIONS_MAP ${GENERATED_DIR}/options_map.generated.h)
+set(GENERATED_OPTION_VARS ${GENERATED_DIR}/option_vars.generated.h)
set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h)
set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h)
set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h)
@@ -360,8 +361,8 @@ file(MAKE_DIRECTORY ${TOUCHES_DIR} ${GENERATED_DIR} ${GENERATED_INCLUDES_DIR})
file(GLOB NVIM_SOURCES CONFIGURE_DEPENDS *.c)
file(GLOB NVIM_HEADERS CONFIGURE_DEPENDS *.h)
-file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../vterm/*.c)
-file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../vterm/*.h)
+file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c)
+file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h)
file(GLOB NLUA0_SOURCES CONFIGURE_DEPENDS ../mpack/*.c)
@@ -390,6 +391,7 @@ foreach(subdir
msgpack_rpc
tui
tui/termkey
+ vterm
event
eval
lua
@@ -555,7 +557,7 @@ foreach(sfile ${NVIM_SOURCES}
set(PREPROC_OUTPUT -w -E -o ${gf_i})
endif()
- set(depends "${HEADER_GENERATOR}" "${sfile}" "${LUA_GEN_DEPS}")
+ set(depends "${HEADER_GENERATOR}" "${sfile}" "${LUA_GEN_DEPS}" "${GENERATOR_C_GRAMMAR}")
if("${f}" STREQUAL "version.c")
# Ensure auto/versiondef_git.h exists after "make clean".
list(APPEND depends update_version_stamp "${NVIM_VERSION_GIT_H}" "${NVIM_VERSION_DEF_H}")
@@ -657,6 +659,7 @@ list(APPEND NVIM_GENERATED_FOR_HEADERS
"${GENERATED_EVENTS_ENUM}"
"${GENERATED_KEYSETS_DEFS}"
"${GENERATED_OPTIONS_ENUM}"
+ "${GENERATED_OPTION_VARS}"
)
list(APPEND NVIM_GENERATED_FOR_SOURCES
@@ -686,8 +689,8 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
)
-add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
- COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
+add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
+ COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
)
@@ -824,6 +827,7 @@ target_link_libraries(libnvim PRIVATE main_lib PUBLIC libuv)
#-------------------------------------------------------------------------------
find_program(CLANG_TIDY_PRG clang-tidy)
+mark_as_advanced(CLANG_TIDY_PRG)
set(EXCLUDE_CLANG_TIDY typval_encode.c.h ui_events.in.h)
if(WIN32)
list(APPEND EXCLUDE_CLANG_TIDY
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 22932fd1a2..d436dbb7f1 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -2,7 +2,6 @@
#include <lauxlib.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,6 +22,7 @@
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -68,32 +68,31 @@ static int64_t next_autocmd_id = 1;
/// match any combination of them.
///
/// @param opts Dict 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|.
-/// Cannot be used with {buffer}
-/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+/// - buffer: (integer) Buffer number or list of buffer numbers for buffer local autocommands
/// |autocmd-buflocal|. Cannot be used with {pattern}
+/// - event: (string|table) event or events to match against |autocmd-events|.
+/// - id: (integer) Autocommand ID to match.
+/// - group: (string|table) the autocommand group name or id to match against.
+/// - pattern: (string|table) pattern or patterns to match against |autocmd-pattern|.
+/// Cannot be used with {buffer}
/// @return Array of autocommands matching the criteria, with each item
/// containing the following fields:
-/// - id (number): the autocommand id (only when defined with the API).
-/// - group (integer): the autocommand group id.
-/// - group_name (string): the autocommand group name.
-/// - desc (string): the autocommand description.
-/// - event (string): the autocommand event.
-/// - command (string): the autocommand command. Note: this will be empty if a callback is set.
-/// - callback (function|string|nil): Lua function or name of a Vim script function
+/// - buffer: (integer) the buffer number.
+/// - buflocal: (boolean) true if the autocommand is buffer local.
+/// - command: (string) the autocommand command. Note: this will be empty if a callback is set.
+/// - callback: (function|string|nil): Lua function or name of a Vim script function
/// which is executed when this autocommand is triggered.
-/// - once (boolean): whether the autocommand is only run once.
-/// - pattern (string): the autocommand pattern.
+/// - desc: (string) the autocommand description.
+/// - event: (string) the autocommand event.
+/// - id: (integer) the autocommand id (only when defined with the API).
+/// - group: (integer) the autocommand group id.
+/// - group_name: (string) the autocommand group name.
+/// - once: (boolean) whether the autocommand is only run once.
+/// - pattern: (string) the autocommand pattern.
/// If the autocommand is buffer local |autocmd-buffer-local|:
-/// - buflocal (boolean): true if the autocommand is buffer local.
-/// - buffer (number): the buffer number.
Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
- // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... })
-
ArrayBuilder autocmd_list = KV_INITIAL_VALUE;
kvi_init(autocmd_list);
char *pattern_filters[AUCMD_MAX_PATTERNS];
@@ -127,6 +126,8 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
});
}
+ int id = (HAS_KEY(opts, get_autocmds, id)) ? (int)opts->id : -1;
+
if (HAS_KEY(opts, get_autocmds, event)) {
check_event = true;
@@ -237,6 +238,10 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
continue;
}
+ if (id != -1 && ac->id != id) {
+ continue;
+ }
+
// Skip autocmds from invalid groups if passed.
if (group != 0 && ap->group != group) {
continue;
@@ -285,10 +290,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
PUT_C(autocmd_info, "desc", CSTR_AS_OBJ(ac->desc));
}
- if (ac->exec.type == CALLABLE_CB) {
+ if (ac->handler_cmd) {
+ PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->handler_cmd));
+ } else {
PUT_C(autocmd_info, "command", STRING_OBJ(STRING_INIT));
- Callback *cb = &ac->exec.callable.cb;
+ Callback *cb = &ac->handler_fn;
switch (cb->type) {
case kCallbackLua:
if (nlua_ref_is_function(cb->data.luaref)) {
@@ -302,8 +309,6 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
case kCallbackNone:
abort();
}
- } else {
- PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->exec.callable.cmd));
}
PUT_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat));
@@ -317,23 +322,6 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(false));
}
- // TODO(sctx): It would be good to unify script_ctx to actually work with lua
- // right now it's just super weird, and never really gives you the info that
- // you would expect from this.
- //
- // I think we should be able to get the line number, filename, etc. from lua
- // when we're executing something, and it should be easy to then save that
- // info here.
- //
- // I think it's a big loss not getting line numbers of where options, autocmds,
- // etc. are set (just getting "Sourced (lua)" or something is not that helpful.
- //
- // Once we do that, we can put these into the autocmd_info, but I don't think it's
- // useful to do that at this time.
- //
- // PUT_C(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid));
- // PUT_C(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
-
kvi_push(autocmd_list, DICT_OBJ(autocmd_info));
}
}
@@ -386,9 +374,9 @@ cleanup:
/// - 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>]
+/// - file: (string) [<afile>] (not expanded to a full path)
+/// - match: (string) [<amatch>] (expanded to a full path)
+/// - buf: (number) [<abuf>]
/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
/// - command (string) optional: Vim command to execute on event. Cannot be used with
/// {callback}
@@ -406,8 +394,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
{
int64_t autocmd_id = -1;
char *desc = NULL;
- AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
- Callback cb = CALLBACK_NONE;
+ char *handler_cmd = NULL;
+ Callback handler_fn = CALLBACK_NONE;
Array event_array = unpack_string_or_array(event, "event", true, arena, err);
if (ERROR_SET(err)) {
@@ -432,13 +420,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
- cb.type = kCallbackLua;
- cb.data.luaref = callback->data.luaref;
+ handler_fn.type = kCallbackLua;
+ handler_fn.data.luaref = callback->data.luaref;
callback->data.luaref = LUA_NOREF;
break;
case kObjectTypeString:
- cb.type = kCallbackFuncref;
- cb.data.funcref = string_to_cstr(callback->data.string);
+ handler_fn.type = kCallbackFuncref;
+ handler_fn.data.funcref = string_to_cstr(callback->data.string);
break;
default:
VALIDATE_EXP(false, "callback", "Lua function or Vim function name",
@@ -446,12 +434,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
}
-
- aucmd.type = CALLABLE_CB;
- aucmd.callable.cb = cb;
} else if (HAS_KEY(opts, create_autocmd, command)) {
- aucmd.type = CALLABLE_EX;
- aucmd.callable.cmd = string_to_cstr(opts->command);
+ handler_cmd = string_to_cstr(opts->command);
} else {
VALIDATE(false, "%s", "Required: 'command' or 'callback'", {
goto cleanup;
@@ -491,7 +475,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
int retval;
FOREACH_ITEM(patterns, pat, {
- // See: TODO(sctx)
WITH_SCRIPT_CONTEXT(channel_id, {
retval = autocmd_register(autocmd_id,
event_nr,
@@ -501,7 +484,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
opts->once,
opts->nested,
desc,
- aucmd);
+ handler_cmd,
+ &handler_fn);
});
if (retval == FAIL) {
@@ -512,7 +496,11 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
});
cleanup:
- aucmd_exec_free(&aucmd);
+ if (handler_cmd) {
+ XFREE_CLEAR(handler_cmd);
+ } else {
+ callback_free(&handler_fn);
+ }
return autocmd_id;
}
@@ -631,7 +619,7 @@ Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augrou
FUNC_API_SINCE(9)
{
char *augroup_name = name.data;
- bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err);
+ bool clear_autocmds = GET_BOOL_OR_TRUE(opts, create_augroup, clear);
int augroup = -1;
WITH_SCRIPT_CONTEXT(channel_id, {
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 9480292d9a..aa349790b3 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -23,7 +23,6 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
#include "nvim/extmark_defs.h"
@@ -360,93 +359,91 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
memchrsub(lines[i], NUL, NL, l.size);
}
- try_start();
-
- if (!MODIFIABLE(buf)) {
- api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
- goto end;
- }
-
- if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to save undo information");
- goto end;
- }
-
- bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
-
- // If the size of the range is reducing (ie, new_len < old_len) we
- // need to delete some old_len. We do this at the start, by
- // repeatedly deleting line "start".
- size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
- for (size_t i = 0; i < to_delete; i++) {
- if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to delete line");
+ TRY_WRAP(err, {
+ if (!MODIFIABLE(buf)) {
+ api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
goto end;
}
- }
- if (to_delete > 0) {
- extra -= (ptrdiff_t)to_delete;
- }
+ if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to save undo information");
+ goto end;
+ }
- // For as long as possible, replace the existing old_len with the
- // new old_len. This is a more efficient operation, as it requires
- // less memory allocation and freeing.
- size_t to_replace = old_len < new_len ? old_len : new_len;
- bcount_t inserted_bytes = 0;
- for (size_t i = 0; i < to_replace; i++) {
- int64_t lnum = start + (int64_t)i;
+ bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
- VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
- goto end;
- });
+ // If the size of the range is reducing (ie, new_len < old_len) we
+ // need to delete some old_len. We do this at the start, by
+ // repeatedly deleting line "start".
+ size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
+ for (size_t i = 0; i < to_delete; i++) {
+ if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to delete line");
+ goto end;
+ }
+ }
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to replace line");
- goto end;
+ if (to_delete > 0) {
+ extra -= (ptrdiff_t)to_delete;
}
- inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- }
+ // For as long as possible, replace the existing old_len with the
+ // new old_len. This is a more efficient operation, as it requires
+ // less memory allocation and freeing.
+ size_t to_replace = old_len < new_len ? old_len : new_len;
+ bcount_t inserted_bytes = 0;
+ for (size_t i = 0; i < to_replace; i++) {
+ int64_t lnum = start + (int64_t)i;
+
+ VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to replace line");
+ goto end;
+ }
- // Now we may need to insert the remaining new old_len
- for (size_t i = to_replace; i < new_len; i++) {
- int64_t lnum = start + (int64_t)i - 1;
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
+ }
- VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
- goto end;
- });
+ // Now we may need to insert the remaining new old_len
+ for (size_t i = to_replace; i < new_len; i++) {
+ int64_t lnum = start + (int64_t)i - 1;
- if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to insert line");
- goto end;
- }
+ VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
+ goto end;
+ });
- inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
+ if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to insert line");
+ goto end;
+ }
- extra++;
- }
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- // Adjust marks. Invalidate any which lie in the
- // changed range, and move any in the remainder of the buffer.
- linenr_T adjust = end > start ? MAXLNUM : 0;
- mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), adjust, (linenr_T)extra,
- true, true, kExtmarkNOOP);
+ extra++;
+ }
- extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
- deleted_bytes, (int)new_len, 0, inserted_bytes,
- kExtmarkUndo);
+ // Adjust marks. Invalidate any which lie in the
+ // changed range, and move any in the remainder of the buffer.
+ linenr_T adjust = end > start ? MAXLNUM : 0;
+ mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), adjust, (linenr_T)extra,
+ true, true, kExtmarkNOOP);
- changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
+ extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
+ deleted_bytes, (int)new_len, 0, inserted_bytes,
+ kExtmarkUndo);
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_buffer == buf) {
- fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra);
- }
- }
+ changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
-end:
- try_end(err);
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_buffer == buf) {
+ fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra);
+ }
+ }
+ end:;
+ });
}
/// Sets (replaces) a range in the buffer
@@ -593,101 +590,99 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
new_byte += (bcount_t)(last_item.size) + 1;
}
- try_start();
-
- if (!MODIFIABLE(buf)) {
- api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
- goto end;
- }
-
- // Small note about undo states: unlike set_lines, we want to save the
- // undo state of one past the end_row, since end_row is inclusive.
- if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to save undo information");
- goto end;
- }
-
- ptrdiff_t extra = 0; // lines added to text, can be negative
- size_t old_len = (size_t)(end_row - start_row + 1);
-
- // If the size of the range is reducing (ie, new_len < old_len) we
- // need to delete some old_len. We do this at the start, by
- // repeatedly deleting line "start".
- size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
- for (size_t i = 0; i < to_delete; i++) {
- if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to delete line");
+ TRY_WRAP(err, {
+ if (!MODIFIABLE(buf)) {
+ api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
goto end;
}
- }
- if (to_delete > 0) {
- extra -= (ptrdiff_t)to_delete;
- }
-
- // For as long as possible, replace the existing old_len with the
- // new old_len. This is a more efficient operation, as it requires
- // less memory allocation and freeing.
- size_t to_replace = old_len < new_len ? old_len : new_len;
- for (size_t i = 0; i < to_replace; i++) {
- int64_t lnum = start_row + (int64_t)i;
-
- VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ // Small note about undo states: unlike set_lines, we want to save the
+ // undo state of one past the end_row, since end_row is inclusive.
+ if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to save undo information");
goto end;
- });
+ }
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to replace line");
- goto end;
+ ptrdiff_t extra = 0; // lines added to text, can be negative
+ size_t old_len = (size_t)(end_row - start_row + 1);
+
+ // If the size of the range is reducing (ie, new_len < old_len) we
+ // need to delete some old_len. We do this at the start, by
+ // repeatedly deleting line "start".
+ size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
+ for (size_t i = 0; i < to_delete; i++) {
+ if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to delete line");
+ goto end;
+ }
}
- }
- // Now we may need to insert the remaining new old_len
- for (size_t i = to_replace; i < new_len; i++) {
- int64_t lnum = start_row + (int64_t)i - 1;
+ if (to_delete > 0) {
+ extra -= (ptrdiff_t)to_delete;
+ }
- VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
- goto end;
- });
+ // For as long as possible, replace the existing old_len with the
+ // new old_len. This is a more efficient operation, as it requires
+ // less memory allocation and freeing.
+ size_t to_replace = old_len < new_len ? old_len : new_len;
+ for (size_t i = 0; i < to_replace; i++) {
+ int64_t lnum = start_row + (int64_t)i;
- if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to insert line");
- goto end;
+ VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to replace line");
+ goto end;
+ }
}
- extra++;
- }
+ // Now we may need to insert the remaining new old_len
+ for (size_t i = to_replace; i < new_len; i++) {
+ int64_t lnum = start_row + (int64_t)i - 1;
- colnr_T col_extent = (colnr_T)(end_col
- - ((end_row == start_row) ? start_col : 0));
-
- // Adjust marks. Invalidate any which lie in the
- // changed range, and move any in the remainder of the buffer.
- // Do not adjust any cursors. need to use column-aware logic (below)
- linenr_T adjust = end_row >= start_row ? MAXLNUM : 0;
- mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, adjust, (linenr_T)extra,
- true, true, kExtmarkNOOP);
-
- extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
- (int)(end_row - start_row), col_extent, old_byte,
- (int)new_len - 1, (colnr_T)last_item.size, new_byte,
- kExtmarkUndo);
-
- changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
-
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_buffer == buf) {
- if (win->w_cursor.lnum >= start_row && win->w_cursor.lnum <= end_row) {
- fix_cursor_cols(win, (linenr_T)start_row, (colnr_T)start_col, (linenr_T)end_row,
- (colnr_T)end_col, (linenr_T)new_len, (colnr_T)last_item.size);
- } else {
- fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
+ VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to insert line");
+ goto end;
}
+
+ extra++;
}
- }
-end:
- try_end(err);
+ colnr_T col_extent = (colnr_T)(end_col
+ - ((end_row == start_row) ? start_col : 0));
+
+ // Adjust marks. Invalidate any which lie in the
+ // changed range, and move any in the remainder of the buffer.
+ // Do not adjust any cursors. need to use column-aware logic (below)
+ linenr_T adjust = end_row >= start_row ? MAXLNUM : 0;
+ mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, adjust, (linenr_T)extra,
+ true, true, kExtmarkNOOP);
+
+ extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
+ (int)(end_row - start_row), col_extent, old_byte,
+ (int)new_len - 1, (colnr_T)last_item.size, new_byte,
+ kExtmarkUndo);
+
+ changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
+
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_buffer == buf) {
+ if (win->w_cursor.lnum >= start_row && win->w_cursor.lnum <= end_row) {
+ fix_cursor_cols(win, (linenr_T)start_row, (colnr_T)start_col, (linenr_T)end_row,
+ (colnr_T)end_col, (linenr_T)new_len, (colnr_T)last_item.size);
+ } else {
+ fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
+ }
+ }
+ }
+ end:;
+ });
}
/// Gets a range from the buffer.
@@ -965,26 +960,27 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
return;
}
- try_start();
-
- const bool is_curbuf = buf == curbuf;
- const int save_acd = p_acd;
- if (!is_curbuf) {
- // Temporarily disable 'autochdir' when setting file name for another buffer.
- p_acd = false;
- }
+ int ren_ret = OK;
+ TRY_WRAP(err, {
+ const bool is_curbuf = buf == curbuf;
+ const int save_acd = p_acd;
+ if (!is_curbuf) {
+ // Temporarily disable 'autochdir' when setting file name for another buffer.
+ p_acd = false;
+ }
- // Using aucmd_*: autocommands will be executed by rename_buffer
- aco_save_T aco;
- aucmd_prepbuf(&aco, buf);
- int ren_ret = rename_buffer(name.data);
- aucmd_restbuf(&aco);
+ // Using aucmd_*: autocommands will be executed by rename_buffer
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+ ren_ret = rename_buffer(name.data);
+ aucmd_restbuf(&aco);
- if (!is_curbuf) {
- p_acd = save_acd;
- }
+ if (!is_curbuf) {
+ p_acd = save_acd;
+ }
+ });
- if (try_end(err)) {
+ if (ERROR_SET(err)) {
return;
}
@@ -1184,12 +1180,12 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena,
/// This temporarily switches current buffer to "buffer".
/// If the current window already shows "buffer", the window is not switched.
/// If a window inside the current tabpage (including a float) already shows the
-/// buffer, then one of these windows will be set as current window temporarily.
+/// buffer, then one of those windows will be set as current window temporarily.
/// Otherwise a temporary scratch window (called the "autocmd window" for
/// historical reasons) will be used.
///
/// This is useful e.g. to call Vimscript functions that only work with the
-/// current buffer/window currently, like |termopen()|.
+/// current buffer/window currently, like `jobstart(…, {'term': v:true})`.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param fun Function to call inside the buffer (currently Lua callable
@@ -1204,15 +1200,18 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
if (!buf) {
return NIL;
}
- try_start();
- aco_save_T aco;
- aucmd_prepbuf(&aco, buf);
- Array args = ARRAY_DICT_INIT;
- Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ Object res = OBJECT_INIT;
+ TRY_WRAP(err, {
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+
+ Array args = ARRAY_DICT_INIT;
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+
+ aucmd_restbuf(&aco);
+ });
- aucmd_restbuf(&aco);
- try_end(err);
return res;
}
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index ab57d5c009..23e08bd3fe 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -26,6 +26,7 @@
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/ops.h"
#include "nvim/pos_defs.h"
#include "nvim/regexp.h"
@@ -226,8 +227,8 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err
addr = "?";
break;
}
- PUT_KEY(result, cmd, addr, CSTR_AS_OBJ(addr));
- PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd));
+ PUT_KEY(result, cmd, addr, cstr_as_string(addr));
+ PUT_KEY(result, cmd, nextcmd, cstr_as_string(ea.nextcmd));
// TODO(bfredl): nested keydict would be nice..
Dict mods = arena_dict(arena, 20);
@@ -930,6 +931,9 @@ void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
gap = &ucmds;
} else {
buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
gap = &buf->b_ucmds;
}
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index b38a7d4173..1d81b21be6 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -1,3 +1,5 @@
+// Island of misfit toys.
+
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@@ -7,6 +9,7 @@
#include "nvim/api/extmark.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/vimscript.h"
@@ -18,14 +21,14 @@
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
+#include "nvim/marktree.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
-#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/channel_defs.h"
-#include "nvim/msgpack_rpc/unpacker.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/pos_defs.h"
+#include "nvim/strings.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -82,6 +85,17 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
return buf->b_fnum;
}
+static uint32_t src2ns(Integer *src_id)
+{
+ if (*src_id == 0) {
+ *src_id = nvim_create_namespace((String)STRING_INIT);
+ }
+ if (*src_id < 0) {
+ return (((uint32_t)1) << 31) - 1;
+ }
+ return (uint32_t)(*src_id);
+}
+
/// Clears highlights and virtual text from namespace and range of lines
///
/// @deprecated use |nvim_buf_clear_namespace()|.
@@ -100,6 +114,80 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err);
}
+/// Adds a highlight to buffer.
+///
+/// @deprecated use |nvim_buf_set_extmark()| or |vim.hl.range()|
+///
+/// Namespaces are used for batch deletion/updating of a set of highlights. To
+/// create a namespace, use |nvim_create_namespace()| which returns a namespace
+/// id. Pass it in to this function as `ns_id` to add highlights to the
+/// namespace. All highlights in the same namespace can then be cleared with
+/// single call to |nvim_buf_clear_namespace()|. If the highlight never will be
+/// deleted by an API call, pass `ns_id = -1`.
+///
+/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
+/// highlight, the allocated id is then returned. If `hl_group` is the empty
+/// string no highlight is added, but a new `ns_id` is still returned. This is
+/// supported for backwards compatibility, new code should use
+/// |nvim_create_namespace()| to create a new empty namespace.
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id namespace to use or -1 for ungrouped highlight
+/// @param hl_group Name of the highlight group to use
+/// @param line Line to highlight (zero-indexed)
+/// @param col_start Start of (byte-indexed) column range to highlight
+/// @param col_end End of (byte-indexed) column range to highlight,
+/// or -1 to highlight to end of line
+/// @param[out] err Error details, if any
+/// @return The ns_id that was used
+Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, Integer line,
+ Integer col_start, Integer col_end, Error *err)
+ FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(13)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
+ return 0;
+ });
+ VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
+ return 0;
+ });
+
+ if (col_end < 0 || col_end > MAXCOL) {
+ col_end = MAXCOL;
+ }
+
+ uint32_t ns = src2ns(&ns_id);
+
+ if (!(line < buf->b_ml.ml_line_count)) {
+ // safety check, we can't add marks outside the range
+ return ns_id;
+ }
+
+ int hl_id = 0;
+ if (hl_group.size > 0) {
+ hl_id = syn_check_group(hl_group.data, hl_group.size);
+ } else {
+ return ns_id;
+ }
+
+ int end_line = (int)line;
+ if (col_end == MAXCOL) {
+ col_end = 0;
+ end_line++;
+ }
+
+ DecorInline decor = DECOR_INLINE_INIT;
+ decor.data.hl.hl_id = hl_id;
+
+ extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
+ decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
+ return ns_id;
+}
/// Set the virtual text (annotation) for a buffer line.
///
/// @deprecated use nvim_buf_set_extmark to use full virtual text functionality.
@@ -636,23 +724,27 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
/// Gets the value of a global or local (buffer, window) option.
///
/// @param[in] from Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param name The option name.
/// @param[out] err Details of an error that may have occurred.
///
/// @return the option value.
-static Object get_option_from(void *from, OptScope req_scope, String name, Error *err)
+static Object get_option_from(void *from, OptScope scope, String name, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return (Object)OBJECT_INIT;
});
OptIndex opt_idx = find_option(name.data);
+ VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, {
+ return (Object)OBJECT_INIT;
+ });
+
OptVal value = NIL_OPTVAL;
- if (option_has_scope(opt_idx, req_scope)) {
- value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
- req_scope, from, err);
+ if (option_has_scope(opt_idx, scope)) {
+ value = get_option_value_for(opt_idx, scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
+ scope, from, err);
if (ERROR_SET(err)) {
return (Object)OBJECT_INIT;
}
@@ -668,12 +760,12 @@ static Object get_option_from(void *from, OptScope req_scope, String name, Error
/// Sets the value of a global or local (buffer, window) option.
///
/// @param[in] to Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param name The option name.
/// @param value New option value.
/// @param[out] err Details of an error that may have occurred.
-static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name,
- Object value, Error *err)
+static void set_option_to(uint64_t channel_id, void *to, OptScope scope, String name, Object value,
+ Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return;
@@ -698,12 +790,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, Str
// For global-win-local options -> setlocal
// For win-local options -> setglobal and setlocal (opt_flags == 0)
const int opt_flags
- = (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
+ = (scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
? 0
- : ((req_scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
+ : ((scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, scope, to, err);
});
}
@@ -798,7 +890,8 @@ theend:
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
void nvim_subscribe(uint64_t channel_id, String event)
- FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
+// XXX: c_grammar.lua is order-sensitive.
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
@@ -808,7 +901,105 @@ void nvim_subscribe(uint64_t channel_id, String event)
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
void nvim_unsubscribe(uint64_t channel_id, String event)
- FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
+// XXX: c_grammar.lua is order-sensitive.
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
+
+enum { LINE_BUFFER_MIN_SIZE = 4096, };
+
+/// Writes a message to vim output or error buffer. The string is split
+/// and flushed after each newline. Incomplete lines are kept for writing
+/// later.
+///
+/// @param message Message to write
+/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
+/// @param writeln Append a trailing newline
+static void write_msg(String message, bool to_err, bool writeln)
+{
+ static StringBuilder out_line_buf = KV_INITIAL_VALUE;
+ static StringBuilder err_line_buf = KV_INITIAL_VALUE;
+ StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
+
+#define PUSH_CHAR(c) \
+ if (kv_max(*line_buf) == 0) { \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } \
+ if (c == NL) { \
+ kv_push(*line_buf, NUL); \
+ if (to_err) { \
+ emsg(line_buf->items); \
+ } else { \
+ msg(line_buf->items, 0); \
+ } \
+ if (msg_silent == 0) { \
+ msg_didout = true; \
+ } \
+ kv_drop(*line_buf, kv_size(*line_buf)); \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } else if (c == NUL) { \
+ kv_push(*line_buf, NL); \
+ } else { \
+ kv_push(*line_buf, c); \
+ }
+
+ no_wait_return++;
+ for (uint32_t i = 0; i < message.size; i++) {
+ if (got_int) {
+ break;
+ }
+ PUSH_CHAR(message.data[i]);
+ }
+ if (writeln) {
+ PUSH_CHAR(NL);
+ }
+ no_wait_return--;
+ msg_end();
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_out_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, false, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_writeln(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, true);
+}
+
+/// @deprecated
+///
+/// Use `nvim_echo` or `nvim_exec_lua("vim.notify(...)", ...)` instead.
+///
+/// @param msg Message to display to the user
+/// @param log_level The log level
+/// @param opts Reserved for future use.
+/// @param[out] err Error details, if any
+Object nvim_notify(String msg, Integer log_level, Dict opts, Arena *arena, Error *err)
+ FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(13)
+{
+ MAXSIZE_TEMP_ARRAY(args, 3);
+ ADD_C(args, STRING_OBJ(msg));
+ ADD_C(args, INTEGER_OBJ(log_level));
+ ADD_C(args, DICT_OBJ(opts));
+
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
+}
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index c94b8df9ea..8b31196eef 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -27,6 +27,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/move.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -48,7 +49,7 @@ void api_extmark_free_all_mem(void)
/// 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()|.
+/// |nvim_buf_set_extmark()|.
///
/// Namespaces can be named or anonymous. If `name` matches an existing
/// namespace, the associated id is returned. If `name` is an empty string
@@ -61,7 +62,7 @@ Integer nvim_create_namespace(String name)
{
handle_T id = map_get(String, int)(&namespace_ids, name);
if (id > 0) {
- return id;
+ return (Integer)id;
}
id = next_namespace_id++;
if (name.size > 0) {
@@ -384,6 +385,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - hl_group : highlight group used for the text range. This and below
/// highlight groups can be supplied either as a string or as an integer,
/// the latter of which can be obtained using |nvim_get_hl_id_by_name()|.
+///
+/// Multiple highlight groups can be stacked by passing an array (highest
+/// priority last).
/// - hl_eol : when true, for a multiline highlight covering the
/// EOL of a line, continue the highlight for the rest
/// of the screen line (just like for diff and
@@ -396,6 +400,15 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// (highest priority last).
/// - virt_text_pos : position of virtual text. Possible values:
/// - "eol": right after eol character (default).
+/// - "eol_right_align": display right aligned in the window
+/// unless the virtual text is longer than
+/// the space available. If the virtual
+/// text is too long, it is truncated to
+/// fit in the window after the EOL
+/// character. If the line is wrapped, the
+/// virtual text is shown after the end of
+/// the line rather than the previous
+/// screen line.
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
/// - "right_align": display right aligned in the window.
@@ -498,6 +511,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT;
char *url = NULL;
bool has_hl = false;
+ bool has_hl_multiple = false;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -550,8 +564,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = (int)val;
}
- hl.hl_id = (int)opts->hl_group;
- has_hl = hl.hl_id > 0;
+ if (HAS_KEY(opts, set_extmark, hl_group)) {
+ if (opts->hl_group.type == kObjectTypeArray) {
+ Array arr = opts->hl_group.data.array;
+ if (arr.size >= 1) {
+ hl.hl_id = object_to_hl_id(arr.items[0], "hl_group item", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
+ for (size_t i = 1; i < arr.size; i++) {
+ int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ if (hl_id) {
+ has_hl_multiple = true;
+ }
+ }
+ } else {
+ hl.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
+ has_hl = hl.hl_id > 0;
+ }
+
sign.hl_id = (int)opts->sign_hl_group;
sign.cursorline_hl_id = (int)opts->cursorline_hl_group;
sign.number_hl_id = (int)opts->number_hl_group;
@@ -590,6 +629,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
virt_text.pos = kVPosOverlay;
} else if (strequal("right_align", str.data)) {
virt_text.pos = kVPosRightAlign;
+ } else if (strequal("eol_right_align", str.data)) {
+ virt_text.pos = kVPosEndOfLineRightAlign;
} else if (strequal("inline", str.data)) {
virt_text.pos = kVPosInline;
} else {
@@ -793,6 +834,21 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
+ if (has_hl_multiple) {
+ Array arr = opts->hl_group.data.array;
+ for (size_t i = arr.size - 1; i > 0; i--) { // skip hl_group[0], handled as hl.hl_id below
+ int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err);
+ if (hl_id > 0) {
+ DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT;
+ sh.hl_id = hl_id;
+ sh.flags = opts->hl_eol ? kSHHlEol : 0;
+ sh.next = decor_indexed;
+ decor_indexed = decor_put_sh(sh);
+ decor_flags |= MT_FLAG_DECOR_HL;
+ }
+ }
+ }
+
DecorInline decor = DECOR_INLINE_INIT;
if (decor_alloc || decor_indexed != DECOR_ID_INVALID || url != NULL
|| schar_high(hl.conceal_char)) {
@@ -856,95 +912,6 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
return extmark_del_id(buf, (uint32_t)ns_id, (uint32_t)id);
}
-uint32_t src2ns(Integer *src_id)
-{
- if (*src_id == 0) {
- *src_id = nvim_create_namespace((String)STRING_INIT);
- }
- if (*src_id < 0) {
- return (((uint32_t)1) << 31) - 1;
- }
- return (uint32_t)(*src_id);
-}
-
-/// Adds a highlight to buffer.
-///
-/// Useful for plugins that dynamically generate highlights to a buffer
-/// (like a semantic highlighter or linter). The function adds a single
-/// highlight to a buffer. Unlike |matchaddpos()| highlights follow changes to
-/// line numbering (as lines are inserted/removed above the highlighted line),
-/// like signs and marks do.
-///
-/// Namespaces are used for batch deletion/updating of a set of highlights. To
-/// create a namespace, use |nvim_create_namespace()| which returns a namespace
-/// id. Pass it in to this function as `ns_id` to add highlights to the
-/// namespace. All highlights in the same namespace can then be cleared with
-/// single call to |nvim_buf_clear_namespace()|. If the highlight never will be
-/// deleted by an API call, pass `ns_id = -1`.
-///
-/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
-/// highlight, the allocated id is then returned. If `hl_group` is the empty
-/// string no highlight is added, but a new `ns_id` is still returned. This is
-/// supported for backwards compatibility, new code should use
-/// |nvim_create_namespace()| to create a new empty namespace.
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param ns_id namespace to use or -1 for ungrouped highlight
-/// @param hl_group Name of the highlight group to use
-/// @param line Line to highlight (zero-indexed)
-/// @param col_start Start of (byte-indexed) column range to highlight
-/// @param col_end End of (byte-indexed) column range to highlight,
-/// or -1 to highlight to end of line
-/// @param[out] err Error details, if any
-/// @return The ns_id that was used
-Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, Integer line,
- Integer col_start, Integer col_end, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
- if (!buf) {
- return 0;
- }
-
- VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
- return 0;
- });
- VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
- return 0;
- });
-
- if (col_end < 0 || col_end > MAXCOL) {
- col_end = MAXCOL;
- }
-
- uint32_t ns = src2ns(&ns_id);
-
- if (!(line < buf->b_ml.ml_line_count)) {
- // safety check, we can't add marks outside the range
- return ns_id;
- }
-
- int hl_id = 0;
- if (hl_group.size > 0) {
- hl_id = syn_check_group(hl_group.data, hl_group.size);
- } else {
- return ns_id;
- }
-
- int end_line = (int)line;
- if (col_end == MAXCOL) {
- col_end = 0;
- end_line++;
- }
-
- DecorInline decor = DECOR_INLINE_INIT;
- decor.data.hl.hl_id = hl_id;
-
- extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
- decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
- return ns_id;
-}
-
/// Clears |namespace|d objects (highlights, |extmarks|, virtual text) from
/// a region.
///
@@ -1012,8 +979,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// ```
/// ["start", tick]
/// ```
-/// - on_buf: called for each buffer being redrawn (before
-/// window callbacks)
+/// - on_buf: called for each buffer being redrawn (once per edit,
+/// before window callbacks)
/// ```
/// ["buf", bufnr, tick]
/// ```
diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h
index af2d51c95c..0b4d84110b 100644
--- a/src/nvim/api/extmark.h
+++ b/src/nvim/api/extmark.h
@@ -1,5 +1,6 @@
#pragma once
+#include <stdbool.h>
#include <stdint.h> // IWYU pragma: keep
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 96aabb851f..6625908cda 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -8,18 +8,19 @@ typedef struct {
typedef struct {
OptionalKeys is_set__context_;
- Array types;
+ ArrayOf(String) types;
} Dict(context);
typedef struct {
OptionalKeys is_set__set_decoration_provider_;
- LuaRef on_start;
- LuaRef on_buf;
- LuaRef on_win;
- LuaRef on_line;
- LuaRef on_end;
- LuaRef _on_hl_def;
- LuaRef _on_spell_nav;
+ LuaRefOf(("start" _, Integer tick), *Boolean) on_start;
+ LuaRefOf(("buf" _, Integer bufnr, Integer tick)) on_buf;
+ LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow),
+ *Boolean) on_win;
+ LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row), *Boolean) on_line;
+ LuaRefOf(("end" _, Integer tick)) on_end;
+ LuaRefOf(("hl_def" _)) _on_hl_def;
+ LuaRefOf(("spell_nav" _)) _on_spell_nav;
} Dict(set_decoration_provider);
typedef struct {
@@ -28,7 +29,7 @@ typedef struct {
Integer end_line;
Integer end_row;
Integer end_col;
- HLGroupID hl_group;
+ Object hl_group;
Array virt_text;
String virt_text_pos;
Integer virt_text_win_col;
@@ -116,7 +117,7 @@ typedef struct {
String relative;
String split;
Window win;
- Array bufpos;
+ ArrayOf(Integer) bufpos;
Boolean external;
Boolean focusable;
Boolean mouse;
@@ -172,17 +173,17 @@ typedef struct {
Boolean altfont;
Boolean nocombine;
Boolean default_ DictKey(default);
- Object cterm;
- Object foreground;
- Object fg;
- Object background;
- Object bg;
- Object ctermfg;
- Object ctermbg;
- Object special;
- Object sp;
- Object link;
- Object global_link;
+ Union(Integer, String) cterm;
+ Union(Integer, String) foreground;
+ Union(Integer, String) fg;
+ Union(Integer, String) background;
+ Union(Integer, String) bg;
+ Union(Integer, String) ctermfg;
+ Union(Integer, String) ctermbg;
+ Union(Integer, String) special;
+ Union(Integer, String) sp;
+ HLGroupID link;
+ HLGroupID global_link;
Boolean fallback;
Integer blend;
Boolean fg_indexed;
@@ -230,9 +231,9 @@ typedef struct {
typedef struct {
OptionalKeys is_set__clear_autocmds_;
Buffer buffer;
- Object event;
- Object group;
- Object pattern;
+ Union(String, ArrayOf(String)) event;
+ Union(Integer, String) group;
+ Union(String, ArrayOf(String)) pattern;
} Dict(clear_autocmds);
typedef struct {
@@ -241,31 +242,33 @@ typedef struct {
Object callback;
String command;
String desc;
- Object group;
+ Union(Integer, String) group;
Boolean nested;
Boolean once;
- Object pattern;
+ Union(String, ArrayOf(String)) pattern;
} Dict(create_autocmd);
typedef struct {
OptionalKeys is_set__exec_autocmds_;
Buffer buffer;
- Object group;
+ Union(Integer, String) group;
Boolean modeline;
- Object pattern;
+ Union(String, ArrayOf(String)) pattern;
Object data;
} Dict(exec_autocmds);
typedef struct {
OptionalKeys is_set__get_autocmds_;
- Object event;
- Object group;
- Object pattern;
- Object buffer;
+ Union(String, ArrayOf(String)) event;
+ Union(Integer, String) group;
+ Union(String, ArrayOf(String)) pattern;
+ Union(Integer, ArrayOf(Integer)) buffer;
+ Integer id;
} Dict(get_autocmds);
typedef struct {
- Object clear;
+ OptionalKeys is_set__create_augroup_;
+ Boolean clear;
} Dict(create_augroup);
typedef struct {
@@ -275,12 +278,12 @@ typedef struct {
Integer count;
String reg;
Boolean bang;
- Array args;
+ ArrayOf(String) args;
Dict magic;
Dict mods;
- Object nargs;
- Object addr;
- Object nextcmd;
+ Union(Integer, String) nargs;
+ String addr;
+ String nextcmd;
} Dict(cmd);
typedef struct {
@@ -324,6 +327,7 @@ typedef struct {
} Dict(cmd_opts);
typedef struct {
+ Boolean err;
Boolean verbose;
} Dict(echo_opts);
@@ -333,11 +337,30 @@ typedef struct {
typedef struct {
OptionalKeys is_set__buf_attach_;
- LuaRef on_lines;
- LuaRef on_bytes;
- LuaRef on_changedtick;
- LuaRef on_detach;
- LuaRef on_reload;
+ LuaRefOf(("lines" _,
+ Integer bufnr,
+ Integer changedtick,
+ Integer first,
+ Integer last_old,
+ Integer last_new,
+ Integer byte_count,
+ Integer *deleted_codepoints,
+ Integer *deleted_codeunits), *Boolean) on_lines;
+ LuaRefOf(("bytes" _,
+ Integer bufnr,
+ Integer changedtick,
+ Integer start_row,
+ Integer start_col,
+ Integer start_byte,
+ Integer old_end_row,
+ Integer old_end_col,
+ Integer old_end_byte,
+ Integer new_end_row,
+ Integer new_end_col,
+ Integer new_end_byte), *Boolean) on_bytes;
+ LuaRefOf(("changedtick" _, Integer bufnr, Integer changedtick)) on_changedtick;
+ LuaRefOf(("detach" _, Integer bufnr)) on_detach;
+ LuaRefOf(("reload" _, Integer bufnr)) on_reload;
Boolean utf_sizes;
Boolean preview;
} Dict(buf_attach);
@@ -350,7 +373,7 @@ typedef struct {
typedef struct {
OptionalKeys is_set__open_term_;
- LuaRef on_input;
+ LuaRefOf(("input" _, Integer term, Integer bufnr, any data)) on_input;
Boolean force_crlf;
} Dict(open_term);
@@ -361,12 +384,13 @@ typedef struct {
typedef struct {
OptionalKeys is_set__xdl_diff_;
- LuaRef on_hunk;
+ LuaRefOf((Integer start_a, Integer count_a, Integer start_b, Integer count_b),
+ *Integer) on_hunk;
String result_type;
String algorithm;
Integer ctxlen;
Integer interhunkctxlen;
- Object linematch;
+ Union(Boolean, Integer) linematch;
Boolean ignore_whitespace;
Boolean ignore_whitespace_change;
Boolean ignore_whitespace_change_at_eol;
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 3289daeb6f..64f8a35d54 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -14,6 +14,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -23,15 +24,15 @@
#endif
static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
- int *scope, OptScope *req_scope, void **from, char **filetype,
+ int *opt_flags, OptScope *scope, void **from, char **filetype,
Error *err)
{
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
if (!strcmp(opts->scope.data, "local")) {
- *scope = OPT_LOCAL;
+ *opt_flags = OPT_LOCAL;
} else if (!strcmp(opts->scope.data, "global")) {
- *scope = OPT_GLOBAL;
+ *opt_flags = OPT_GLOBAL;
} else {
VALIDATE_EXP(false, "scope", "'local' or 'global'", NULL, {
return FAIL;
@@ -39,14 +40,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- *req_scope = kOptScopeGlobal;
+ *scope = kOptScopeGlobal;
if (filetype != NULL && HAS_KEY_X(opts, filetype)) {
*filetype = opts->filetype.data;
}
if (HAS_KEY_X(opts, win)) {
- *req_scope = kOptScopeWin;
+ *scope = kOptScopeWin;
*from = find_window_by_handle(opts->win, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -54,12 +55,12 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
if (HAS_KEY_X(opts, buf)) {
- VALIDATE(!(HAS_KEY_X(opts, scope) && *scope == OPT_GLOBAL), "%s",
+ VALIDATE(!(HAS_KEY_X(opts, scope) && *opt_flags == OPT_GLOBAL), "%s",
"cannot use both global 'scope' and 'buf'", {
return FAIL;
});
- *scope = OPT_LOCAL;
- *req_scope = kOptScopeBuf;
+ *opt_flags = OPT_LOCAL;
+ *scope = kOptScopeBuf;
*from = find_buffer_by_handle(opts->buf, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -81,10 +82,10 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
if (*opt_idxp == kOptInvalid) {
// unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
- } else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) {
+ } else if (*scope == kOptScopeBuf || *scope == kOptScopeWin) {
// if 'buf' or 'win' is passed, make sure the option supports it
- if (!option_has_scope(*opt_idxp, *req_scope)) {
- char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win";
+ if (!option_has_scope(*opt_idxp, *scope)) {
+ char *tgt = *scope == kOptScopeBuf ? "buf" : "win";
char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
char *req = option_has_scope(*opt_idxp, kOptScopeBuf)
? "buffer-local "
@@ -95,7 +96,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- return OK;
+ return ERROR_SET(err) ? FAIL : OK;
#undef HAS_KEY_X
}
@@ -151,13 +152,13 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9) FUNC_API_RET_ALLOC
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *from = NULL;
char *filetype = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, &filetype,
- err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from,
+ &filetype, err)) {
return (Object)OBJECT_INIT;
}
@@ -181,7 +182,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
from = ftbuf;
}
- OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
+ OptVal value = get_option_value_for(opt_idx, opt_flags, scope, from, err);
if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things
@@ -224,10 +225,11 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
FUNC_API_SINCE(9)
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *to = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &to, NULL,
+ err)) {
return;
}
@@ -237,9 +239,9 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
// - option is global or local to window (global-local)
//
// Then force scope to local since we don't want to change the global option
- if (req_scope == kOptScopeWin && scope == 0) {
+ if (scope == kOptScopeWin && opt_flags == 0) {
if (option_has_scope(opt_idx, kOptScopeGlobal)) {
- scope = OPT_LOCAL;
+ opt_flags = OPT_LOCAL;
}
}
@@ -255,7 +257,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
});
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, opt_idx, optval, scope, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, scope, to, err);
});
}
@@ -310,16 +312,16 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error
FUNC_API_SINCE(11)
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *from = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL,
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from, NULL,
err)) {
return (Dict)ARRAY_DICT_INIT;
}
- buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
- win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin;
+ buf_T *buf = (scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
+ win_T *win = (scope == kOptScopeWin) ? (win_T *)from : curwin;
- return get_vimoption(name, scope, buf, win, arena, err);
+ return get_vimoption(name, opt_flags, buf, win, arena, err);
}
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 59e7373f68..5f9d20ee73 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <lauxlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -7,7 +8,6 @@
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/typval.h"
@@ -15,6 +15,7 @@
#include "nvim/eval/userfunc.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 26d5ac09a8..6dee86dcf5 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -21,6 +21,8 @@
# define Dict(name) KeyDict_##name
# define DictHash(name) KeyDict_##name##_get_field
# define DictKey(name)
+# define LuaRefOf(...) LuaRef
+# define Union(...) Object
# include "api/private/defs.h.inline.generated.h"
#endif
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 8ddaecc58e..c98635f8fd 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -31,19 +31,18 @@
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
-#include "nvim/ui.h"
-#include "nvim/ui_defs.h"
-#include "nvim/version.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/api_metadata.generated.h"
-# include "api/private/helpers.c.generated.h"
+# include "api/private/helpers.c.generated.h" // IWYU pragma: keep
#endif
/// Start block that may cause Vimscript exceptions while evaluating another code
///
-/// Used when caller is supposed to be operating when other Vimscript code is being
-/// processed and that “other Vimscript code” must not be affected.
+/// Used just in case caller is supposed to be operating when other Vimscript code
+/// is being processed and that “other Vimscript code” must not be affected.
+///
+/// @warning Avoid calling directly; use TRY_WRAP instead.
///
/// @param[out] tstate Location where try state should be saved.
void try_enter(TryState *const tstate)
@@ -55,74 +54,33 @@ void try_enter(TryState *const tstate)
.current_exception = current_exception,
.msg_list = (const msglist_T *const *)msg_list,
.private_msg_list = NULL,
- .trylevel = trylevel,
.got_int = got_int,
.did_throw = did_throw,
.need_rethrow = need_rethrow,
.did_emsg = did_emsg,
};
+ // `msg_list` controls the collection of abort-causing non-exception errors,
+ // which would otherwise be ignored. This pattern is from do_cmdline().
msg_list = &tstate->private_msg_list;
current_exception = NULL;
- trylevel = 1;
got_int = false;
did_throw = false;
need_rethrow = false;
did_emsg = false;
-}
-
-/// End try block, set the error message if any and restore previous state
-///
-/// @warning Return is consistent with most functions (false on error), not with
-/// try_end (true on error).
-///
-/// @param[in] tstate Previous state to restore.
-/// @param[out] err Location where error should be saved.
-///
-/// @return false if error occurred, true otherwise.
-bool try_leave(const TryState *const tstate, Error *const err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- const bool ret = !try_end(err);
- assert(trylevel == 0);
- assert(!need_rethrow);
- assert(!got_int);
- assert(!did_throw);
- assert(!did_emsg);
- assert(msg_list == &tstate->private_msg_list);
- assert(*msg_list == NULL);
- assert(current_exception == NULL);
- msg_list = (msglist_T **)tstate->msg_list;
- current_exception = tstate->current_exception;
- trylevel = tstate->trylevel;
- got_int = tstate->got_int;
- did_throw = tstate->did_throw;
- need_rethrow = tstate->need_rethrow;
- did_emsg = tstate->did_emsg;
- return ret;
-}
-
-/// Start block that may cause vimscript exceptions
-///
-/// Each try_start() call should be mirrored by try_end() call.
-///
-/// To be used as a replacement of `:try … catch … endtry` in C code, in cases
-/// when error flag could not already be set. If there may be pending error
-/// state at the time try_start() is executed which needs to be preserved,
-/// try_enter()/try_leave() pair should be used instead.
-void try_start(void)
-{
trylevel++;
}
-/// End try block, set the error message if any and return true if an error
-/// occurred.
+/// Ends a `try_enter` block; sets error message if any.
///
-/// @param err Pointer to the stack-allocated error object
-/// @return true if an error occurred
-bool try_end(Error *err)
+/// @warning Avoid calling directly; use TRY_WRAP instead.
+///
+/// @param[out] err Pointer to the stack-allocated error object
+void try_leave(const TryState *const tstate, Error *const err)
+ FUNC_ATTR_NONNULL_ALL
{
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
+ assert(trylevel > 0);
trylevel--;
// Set by emsg(), affects aborting(). See also enter_cleanup().
@@ -165,7 +123,20 @@ bool try_end(Error *err)
discard_current_exception();
}
- return ERROR_SET(err);
+ assert(msg_list == &tstate->private_msg_list);
+ assert(*msg_list == NULL);
+ assert(current_exception == NULL);
+ assert(!got_int);
+ assert(!did_throw);
+ assert(!need_rethrow);
+ assert(!did_emsg);
+ // Restore the exception context.
+ msg_list = (msglist_T **)tstate->msg_list;
+ current_exception = tstate->current_exception;
+ got_int = tstate->got_int;
+ did_throw = tstate->did_throw;
+ need_rethrow = tstate->need_rethrow;
+ did_emsg = tstate->did_emsg;
}
/// Recursively expands a vimscript value in a dict
@@ -805,7 +776,7 @@ char *api_typename(ObjectType t)
UNREACHABLE;
}
-HlMessage parse_hl_msg(Array chunks, Error *err)
+HlMessage parse_hl_msg(Array chunks, bool is_err, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
@@ -820,7 +791,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
String str = copy_string(chunk.items[0].data.string, NULL);
- int hl_id = 0;
+ int hl_id = is_err ? HLF_E : 0;
if (chunk.size == 2) {
hl_id = object_to_hl_id(chunk.items[1], "text highlight", err);
}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index d06f5c9c65..d581c6bc10 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -1,7 +1,7 @@
#pragma once
#include <stdbool.h>
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include "klib/kvec.h"
#include "nvim/api/private/defs.h" // IWYU pragma: keep
@@ -147,27 +147,19 @@ typedef struct {
except_T *current_exception;
msglist_T *private_msg_list;
const msglist_T *const *msg_list;
- int trylevel;
int got_int;
bool did_throw;
int need_rethrow;
int did_emsg;
} TryState;
-// `msg_list` controls the collection of abort-causing non-exception errors,
-// which would otherwise be ignored. This pattern is from do_cmdline().
-//
// TODO(bfredl): prepare error-handling at "top level" (nv_event).
#define TRY_WRAP(err, code) \
do { \
- msglist_T **saved_msg_list = msg_list; \
- msglist_T *private_msg_list; \
- msg_list = &private_msg_list; \
- private_msg_list = NULL; \
- try_start(); \
+ TryState tstate; \
+ try_enter(&tstate); \
code; \
- try_end(err); \
- msg_list = saved_msg_list; /* Restore the exception context. */ \
+ try_leave(&tstate, err); \
} while (0)
// Execute code with cursor position saved and restored and textlock active.
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 56a3f1cf23..dce47cd99f 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -7,11 +7,12 @@
#include "nvim/api/vim.h"
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
-#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
+#include "nvim/types_defs.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/tabpage.c.generated.h"
+# include "api/tabpage.c.generated.h" // IWYU pragma: keep
#endif
/// Gets the windows in a tabpage
@@ -146,11 +147,9 @@ void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err)
}
if (tp == curtab) {
- try_start();
- win_goto(wp);
- if (!try_end(err) && curwin != wp) {
- api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win);
- }
+ TRY_WRAP(err, {
+ win_goto(wp);
+ });
} else if (tp->tp_curwin != wp) {
tp->tp_prevwin = tp->tp_curwin;
tp->tp_curwin = wp;
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index b09a9ed253..f6f32a73ed 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -23,7 +23,6 @@
#include "nvim/event/wstream.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
@@ -34,6 +33,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
@@ -537,7 +537,7 @@ static void prepare_call(RemoteUI *ui, const char *name)
ui_alloc_buf(ui);
}
- // To optimize data transfer(especially for "grid_line"), we bundle adjacent
+ // To optimize data transfer (especially for "grid_line"), we bundle adjacent
// calls to same method together, so only add a new call entry if the last
// method call is different from "name"
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
index cdccc27ba4..3f996ec219 100644
--- a/src/nvim/api/ui.h
+++ b/src/nvim/api/ui.h
@@ -4,6 +4,7 @@
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/highlight_defs.h" // IWYU pragma: keep
+#include "nvim/macros_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
#include "nvim/ui_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 0ed208fc1a..74718e7ac5 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -136,13 +136,13 @@ void tabline_update(Tabpage current, Array tabs, Buffer current_buffer, Array bu
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_show(Array content, Integer pos, String firstc, String prompt, Integer indent,
- Integer level)
+ Integer level, Integer hl_id)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_pos(Integer pos, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_special_char(String c, Boolean shift, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void cmdline_hide(Integer level)
+void cmdline_hide(Integer level, Boolean abort)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_block_show(Array lines)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
@@ -158,7 +158,7 @@ void wildmenu_select(Integer selected)
void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void msg_show(String kind, Array content, Boolean replace_last)
+void msg_show(String kind, Array content, Boolean replace_last, Boolean history)
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 83f9aa573d..c103a56032 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -20,6 +20,7 @@
#include "nvim/api/vim.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
@@ -27,8 +28,8 @@
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -44,6 +45,7 @@
#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"
@@ -76,7 +78,6 @@
#include "nvim/runtime.h"
#include "nvim/sign_defs.h"
#include "nvim/state.h"
-#include "nvim/state_defs.h"
#include "nvim/statusline.h"
#include "nvim/statusline_defs.h"
#include "nvim/strings.h"
@@ -86,8 +87,6 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-#define LINE_BUFFER_MIN_SIZE 4096
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vim.c.generated.h"
#endif
@@ -518,26 +517,6 @@ Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err)
return nlua_exec(code, args, kRetObject, arena, err);
}
-/// Notify the user with a message
-///
-/// Relays the call to vim.notify . By default forwards your message in the
-/// echo area but can be overridden to trigger desktop notifications.
-///
-/// @param msg Message to display to the user
-/// @param log_level The log level
-/// @param opts Reserved for future use.
-/// @param[out] err Error details, if any
-Object nvim_notify(String msg, Integer log_level, Dict opts, Arena *arena, Error *err)
- FUNC_API_SINCE(7)
-{
- MAXSIZE_TEMP_ARRAY(args, 3);
- ADD_C(args, STRING_OBJ(msg));
- ADD_C(args, INTEGER_OBJ(log_level));
- ADD_C(args, DICT_OBJ(opts));
-
- return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
-}
-
/// Calculates the number of display cells occupied by `text`.
/// Control characters including [<Tab>] count as one cell.
///
@@ -594,11 +573,10 @@ ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Er
kvi_init(cookie.rv);
int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
- TryState tstate;
- try_enter(&tstate);
- do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
+ });
return arena_take_arraybuilder(arena, &cookie.rv);
}
@@ -668,16 +646,9 @@ void nvim_set_current_dir(String dir, Error *err)
memcpy(string, dir.data, dir.size);
string[dir.size] = NUL;
- try_start();
-
- if (!changedir_func(string, kCdScopeGlobal)) {
- if (!try_end(err)) {
- api_set_error(err, kErrorTypeException, "Failed to change directory");
- }
- return;
- }
-
- try_end(err);
+ TRY_WRAP(err, {
+ changedir_func(string, kCdScopeGlobal);
+ });
}
/// Gets the current line.
@@ -776,20 +747,24 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, NULL, err);
}
-/// Echo a message.
+/// Prints a message given by a list of `[text, hl_group]` "chunks".
///
-/// @param chunks A list of `[text, hl_group]` arrays, each representing a
-/// text chunk with specified highlight group name or ID.
-/// `hl_group` element can be omitted for no highlight.
+/// Example:
+/// ```lua
+/// vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {})
+/// ```
+///
+/// @param chunks List of `[text, hl_group]` pairs, where each is a `text` string highlighted by
+/// the (optional) name or ID `hl_group`.
/// @param history if true, add to |message-history|.
/// @param opts Optional parameters.
-/// - verbose: Message is 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.
+/// - err: Treat the message like `:echoerr`. Sets `hl_group` to |hl-ErrorMsg| by default.
+/// - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
+/// will write the message to the "log" file instead of standard 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);
+ HlMessage hl_msg = parse_hl_msg(chunks, opts->err, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -798,7 +773,8 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
verbose_enter();
}
- msg_multihl(hl_msg, history ? "echomsg" : "echo", history);
+ char *kind = opts->verbose ? NULL : opts->err ? "echoerr" : history ? "echomsg" : "echo";
+ msg_multihl(hl_msg, kind, history, opts->err);
if (opts->verbose) {
verbose_leave();
@@ -814,37 +790,6 @@ error:
hl_msg_free(hl_msg);
}
-/// Writes a message to the Vim output buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_out_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, false, false);
-}
-
-/// Writes a message to the Vim error buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_err_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, false);
-}
-
-/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
-/// flushed (and displayed).
-///
-/// @param str Message
-/// @see nvim_err_write()
-void nvim_err_writeln(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, true);
-}
-
/// Gets the current list of buffer handles
///
/// Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
@@ -892,19 +837,9 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
return;
}
- if (curwin->w_p_wfb) {
- api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
- return;
- }
-
- try_start();
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
- if (!try_end(err) && result == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to buffer %d",
- buffer);
- }
+ TRY_WRAP(err, {
+ do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
+ });
}
/// Gets the current list of window handles.
@@ -951,14 +886,9 @@ void nvim_set_current_win(Window window, Error *err)
return;
}
- try_start();
- goto_tabpage_win(win_find_tabpage(win), win);
- if (!try_end(err) && win != curwin) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to window %d",
- window);
- }
+ TRY_WRAP(err, {
+ goto_tabpage_win(win_find_tabpage(win), win);
+ });
}
/// Creates a new, empty, unnamed buffer.
@@ -973,74 +903,76 @@ void nvim_set_current_win(Window window, Error *err)
Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
FUNC_API_SINCE(6)
{
- try_start();
- // Block autocommands for now so they don't mess with the buffer before we
- // finish configuring it.
- block_autocmds();
-
- buf_T *buf = buflist_new(NULL, NULL, 0,
- BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
- if (buf == NULL) {
- unblock_autocmds();
- goto fail;
- }
+ Buffer ret = 0;
- // Open the memline for the buffer. This will avoid spurious autocmds when
- // a later nvim_buf_set_lines call would have needed to "open" the buffer.
- if (ml_open(buf) == FAIL) {
- unblock_autocmds();
- goto fail;
- }
-
- // Set last_changedtick to avoid triggering a TextChanged autocommand right
- // after it was added.
- buf->b_last_changedtick = buf_get_changedtick(buf);
- buf->b_last_changedtick_i = buf_get_changedtick(buf);
- buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+ TRY_WRAP(err, {
+ // Block autocommands for now so they don't mess with the buffer before we
+ // finish configuring it.
+ block_autocmds();
+
+ buf_T *buf = buflist_new(NULL, NULL, 0,
+ BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
+ if (buf == NULL) {
+ unblock_autocmds();
+ goto fail;
+ }
- // Only strictly needed for scratch, but could just as well be consistent
- // and do this now. Buffer is created NOW, not when it later first happens
- // to reach a window or aucmd_prepbuf() ..
- buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
+ // Open the memline for the buffer. This will avoid spurious autocmds when
+ // a later nvim_buf_set_lines call would have needed to "open" the buffer.
+ if (ml_open(buf) == FAIL) {
+ unblock_autocmds();
+ goto fail;
+ }
- if (scratch) {
- set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
- kOptScopeBuf, buf);
- set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
- kOptScopeBuf, buf);
- assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
- buf->b_p_swf = false;
- buf->b_p_ml = false;
- }
+ // Set last_changedtick to avoid triggering a TextChanged autocommand right
+ // after it was added.
+ buf->b_last_changedtick = buf_get_changedtick(buf);
+ buf->b_last_changedtick_i = buf_get_changedtick(buf);
+ buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+
+ // Only strictly needed for scratch, but could just as well be consistent
+ // and do this now. Buffer is created NOW, not when it later first happens
+ // to reach a window or aucmd_prepbuf() ..
+ buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
+
+ if (scratch) {
+ set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
+ buf->b_p_swf = false;
+ buf->b_p_ml = false;
+ }
- unblock_autocmds();
+ unblock_autocmds();
- bufref_T bufref;
- set_bufref(&bufref, buf);
- if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
- && !bufref_valid(&bufref)) {
- goto fail;
- }
- if (listed
- && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
- && !bufref_valid(&bufref)) {
- goto fail;
- }
+ bufref_T bufref;
+ set_bufref(&bufref, buf);
+ if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
+ && !bufref_valid(&bufref)) {
+ goto fail;
+ }
+ if (listed
+ && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
+ && !bufref_valid(&bufref)) {
+ goto fail;
+ }
- try_end(err);
- return buf->b_fnum;
+ ret = buf->b_fnum;
+ fail:;
+ });
-fail:
- if (!try_end(err)) {
+ if (ret == 0 && !ERROR_SET(err)) {
api_set_error(err, kErrorTypeException, "Failed to create buffer");
}
- return 0;
+ return ret;
}
/// Open a terminal instance in a buffer
///
/// By default (and currently the only option) the terminal will not be
-/// connected to an external process. Instead, input send on the channel
+/// connected to an external process. Instead, input sent on the channel
/// will be echoed directly by the terminal. This is useful to display
/// ANSI terminal sequences returned as part of a rpc message, or similar.
///
@@ -1051,6 +983,19 @@ fail:
/// Then |nvim_chan_send()| can be called immediately to process sequences
/// in a virtual terminal having the intended size.
///
+/// Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you
+/// can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]()
+/// [terminal-scrollback-pager]()
+///
+/// ```lua
+/// vim.api.nvim_create_user_command('TermHl', function()
+/// local b = vim.api.nvim_create_buf(false, true)
+/// local chan = vim.api.nvim_open_term(b, {})
+/// vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
+/// vim.api.nvim_win_set_buf(0, b)
+/// end, { desc = 'Highlights ANSI termcodes in curbuf' })
+/// ```
+///
/// @param buffer the buffer to use (expected to be empty)
/// @param opts Optional parameters.
/// - on_input: Lua callback for input sent, i e keypresses in terminal
@@ -1205,14 +1150,9 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
return;
}
- try_start();
- goto_tabpage_tp(tp, true, true);
- if (!try_end(err) && tp != curtab) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to tabpage %d",
- tabpage);
- }
+ TRY_WRAP(err, {
+ goto_tabpage_tp(tp, true, true);
+ });
}
/// Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat the input. UIs call
@@ -1545,20 +1485,17 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
return rv;
}
-/// Self-identifies the client.
+/// Self-identifies the client. Sets the `client` object returned by |nvim_get_chan_info()|.
///
-/// The client/plugin/application should call this after connecting, to provide
-/// hints about its identity and purpose, for debugging and orchestration.
+/// Clients should call this just after connecting, to provide hints for debugging and
+/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
+/// `name`.)
///
-/// Can be called more than once; the caller should merge old info if
-/// appropriate. Example: library first identifies the channel, then a plugin
-/// using that library later identifies itself.
-///
-/// @note "Something is better than nothing". You don't need to include all the
-/// fields.
+/// Can be called more than once; the caller should merge old info if appropriate. Example: library
+/// first identifies the channel, then a plugin using that library later identifies itself.
///
/// @param channel_id
-/// @param name Short name for the connected client
+/// @param name Client short-name. Sets the `client.name` field of |nvim_get_chan_info()|.
/// @param version Dict describing the version, with these
/// (optional) keys:
/// - "major" major version (defaults to 0 if not set, for no release yet)
@@ -1632,6 +1569,8 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String
/// Gets information about a channel.
///
+/// See |nvim_list_uis()| for an example of how to get channel info.
+///
/// @param chan channel_id, or 0 for current channel
/// @returns Channel info dict with these keys:
/// - "id" Channel id.
@@ -1649,8 +1588,8 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String
/// "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g.
/// for conpty on Windows).
/// - "buffer" (optional) Buffer connected to |terminal| instance.
-/// - "client" (optional) Info about the peer (client on the other end of the RPC channel),
-/// which it provided via |nvim_set_client_info()|.
+/// - "client" (optional) Info about the peer (client on the other end of the channel), as set
+/// by |nvim_set_client_info()|.
///
Dict nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err)
FUNC_API_SINCE(4)
@@ -1676,55 +1615,6 @@ Array nvim_list_chans(Arena *arena)
return channel_all_info(arena);
}
-/// Writes a message to vim output or error buffer. The string is split
-/// and flushed after each newline. Incomplete lines are kept for writing
-/// later.
-///
-/// @param message Message to write
-/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
-/// @param writeln Append a trailing newline
-static void write_msg(String message, bool to_err, bool writeln)
-{
- static StringBuilder out_line_buf = KV_INITIAL_VALUE;
- static StringBuilder err_line_buf = KV_INITIAL_VALUE;
- StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
-
-#define PUSH_CHAR(c) \
- if (kv_max(*line_buf) == 0) { \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } \
- if (c == NL) { \
- kv_push(*line_buf, NUL); \
- if (to_err) { \
- emsg(line_buf->items); \
- } else { \
- msg(line_buf->items, 0); \
- } \
- if (msg_silent == 0) { \
- msg_didout = true; \
- } \
- kv_drop(*line_buf, kv_size(*line_buf)); \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } else if (c == NUL) { \
- kv_push(*line_buf, NL); \
- } else { \
- kv_push(*line_buf, c); \
- }
-
- no_wait_return++;
- for (uint32_t i = 0; i < message.size; i++) {
- if (got_int) {
- break;
- }
- PUSH_CHAR(message.data[i]);
- }
- if (writeln) {
- PUSH_CHAR(NL);
- }
- no_wait_return--;
- msg_end();
-}
-
// Functions used for testing purposes
/// Returns object given as argument.
@@ -1796,6 +1686,14 @@ Dict nvim__stats(Arena *arena)
/// Gets a list of dictionaries representing attached UIs.
///
+/// Example: The Nvim builtin |TUI| sets its channel info as described in |startup-tui|. In
+/// particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by
+/// inspecting the client name of each UI:
+///
+/// ```lua
+/// vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name)
+/// ```
+///
/// @return Array of UI dictionaries, each with these keys:
/// - "height" Requested height of the UI
/// - "width" Requested width of the UI
@@ -2086,7 +1984,9 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err)
/// the "highlights" key in {opts} is true. Each element of the array is a
/// |Dict| with these keys:
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
-/// - group: (string) Name of highlight group.
+/// - group: (string) Name of highlight group. May be removed in the future, use
+/// `groups` instead.
+/// - groups: (array) Names of stacked highlight groups (highest priority last).
Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(8) FUNC_API_FAST
{
@@ -2138,6 +2038,7 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
});
int stc_hl_id = 0;
+ int scl_hl_id = 0;
statuscol_T statuscol = { 0 };
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
@@ -2146,23 +2047,18 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
int cul_id = 0;
int num_id = 0;
linenr_T lnum = statuscol_lnum;
+ foldinfo_T cursorline_fi = { 0 };
decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
statuscol.sattrs = sattrs;
statuscol.foldinfo = fold_info(wp, lnum);
- wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+ win_update_cursorline(wp, &cursorline_fi);
+ statuscol.sign_cul_id = use_cursor_line_highlight(wp, lnum) ? cul_id : 0;
+ scl_hl_id = use_cursor_line_highlight(wp, lnum) ? HLF_CLS : HLF_SC;
- if (wp->w_p_cul) {
- if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) {
- wp->w_cursorline = statuscol.foldinfo.fi_lnum;
- }
- statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR);
- }
-
- statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0;
if (num_id) {
stc_hl_id = num_id;
- } else if (statuscol.use_cul) {
+ } else if (use_cursor_line_highlight(wp, lnum)) {
stc_hl_id = HLF_CLN;
} else if (wp->w_p_rnu) {
stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB);
@@ -2215,22 +2111,19 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
// If first character doesn't have a defined highlight,
// add the default highlight at the beginning of the highlight list
+ const char *dfltname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
+ opts->use_winbar, stc_hl_id);
if (hltab->start == NULL || (hltab->start - buf) != 0) {
- Dict hl_info = arena_dict(arena, 2);
- const char *grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
- opts->use_winbar, stc_hl_id);
-
+ Dict hl_info = arena_dict(arena, 3);
PUT_C(hl_info, "start", INTEGER_OBJ(0));
- PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
-
+ PUT_C(hl_info, "group", CSTR_AS_OBJ(dfltname));
+ Array groups = arena_array(arena, 1);
+ ADD_C(groups, CSTR_AS_OBJ(dfltname));
+ PUT_C(hl_info, "groups", ARRAY_OBJ(groups));
ADD_C(hl_values, DICT_OBJ(hl_info));
}
for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) {
- Dict hl_info = arena_dict(arena, 2);
-
- PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
-
const char *grpname;
if (sp->userhl == 0) {
grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
@@ -2240,7 +2133,18 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
grpname = arena_memdupz(arena, user_group, strlen(user_group));
}
+
+ const char *combine = sp->item == STL_SIGNCOL ? syn_id2name(scl_hl_id)
+ : sp->item == STL_FOLDCOL ? grpname : dfltname;
+ Dict hl_info = arena_dict(arena, 3);
+ PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
+ Array groups = arena_array(arena, 1 + (combine != grpname));
+ if (combine != grpname) {
+ ADD_C(groups, CSTR_AS_OBJ(combine));
+ }
+ ADD_C(groups, CSTR_AS_OBJ(grpname));
+ PUT_C(hl_info, "groups", ARRAY_OBJ(groups));
ADD_C(hl_values, DICT_OBJ(hl_info));
}
PUT_C(result, "highlights", ARRAY_OBJ(hl_values));
@@ -2270,9 +2174,13 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
/// @return Dict containing these keys:
/// - winid: (number) floating window id
/// - bufnr: (number) buffer id in floating window
-Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena)
+Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena, Error *err)
{
Dict rv = arena_dict(arena, 2);
+ if ((get_cot_flags() & kOptCotFlagPopup) == 0) {
+ api_set_error(err, kErrorTypeException, "completeopt option does not include popup");
+ return rv;
+ }
if (HAS_KEY(opts, complete_set, info)) {
win_T *wp = pum_set_info((int)index, opts->info.data);
if (wp) {
@@ -2389,13 +2297,23 @@ void nvim__redraw(Dict(redraw) *opts, Error *err)
"%s", "Invalid 'range': Expected 2-tuple of Integers", {
return;
});
- linenr_T first = (linenr_T)kv_A(opts->range, 0).data.integer + 1;
- linenr_T last = (linenr_T)kv_A(opts->range, 1).data.integer;
+ int64_t begin_raw = kv_A(opts->range, 0).data.integer;
+ int64_t end_raw = kv_A(opts->range, 1).data.integer;
+
buf_T *rbuf = win ? win->w_buffer : (buf ? buf : curbuf);
- if (last == -1) {
- last = rbuf->b_ml.ml_line_count;
+ linenr_T line_count = rbuf->b_ml.ml_line_count;
+
+ int begin = (int)MIN(begin_raw, line_count);
+ int end;
+ if (end_raw == -1) {
+ end = line_count;
+ } else {
+ end = (int)MIN(MAX(begin, end_raw), line_count);
+ }
+
+ if (begin < end) {
+ redraw_buf_range_later(rbuf, 1 + begin, end);
}
- redraw_buf_range_later(rbuf, first, last);
}
// Redraw later types require update_screen() so call implicitly unless set to false.
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 165cc93fbe..fc7e7e1a06 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
@@ -21,6 +22,7 @@
#include "nvim/garray_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/runtime.h"
#include "nvim/vim_defs.h"
#include "nvim/viml/parser/expressions.h"
@@ -78,24 +80,24 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
capture_ga = &capture_local;
}
- try_start();
- if (opts->output) {
- msg_silent++;
- msg_col = 0; // prevent leading spaces
- }
+ TRY_WRAP(err, {
+ if (opts->output) {
+ msg_silent++;
+ msg_col = 0; // prevent leading spaces
+ }
- const sctx_T save_current_sctx = api_set_sctx(channel_id);
+ const sctx_T save_current_sctx = api_set_sctx(channel_id);
- do_source_str(src.data, "nvim_exec2()");
- if (opts->output) {
- capture_ga = save_capture_ga;
- msg_silent = save_msg_silent;
- // Put msg_col back where it was, since nothing should have been written.
- msg_col = save_msg_col;
- }
+ do_source_str(src.data, "nvim_exec2()");
+ if (opts->output) {
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ // Put msg_col back where it was, since nothing should have been written.
+ msg_col = save_msg_col;
+ }
- current_sctx = save_current_sctx;
- try_end(err);
+ current_sctx = save_current_sctx;
+ });
if (ERROR_SET(err)) {
goto theend;
@@ -125,19 +127,17 @@ theend:
///
/// On execution error: fails with Vimscript error, updates v:errmsg.
///
-/// Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate multiple lines of Vim script
-/// or an Ex command directly, use |nvim_exec2()|. To construct an Ex command using a structured
-/// format and then execute it, use |nvim_cmd()|. To modify an Ex command before evaluating it, use
-/// |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
+/// Prefer |nvim_cmd()| or |nvim_exec2()| instead. To modify an Ex command in a structured way
+/// before executing it, modify the result of |nvim_parse_cmd()| then pass it to |nvim_cmd()|.
///
/// @param command Ex command string
/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
FUNC_API_SINCE(1)
{
- try_start();
- do_cmdline_cmd(command.data);
- try_end(err);
+ TRY_WRAP(err, {
+ do_cmdline_cmd(command.data);
+ });
}
/// Evaluates a Vimscript |expression|. Dicts and Lists are recursively expanded.
@@ -231,10 +231,9 @@ static Object _call_function(String fn, Array args, dict_T *self, Arena *arena,
funcexe.fe_selfdict = self;
TRY_WRAP(err, {
- // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
- // (see above) to capture abort-causing non-exception errors.
- call_func(fn.data, (int)fn.size, &rettv, (int)args.size,
- vim_args, &funcexe);
+ // call_func() retval is deceptive, ignore it. Instead TRY_WRAP sets `msg_list` to capture
+ // abort-causing non-exception errors.
+ (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size, vim_args, &funcexe);
});
if (!ERROR_SET(err)) {
@@ -282,20 +281,23 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena,
typval_T rettv;
bool mustfree = false;
switch (dict.type) {
- case kObjectTypeString:
- try_start();
- if (eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE) == FAIL) {
- api_set_error(err, kErrorTypeException,
- "Failed to evaluate dict expression");
- }
- clear_evalarg(&EVALARG_EVALUATE, NULL);
- if (try_end(err)) {
+ case kObjectTypeString: {
+ int eval_ret;
+ TRY_WRAP(err, {
+ eval_ret = eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE);
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+ });
+ if (ERROR_SET(err)) {
return rv;
}
+ if (eval_ret != OK) {
+ abort(); // Should not happen.
+ }
// Evaluation of the string arg created a new dict or increased the
// refcount of a dict. Not necessary for a RPC dict.
mustfree = true;
break;
+ }
case kObjectTypeDict:
object_to_vim(dict, &rettv, err);
break;
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 6f5a9a90c0..1132452faf 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <stdbool.h>
#include <string.h>
@@ -7,25 +8,22 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/tabpage.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
-#include "nvim/decoration.h"
#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
#include "nvim/eval/window.h"
-#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
@@ -33,7 +31,6 @@
#include "nvim/syntax.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
#include "nvim/ui_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -104,10 +101,12 @@
/// @param 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
+/// - "cursor" Cursor position in current window.
+/// - "editor" The global editor grid.
+/// - "laststatus" 'laststatus' if present, or last row.
+/// - "mouse" Mouse position.
+/// - "tabline" Tabline if present, or first row.
+/// - "win" Window given by the `win` field, or current window.
/// - win: |window-ID| window to split, or relative window when creating a
/// float (relative="win").
/// - anchor: Decides which corner of the float to place at (row,col):
@@ -702,7 +701,9 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
FUNC_API_SINCE(6)
{
/// Keep in sync with FloatRelative in buffer_defs.h
- static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" };
+ static const char *const float_relative_str[] = {
+ "editor", "win", "cursor", "mouse", "tabline", "laststatus"
+ };
/// Keep in sync with WinSplit in buffer_defs.h
static const char *const win_split_str[] = { "left", "right", "above", "below" };
@@ -808,6 +809,10 @@ static bool parse_float_relative(String relative, FloatRelative *out)
*out = kFloatRelativeCursor;
} else if (striequal(str, "mouse")) {
*out = kFloatRelativeMouse;
+ } else if (striequal(str, "tabline")) {
+ *out = kFloatRelativeTabline;
+ } else if (striequal(str, "laststatus")) {
+ *out = kFloatRelativeLaststatus;
} else {
return false;
}
@@ -890,7 +895,7 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
*is_present = true;
}
-static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
+static bool parse_bordertext_pos(win_T *wp, String bordertext_pos, BorderTextType bordertext_type,
WinConfig *fconfig, Error *err)
{
AlignTextPos *align;
@@ -904,7 +909,9 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex
}
if (bordertext_pos.size == 0) {
- *align = kAlignLeft;
+ if (!wp) {
+ *align = kAlignLeft;
+ }
return true;
}
@@ -1245,7 +1252,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
}
// handles unset 'title_pos' same as empty string
- if (!parse_bordertext_pos(config->title_pos, kBorderTextTitle, fconfig, err)) {
+ if (!parse_bordertext_pos(wp, config->title_pos, kBorderTextTitle, fconfig, err)) {
goto fail;
}
} else {
@@ -1272,7 +1279,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
}
// handles unset 'footer_pos' same as empty string
- if (!parse_bordertext_pos(config->footer_pos, kBorderTextFooter, fconfig, err)) {
+ if (!parse_bordertext_pos(wp, config->footer_pos, kBorderTextFooter, fconfig, err)) {
goto fail;
}
} else {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 5a4972ef23..d968af421d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -1,4 +1,3 @@
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -28,7 +27,7 @@
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/window.c.generated.h"
+# include "api/window.c.generated.h" // IWYU pragma: keep
#endif
/// Gets the current buffer in a window
@@ -63,11 +62,6 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
return;
}
- if (win->w_p_wfb) {
- api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
- return;
- }
-
if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return;
@@ -187,14 +181,9 @@ void nvim_win_set_height(Window window, Integer height, Error *err)
return;
}
- if (height > INT_MAX || height < INT_MIN) {
- api_set_error(err, kErrorTypeValidation, "Height value outside range");
- return;
- }
-
- try_start();
- win_setheight_win((int)height, win);
- try_end(err);
+ TRY_WRAP(err, {
+ win_setheight_win((int)height, win);
+ });
}
/// Gets the window width
@@ -229,14 +218,9 @@ void nvim_win_set_width(Window window, Integer width, Error *err)
return;
}
- if (width > INT_MAX || width < INT_MIN) {
- api_set_error(err, kErrorTypeValidation, "Width value outside range");
- return;
- }
-
- try_start();
- win_setwidth_win((int)width, win);
- try_end(err);
+ TRY_WRAP(err, {
+ win_setwidth_win((int)width, win);
+ });
}
/// Gets a window-scoped (w:) variable
@@ -383,19 +367,16 @@ void nvim_win_hide(Window window, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- TryState tstate;
- try_enter(&tstate);
-
- // Never close the autocommand window.
- if (is_aucmd_win(win)) {
- emsg(_(e_autocmd_close));
- } else if (tabpage == curtab) {
- win_close(win, false, false);
- } else {
- win_close_othertab(win, false, tabpage);
- }
-
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ // Never close the autocommand window.
+ if (is_aucmd_win(win)) {
+ emsg(_(e_autocmd_close));
+ } else if (tabpage == curtab) {
+ win_close(win, false, false);
+ } else {
+ win_close_othertab(win, false, tabpage);
+ }
+ });
}
/// Closes the window (like |:close| with a |window-ID|).
@@ -415,10 +396,9 @@ void nvim_win_close(Window window, Boolean force, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- TryState tstate;
- try_enter(&tstate);
- ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
+ });
}
/// Calls a function with window as temporary current window.
@@ -441,15 +421,15 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- try_start();
Object res = OBJECT_INIT;
- win_execute_T win_execute_args;
- if (win_execute_before(&win_execute_args, win, tabpage)) {
- Array args = ARRAY_DICT_INIT;
- res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
- }
- win_execute_after(&win_execute_args);
- try_end(err);
+ TRY_WRAP(err, {
+ win_execute_T win_execute_args;
+ if (win_execute_before(&win_execute_args, win, tabpage)) {
+ Array args = ARRAY_DICT_INIT;
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ }
+ win_execute_after(&win_execute_args);
+ });
return res;
}
@@ -476,6 +456,7 @@ void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)
}
win->w_ns_hl = (NS)ns_id;
+ win->w_ns_hl_winhl = -1;
win->w_hl_needs_update = true;
redraw_later(win, UPD_NOT_VALID);
}
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index bb639edc07..361bb8db12 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -31,6 +31,7 @@
#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/os/input.h"
@@ -1096,6 +1097,10 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
tabpage_T *const new_lu_tp = curtab;
+ // Stop Visual mode, the cursor and "VIsual" may very well be invalid after
+ // switching to another buffer.
+ reset_VIsual_and_resel();
+
// 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.
diff --git a/src/nvim/ascii_defs.h b/src/nvim/ascii_defs.h
index 155a18fb95..86187b553c 100644
--- a/src/nvim/ascii_defs.h
+++ b/src/nvim/ascii_defs.h
@@ -103,6 +103,15 @@ static inline bool ascii_iswhite_or_nul(int c)
return ascii_iswhite(c) || c == NUL;
}
+/// Checks if `c` is a space or tab or newline character or NUL.
+///
+/// @see {ascii_isdigit}
+static inline bool ascii_iswhite_nl_or_nul(int c)
+ FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE
+{
+ return ascii_iswhite(c) || c == '\n' || c == NUL;
+}
+
/// Check whether character is a decimal digit.
///
/// Library isdigit() function is officially locale-dependent and, for
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index c08ef7a4c1..96da4695de 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -30,8 +30,8 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/hashtab.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
@@ -42,7 +42,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -75,7 +74,6 @@ static const char e_autocommand_nesting_too_deep[]
// Naming Conventions:
// - general autocmd behavior start with au_
// - AutoCmd start with aucmd_
-// - Autocmd.exec stat with aucmd_exec
// - AutoPat start with aupat_
// - Groups start with augroup_
// - Events start with event_
@@ -255,24 +253,24 @@ static void au_show_for_event(int group, event_T event, const char *pat)
return;
}
- char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
+ char *handler_str = aucmd_handler_to_string(ac);
if (ac->desc != NULL) {
size_t msglen = 100;
char *msg = xmallocz(msglen);
- if (ac->exec.type == CALLABLE_CB) {
- msg_puts_hl(exec_to_string, HLF_8, false);
- snprintf(msg, msglen, " [%s]", ac->desc);
+ if (ac->handler_cmd) {
+ snprintf(msg, msglen, "%s [%s]", handler_str, ac->desc);
} else {
- snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc);
+ msg_puts_hl(handler_str, HLF_8, false);
+ snprintf(msg, msglen, " [%s]", ac->desc);
}
msg_outtrans(msg, 0, false);
XFREE_CLEAR(msg);
- } else if (ac->exec.type == CALLABLE_CB) {
- msg_puts_hl(exec_to_string, HLF_8, false);
+ } else if (ac->handler_cmd) {
+ msg_outtrans(handler_str, 0, false);
} else {
- msg_outtrans(exec_to_string, 0, false);
+ msg_puts_hl(handler_str, HLF_8, false);
}
- XFREE_CLEAR(exec_to_string);
+ XFREE_CLEAR(handler_str);
if (p_verbose > 0) {
last_set_msg(ac->script_ctx);
}
@@ -304,7 +302,11 @@ static void aucmd_del(AutoCmd *ac)
xfree(ac->pat);
}
ac->pat = NULL;
- aucmd_exec_free(&ac->exec);
+ if (ac->handler_cmd) {
+ XFREE_CLEAR(ac->handler_cmd);
+ } else {
+ callback_free(&ac->handler_fn);
+ }
XFREE_CLEAR(ac->desc);
au_need_clean = true;
@@ -900,8 +902,8 @@ void do_all_autocmd_events(const char *pat, bool once, int nested, char *cmd, bo
// If *cmd == NUL: show entries.
// If forceit == true: delete entries.
// If group is not AUGROUP_ALL: only use this group.
-int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char *cmd, bool del,
- int group)
+int do_autocmd_event(event_T event, const char *pat, bool once, int nested, const char *cmd,
+ bool del, int group)
FUNC_ATTR_NONNULL_ALL
{
// Cannot be used to show all patterns. See au_show_for_event or au_show_for_all_events
@@ -959,10 +961,8 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char
}
if (is_adding_cmd) {
- AucmdExecutable exec = AUCMD_EXECUTABLE_INIT;
- exec.type = CALLABLE_EX;
- exec.callable.cmd = cmd;
- autocmd_register(0, event, pat, patlen, group, once, nested, NULL, exec);
+ Callback handler_fn = CALLBACK_INIT;
+ autocmd_register(0, event, pat, patlen, group, once, nested, NULL, cmd, &handler_fn);
}
pat = aucmd_next_pattern(pat, (size_t)patlen);
@@ -973,8 +973,13 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char
return OK;
}
+/// Registers an autocmd. The handler may be a Ex command or callback function, decided by
+/// the `handler_cmd` or `handler_fn` args.
+///
+/// @param handler_cmd Handler Ex command, or NULL if handler is a function (`handler_fn`).
+/// @param handler_fn Handler function, ignored if `handler_cmd` is not NULL.
int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int group, bool once,
- bool nested, char *desc, AucmdExecutable aucmd)
+ bool nested, char *desc, const char *handler_cmd, Callback *handler_fn)
{
// 0 is not a valid group.
assert(group != 0);
@@ -1082,7 +1087,12 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int
AutoCmd *ac = kv_pushp(autocmds[(int)event]);
ac->pat = ap;
ac->id = id;
- ac->exec = aucmd_exec_copy(aucmd);
+ if (handler_cmd) {
+ ac->handler_cmd = xstrdup(handler_cmd);
+ } else {
+ ac->handler_cmd = NULL;
+ callback_copy(&ac->handler_fn, handler_fn);
+ }
ac->script_ctx = current_sctx;
ac->script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&ac->script_ctx);
@@ -1666,7 +1676,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
} else {
autocmd_fname = fname_io;
}
+ char *afile_orig = NULL; ///< Unexpanded <afile>
if (autocmd_fname != NULL) {
+ afile_orig = xstrdup(autocmd_fname);
// Allocate MAXPATHL for when eval_vars() resolves the fullpath.
autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL);
}
@@ -1798,6 +1810,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// save vector size, to avoid an endless loop when more patterns
// are added when executing autocommands
.ausize = kv_size(autocmds[(int)event]),
+ .afile_orig = afile_orig,
.fname = fname,
.sfname = sfname,
.tail = tail,
@@ -1865,6 +1878,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
autocmd_nested = save_autocmd_nested;
xfree(SOURCING_NAME);
estack_pop();
+ xfree(afile_orig);
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
autocmd_fname_full = save_autocmd_fname_full;
@@ -2022,15 +2036,16 @@ static void aucmd_next(AutoPatCmd *apc)
apc->auidx = SIZE_MAX;
}
-static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
+/// Executes an autocmd callback function (as opposed to an Ex command).
+static bool au_callback(const AutoCmd *ac, const AutoPatCmd *apc)
{
- Callback callback = ac->exec.callable.cb;
+ Callback callback = ac->handler_fn;
if (callback.type == kCallbackLua) {
MAXSIZE_TEMP_DICT(data, 7);
PUT_C(data, "id", INTEGER_OBJ(ac->id));
PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event)));
+ PUT_C(data, "file", CSTR_AS_OBJ(apc->afile_orig));
PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match));
- PUT_C(data, "file", CSTR_AS_OBJ(autocmd_fname));
PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
@@ -2089,10 +2104,10 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
if (p_verbose >= 9) {
verbose_enter_scroll();
- char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
- smsg(0, _("autocommand %s"), exec_to_string);
+ char *handler_str = aucmd_handler_to_string(ac);
+ smsg(0, _("autocommand %s"), handler_str);
msg_puts("\n"); // don't overwrite this either
- XFREE_CLEAR(exec_to_string);
+ XFREE_CLEAR(handler_str);
verbose_leave_scroll();
}
@@ -2103,16 +2118,22 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
apc->script_ctx = current_sctx;
char *retval;
- if (ac->exec.type == CALLABLE_CB) {
- // Can potentially reallocate kvec_t data and invalidate the ac pointer
- if (call_autocmd_callback(ac, apc)) {
- // If an autocommand callback returns true, delete the autocommand
- oneshot = true;
- }
-
- // TODO(tjdevries):
- //
- // Major Hack Alert:
+ if (ac->handler_cmd) {
+ retval = xstrdup(ac->handler_cmd);
+ } else {
+ AutoCmd ac_copy = *ac;
+ // Mark oneshot handler as "removed" now, to prevent recursion by e.g. `:doautocmd`. #25526
+ ac->pat = oneshot ? NULL : ac->pat;
+ // May reallocate `acs` kvec_t data and invalidate the `ac` pointer.
+ bool rv = au_callback(&ac_copy, apc);
+ if (oneshot) {
+ // Restore `pat`. Use `acs` because `ac` may have been invalidated by the callback.
+ kv_A(*acs, apc->auidx).pat = ac_copy.pat;
+ }
+ // If an autocommand callback returns true, delete the autocommand
+ oneshot = oneshot || rv;
+
+ // HACK(tjdevries):
// We just return "not-null" and continue going.
// This would be a good candidate for a refactor. You would need to refactor:
// 1. do_cmdline to accept something besides a string
@@ -2121,8 +2142,6 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
// and instead we loop over all the matches and just execute one-by-one.
// However, my expectation would be that could be expensive.
retval = xcalloc(1, 1);
- } else {
- retval = xstrdup(ac->exec.callable.cmd);
}
// Remove one-shot ("once") autocmd in anticipation of its execution.
@@ -2441,60 +2460,14 @@ bool autocmd_delete_id(int64_t id)
return success;
}
-// ===========================================================================
-// AucmdExecutable Functions
-// ===========================================================================
-
-/// Generate a string description for the command/callback of an autocmd
-char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc)
+/// Gets an (allocated) string representation of an autocmd command/callback.
+char *aucmd_handler_to_string(AutoCmd *ac)
FUNC_ATTR_PURE
{
- switch (acc.type) {
- case CALLABLE_EX:
- return xstrdup(acc.callable.cmd);
- case CALLABLE_CB:
- return callback_to_string(&acc.callable.cb, NULL);
- case CALLABLE_NONE:
- return "This is not possible";
+ if (ac->handler_cmd) {
+ return xstrdup(ac->handler_cmd);
}
-
- abort();
-}
-
-void aucmd_exec_free(AucmdExecutable *acc)
-{
- switch (acc->type) {
- case CALLABLE_EX:
- XFREE_CLEAR(acc->callable.cmd);
- break;
- case CALLABLE_CB:
- callback_free(&acc->callable.cb);
- break;
- case CALLABLE_NONE:
- return;
- }
-
- acc->type = CALLABLE_NONE;
-}
-
-AucmdExecutable aucmd_exec_copy(AucmdExecutable src)
-{
- AucmdExecutable dest = AUCMD_EXECUTABLE_INIT;
-
- switch (src.type) {
- case CALLABLE_EX:
- dest.type = CALLABLE_EX;
- dest.callable.cmd = xstrdup(src.callable.cmd);
- return dest;
- case CALLABLE_CB:
- dest.type = CALLABLE_CB;
- callback_copy(&dest.callable.cb, &src.callable.cb);
- return dest;
- case CALLABLE_NONE:
- return dest;
- }
-
- abort();
+ return callback_to_string(&ac->handler_fn, NULL);
}
bool au_event_is_empty(event_T event)
diff --git a/src/nvim/autocmd_defs.h b/src/nvim/autocmd_defs.h
index 490782b209..970aced506 100644
--- a/src/nvim/autocmd_defs.h
+++ b/src/nvim/autocmd_defs.h
@@ -37,10 +37,11 @@ typedef struct {
} AutoPat;
typedef struct {
- AucmdExecutable exec; ///< Command or callback function
AutoPat *pat; ///< Pattern reference (NULL when autocmd was removed)
int64_t id; ///< ID used for uniquely tracking an autocmd
char *desc; ///< Description for the autocmd
+ char *handler_cmd; ///< Handler Ex command (NULL if handler is a function).
+ Callback handler_fn; ///< Handler callback (ignored if `handler_cmd` is not NULL).
sctx_T script_ctx; ///< Script context where it is defined
bool once; ///< "One shot": removed after execution
bool nested; ///< If autocommands nest here
@@ -52,6 +53,7 @@ struct AutoPatCmd_S {
AutoPat *lastpat; ///< Last matched AutoPat
size_t auidx; ///< Current autocmd index to execute
size_t ausize; ///< Saved AutoCmd vector size
+ char *afile_orig; ///< Unexpanded <afile>
char *fname; ///< Fname to match with
char *sfname; ///< Sfname to match with
char *tail; ///< Tail of fname
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index abcce0dfe8..9e6877cbfa 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -875,6 +875,7 @@ static void free_buffer(buf_T *buf)
aubuflocal_remove(buf);
xfree(buf->additional_data);
xfree(buf->b_prompt_text);
+ kv_destroy(buf->b_wininfo);
callback_free(&buf->b_prompt_callback);
callback_free(&buf->b_prompt_interrupt);
clear_fmark(&buf->b_last_cursor, 0);
@@ -901,13 +902,10 @@ static void free_buffer(buf_T *buf)
/// Free the b_wininfo list for buffer "buf".
static void clear_wininfo(buf_T *buf)
{
- wininfo_T *wip;
-
- while (buf->b_wininfo != NULL) {
- wip = buf->b_wininfo;
- buf->b_wininfo = wip->wi_next;
- free_wininfo(wip, buf);
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ free_wininfo(kv_A(buf->b_wininfo, i), buf);
}
+ kv_size(buf->b_wininfo) = 0;
}
/// Free stuff in the buffer for ":bdel" and when wiping out the buffer.
@@ -1393,7 +1391,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
// If the buffer to be deleted is not the current one, delete it here.
if (buf != curbuf) {
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
// Remove the buffer to be deleted from the jump list.
mark_jumplist_forget_file(curwin, buf_fnum);
}
@@ -1419,7 +1417,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) {
buf = au_new_curbuf.br_buf;
} else if (curwin->w_jumplistlen > 0) {
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
// Remove the buffer from the jump list.
mark_jumplist_forget_file(curwin, buf_fnum);
}
@@ -1429,7 +1427,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
if (curwin->w_jumplistlen > 0) {
int jumpidx = curwin->w_jumplistidx;
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
// If the index is the same as the length, the current position was not yet added to the
// jump list. So we can safely go back to the last entry and search from there.
if (jumpidx == curwin->w_jumplistlen) {
@@ -1443,7 +1441,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
}
forward = jumpidx;
- while ((jop_flags & JOP_CLEAN) || jumpidx != curwin->w_jumplistidx) {
+ while ((jop_flags & kOptJopFlagClean) || jumpidx != curwin->w_jumplistidx) {
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
if (buf != NULL) {
@@ -1460,7 +1458,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
}
}
if (buf != NULL) { // found a valid buffer: stop searching
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
curwin->w_jumplistidx = jumpidx;
update_jumplist = false;
}
@@ -1926,7 +1924,8 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
}
clear_wininfo(buf);
- buf->b_wininfo = xcalloc(1, sizeof(wininfo_T));
+ WinInfo *curwin_info = xcalloc(1, sizeof(WinInfo));
+ kv_push(buf->b_wininfo, curwin_info);
if (buf == curbuf) {
free_buffer_stuff(buf, kBffInitChangedtick); // delete local vars et al.
@@ -1964,9 +1963,9 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
buf_copy_options(buf, BCO_ALWAYS);
}
- buf->b_wininfo->wi_mark = (fmark_T)INIT_FMARK;
- buf->b_wininfo->wi_mark.mark.lnum = lnum;
- buf->b_wininfo->wi_win = curwin;
+ curwin_info->wi_mark = (fmark_T)INIT_FMARK;
+ curwin_info->wi_mark.mark.lnum = lnum;
+ curwin_info->wi_win = curwin;
hash_init(&buf->b_s.b_keywtab);
hash_init(&buf->b_s.b_keywtab_ic);
@@ -2159,11 +2158,11 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
// current buffer isn't empty: open new tab or window
- if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
+ if (wp == NULL && (swb_flags & (kOptSwbFlagVsplit | kOptSwbFlagSplit | kOptSwbFlagNewtab))
&& !buf_is_empty(curbuf)) {
- if (swb_flags & SWB_NEWTAB) {
+ if (swb_flags & kOptSwbFlagNewtab) {
tabpage_new();
- } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
+ } else if (win_split(0, (swb_flags & kOptSwbFlagVsplit) ? WSP_VERT : 0)
== FAIL) {
return FAIL;
}
@@ -2183,7 +2182,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = true;
}
- if (jop_flags & JOP_VIEW && restore_view) {
+ if (jop_flags & kOptJopFlagView && restore_view) {
mark_view_restore(fm);
}
return OK;
@@ -2631,30 +2630,26 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
bool copy_options)
FUNC_ATTR_NONNULL_ARG(1)
{
- wininfo_T *wip;
+ WinInfo *wip;
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ size_t i;
+ for (i = 0; i < kv_size(buf->b_wininfo); i++) {
+ wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == win) {
break;
}
}
- if (wip == NULL) {
+
+ if (i == kv_size(buf->b_wininfo)) {
// allocate a new entry
- wip = xcalloc(1, sizeof(wininfo_T));
+ wip = xcalloc(1, sizeof(WinInfo));
wip->wi_win = win;
if (lnum == 0) { // set lnum even when it's 0
lnum = 1;
}
} else {
// remove the entry from the list
- if (wip->wi_prev) {
- wip->wi_prev->wi_next = wip->wi_next;
- } else {
- buf->b_wininfo = wip->wi_next;
- }
- if (wip->wi_next) {
- wip->wi_next->wi_prev = wip->wi_prev;
- }
+ kv_shift(buf->b_wininfo, i, 1);
if (copy_options && wip->wi_optset) {
clear_winopt(&wip->wi_opt);
deleteFoldRecurse(buf, &wip->wi_folds);
@@ -2679,17 +2674,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
}
// insert the entry in front of the list
- wip->wi_next = buf->b_wininfo;
- buf->b_wininfo = wip;
- wip->wi_prev = NULL;
- if (wip->wi_next) {
- wip->wi_next->wi_prev = wip;
- }
+ kv_pushp(buf->b_wininfo);
+ memmove(&kv_A(buf->b_wininfo, 1), &kv_A(buf->b_wininfo, 0),
+ (kv_size(buf->b_wininfo) - 1) * sizeof(kv_A(buf->b_wininfo, 0)));
+ kv_A(buf->b_wininfo, 0) = wip;
}
/// Check that "wip" has 'diff' set and the diff is only for another tab page.
/// That's because a diff is local to a tab page.
-static bool wininfo_other_tab_diff(wininfo_T *wip)
+static bool wininfo_other_tab_diff(WinInfo *wip)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (!wip->wi_opt.wo_diff) {
@@ -2713,42 +2706,38 @@ static bool wininfo_other_tab_diff(wininfo_T *wip)
/// @param skip_diff_buffer when true, avoid windows with 'diff' set that is in another tab page.
///
/// @return NULL when there isn't any info.
-static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer)
+static WinInfo *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
- wininfo_T *wip;
-
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == curwin
&& (!skip_diff_buffer || !wininfo_other_tab_diff(wip))
&& (!need_options || wip->wi_optset)) {
- break;
+ return wip;
}
}
- 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 (skip_diff_buffer) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (!wininfo_other_tab_diff(wip)
&& (!need_options
|| wip->wi_optset
|| (wip->wi_win != NULL
&& wip->wi_win->w_buffer == buf))) {
- break;
+ return wip;
}
}
- } else {
- wip = buf->b_wininfo;
+ } else if (kv_size(buf->b_wininfo)) {
+ return kv_A(buf->b_wininfo, 0);
}
- return wip;
+ return NULL;
}
/// Reset the local window options to the values last used in this window.
@@ -2760,7 +2749,7 @@ void get_winopts(buf_T *buf)
clear_winopt(&curwin->w_onebuf_opt);
clearFolding(curwin);
- wininfo_T *const wip = find_wininfo(buf, true, true);
+ WinInfo *const wip = find_wininfo(buf, true, true);
if (wip != NULL && wip->wi_win != curwin && wip->wi_win != NULL
&& wip->wi_win->w_buffer == buf) {
win_T *wp = wip->wi_win;
@@ -2800,7 +2789,7 @@ fmark_T *buflist_findfmark(buf_T *buf)
{
static fmark_T no_position = { { 1, 0, 0 }, 0, 0, { 0 }, NULL };
- wininfo_T *const wip = find_wininfo(buf, false, false);
+ WinInfo *const wip = find_wininfo(buf, false, false);
return (wip == NULL) ? &no_position : &(wip->wi_mark);
}
@@ -2819,6 +2808,7 @@ void buflist_list(exarg_T *eap)
garray_T buflist;
buf_T **buflist_data = NULL;
+ msg_ext_set_kind("list_cmd");
if (vim_strchr(eap->arg, 't')) {
ga_init(&buflist, sizeof(buf_T *), 50);
for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
@@ -3267,8 +3257,8 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate)
n);
validate_virtcol(curwin);
size_t len = strlen(buffer);
- col_print(buffer + len, IOSIZE - len,
- (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
+ (void)col_print(buffer + len, IOSIZE - len,
+ (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
append_arg_number(curwin, buffer, IOSIZE);
@@ -3296,13 +3286,13 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate)
xfree(buffer);
}
-void col_print(char *buf, size_t buflen, int col, int vcol)
+int col_print(char *buf, size_t buflen, int col, int vcol)
{
if (col == vcol) {
- vim_snprintf(buf, buflen, "%d", col);
- } else {
- vim_snprintf(buf, buflen, "%d-%d", col, vcol);
+ return vim_snprintf(buf, buflen, "%d", col);
}
+
+ return vim_snprintf(buf, buflen, "%d-%d", col, vcol);
}
static char *lasttitle = NULL;
@@ -3341,96 +3331,11 @@ void maketitle(void)
title_str = p_titlestring;
}
} else {
- // Format: "fname + (path) (1 of 2) - VIM".
-
-#define SPACE_FOR_FNAME (sizeof(buf) - 100)
-#define SPACE_FOR_DIR (sizeof(buf) - 20)
-#define SPACE_FOR_ARGNR (sizeof(buf) - 10) // At least room for " - Nvim".
- char *buf_p = buf;
- if (curbuf->b_fname == NULL) {
- const size_t size = xstrlcpy(buf_p, _("[No Name]"),
- SPACE_FOR_FNAME + 1);
- buf_p += MIN(size, SPACE_FOR_FNAME);
- } else {
- buf_p += transstr_buf(path_tail(curbuf->b_fname), -1, buf_p, SPACE_FOR_FNAME + 1, true);
- }
-
- switch (bufIsChanged(curbuf)
- | (curbuf->b_p_ro << 1)
- | (!MODIFIABLE(curbuf) << 2)) {
- case 0:
- break;
- case 1:
- buf_p = strappend(buf_p, " +"); break;
- case 2:
- buf_p = strappend(buf_p, " ="); break;
- case 3:
- buf_p = strappend(buf_p, " =+"); break;
- case 4:
- case 6:
- buf_p = strappend(buf_p, " -"); break;
- case 5:
- case 7:
- buf_p = strappend(buf_p, " -+"); break;
- default:
- abort();
- }
-
- if (curbuf->b_fname != NULL) {
- // Get path of file, replace home dir with ~.
- *buf_p++ = ' ';
- *buf_p++ = '(';
- home_replace(curbuf, curbuf->b_ffname, buf_p,
- (SPACE_FOR_DIR - (size_t)(buf_p - buf)), true);
-#ifdef BACKSLASH_IN_FILENAME
- // Avoid "c:/name" to be reduced to "c".
- if (isalpha((uint8_t)(*buf_p)) && *(buf_p + 1) == ':') {
- buf_p += 2;
- }
-#endif
- // Remove the file name.
- char *p = path_tail_with_sep(buf_p);
- if (p == buf_p) {
- // Must be a help buffer.
- xstrlcpy(buf_p, _("help"), SPACE_FOR_DIR - (size_t)(buf_p - buf));
- } else {
- *p = NUL;
- }
-
- // Translate unprintable chars and concatenate. Keep some
- // room for the server name. When there is no room (very long
- // file name) use (...).
- if ((size_t)(buf_p - buf) < SPACE_FOR_DIR) {
- char *const tbuf = transstr(buf_p, true);
- const size_t free_space = SPACE_FOR_DIR - (size_t)(buf_p - buf) + 1;
- const size_t dir_len = xstrlcpy(buf_p, tbuf, free_space);
- buf_p += MIN(dir_len, free_space - 1);
- xfree(tbuf);
- } else {
- const size_t free_space = SPACE_FOR_ARGNR - (size_t)(buf_p - buf) + 1;
- const size_t dots_len = xstrlcpy(buf_p, "...", free_space);
- buf_p += MIN(dots_len, free_space - 1);
- }
- *buf_p++ = ')';
- *buf_p = NUL;
- } else {
- *buf_p = NUL;
- }
-
- append_arg_number(curwin, buf_p, (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)));
-
- xstrlcat(buf_p, " - Nvim", (sizeof(buf) - (size_t)(buf_p - buf)));
-
- if (maxlen > 0) {
- // Make it shorter by removing a bit in the middle.
- if (vim_strsize(buf) > maxlen) {
- trunc_string(buf, buf, maxlen, sizeof(buf));
- }
- }
+ // Format: "fname + (path) (1 of 2) - Nvim".
+ char *default_titlestring = "%t%( %M%)%( (%{expand(\"%:~:h\")})%)%a - Nvim";
+ build_stl_str_hl(curwin, buf, sizeof(buf), default_titlestring,
+ kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL);
title_str = buf;
-#undef SPACE_FOR_FNAME
-#undef SPACE_FOR_DIR
-#undef SPACE_FOR_ARGNR
}
}
bool mustset = value_change(title_str, &lasttitle);
@@ -3511,15 +3416,16 @@ void free_titles(void)
/// Get relative cursor position in window into "buf[buflen]", in the localized
/// percentage form like %99, 99%; using "Top", "Bot" or "All" when appropriate.
-void get_rel_pos(win_T *wp, char *buf, int buflen)
+int get_rel_pos(win_T *wp, char *buf, int buflen)
{
// Need at least 3 chars for writing.
if (buflen < 3) {
- return;
+ return 0;
}
linenr_T above; // number of lines above window
linenr_T below; // number of lines below window
+ int len;
above = wp->w_topline - 1;
above += win_get_fill(wp, wp->w_topline) - wp->w_topfill;
@@ -3530,25 +3436,24 @@ 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) {
- xstrlcpy(buf, (above == 0 ? _("All") : _("Bot")), (size_t)buflen);
+ len = vim_snprintf(buf, (size_t)buflen, "%s", (above == 0) ? _("All") : _("Bot"));
} else if (above <= 0) {
- xstrlcpy(buf, _("Top"), (size_t)buflen);
+ len = vim_snprintf(buf, (size_t)buflen, "%s", _("Top"));
} else {
int perc = (above > 1000000
? (above / ((above + below) / 100))
: (above * 100 / (above + below)));
-
- char *p = buf;
- size_t l = (size_t)buflen;
- if (perc < 10) {
- // prepend one space
- buf[0] = ' ';
- p++;
- l--;
- }
// localized percentage value
- vim_snprintf(p, l, _("%d%%"), perc);
+ len = vim_snprintf(buf, (size_t)buflen, _("%s%d%%"), (perc < 10) ? " " : "", perc);
}
+ if (len < 0) {
+ buf[0] = NUL;
+ len = 0;
+ } else if (len > buflen - 1) {
+ len = buflen - 1;
+ }
+
+ return len;
}
/// Append (2 of 8) to "buf[buflen]", if editing more than one file.
@@ -3722,7 +3627,7 @@ void ex_buffer_all(exarg_T *eap)
// Open the buffer in this window.
swap_exists_action = SEA_DIALOG;
- set_curbuf(buf, DOBUF_GOTO, !(jop_flags & JOP_CLEAN));
+ set_curbuf(buf, DOBUF_GOTO, !(jop_flags & kOptJopFlagClean));
if (!bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
swap_exists_action = SEA_NONE;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 8cd1ffc979..3b84a4420f 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -70,7 +70,7 @@ typedef struct {
// Mask to check for flags that prevent normal writing
#define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR)
-typedef struct wininfo_S wininfo_T;
+typedef struct wininfo_S WinInfo;
typedef struct frame_S frame_T;
typedef uint64_t disptick_T; // display tick type
@@ -85,7 +85,7 @@ typedef struct {
// 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.
+// Also used in WinInfo.
typedef struct {
int wo_arab;
#define w_p_arab w_onebuf_opt.wo_arab // 'arabic'
@@ -219,8 +219,6 @@ typedef struct {
// 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
win_T *wi_win; // pointer to window that did set wi_mark
fmark_T wi_mark; // last cursor mark in the file
bool wi_optset; // true when wi_opt has useful values
@@ -316,8 +314,6 @@ 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
@@ -413,7 +409,7 @@ struct file_buffer {
// change
linenr_T b_mod_xlines; // number of extra buffer lines inserted;
// negative when lines were deleted
- wininfo_T *b_wininfo; // list of last used info for each window
+ kvec_t(WinInfo *) b_wininfo; // list of last used info for each window
disptick_T b_mod_tick_syn; // last display tick syntax was updated
disptick_T b_mod_tick_decor; // last display tick decoration providers
// where invoked
@@ -905,6 +901,8 @@ typedef enum {
kFloatRelativeWindow = 1,
kFloatRelativeCursor = 2,
kFloatRelativeMouse = 3,
+ kFloatRelativeTabline = 4,
+ kFloatRelativeLaststatus = 5,
} FloatRelative;
/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c)
@@ -1036,7 +1034,7 @@ struct window_S {
synblock_T *w_s; ///< for :ownsyntax
int w_ns_hl;
- int w_ns_hl_winhl;
+ int w_ns_hl_winhl; ///< when set to -1, 'winhighlight' shouldn't be used
int w_ns_hl_active;
int *w_ns_hl_attr;
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index e725678937..ab07d67ef3 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -16,12 +16,13 @@
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "buffer_updates.c.generated.h"
+# include "buffer_updates.c.generated.h" // IWYU pragma: keep
#endif
// Register a channel. Return True if the channel was added, or already added.
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 5f830b4219..1afa10df63 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -27,7 +27,6 @@
#include "nvim/fileio.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/iconv_defs.h"
#include "nvim/input.h"
@@ -351,7 +350,7 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
msg_silent = 0; // Must give this prompt.
// Don't use emsg() here, don't want to flush the buffers.
msg(_("WARNING: The file has been changed since reading it!!!"), HLF_E);
- if (ask_yesno(_("Do you really want to write to it"), true) == 'n') {
+ if (ask_yesno(_("Do you really want to write to it")) == 'n') {
return FAIL;
}
msg_scroll = false; // Always overwrite the file message now.
@@ -725,9 +724,9 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
FileInfo file_info;
const bool no_prepend_dot = false;
- if ((bkc & BKC_YES) || append) { // "yes"
+ if ((bkc & kOptBkcFlagYes) || append) { // "yes"
*backup_copyp = true;
- } else if ((bkc & BKC_AUTO)) { // "auto"
+ } else if ((bkc & kOptBkcFlagAuto)) { // "auto"
// Don't rename the file when:
// - it's a hard link
// - it's a symbolic link
@@ -773,19 +772,19 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
}
// Break symlinks and/or hardlinks if we've been asked to.
- if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK)) {
+ if ((bkc & kOptBkcFlagBreaksymlink) || (bkc & kOptBkcFlagBreakhardlink)) {
#ifdef UNIX
bool file_info_link_ok = os_fileinfo_link(fname, &file_info);
// Symlinks.
- if ((bkc & BKC_BREAKSYMLINK)
+ if ((bkc & kOptBkcFlagBreaksymlink)
&& file_info_link_ok
&& !os_fileinfo_id_equal(&file_info, file_info_old)) {
*backup_copyp = false;
}
// Hardlinks.
- if ((bkc & BKC_BREAKHARDLINK)
+ if ((bkc & kOptBkcFlagBreakhardlink)
&& os_fileinfo_hardlinks(file_info_old) > 1
&& (!file_info_link_ok
|| os_fileinfo_id_equal(&file_info, file_info_old))) {
@@ -1148,6 +1147,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
msg_scroll = true; // don't overwrite previous file message
}
if (!filtering) {
+ msg_ext_set_kind("bufwrite");
// show that we are busy
#ifndef UNIX
filemess(buf, sfname, "");
@@ -1763,6 +1763,7 @@ restore_backup:
if (msg_add_fileformat(fileformat)) {
insert_space = true;
}
+ msg_ext_set_kind("bufwrite");
msg_add_lines(insert_space, lnum, nchars); // add line/char count
if (!shortmess(SHM_WRITE)) {
if (append) {
diff --git a/src/nvim/change.c b/src/nvim/change.c
index f3a8e0b208..ecd6012679 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -25,7 +25,6 @@
#include "nvim/fold.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -914,7 +913,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// fixpos is true, we don't want to end up positioned at the NUL,
// unless "restart_edit" is set or 'virtualedit' contains "onemore".
if (col > 0 && fixpos && restart_edit == 0
- && (get_ve_flags(curwin) & VE_ONEMORE) == 0) {
+ && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
curwin->w_cursor.col--;
curwin->w_cursor.coladd = 0;
curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 021fdd4b79..912d515f84 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <lauxlib.h>
#include <stddef.h>
@@ -22,7 +23,6 @@
#include "nvim/event/proc.h"
#include "nvim/event/rstream.h"
#include "nvim/event/socket.h"
-#include "nvim/event/stream.h"
#include "nvim/event/wstream.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
@@ -32,6 +32,7 @@
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 1afd590b0e..f72420a00f 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -95,7 +95,7 @@ int buf_init_chartab(buf_T *buf, bool global)
int c = 0;
while (c < ' ') {
- g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
+ g_chartab[c++] = (dy_flags & kOptDyFlagUhex) ? 4 : 2;
}
while (c <= '~') {
@@ -109,7 +109,7 @@ int buf_init_chartab(buf_T *buf, bool global)
g_chartab[c++] = (CT_PRINT_CHAR | CT_FNAME_CHAR) + 1;
} else {
// the rest is unprintable by default
- g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
+ g_chartab[c++] = (dy_flags & kOptDyFlagUhex) ? 4 : 2;
}
}
}
@@ -237,7 +237,7 @@ static int parse_isopt(const char *var, buf_T *buf, bool only_check)
if (c < ' ' || c > '~') {
if (tilde) {
g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK)
- + ((dy_flags & DY_UHEX) ? 4 : 2));
+ + ((dy_flags & kOptDyFlagUhex) ? 4 : 2));
g_chartab[c] &= (uint8_t) ~CT_PRINT_CHAR;
} else {
g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + 1);
@@ -614,7 +614,7 @@ void transchar_nonprint(const buf_T *buf, char *charbuf, int c)
}
assert(c <= 0xff);
- if (dy_flags & DY_UHEX || c > 0x7f) {
+ if (dy_flags & kOptDyFlagUhex || c > 0x7f) {
// 'display' has "uhex"
transchar_hex(charbuf, c);
} else {
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 700d554821..3a9eeb73c8 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -42,7 +42,6 @@
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
-#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/mapping.h"
@@ -100,7 +99,7 @@ static int compl_selected;
static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
- return (wop_flags & WOP_FUZZY)
+ return (wop_flags & kOptWopFlagFuzzy)
&& xp->xp_context != EXPAND_BOOL_SETTINGS
&& xp->xp_context != EXPAND_COLORS
&& xp->xp_context != EXPAND_COMPILER
@@ -133,7 +132,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
bool cmdline_fuzzy_complete(const char *const fuzzystr)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
- return (wop_flags & WOP_FUZZY) && *fuzzystr != NUL;
+ return (wop_flags & kOptWopFlagFuzzy) && *fuzzystr != NUL;
}
/// Sort function for the completion matches.
@@ -806,7 +805,7 @@ static char *find_longest_match(expand_T *xp, int options)
}
if (i < xp->xp_numfiles) {
if (!(options & WILD_NO_BEEP)) {
- vim_beep(BO_WILD);
+ vim_beep(kOptBoFlagWildmode);
}
break;
}
@@ -1069,7 +1068,7 @@ int showmatches(expand_T *xp, bool wildmenu)
bool compl_use_pum = (ui_has(kUICmdline)
? ui_has(kUIPopupmenu)
- : wildmenu && (wop_flags & WOP_PUM))
+ : wildmenu && (wop_flags & kOptWopFlagPum))
|| ui_has(kUIWildmenu);
if (compl_use_pum) {
@@ -1084,6 +1083,7 @@ int showmatches(expand_T *xp, bool wildmenu)
ui_flush();
cmdline_row = msg_row;
msg_didany = false; // lines_left will be set again
+ msg_ext_set_kind("wildlist");
msg_start(); // prepare for paging
}
@@ -1938,7 +1938,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
case CMD_tjump:
case CMD_stjump:
case CMD_ptjump:
- if (wop_flags & WOP_TAGFILE) {
+ if (wop_flags & kOptWopFlagTagfile) {
xp->xp_context = EXPAND_TAGS_LISTFILES;
} else {
xp->xp_context = EXPAND_TAGS;
@@ -2000,6 +2000,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
FALLTHROUGH;
case CMD_buffer:
case CMD_sbuffer:
+ case CMD_pbuffer:
case CMD_checktime:
xp->xp_context = EXPAND_BUFFERS;
xp->xp_pattern = (char *)arg;
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 5993eefd67..d2285cab24 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -13,6 +13,7 @@
#include "nvim/cmdhist.h"
#include "nvim/errors.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_getln.h"
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
index c933982593..f45345372b 100644
--- a/src/nvim/cmdhist.h
+++ b/src/nvim/cmdhist.h
@@ -1,7 +1,9 @@
#pragma once
+#include <stddef.h> // IWYU pragma: keep
+
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/os/time_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/context.c b/src/nvim/context.c
index 461b10a9d5..9d8fdb7e74 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -20,6 +20,7 @@
#include "nvim/hashtab.h"
#include "nvim/keycodes.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/shada.h"
diff --git a/src/nvim/context.h b/src/nvim/context.h
index 4375030fbc..5ae2a078b0 100644
--- a/src/nvim/context.h
+++ b/src/nvim/context.h
@@ -1,6 +1,6 @@
#pragma once
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 35afca2fe9..580ed856e4 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -11,6 +11,7 @@
#include "nvim/drawscreen.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
+#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
@@ -105,7 +106,7 @@ static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, col
|| (State & MODE_TERMINAL)
|| restart_edit != NUL
|| (VIsual_active && *p_sel != 'o')
- || ((get_ve_flags(wp) & VE_ONEMORE) && wcol < MAXCOL);
+ || ((get_ve_flags(wp) & kOptVeFlagOnemore) && wcol < MAXCOL);
char *line = ml_get_buf(wp->w_buffer, pos->lnum);
int linelen = ml_get_buf_len(wp->w_buffer, pos->lnum);
@@ -341,11 +342,13 @@ void check_cursor_col(win_T *win)
} else if (win->w_cursor.col >= len) {
// Allow cursor past end-of-line when:
// - in Insert mode or restarting Insert mode
+ // - in Terminal mode
// - in Visual mode and 'selection' isn't "old"
// - 'virtualedit' is set
if ((State & MODE_INSERT) || restart_edit
+ || (State & MODE_TERMINAL)
|| (VIsual_active && *p_sel != 'o')
- || (cur_ve_flags & VE_ONEMORE)
+ || (cur_ve_flags & kOptVeFlagOnemore)
|| virtual_active(win)) {
win->w_cursor.col = len;
} else {
@@ -362,7 +365,7 @@ void check_cursor_col(win_T *win)
// line.
if (oldcol == MAXCOL) {
win->w_cursor.coladd = 0;
- } else if (cur_ve_flags == VE_ALL) {
+ } else if (cur_ve_flags == kOptVeFlagAll) {
if (oldcoladd > win->w_cursor.col) {
win->w_cursor.coladd = oldcoladd - win->w_cursor.col;
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 1f11367618..a058394b9f 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -45,6 +45,7 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = {
{ "more", 0, 0, 0, 0, 0, 0, 0, 0, "m", SHAPE_MOUSE },
{ "more_lastline", 0, 0, 0, 0, 0, 0, 0, 0, "ml", SHAPE_MOUSE },
{ "showmatch", 0, 0, 0, 100, 100, 100, 0, 0, "sm", SHAPE_CURSOR },
+ { "terminal", 0, 0, 0, 0, 0, 0, 0, 0, "t", SHAPE_CURSOR },
};
/// Converts cursor_shapes into an Array of Dictionaries
@@ -321,6 +322,8 @@ int cursor_get_mode_idx(void)
{
if (State == MODE_SHOWMATCH) {
return SHAPE_IDX_SM;
+ } else if (State == MODE_TERMINAL) {
+ return SHAPE_IDX_TERM;
} else if (State & VREPLACE_FLAG) {
return SHAPE_IDX_R;
} else if (State & REPLACE_FLAG) {
diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h
index 21967a81f4..6d9e7de2e5 100644
--- a/src/nvim/cursor_shape.h
+++ b/src/nvim/cursor_shape.h
@@ -23,7 +23,8 @@ typedef enum {
SHAPE_IDX_MORE = 14, ///< Hit-return or More
SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line
SHAPE_IDX_SM = 16, ///< showing matching paren
- SHAPE_IDX_COUNT = 17,
+ SHAPE_IDX_TERM = 17, ///< Terminal mode
+ SHAPE_IDX_COUNT = 18,
} ModeShape;
typedef enum {
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index b71ff23f57..e60d04fdfd 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -110,13 +110,11 @@ void do_debug(char *cmd)
}
if (debug_oldval != NULL) {
smsg(0, _("Oldval = \"%s\""), debug_oldval);
- xfree(debug_oldval);
- debug_oldval = NULL;
+ XFREE_CLEAR(debug_oldval);
}
if (debug_newval != NULL) {
smsg(0, _("Newval = \"%s\""), debug_newval);
- xfree(debug_newval);
- debug_newval = NULL;
+ XFREE_CLEAR(debug_newval);
}
char *sname = estack_sfile(ESTACK_NONE);
if (sname != NULL) {
@@ -153,8 +151,7 @@ void do_debug(char *cmd)
debug_break_level = -1;
xfree(cmdline);
- cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
- CALLBACK_NONE);
+ cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE, false, NULL);
debug_break_level = n;
if (typeahead_saved) {
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 3633940b14..149504f424 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -17,12 +17,11 @@
#include "nvim/fold.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/marktree.h"
#include "nvim/memory.h"
-#include "nvim/move.h"
+#include "nvim/memory_defs.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -304,17 +303,30 @@ static void decor_free_inner(DecorVirtText *vt, uint32_t first_idx)
}
}
+/// Check if we are in a callback while drawing, which might invalidate the marktree iterator.
+///
+/// This should be called whenever a structural modification has been done to a
+/// marktree in a public API function (i e any change which adds or deletes marks).
+void decor_state_invalidate(buf_T *buf)
+{
+ if (decor_state.win && decor_state.win->w_buffer == buf) {
+ decor_state.itr_valid = false;
+ }
+}
+
void decor_check_to_be_deleted(void)
{
assert(!decor_state.running_decor_provider);
decor_free_inner(to_free_virt, to_free_sh);
to_free_virt = NULL;
to_free_sh = DECOR_ID_INVALID;
+ decor_state.win = NULL;
}
void decor_state_free(DecorState *state)
{
- kv_destroy(state->active);
+ kv_destroy(state->slots);
+ kv_destroy(state->ranges_i);
}
void clear_virttext(VirtText *text)
@@ -399,14 +411,30 @@ bool decor_redraw_reset(win_T *wp, DecorState *state)
{
state->row = -1;
state->win = wp;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange item = kv_A(state->active, i);
- if (item.owned && item.kind == kDecorKindVirtText) {
- clear_virttext(&item.data.vt->data.virt_text);
- xfree(item.data.vt);
+
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ int const beg_pos[] = { 0, state->future_begin };
+ int const end_pos[] = { state->current_end, (int)kv_size(state->ranges_i) };
+
+ for (int pos_i = 0; pos_i < 2; pos_i++) {
+ for (int i = beg_pos[pos_i]; i < end_pos[pos_i]; i++) {
+ DecorRange *const r = &slots[indices[i]].range;
+ if (r->owned && r->kind == kDecorKindVirtText) {
+ clear_virttext(&r->data.vt->data.virt_text);
+ xfree(r->data.vt);
+ }
}
}
- kv_size(state->active) = 0;
+
+ kv_size(state->slots) = 0;
+ kv_size(state->ranges_i) = 0;
+ state->free_slot_i = -1;
+ state->current_end = 0;
+ state->future_begin = 0;
+ state->new_range_ordering = 0;
+
return wp->w_buffer->b_marktree->n_keys;
}
@@ -431,6 +459,8 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
{
buf_T *buf = wp->w_buffer;
state->top_row = top_row;
+ state->itr_valid = true;
+
if (!marktree_itr_get_overlap(buf->b_marktree, top_row, 0, state->itr)) {
return false;
}
@@ -452,14 +482,37 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
bool decor_redraw_line(win_T *wp, int row, DecorState *state)
{
+ int count = (int)kv_size(state->ranges_i);
+ int const cur_end = state->current_end;
+ int fut_beg = state->future_begin;
+
+ // Move future ranges to start right after current ranges.
+ // Otherwise future ranges will grow forward indefinitely.
+ if (fut_beg == count) {
+ fut_beg = count = cur_end;
+ } else if (fut_beg != cur_end) {
+ int *const indices = state->ranges_i.items;
+ memmove(indices + cur_end, indices + fut_beg, (size_t)(count - fut_beg) * sizeof(indices[0]));
+
+ count = cur_end + (count - fut_beg);
+ fut_beg = cur_end;
+ }
+
+ kv_size(state->ranges_i) = (size_t)count;
+ state->future_begin = fut_beg;
+
if (state->row == -1) {
decor_redraw_start(wp, row, state);
+ } else if (!state->itr_valid) {
+ marktree_itr_get(wp->w_buffer->b_marktree, row, 0, state->itr);
+ state->itr_valid = true;
}
+
state->row = row;
state->col_until = -1;
state->eol_col = -1;
- if (kv_size(state->active)) {
+ if (cur_end != 0 || fut_beg != count) {
return true;
}
@@ -489,18 +542,51 @@ static void decor_range_add_from_inline(DecorState *state, int start_row, int st
}
}
-static void decor_range_insert(DecorState *state, DecorRange range)
+static void decor_range_insert(DecorState *state, DecorRange *range)
{
- kv_pushp(state->active);
- size_t index;
- for (index = kv_size(state->active) - 1; index > 0; index--) {
- DecorRange item = kv_A(state->active, index - 1);
- if (item.priority <= range.priority) {
- break;
+ range->ordering = state->new_range_ordering++;
+
+ int index;
+ // Get space for a new `DecorRange` from the freelist or allocate.
+ if (state->free_slot_i >= 0) {
+ index = state->free_slot_i;
+ DecorRangeSlot *slot = &kv_A(state->slots, index);
+ state->free_slot_i = slot->next_free_i;
+ slot->range = *range;
+ } else {
+ index = (int)kv_size(state->slots);
+ kv_pushp(state->slots)->range = *range;
+ }
+
+ int const row = range->start_row;
+ int const col = range->start_col;
+
+ int const count = (int)kv_size(state->ranges_i);
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ int begin = state->future_begin;
+ int end = count;
+ while (begin < end) {
+ int const mid = begin + ((end - begin) >> 1);
+ DecorRange *const mr = &slots[indices[mid]].range;
+
+ int const mrow = mr->start_row;
+ int const mcol = mr->start_col;
+ if (mrow < row || (mrow == row && mcol <= col)) {
+ begin = mid + 1;
+ if (mrow == row && mcol == col) {
+ break;
+ }
+ } else {
+ end = mid;
}
- kv_A(state->active, index) = kv_A(state->active, index - 1);
}
- kv_A(state->active, index) = range;
+
+ kv_pushp(state->ranges_i);
+ int *const item = &kv_A(state->ranges_i, begin);
+ memmove(item + 1, item, (size_t)(count - begin) * sizeof(*item));
+ *item = index;
}
void decor_range_add_virt(DecorState *state, int start_row, int start_col, int end_row, int end_col,
@@ -516,7 +602,7 @@ void decor_range_add_virt(DecorState *state, int start_row, int start_col, int e
.priority = vt->priority,
.draw_col = -10,
};
- decor_range_insert(state, range);
+ decor_range_insert(state, &range);
}
void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end_row, int end_col,
@@ -541,7 +627,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end
if (sh->hl_id) {
range.attr_id = syn_id2attr(sh->hl_id);
}
- decor_range_insert(state, range);
+ decor_range_insert(state, &range);
}
if (sh->flags & (kSHUIWatched)) {
@@ -549,7 +635,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end
range.data.ui.ns_id = ns;
range.data.ui.mark_id = mark_id;
range.data.ui.pos = (sh->flags & kSHUIWatchedOverlay) ? kVPosOverlay : kVPosEndOfLine;
- decor_range_insert(state, range);
+ decor_range_insert(state, &range);
}
}
@@ -569,29 +655,32 @@ void decor_init_draw_col(int win_col, bool hidden, DecorRange *item)
void decor_recheck_draw_col(int win_col, bool hidden, DecorState *state)
{
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
- if (item->draw_col == -3) {
- decor_init_draw_col(win_col, hidden, item);
+ int const end = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ for (int i = 0; i < end; i++) {
+ DecorRange *const r = &slots[indices[i]].range;
+ if (r->draw_col == -3) {
+ decor_init_draw_col(win_col, hidden, r);
}
}
}
-int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
+int decor_redraw_col_impl(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
{
- buf_T *buf = wp->w_buffer;
- if (col <= state->col_until) {
- return state->current;
- }
- state->col_until = MAXCOL;
+ buf_T *const buf = wp->w_buffer;
+ int const row = state->row;
+ int col_until = MAXCOL;
+
while (true) {
// TODO(bfredl): check duplicate entry in "intersection"
// branch
MTKey mark = marktree_itr_current(state->itr);
- if (mark.pos.row < 0 || mark.pos.row > state->row) {
+ if (mark.pos.row < 0 || mark.pos.row > row) {
break;
- } else if (mark.pos.row == state->row && mark.pos.col > col) {
- state->col_until = mark.pos.col - 1;
+ } else if (mark.pos.row == row && mark.pos.col > col) {
+ col_until = mark.pos.col - 1;
break;
}
@@ -607,73 +696,132 @@ next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
}
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ int count = (int)kv_size(state->ranges_i);
+ int cur_end = state->current_end;
+ int fut_beg = state->future_begin;
+
+ // Promote future ranges before the cursor to active.
+ for (; fut_beg < count; fut_beg++) {
+ int const index = indices[fut_beg];
+ DecorRange *const r = &slots[index].range;
+ if (r->start_row > row || (r->start_row == row && r->start_col > col)) {
+ break;
+ }
+ int const ordering = r->ordering;
+ DecorPriority const priority = r->priority;
+
+ int begin = 0;
+ int end = cur_end;
+ while (begin < end) {
+ int mid = begin + ((end - begin) >> 1);
+ int mi = indices[mid];
+ DecorRange *mr = &slots[mi].range;
+ if (mr->priority < priority || (mr->priority == priority && mr->ordering < ordering)) {
+ begin = mid + 1;
+ } else {
+ end = mid;
+ }
+ }
+
+ int *const item = indices + begin;
+ memmove(item + 1, item, (size_t)(cur_end - begin) * sizeof(*item));
+ *item = index;
+ cur_end++;
+ }
+
+ if (fut_beg < count) {
+ DecorRange *r = &slots[indices[fut_beg]].range;
+ if (r->start_row == row) {
+ col_until = MIN(col_until, r->start_col - 1);
+ }
+ }
+
+ int new_cur_end = 0;
+
int attr = 0;
- size_t j = 0;
int conceal = 0;
schar_T 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);
- bool active = false, keep = true;
- if (item.end_row < state->row
- || (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && decor_virt_pos(&item))) {
- keep = false;
- }
+ for (int i = 0; i < cur_end; i++) {
+ int const index = indices[i];
+ DecorRangeSlot *const slot = slots + index;
+ DecorRange *const r = &slot->range;
+
+ bool keep;
+ if (r->end_row < row || (r->end_row == row && r->end_col <= col)) {
+ keep = r->start_row >= row && decor_virt_pos(r);
} else {
- if (item.start_row < state->row
- || (item.start_row == state->row && item.start_col <= col)) {
- active = true;
- if (item.end_row == state->row && item.end_col > col) {
- state->col_until = MIN(state->col_until, item.end_col - 1);
- }
- } else {
- if (item.start_row == state->row) {
- state->col_until = MIN(state->col_until, item.start_col - 1);
- }
+ keep = true;
+
+ if (r->end_row == row && r->end_col > col) {
+ col_until = MIN(col_until, r->end_col - 1);
}
- }
- if (active && item.attr_id > 0) {
- attr = hl_combine_attr(attr, item.attr_id);
- }
- if (active && item.kind == kDecorKindHighlight && (item.data.sh.flags & kSHConceal)) {
- conceal = 1;
- if (item.start_row == state->row && item.start_col == col) {
- DecorSignHighlight *sh = &item.data.sh;
- conceal = 2;
- conceal_char = sh->text[0];
- state->col_until = MIN(state->col_until, item.start_col);
- conceal_attr = item.attr_id;
+
+ if (r->attr_id > 0) {
+ attr = hl_combine_attr(attr, r->attr_id);
}
- }
- if (active && item.kind == kDecorKindHighlight) {
- if (item.data.sh.flags & kSHSpellOn) {
- spell = kTrue;
- } else if (item.data.sh.flags & kSHSpellOff) {
- spell = kFalse;
+
+ if (r->kind == kDecorKindHighlight && (r->data.sh.flags & kSHConceal)) {
+ conceal = 1;
+ if (r->start_row == row && r->start_col == col) {
+ DecorSignHighlight *sh = &r->data.sh;
+ conceal = 2;
+ conceal_char = sh->text[0];
+ col_until = MIN(col_until, r->start_col);
+ conceal_attr = r->attr_id;
+ }
}
- if (item.data.sh.url != NULL) {
- attr = hl_add_url(attr, item.data.sh.url);
+
+ if (r->kind == kDecorKindHighlight) {
+ if (r->data.sh.flags & kSHSpellOn) {
+ spell = kTrue;
+ } else if (r->data.sh.flags & kSHSpellOff) {
+ spell = kFalse;
+ }
+ if (r->data.sh.url != NULL) {
+ attr = hl_add_url(attr, r->data.sh.url);
+ }
}
}
- if (item.start_row == state->row && item.start_col <= col
- && decor_virt_pos(&item) && item.draw_col == -10) {
- decor_init_draw_col(win_col, hidden, &item);
+
+ if (r->start_row == row && r->start_col <= col
+ && decor_virt_pos(r) && r->draw_col == -10) {
+ decor_init_draw_col(win_col, hidden, r);
}
+
if (keep) {
- kv_A(state->active, j++) = item;
- } else if (item.owned) {
- if (item.kind == kDecorKindVirtText) {
- clear_virttext(&item.data.vt->data.virt_text);
- xfree(item.data.vt);
- } else if (item.kind == kDecorKindHighlight) {
- xfree((void *)item.data.sh.url);
+ indices[new_cur_end++] = index;
+ } else {
+ if (r->owned) {
+ if (r->kind == kDecorKindVirtText) {
+ clear_virttext(&r->data.vt->data.virt_text);
+ xfree(r->data.vt);
+ } else if (r->kind == kDecorKindHighlight) {
+ xfree((void *)r->data.sh.url);
+ }
}
+
+ int *fi = &state->free_slot_i;
+ slot->next_free_i = *fi;
+ *fi = index;
}
}
- kv_size(state->active) = j;
+ cur_end = new_cur_end;
+
+ if (fut_beg == count) {
+ fut_beg = count = cur_end;
+ }
+
+ kv_size(state->ranges_i) = (size_t)count;
+ state->future_begin = fut_beg;
+ state->current_end = cur_end;
+ state->col_until = col_until;
+
state->current = attr;
state->conceal = conceal;
state->conceal_char = conceal_char;
@@ -708,9 +856,9 @@ static const uint32_t sign_filter[4] = {[kMTMetaSignText] = kMTFilterSelect,
/// Return the sign attributes on the currently refreshed row.
///
/// @param[out] sattrs Output array for sign text and texthl id
-/// @param[out] line_attr Highest priority linehl id
-/// @param[out] cul_attr Highest priority culhl id
-/// @param[out] num_attr Highest priority numhl id
+/// @param[out] line_id Highest priority linehl id
+/// @param[out] cul_id Highest priority culhl id
+/// @param[out] num_id Highest priority numhl id
void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], int *line_id,
int *cul_id, int *num_id)
{
@@ -870,16 +1018,18 @@ bool decor_redraw_eol(win_T *wp, DecorState *state, int *eol_attr, int eol_col)
{
decor_redraw_col(wp, MAXCOL, MAXCOL, false, state);
state->eol_col = eol_col;
+
+ int const count = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
bool has_virt_pos = false;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange item = kv_A(state->active, i);
- if (item.start_row == state->row && decor_virt_pos(&item)) {
- has_virt_pos = true;
- }
+ for (int i = 0; i < count; i++) {
+ DecorRange *r = &slots[indices[i]].range;
+ has_virt_pos |= r->start_row == state->row && decor_virt_pos(r);
- if (item.kind == kDecorKindHighlight
- && (item.data.sh.flags & kSHHlEol) && item.start_row <= state->row) {
- *eol_attr = hl_combine_attr(*eol_attr, item.attr_id);
+ if (r->kind == kDecorKindHighlight && (r->data.sh.flags & kSHHlEol)) {
+ *eol_attr = hl_combine_attr(*eol_attr, r->attr_id);
}
}
return has_virt_pos;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 1b595fb86f..bdbb1795cb 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -15,8 +15,8 @@
// actual Decor* data is in decoration_defs.h
/// Keep in sync with VirtTextPos in decoration_defs.h
-EXTERN const char *const virt_text_pos_str[]
-INIT( = { "eol", "overlay", "win_col", "right_align", "inline" });
+EXTERN const char *const virt_text_pos_str[] INIT( = { "eol", "eol_right_align", "inline",
+ "overlay", "right_align", "win_col" });
/// Keep in sync with HlMode in decoration_defs.h
EXTERN const char *const hl_mode_str[] INIT( = { "", "replace", "combine", "blend" });
@@ -27,13 +27,19 @@ typedef enum {
kDecorKindVirtText,
kDecorKindVirtLines,
kDecorKindUIWatched,
-} DecorRangeKind;
+} DecorRangeKindEnum;
+
+typedef uint8_t DecorRangeKind;
typedef struct {
int start_row;
int start_col;
int end_row;
int end_col;
+ int ordering; ///< range insertion order
+ DecorPriority priority;
+ bool owned; ///< ephemeral decoration, free memory immediately
+ DecorRangeKind kind;
// next pointers MUST NOT be used, these are separate ranges
// vt->next could be pointing to freelist memory at this point
union {
@@ -46,9 +52,6 @@ typedef struct {
} ui;
} data;
int attr_id; ///< cached lookup of inl.hl_id if it was a highlight
- bool owned; ///< ephemeral decoration, free memory immediately
- DecorPriority priority;
- DecorRangeKind kind;
/// Screen column to draw the virtual text.
/// When -1, it should be drawn on the current screen line after deciding where.
/// When -3, it may be drawn at a position yet to be assigned.
@@ -57,9 +60,28 @@ typedef struct {
int draw_col;
} DecorRange;
+/// DecorRange can be removed from `DecorState` list in any order,
+/// so we track available slots using a freelist (with `next_free_i`).
+/// The list head is in `DecorState.free_slot_i`.
+typedef union {
+ DecorRange range;
+ int next_free_i;
+} DecorRangeSlot;
+
typedef struct {
MarkTreeIter itr[1];
- kvec_t(DecorRange) active;
+ kvec_t(DecorRangeSlot) slots;
+ kvec_t(int) ranges_i;
+ /// Indices in [0; current_end) of `ranges_i` point to ranges that start
+ /// before current position. Sorted by priority and order of insertion.
+ int current_end;
+ /// Indices in [future_begin, kv_size(ranges_i)) of `ranges_i` point to
+ /// ranges that start after current position. Sorted by starting position.
+ int future_begin;
+ /// Head of DecorRangeSlot freelist. -1 if none are freed.
+ int free_slot_i;
+ /// Index for keeping track of range insertion order.
+ int new_range_ordering;
win_T *win;
int top_row;
int row;
@@ -74,6 +96,7 @@ typedef struct {
TriState spell;
bool running_decor_provider;
+ bool itr_valid;
} DecorState;
EXTERN DecorState decor_state INIT( = { 0 });
@@ -83,4 +106,14 @@ EXTERN kvec_t(DecorSignHighlight) decor_items INIT( = KV_INITIAL_VALUE);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.h.generated.h"
+# include "decoration.h.inline.generated.h"
#endif
+
+static inline int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (col <= state->col_until) {
+ return state->current;
+ }
+ return decor_redraw_col_impl(wp, col, win_col, hidden, state);
+}
diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h
index 58ba93a7ba..36ad6df7a0 100644
--- a/src/nvim/decoration_defs.h
+++ b/src/nvim/decoration_defs.h
@@ -19,10 +19,11 @@ typedef kvec_t(VirtTextChunk) VirtText;
/// Keep in sync with virt_text_pos_str[] in decoration.h
typedef enum {
kVPosEndOfLine,
+ kVPosEndOfLineRightAlign,
+ kVPosInline,
kVPosOverlay,
- kVPosWinCol,
kVPosRightAlign,
- kVPosInline,
+ kVPosWinCol,
} VirtTextPos;
typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines;
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 74f444d8e8..7c99fbf889 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -55,14 +55,13 @@ static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref
// We get the provider here via an index in case the above call to nlua_call_ref causes
// decor_providers to be reallocated.
DecorProvider *provider = &kv_A(decor_providers, provider_idx);
-
if (!ERROR_SET(&err)
&& api_object_to_bool(ret, "provider %s retval", default_true, &err)) {
provider->error_count = 0;
return true;
}
- if (ERROR_SET(&err)) {
+ if (ERROR_SET(&err) && provider->error_count < DP_MAX_ERROR) {
decor_provider_error(provider, name, err.msg);
provider->error_count++;
@@ -121,7 +120,8 @@ void decor_providers_invoke_win(win_T *wp)
{
// this might change in the future
// then we would need decor_state.running_decor_provider just like "on_line" below
- assert(kv_size(decor_state.active) == 0);
+ assert(decor_state.current_end == 0
+ && decor_state.future_begin == (int)kv_size(decor_state.ranges_i));
if (kv_size(decor_providers) > 0) {
validate_botline(wp);
@@ -155,7 +155,7 @@ void decor_providers_invoke_win(win_T *wp)
/// @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 decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
+void decor_providers_invoke_line(win_T *wp, int row)
{
decor_state.running_decor_provider = true;
for (size_t i = 0; i < kv_size(decor_providers); i++) {
@@ -165,9 +165,7 @@ void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
ADD_C(args, WINDOW_OBJ(wp->handle));
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
ADD_C(args, INTEGER_OBJ(row));
- if (decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) {
- *has_decor = true;
- } else {
+ if (!decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) {
// return 'false' or error: skip rest of this window
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
}
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index f1dd08f0e6..68441f7adc 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -44,6 +44,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
@@ -1621,6 +1622,11 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
} else {
// second overlap of new block with existing block
dp->df_count[idx_new] += (linenr_T)hunk->count_new;
+ if ((dp->df_lnum[idx_new] + dp->df_count[idx_new] - 1)
+ > curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count) {
+ dp->df_count[idx_new] = curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count
+ - dp->df_lnum[idx_new] + 1;
+ }
}
// Adjust the size of the block to include all the lines to the
@@ -1631,6 +1637,11 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
if (off < 0) {
// new change ends in existing block, adjust the end
dp->df_count[idx_new] += -off;
+ if ((dp->df_lnum[idx_new] + dp->df_count[idx_new] - 1)
+ > curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count) {
+ dp->df_count[idx_new] = curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count
+ - dp->df_lnum[idx_new] + 1;
+ }
off = 0;
}
@@ -1809,7 +1820,8 @@ static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, i
topdiffchange = 0;
}
- // check if the fromwin topline is matched by the current diff. if so, set it to the top of the diff block
+ // 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
@@ -1854,8 +1866,10 @@ static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller,
}
} else {
(*linesfiller) = 0;
- ch_virtual_lines = get_max_diff_length(curdif);
- isfiller = (curdif->df_count[toidx] ? false : true);
+ if (curdif) {
+ ch_virtual_lines = get_max_diff_length(curdif);
+ isfiller = (curdif->df_count[toidx] ? false : true);
+ }
if (isfiller) {
while (curdif && curdif->df_next && curdif->df_lnum[toidx] ==
curdif->df_next->df_lnum[toidx]
@@ -2010,10 +2024,15 @@ static void run_linematch_algorithm(diff_T *dp)
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);
+ if (dp->df_count[i] > 0) {
+ // 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);
+ } else {
+ diffbufs_mm[ndiffs].size = 0;
+ diffbufs_mm[ndiffs].ptr = NULL;
+ }
diffbufs[ndiffs] = &diffbufs_mm[ndiffs];
@@ -2049,6 +2068,12 @@ static void run_linematch_algorithm(diff_T *dp)
/// Returns > 0 for inserting that many filler lines above it (never happens
/// when 'diffopt' doesn't contain "filler").
/// This should only be used for windows where 'diff' is set.
+/// When diffopt contains linematch, a changed/added/deleted line
+/// may also have filler lines above it. In such a case, the possibilities
+/// are no longer mutually exclusive. The number of filler lines is
+/// returned from diff_check, and the integer 'linestatus' passed by
+/// pointer is set to -1 to indicate a changed line, and -2 to indicate an
+/// added line
///
/// @param wp
/// @param lnum
@@ -2102,7 +2127,8 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
// Useful for scrollbind calculations which need to count all the filler lines
// above the screen.
if (lnum >= wp->w_topline && lnum < wp->w_botline
- && !dp->is_linematched && diff_linematch(dp)) {
+ && !dp->is_linematched && diff_linematch(dp)
+ && diff_check_sanity(curtab, dp)) {
run_linematch_algorithm(dp);
}
@@ -2417,7 +2443,7 @@ int diffopt_changed(void)
char *p = p_dip;
while (*p != NUL) {
- // Note: Keep this in sync with p_dip_values
+ // Note: Keep this in sync with opt_dip_values.
if (strncmp(p, "filler", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_FILLER;
@@ -2464,7 +2490,7 @@ int diffopt_changed(void)
p += 8;
diff_flags_new |= DIFF_INTERNAL;
} else if (strncmp(p, "algorithm:", 10) == 0) {
- // Note: Keep this in sync with p_dip_algorithm_values.
+ // Note: Keep this in sync with opt_dip_algorithm_values.
p += 10;
if (strncmp(p, "myers", 5) == 0) {
p += 5;
@@ -2745,7 +2771,7 @@ bool diff_infold(win_T *wp, linenr_T lnum)
void nv_diffgetput(bool put, size_t count)
{
if (bt_prompt(curbuf)) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
return;
}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index ea0d1ba708..f32123e686 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -22,7 +22,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/mapping.h"
@@ -1024,6 +1023,7 @@ static digr_T digraphdefault[] =
{ '?', '=', 0x2245 },
{ '?', '2', 0x2248 },
{ '=', '?', 0x224c },
+ { '.', '=', 0x2250 },
{ 'H', 'I', 0x2253 },
{ '!', '=', 0x2260 },
{ '=', '3', 0x2261 },
@@ -1715,6 +1715,7 @@ void listdigraphs(bool use_headers)
{
result_T previous = 0;
+ msg_ext_set_kind("list_cmd");
msg_putchar('\n');
const digr_T *dp = digraphdefault;
@@ -1954,16 +1955,16 @@ void f_digraph_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "digraph_getlist()" function
void f_digraph_getlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ if (tv_check_for_opt_bool_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
bool flag_list_all;
if (argvars[0].v_type == VAR_UNKNOWN) {
flag_list_all = false;
} else {
- bool error = false;
- varnumber_T flag = tv_get_number_chk(&argvars[0], &error);
- if (error) {
- return;
- }
+ varnumber_T flag = tv_get_bool(&argvars[0]);
flag_list_all = flag != 0;
}
@@ -2183,22 +2184,22 @@ static void keymap_unload(void)
/// @param fmt format string containing one %s item
/// @param buf buffer for the result
/// @param len length of buffer
-bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
+int get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
{
char *p;
if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) {
- return false;
+ return 0;
}
buf_T *old_curbuf = curbuf;
win_T *old_curwin = curwin;
+ char to_evaluate[] = "b:keymap_name";
curbuf = wp->w_buffer;
curwin = wp;
- STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
- char *s = p = eval_to_string(buf, false, false);
+ char *s = p = eval_to_string(to_evaluate, false, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;
@@ -2209,9 +2210,12 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
p = "lang";
}
}
- if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) {
+ int plen = vim_snprintf(buf, (size_t)len, fmt, p);
+ xfree(s);
+ if (plen < 0 || plen > len - 1) {
buf[0] = NUL;
+ plen = 0;
}
- xfree(s);
- return buf[0] != NUL;
+
+ return plen;
}
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 79f3298eb4..37a42917b0 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -31,6 +31,7 @@
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
+#include "nvim/insexpand.h"
#include "nvim/mark_defs.h"
#include "nvim/marktree_defs.h"
#include "nvim/match.h"
@@ -80,6 +81,8 @@ typedef struct {
int cul_attr; ///< set when 'cursorline' active
int line_attr; ///< attribute for the whole line
int line_attr_lowprio; ///< low-priority attribute for the line
+ int sign_num_attr; ///< line number attribute (sign numhl)
+ int sign_cul_attr; ///< cursorline sign attribute (sign culhl)
int fromcol; ///< start of inverting
int tocol; ///< end of inverting
@@ -251,12 +254,20 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells
static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int win_row)
{
- DecorState *state = &decor_state;
- const int max_col = wp->w_grid.cols;
+ DecorState *const state = &decor_state;
+ int const max_col = wp->w_grid.cols;
int right_pos = max_col;
- bool do_eol = state->eol_col > -1;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
+ bool const do_eol = state->eol_col > -1;
+
+ int const end = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ /// Total width of all virtual text with "eol_right_align" alignment
+ int totalWidthOfEolRightAlignedVirtText = 0;
+
+ for (int i = 0; i < end; i++) {
+ DecorRange *item = &slots[indices[i]].range;
if (!(item->start_row == state->row && decor_virt_pos(item))) {
continue;
}
@@ -269,7 +280,44 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
if (decor_virt_pos(item) && item->draw_col == -1) {
bool updated = true;
VirtTextPos pos = decor_virt_pos_kind(item);
- if (pos == kVPosRightAlign) {
+
+ if (do_eol && pos == kVPosEndOfLineRightAlign) {
+ int eolOffset = 0;
+ if (totalWidthOfEolRightAlignedVirtText == 0) {
+ // Look ahead to the remaining decor items
+ for (int j = i; j < end; j++) {
+ /// A future decor to be handled in this function's call
+ DecorRange *lookaheadItem = &slots[indices[j]].range;
+
+ if (lookaheadItem->start_row != state->row
+ || !decor_virt_pos(lookaheadItem)
+ || lookaheadItem->draw_col != -1) {
+ continue;
+ }
+
+ /// The Virtual Text of the decor item we're looking ahead to
+ DecorVirtText *lookaheadVt = NULL;
+ if (item->kind == kDecorKindVirtText) {
+ assert(item->data.vt);
+ lookaheadVt = item->data.vt;
+ }
+
+ if (decor_virt_pos_kind(lookaheadItem) == kVPosEndOfLineRightAlign) {
+ // An extra space is added for single character spacing in EOL alignment
+ totalWidthOfEolRightAlignedVirtText += (lookaheadVt->width + 1);
+ }
+ }
+
+ // Remove one space from the total width since there's no single space after the last entry
+ totalWidthOfEolRightAlignedVirtText--;
+
+ if (totalWidthOfEolRightAlignedVirtText <= (right_pos - state->eol_col)) {
+ eolOffset = right_pos - totalWidthOfEolRightAlignedVirtText - state->eol_col;
+ }
+ }
+
+ item->draw_col = state->eol_col + eolOffset;
+ } else if (pos == kVPosRightAlign) {
right_pos -= vt->width;
item->draw_col = right_pos;
} else if (pos == kVPosEndOfLine && do_eol) {
@@ -296,7 +344,7 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
int vcol = item->draw_col - col_off;
int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text,
vt->hl_mode, max_col, vcol);
- if (vt->pos == kVPosEndOfLine && do_eol) {
+ if (do_eol && ((vt->pos == kVPosEndOfLine) || (vt->pos == kVPosEndOfLineRightAlign))) {
state->eol_col = col + 1;
}
*end_col = MAX(*end_col, col);
@@ -360,14 +408,14 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
// TODO(bfredl): integrate with grid.c linebuf code? madness?
static void draw_col_buf(win_T *wp, winlinevars_T *wlv, const char *text, size_t len, int attr,
- bool vcol)
+ const colnr_T *fold_vcol, bool inc_vcol)
{
const char *ptr = text;
while (ptr < text + len && wlv->off < wp->w_grid.cols) {
int cells = line_putchar(wp->w_buffer, &ptr, &linebuf_char[wlv->off],
wp->w_grid.cols - wlv->off, wlv->off);
int myattr = attr;
- if (vcol) {
+ if (inc_vcol) {
advance_color_col(wlv, wlv->vcol);
if (wlv->color_cols && wlv->vcol == *wlv->color_cols) {
myattr = hl_combine_attr(win_hl_attr(wp, HLF_MC), myattr);
@@ -375,7 +423,7 @@ static void draw_col_buf(win_T *wp, winlinevars_T *wlv, const char *text, size_t
}
for (int c = 0; c < cells; c++) {
linebuf_attr[wlv->off] = myattr;
- linebuf_vcol[wlv->off] = vcol ? wlv->vcol++ : -1;
+ linebuf_vcol[wlv->off] = inc_vcol ? wlv->vcol++ : fold_vcol ? *(fold_vcol++) : -1;
wlv->off++;
}
}
@@ -391,11 +439,11 @@ static void draw_col_fill(winlinevars_T *wlv, schar_T fillchar, int width, int a
}
/// Return true if CursorLineSign highlight is to be used.
-static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)
+bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)
{
return wp->w_p_cul
&& lnum == wp->w_cursorline
- && (wp->w_p_culopt_flags & CULOPT_NBR);
+ && (wp->w_p_culopt_flags & kOptCuloptFlagNumber);
}
/// Setup for drawing the 'foldcolumn', if there is one.
@@ -404,7 +452,7 @@ static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv)
int fdc = compute_foldcolumn(wp, 0);
if (fdc > 0) {
int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLF : HLF_FC);
- fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, &wlv->off, NULL);
+ fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, &wlv->off, NULL, NULL);
}
}
@@ -413,8 +461,9 @@ static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv)
/// @param fdc Current width of the foldcolumn
/// @param[out] wlv_off Pointer to linebuf offset, incremented for default column
/// @param[out] out_buffer Char array to fill, only used for 'statuscolumn'
+/// @param[out] out_vcol vcol array to fill, only used for 'statuscolumn'
void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, int fdc, int *wlv_off,
- schar_T *out_buffer)
+ colnr_T *out_vcol, schar_T *out_buffer)
{
bool closed = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
int level = foldinfo.fi_level;
@@ -440,10 +489,12 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
symbol = schar_from_ascii('>');
}
+ int vcol = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3;
if (out_buffer) {
+ out_vcol[i] = vcol;
out_buffer[i] = symbol;
} else {
- linebuf_vcol[*wlv_off] = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3;
+ linebuf_vcol[*wlv_off] = vcol;
linebuf_attr[*wlv_off] = attr;
linebuf_char[(*wlv_off)++] = symbol;
}
@@ -454,14 +505,15 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
/// If "nrcol" is true, the sign is going to be displayed in the number column.
/// Otherwise the sign is going to be displayed in the sign column. If there is no
/// sign, draw blank cells instead.
-static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, int sign_cul_attr)
+static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx)
{
SignTextAttrs sattr = wlv->sattrs[sign_idx];
+ int scl_attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
if (sattr.text[0] && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
- int attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr)
- ? sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0;
int fill = nrcol ? number_width(wp) + 1 : SIGN_WIDTH;
+ int attr = wlv->sign_cul_attr ? wlv->sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0;
+ attr = hl_combine_attr(scl_attr, attr);
draw_col_fill(wlv, schar_from_ascii(' '), fill, attr);
int sign_pos = wlv->off - SIGN_WIDTH - (int)nrcol;
assert(sign_pos >= 0);
@@ -469,8 +521,7 @@ static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, i
linebuf_char[sign_pos + 1] = sattr.text[1];
} else {
assert(!nrcol); // handled in draw_lnum_col()
- int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
- draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, attr);
+ draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, scl_attr);
}
}
@@ -507,10 +558,10 @@ static bool use_cursor_line_nr(win_T *wp, winlinevars_T *wlv)
{
return wp->w_p_cul
&& wlv->lnum == wp->w_cursorline
- && (wp->w_p_culopt_flags & CULOPT_NBR)
+ && (wp->w_p_culopt_flags & kOptCuloptFlagNumber)
&& (wlv->row == wlv->startrow + wlv->filler_lines
|| (wlv->row > wlv->startrow + wlv->filler_lines
- && (wp->w_p_culopt_flags & CULOPT_LINE)));
+ && (wp->w_p_culopt_flags & kOptCuloptFlagLine)));
}
static int get_line_number_attr(win_T *wp, winlinevars_T *wlv)
@@ -537,7 +588,7 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv)
/// Display the absolute or relative line number. After the first row fill with
/// blanks when the 'n' flag isn't in 'cpo'.
-static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int sign_cul_attr)
+static void draw_lnum_col(win_T *wp, winlinevars_T *wlv)
{
bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL;
@@ -550,12 +601,12 @@ static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int
// then display the sign instead of the line number.
if (wp->w_minscwidth == SCL_NUM && wlv->sattrs[0].text[0]
&& wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
- draw_sign(true, wp, wlv, 0, sign_cul_attr);
+ draw_sign(true, wp, wlv, 0);
} else {
// Draw the line number (empty space after wrapping).
int width = number_width(wp) + 1;
- int attr = (sign_num_attr > 0 && wlv->filler_todo <= 0)
- ? sign_num_attr : get_line_number_attr(wp, wlv);
+ int attr = hl_combine_attr(get_line_number_attr(wp, wlv),
+ wlv->filler_todo <= 0 ? wlv->sign_num_attr : 0);
if (wlv->row == wlv->startrow + wlv->filler_lines
&& (wp->w_skipcol == 0 || wlv->row > 0 || (wp->w_p_nu && wp->w_p_rnu))) {
char buf[32];
@@ -569,7 +620,7 @@ static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int
char *num = skipwhite(buf);
rl_mirror_ascii(num, skiptowhite(num));
}
- draw_col_buf(wp, wlv, buf, (size_t)width, attr, false);
+ draw_col_buf(wp, wlv, buf, (size_t)width, attr, NULL, false);
} else {
draw_col_fill(wlv, schar_from_ascii(' '), width, attr);
}
@@ -624,22 +675,27 @@ static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int vir
char *p = buf;
char transbuf[MAXPATHL];
- int attr = stcp->num_attr;
+ colnr_T *fold_vcol = NULL;
size_t len = strlen(buf);
+ int scl_attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
+ int num_attr = hl_combine_attr(get_line_number_attr(wp, wlv),
+ wlv->filler_todo <= 0 ? wlv->sign_num_attr : 0);
+ int cur_attr = num_attr;
// Draw each segment with the specified highlighting.
for (stl_hlrec_t *sp = stcp->hlrec; sp->start != NULL; sp++) {
ptrdiff_t textlen = sp->start - p;
// Make all characters printable.
size_t translen = transstr_buf(p, textlen, transbuf, MAXPATHL, true);
- draw_col_buf(wp, wlv, transbuf, translen, attr, false);
+ draw_col_buf(wp, wlv, transbuf, translen, cur_attr, fold_vcol, false);
+ int attr = sp->item == STL_SIGNCOL ? scl_attr : sp->item == STL_FOLDCOL ? 0 : num_attr;
+ cur_attr = hl_combine_attr(attr, sp->userhl < 0 ? syn_id2attr(-sp->userhl) : 0);
+ fold_vcol = sp->item == STL_FOLDCOL ? stcp->fold_vcol : NULL;
p = sp->start;
- int hl = sp->userhl;
- attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr;
}
size_t translen = transstr_buf(p, buf + len - p, transbuf, MAXPATHL, true);
- draw_col_buf(wp, wlv, transbuf, translen, attr, false);
- draw_col_fill(wlv, schar_from_ascii(' '), stcp->width - width, stcp->num_attr);
+ draw_col_buf(wp, wlv, transbuf, translen, cur_attr, fold_vcol, false);
+ draw_col_fill(wlv, schar_from_ascii(' '), stcp->width - width, cur_attr);
}
static void handle_breakindent(win_T *wp, winlinevars_T *wlv)
@@ -711,7 +767,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
// Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
int attr = hl_combine_attr(wlv->cul_attr, win_hl_attr(wp, HLF_AT));
colnr_T vcol_before = wlv->vcol;
- draw_col_buf(wp, wlv, sbr, strlen(sbr), attr, true);
+ draw_col_buf(wp, wlv, sbr, strlen(sbr), attr, NULL, true);
wlv->vcol_sbr = wlv->vcol;
// Correct start of highlighted area for 'showbreak'.
@@ -756,17 +812,28 @@ static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v)
if (wlv->virt_inline_i < kv_size(wlv->virt_inline)) {
return true;
}
- DecorState *state = &decor_state;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
- if (item->start_row != state->row
- || item->kind != kDecorKindVirtText
- || item->data.vt->pos != kVPosInline
- || item->data.vt->width == 0) {
- continue;
- }
- if (item->draw_col >= -1 && item->start_col >= v) {
- return true;
+
+ int const count = (int)kv_size(decor_state.ranges_i);
+ int const cur_end = decor_state.current_end;
+ int const fut_beg = decor_state.future_begin;
+ int *const indices = decor_state.ranges_i.items;
+ DecorRangeSlot *const slots = decor_state.slots.items;
+
+ int const beg_pos[] = { 0, fut_beg };
+ int const end_pos[] = { cur_end, count };
+
+ for (int pos_i = 0; pos_i < 2; pos_i++) {
+ for (int i = beg_pos[pos_i]; i < end_pos[pos_i]; i++) {
+ DecorRange *item = &slots[indices[i]].range;
+ if (item->start_row != decor_state.row
+ || item->kind != kDecorKindVirtText
+ || item->data.vt->pos != kVPosInline
+ || item->data.vt->width == 0) {
+ continue;
+ }
+ if (item->draw_col >= -1 && item->start_col >= v) {
+ return true;
+ }
}
}
return false;
@@ -780,8 +847,12 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
wlv->virt_inline = VIRTTEXT_EMPTY;
wlv->virt_inline_i = 0;
DecorState *state = &decor_state;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
+ int const end = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ for (int i = 0; i < end; i++) {
+ DecorRange *item = &slots[indices[i]].range;
if (item->draw_col == -3) {
// No more inline virtual text before this non-inline virtual text item,
// so its position can be decided now.
@@ -913,6 +984,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
colnr_T vcol_prev = -1; // "wlv.vcol" of previous character
ScreenGrid *grid = &wp->w_grid; // grid specific to the window
+ const bool in_curline = wp == curwin && lnum == curwin->w_cursor.lnum;
const bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
const bool has_foldtext = has_fold && *wp->w_p_fdt != NUL;
@@ -932,7 +1004,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
bool area_highlighting = false; // Visual or incsearch highlighting in this line
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 search_attr = 0; // attributes desired by 'hlsearch' or ComplMatchIns
int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
int decor_attr = 0; // attributes desired by syntax and extmarks
bool has_syntax = false; // this buffer has syntax highl.
@@ -946,7 +1018,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
int spell_attr = 0; // attributes desired by spelling
int word_end = 0; // last byte with same spell_attr
int cur_checked_col = 0; // checked column for current line
- bool extra_check = 0; // has syntax or linebreak
+ bool extra_check = false; // has extra highlighting
int multi_attr = 0; // attributes desired by multibyte
int mb_l = 1; // multi-byte byte length
int mb_c = 0; // decoded multi-byte character
@@ -1028,12 +1100,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
}
- has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
-
if (!end_fill) {
- decor_providers_invoke_line(wp, lnum - 1, &has_decor);
+ decor_providers_invoke_line(wp, lnum - 1);
}
+ has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
+
if (has_decor) {
extra_check = true;
}
@@ -1096,7 +1168,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
// Check if the char under the cursor should be inverted (highlighted).
- if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
+ if (!highlight_match && in_curline
&& cursor_is_block_during_visual(*p_sel == 'e')) {
noinvcur = true;
}
@@ -1165,11 +1237,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
wlv.filler_todo = wlv.filler_lines;
// Cursor line highlighting for 'cursorline' in the current window.
- if (wp->w_p_cul && wp->w_p_culopt_flags != CULOPT_NBR && lnum == wp->w_cursorline
+ if (wp->w_p_cul && wp->w_p_culopt_flags != kOptCuloptFlagNumber && lnum == wp->w_cursorline
// Do not show the cursor line in the text when Visual mode is active,
// because it's not clear what is selected then.
&& !(wp == curwin && VIsual_active)) {
- cul_screenline = (is_wrapped && (wp->w_p_culopt_flags & CULOPT_SCRLINE));
+ cul_screenline = (is_wrapped && (wp->w_p_culopt_flags & kOptCuloptFlagScreenline));
if (!cul_screenline) {
apply_cursorline_highlight(wp, &wlv);
} else {
@@ -1178,11 +1250,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
area_highlighting = true;
}
- int line_attr = 0;
- int sign_cul_attr = 0;
- int sign_num_attr = 0;
+ int sign_line_attr = 0;
// TODO(bfredl, vigoux): line_attr should not take priority over decoration!
- decor_redraw_signs(wp, buf, wlv.lnum - 1, wlv.sattrs, &line_attr, &sign_cul_attr, &sign_num_attr);
+ decor_redraw_signs(wp, buf, wlv.lnum - 1, wlv.sattrs,
+ &sign_line_attr, &wlv.sign_cul_attr, &wlv.sign_num_attr);
statuscol_T statuscol = { 0 };
if (*wp->w_p_stc != NUL) {
@@ -1191,19 +1262,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
statuscol.sattrs = wlv.sattrs;
statuscol.foldinfo = foldinfo;
statuscol.width = win_col_off(wp) - (wp == cmdwin_win);
- statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
- statuscol.sign_cul_id = statuscol.use_cul ? sign_cul_attr : 0;
- statuscol.num_attr = sign_num_attr > 0 ? syn_id2attr(sign_num_attr) : 0;
- } else {
- if (sign_cul_attr > 0) {
- sign_cul_attr = syn_id2attr(sign_cul_attr);
- }
- if (sign_num_attr > 0) {
- sign_num_attr = syn_id2attr(sign_num_attr);
- }
+ statuscol.sign_cul_id = use_cursor_line_highlight(wp, lnum) ? wlv.sign_cul_attr : 0;
+ } else if (wlv.sign_cul_attr > 0) {
+ wlv.sign_cul_attr = use_cursor_line_highlight(wp, lnum) ? syn_id2attr(wlv.sign_cul_attr) : 0;
+ }
+ if (wlv.sign_num_attr > 0) {
+ wlv.sign_num_attr = syn_id2attr(wlv.sign_num_attr);
}
- if (line_attr > 0) {
- wlv.line_attr = syn_id2attr(line_attr);
+ if (sign_line_attr > 0) {
+ wlv.line_attr = syn_id2attr(sign_line_attr);
}
// Highlight the current line in the quickfix window.
@@ -1472,6 +1539,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
ptr = line + v; // "line" may have been updated
}
+ if ((State & MODE_INSERT) && in_curline && ins_compl_win_active(wp)) {
+ area_highlighting = true;
+ }
+
win_line_start(wp, &wlv);
bool draw_cols = true;
int leftcols_width = 0;
@@ -1522,9 +1593,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// skip columns
} else if (statuscol.draw) {
// Draw 'statuscolumn' if it is set.
- if (sign_num_attr == 0) {
- statuscol.num_attr = get_line_number_attr(wp, &wlv);
- }
const int v = (int)(ptr - line);
draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, col_rows, &statuscol);
if (wp->w_redr_statuscol) {
@@ -1541,10 +1609,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// wp->w_scwidth is zero if signcol=number is used
for (int sign_idx = 0; sign_idx < wp->w_scwidth; sign_idx++) {
- draw_sign(false, wp, &wlv, sign_idx, sign_cul_attr);
+ draw_sign(false, wp, &wlv, sign_idx);
}
- draw_lnum_col(wp, &wlv, sign_num_attr, sign_cul_attr);
+ draw_lnum_col(wp, &wlv);
}
win_col_offset = wlv.off;
@@ -1608,8 +1676,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
// When still displaying '$' of change command, stop at cursor.
- if (dollar_vcol >= 0 && wp == curwin
- && lnum == wp->w_cursor.lnum && wlv.vcol >= wp->w_virtcol) {
+ if (dollar_vcol >= 0 && in_curline && wlv.vcol >= wp->w_virtcol) {
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
// don't clear anything after wlv.col
wlv_put_linebuf(wp, &wlv, wlv.col, false, bg_attr, 0);
@@ -1718,6 +1785,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (*ptr == NUL) {
has_match_conc = 0;
}
+
+ // Check if ComplMatchIns highlight is needed.
+ if ((State & MODE_INSERT) && in_curline && ins_compl_win_active(wp)) {
+ int ins_match_attr = ins_compl_col_range_attr((int)(ptr - line));
+ if (ins_match_attr > 0) {
+ search_attr = hl_combine_attr(search_attr, ins_match_attr);
+ }
+ }
}
if (wlv.diff_hlf != (hlf_T)0) {
@@ -1949,7 +2024,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
decor_attr = 0;
if (extra_check) {
- const bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
+ const bool no_plain_buffer = (wp->w_s->b_p_spo_flags & kOptSpoFlagNoplainbuffer) != 0;
bool can_spell = !no_plain_buffer;
// Get extmark and syntax attributes, unless still at the start of the line
@@ -2324,7 +2399,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (wlv.n_extra == 0) {
wlv.n_extra = byte2cells(mb_c) - 1;
}
- if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
+ if ((dy_flags & kOptDyFlagUhex) && wp->w_p_rl) {
rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>"
}
wlv.sc_extra = NUL;
@@ -2425,8 +2500,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// With 'virtualedit' we may never reach cursor position, but we still
// need to correct the cursor column, so do that at end of line.
if (!did_wcol && wlv.filler_todo <= 0
- && wp == curwin && lnum == wp->w_cursor.lnum
- && conceal_cursor_line(wp)
+ && in_curline && conceal_cursor_line(wp)
&& (wlv.vcol + wlv.skip_cells >= wp->w_virtcol || mb_schar == NUL)) {
wp->w_wcol = wlv.col - wlv.boguscols;
if (wlv.vcol + wlv.skip_cells < wp->w_virtcol) {
@@ -2619,7 +2693,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// Update w_cline_height and w_cline_folded if the cursor line was
// updated (saves a call to plines_win() later).
- if (wp == curwin && lnum == curwin->w_cursor.lnum) {
+ if (in_curline) {
curwin->w_cline_row = startrow;
curwin->w_cline_height = wlv.row - startrow;
curwin->w_cline_folded = has_fold;
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index e90a0d945f..4d7f80bf76 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -68,7 +68,6 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
-#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/decoration_defs.h"
#include "nvim/decoration_provider.h"
@@ -178,14 +177,11 @@ bool default_grid_alloc(void)
resizing = true;
// 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.
+ // when Rows and Columns have been set.
if ((default_grid.chars != NULL
&& Rows == default_grid.rows
&& Columns == default_grid.cols)
- || Rows == 0
- || Columns == 0
- || (!full_screen && default_grid.chars == NULL)) {
+ || Rows == 0 || Columns == 0) {
resizing = false;
return false;
}
@@ -290,9 +286,23 @@ void screen_resize(int width, int height)
Rows = height;
Columns = width;
check_screensize();
- int max_p_ch = Rows - min_rows() + 1;
- if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) {
- p_ch = max_p_ch ? max_p_ch : 1;
+ if (!ui_has(kUIMessages)) {
+ // clamp 'cmdheight'
+ int max_p_ch = Rows - min_rows(curtab) + 1;
+ if (p_ch > 0 && p_ch > max_p_ch) {
+ p_ch = MAX(max_p_ch, 1);
+ curtab->tp_ch_used = p_ch;
+ }
+ // clamp 'cmdheight' for other tab pages
+ FOR_ALL_TABS(tp) {
+ if (tp == curtab) {
+ continue; // already set above
+ }
+ int max_tp_ch = Rows - min_rows(tp) + 1;
+ if (tp->tp_ch_used > 0 && tp->tp_ch_used > max_tp_ch) {
+ tp->tp_ch_used = MAX(max_tp_ch, 1);
+ }
+ }
}
height = Rows;
width = Columns;
@@ -396,7 +406,7 @@ void check_screensize(void)
{
// Limit Rows and Columns to avoid an overflow in Rows * Columns.
// need room for one window and command line
- Rows = MIN(MAX(Rows, min_rows()), 1000);
+ Rows = MIN(MAX(Rows, min_rows_for_all_tabpages()), 1000);
Columns = MIN(MAX(Columns, MIN_COLUMNS), 10000);
}
@@ -665,6 +675,10 @@ int update_screen(void)
updating_screen = false;
+ if (need_maketitle) {
+ maketitle();
+ }
+
// Clear or redraw the command line. Done last, because scrolling may
// mess up the command line.
if (clear_cmdline || redraw_cmdline || redraw_mode) {
@@ -678,8 +692,11 @@ int update_screen(void)
decor_providers_invoke_end();
- // either cmdline is cleared, not drawn or mode is last drawn
- cmdline_was_last_drawn = false;
+ // Either cmdline is cleared, not drawn or mode is last drawn.
+ // This does not (necessarily) overwrite an external cmdline.
+ if (!ui_has(kUICmdline)) {
+ cmdline_was_last_drawn = false;
+ }
return OK;
}
@@ -842,6 +859,19 @@ void setcursor_mayforce(win_T *wp, bool force)
}
}
+/// Mark the title and icon for redraw if either of them uses statusline format.
+///
+/// @return whether either title or icon uses statusline format.
+bool redraw_custom_title_later(void)
+{
+ if ((p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))) {
+ need_maketitle = true;
+ return true;
+ }
+ return false;
+}
+
/// Show current cursor info in ruler and various other places
///
/// @param always if false, only show ruler if position has changed.
@@ -875,10 +905,7 @@ void show_cursor_info_later(bool force)
curwin->w_redr_status = true;
}
- if ((p_icon && (stl_syntax & STL_IN_ICON))
- || (p_title && (stl_syntax & STL_IN_TITLE))) {
- need_maketitle = true;
- }
+ redraw_custom_title_later();
}
curwin->w_stl_cursor = curwin->w_cursor;
@@ -1018,7 +1045,7 @@ int showmode(void)
if (State & MODE_LANGMAP) {
if (curwin->w_p_arab) {
msg_puts_hl(_(" Arabic"), hl_id, false);
- } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) {
+ } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL) > 0) {
msg_puts_hl(NameBuff, hl_id, false);
}
}
@@ -1092,9 +1119,13 @@ int showmode(void)
win_T *ruler_win = curwin->w_status_height == 0 ? curwin : lastwin_nofloating();
if (redrawing() && ruler_win->w_status_height == 0 && global_stl_height() == 0
&& !(p_ch == 0 && !ui_has(kUIMessages))) {
- grid_line_start(&msg_grid_adj, Rows - 1);
+ if (!ui_has(kUIMessages)) {
+ grid_line_start(&msg_grid_adj, Rows - 1);
+ }
win_redr_ruler(ruler_win);
- grid_line_flush();
+ if (!ui_has(kUIMessages)) {
+ grid_line_flush();
+ }
}
redraw_cmdline = false;
@@ -1498,10 +1529,12 @@ static void win_update(win_T *wp)
decor_providers_invoke_win(wp);
- if (win_redraw_signcols(wp)) {
- wp->w_lines_valid = 0;
- wp->w_redr_type = UPD_NOT_VALID;
- changed_line_abv_curs_win(wp);
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (win->w_buffer == wp->w_buffer && win_redraw_signcols(win)) {
+ win->w_lines_valid = 0;
+ changed_line_abv_curs_win(win);
+ redraw_later(win, UPD_NOT_VALID);
+ }
}
init_search_hl(wp, &screen_search_hl);
@@ -1882,7 +1915,7 @@ static void win_update(win_T *wp)
unsigned save_ve_flags = curwin->w_ve_flags;
if (curwin->w_p_lbr) {
- curwin->w_ve_flags = VE_ALL;
+ curwin->w_ve_flags = kOptVeFlagAll;
}
getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
@@ -1891,7 +1924,7 @@ static void win_update(win_T *wp)
// Highlight to the end of the line, unless 'virtualedit' has
// "block".
if (curwin->w_curswant == MAXCOL) {
- if (get_ve_flags(curwin) & VE_BLOCK) {
+ if (get_ve_flags(curwin) & kOptVeFlagBlock) {
pos_T pos;
int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
@@ -1903,7 +1936,7 @@ static void win_update(win_T *wp)
pos.lnum += cursor_above ? 1 : -1) {
colnr_T t;
- pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum));
+ pos.col = ml_get_buf_len(wp->w_buffer, pos.lnum);
getvvcol(wp, &pos, NULL, NULL, &t);
toc = MAX(toc, t);
}
@@ -1999,14 +2032,7 @@ static void win_update(win_T *wp)
}
foldinfo_T cursorline_fi = { 0 };
- wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
- if (wp->w_p_cul) {
- // Make sure that the cursorline on a closed fold is redrawn
- cursorline_fi = fold_info(wp, wp->w_cursor.lnum);
- if (cursorline_fi.fi_level != 0 && cursorline_fi.fi_lines > 0) {
- wp->w_cursorline = cursorline_fi.fi_lnum;
- }
- }
+ win_update_cursorline(wp, &cursorline_fi);
win_check_ns_hl(wp);
@@ -2068,7 +2094,7 @@ static void win_update(win_T *wp)
// match in fixed position might need redraw
// if lines were inserted or deleted
|| (wp->w_match_head != NULL
- && buf->b_mod_xlines != 0)))))
+ && buf->b_mod_set && buf->b_mod_xlines != 0)))))
|| lnum == wp->w_cursorline
|| lnum == wp->w_last_cursorline) {
if (lnum == mod_top) {
@@ -2227,7 +2253,7 @@ static void win_update(win_T *wp)
&& wp->w_lines[idx].wl_valid
&& wp->w_lines[idx].wl_lnum == lnum
&& lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && !(dy_flags & (kOptDyFlagLastline | kOptDyFlagTruncate))
&& srow + wp->w_lines[idx].wl_size > wp->w_grid.rows
&& win_get_fill(wp, lnum) == 0) {
// This line is not going to fit. Don't draw anything here,
@@ -2287,7 +2313,8 @@ static void win_update(win_T *wp)
// - 'number' is set and below inserted/deleted lines, or
// - 'relativenumber' is set and cursor moved vertically,
// the text doesn't need to be redrawn, but the number column does.
- if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot && buf->b_mod_xlines != 0)
+ if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot
+ && buf->b_mod_set && buf->b_mod_xlines != 0)
|| (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) {
foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum
? cursorline_fi : fold_info(wp, lnum);
@@ -2354,7 +2381,7 @@ redr_statuscol:
// Window ends in filler lines.
wp->w_botline = lnum;
wp->w_filler_rows = wp->w_grid.rows - srow;
- } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
+ } else if (dy_flags & kOptDyFlagTruncate) { // 'display' has "truncate"
// Last line isn't finished: Display "@@@" in the last screen line.
grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr);
@@ -2362,7 +2389,7 @@ redr_statuscol:
grid_line_flush();
set_empty_rows(wp, srow);
wp->w_botline = lnum;
- } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
+ } else if (dy_flags & kOptDyFlagLastline) { // 'display' has "lastline"
// Last line isn't finished: Display "@@@" at the end.
// If this would split a doublewidth char in two, we need to display "@@@@" instead
grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
@@ -2546,9 +2573,9 @@ int compute_foldcolumn(win_T *wp, int col)
{
int fdc = win_fdccol_count(wp);
int wmw = wp == curwin && p_wmw == 0 ? 1 : (int)p_wmw;
- int wwidth = wp->w_grid.cols;
+ int n = wp->w_grid.cols - (col + wmw);
- return MIN(fdc, wwidth - (col + wmw));
+ return MIN(fdc, n);
}
/// Return the width of the 'number' and 'relativenumber' column.
@@ -2752,6 +2779,10 @@ void redraw_statuslines(void)
if (redraw_tabline) {
draw_tabline();
}
+
+ if (need_maketitle) {
+ maketitle();
+ }
}
/// Redraw all status lines at the bottom of frame "frp".
@@ -2827,3 +2858,18 @@ bool win_cursorline_standout(const win_T *wp)
{
return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
}
+
+/// Update w_cursorline, taking care to set it to the to the start of a closed fold.
+///
+/// @param[out] foldinfo foldinfo for the cursor line
+void win_update_cursorline(win_T *wp, foldinfo_T *foldinfo)
+{
+ wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+ if (wp->w_p_cul) {
+ // Make sure that the cursorline on a closed fold is redrawn
+ *foldinfo = fold_info(wp, wp->w_cursor.lnum);
+ if (foldinfo->fi_level != 0 && foldinfo->fi_lines > 0) {
+ wp->w_cursorline = foldinfo->fi_lnum;
+ }
+ }
+}
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
index 36ba8099fd..58eca9ac07 100644
--- a/src/nvim/drawscreen.h
+++ b/src/nvim/drawscreen.h
@@ -4,6 +4,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/macros_defs.h"
+#include "nvim/pos_defs.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 f06dc124f0..9e17c93f3f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4,9 +4,11 @@
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stddef.h>
#include <string.h>
#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -31,6 +33,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -46,6 +49,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -440,7 +444,7 @@ static int insert_check(VimState *state)
msg_scroll = false;
// Open fold at the cursor line, according to 'foldopen'.
- if (fdo_flags & FDO_INSERT) {
+ if (fdo_flags & kOptFdoFlagInsert) {
foldOpenCursor();
}
@@ -604,8 +608,11 @@ static int insert_execute(VimState *state, int key)
|| (ins_compl_enter_selects()
&& (s->c == CAR || s->c == K_KENTER || s->c == NL)))
&& stop_arrow() == OK) {
- ins_compl_delete();
- ins_compl_insert(false);
+ ins_compl_delete(false);
+ ins_compl_insert(false, false);
+ } else if (ascii_iswhite_nl_or_nul(s->c) && ins_compl_preinsert_effect()) {
+ // Delete preinserted text when typing special chars
+ ins_compl_delete(false);
}
}
}
@@ -751,7 +758,7 @@ static int insert_handle_key(InsertState *s)
ins_ctrl_o();
// don't move the cursor left when 'virtualedit' has "onemore".
- if (get_ve_flags(curwin) & VE_ONEMORE) {
+ if (get_ve_flags(curwin) & kOptVeFlagOnemore) {
ins_at_eol = false;
s->nomove = true;
}
@@ -2518,7 +2525,7 @@ int oneright(void)
// move "l" bytes right, but don't end up on the NUL, unless 'virtualedit'
// contains "onemore".
- if (ptr[l] == NUL && (get_ve_flags(curwin) & VE_ONEMORE) == 0) {
+ if (ptr[l] == NUL && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
return FAIL;
}
curwin->w_cursor.col += l;
@@ -2600,7 +2607,7 @@ void cursor_up_inner(win_T *wp, linenr_T n)
// 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))) {
+ if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & kOptFdoFlagAll))) {
hasFolding(wp, lnum, &lnum, NULL);
}
}
@@ -3223,7 +3230,7 @@ static void ins_reg(void)
check_cursor(curwin);
}
if (regname == NUL || !valid_yank_reg(regname, false)) {
- vim_beep(BO_REG);
+ vim_beep(kOptBoFlagRegister);
need_redraw = true; // remove the '"'
} else {
if (literally == Ctrl_O || literally == Ctrl_P) {
@@ -3235,7 +3242,7 @@ static void ins_reg(void)
do_put(regname, NULL, BACKWARD, 1,
(literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND);
} else if (insert_reg(regname, literally) == FAIL) {
- vim_beep(BO_REG);
+ vim_beep(kOptBoFlagRegister);
need_redraw = true; // remove the '"'
} else if (stop_insert_mode) {
// When the '=' register was used and a function was invoked that
@@ -3314,7 +3321,7 @@ static void ins_ctrl_g(void)
// Unknown CTRL-G command, reserved for future expansion.
default:
- vim_beep(BO_CTRLG);
+ vim_beep(kOptBoFlagCtrlg);
}
}
@@ -3412,7 +3419,7 @@ static bool ins_esc(int *count, int cmdchar, bool nomove)
&& (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0)
&& (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active))
&& !revins_on) {
- if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == VE_ALL) {
+ if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == kOptVeFlagAll) {
oneleft();
if (restart_edit != NUL) {
curwin->w_cursor.coladd++;
@@ -3598,7 +3605,7 @@ static void ins_del(void)
const int temp = curwin->w_cursor.col;
if (!can_bs(BS_EOL) // only if "eol" included
|| do_join(2, false, true, false, false) == FAIL) {
- vim_beep(BO_BS);
+ vim_beep(kOptBoFlagBackspace);
} else {
curwin->w_cursor.col = temp;
// Adjust orig_line_count in case more lines have been deleted than
@@ -3610,7 +3617,7 @@ static void ins_del(void)
}
}
} else if (del_char(false) == FAIL) { // delete char under cursor
- vim_beep(BO_BS);
+ vim_beep(kOptBoFlagBackspace);
}
did_ai = false;
did_si = false;
@@ -3649,7 +3656,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
|| (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0
&& curwin->w_cursor.col <= ai_col)
|| (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) {
- vim_beep(BO_BS);
+ vim_beep(kOptBoFlagBackspace);
return false;
}
@@ -3962,7 +3969,7 @@ static void ins_left(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -3985,14 +3992,14 @@ static void ins_left(void)
coladvance(curwin, MAXCOL);
curwin->w_set_curswant = true; // so we stay at the end
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
static void ins_home(int c)
{
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4008,7 +4015,7 @@ static void ins_home(int c)
static void ins_end(int c)
{
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4025,7 +4032,7 @@ static void ins_end(int c)
static void ins_s_left(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4037,7 +4044,7 @@ static void ins_s_left(void)
bck_word(1, false, false);
curwin->w_set_curswant = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
@@ -4046,7 +4053,7 @@ static void ins_s_left(void)
static void ins_right(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4075,7 +4082,7 @@ static void ins_right(void)
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
@@ -4083,7 +4090,7 @@ static void ins_right(void)
static void ins_s_right(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4096,7 +4103,7 @@ static void ins_s_right(void)
fwd_word(1, false, 0);
curwin->w_set_curswant = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
@@ -4120,7 +4127,7 @@ static void ins_up(bool startcol)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4142,7 +4149,7 @@ static void ins_pageup(void)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4165,7 +4172,7 @@ static void ins_down(bool startcol)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4187,7 +4194,7 @@ static void ins_pagedown(void)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4535,7 +4542,7 @@ static int ins_digraph(void)
int ins_copychar(linenr_T lnum)
{
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
- vim_beep(BO_COPY);
+ vim_beep(kOptBoFlagCopy);
return NUL;
}
@@ -4558,7 +4565,7 @@ int ins_copychar(linenr_T lnum)
int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value;
if (c == NUL) {
- vim_beep(BO_COPY);
+ vim_beep(kOptBoFlagCopy);
}
return c;
}
diff --git a/src/nvim/errors.h b/src/nvim/errors.h
index df94945a3d..baea005f15 100644
--- a/src/nvim/errors.h
+++ b/src/nvim/errors.h
@@ -129,6 +129,7 @@ EXTERN const char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
EXTERN const char e_empty_buffer[] INIT(= N_("E749: Empty buffer"));
EXTERN const char e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist"));
+EXTERN const char e_unknown_function_str[] INIT(= N_("E117: Unknown function: %s"));
EXTERN const char e_str_not_inside_function[] INIT(= N_("E193: %s not inside a function"));
EXTERN const char e_invalpat[] INIT(= N_("E682: Invalid search pattern or delimiter"));
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index faacf3c65a..97d1a3c75d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -270,6 +270,7 @@ static struct vimvar {
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
+ VV(VV_STACKTRACE, "stacktrace", VAR_LIST, VV_RO),
// Neovim
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
@@ -4112,10 +4113,10 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua
{
const bool working = (**arg == '+'); // has("+option")
OptIndex opt_idx;
- int scope;
+ int opt_flags;
// Isolate the option name and find its value.
- char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &scope);
+ char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &opt_flags);
if (option_end == NULL) {
if (rettv != NULL) {
@@ -4143,7 +4144,7 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua
ret = FAIL;
} else if (rettv != NULL) {
- OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, scope);
+ OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, opt_flags);
assert(value.type != kOptValTypeNil);
*rettv = optval_as_tv(value, true);
@@ -7965,8 +7966,7 @@ void ex_execute(exarg_T *eap)
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
int save_did_emsg = did_emsg;
- msg_ext_set_kind("echoerr");
- emsg_multiline(ga.ga_data, true);
+ emsg_multiline(ga.ga_data, "echoerr", HLF_E, true);
if (!force_abort) {
did_emsg = save_did_emsg;
}
@@ -7986,24 +7986,25 @@ void ex_execute(exarg_T *eap)
/// Skip over the name of an option variable: "&option", "&g:option" or "&l:option".
///
-/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning.
-/// @param[out] opt_idxp Set to option index in options[] table.
-/// @param[out] scope Set to option scope.
+/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning.
+/// @param[out] opt_idxp Set to option index in options[] table.
+/// @param[out] opt_flags Option flags.
///
/// @return NULL when no option name found. Otherwise pointer to the char after the option name.
-const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp, int *const scope)
+const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp,
+ int *const opt_flags)
{
const char *p = *arg;
p++;
if (*p == 'g' && p[1] == ':') {
- *scope = OPT_GLOBAL;
+ *opt_flags = OPT_GLOBAL;
p += 2;
} else if (*p == 'l' && p[1] == ':') {
- *scope = OPT_LOCAL;
+ *opt_flags = OPT_LOCAL;
p += 2;
} else {
- *scope = 0;
+ *opt_flags = 0;
}
const char *end = find_option_end(p, opt_idxp);
@@ -8490,7 +8491,7 @@ char *do_string_sub(char *str, size_t len, char *pat, char *sub, typval_T *expr,
return ret;
}
-/// common code for getting job callbacks for jobstart, termopen and rpcstart
+/// Common code for getting job callbacks for `jobstart`.
///
/// @return true/false on success/failure.
bool common_job_callbacks(dict_T *vopts, CallbackReader *on_stdout, CallbackReader *on_stderr,
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index bb9b00abc7..8b4aa8101a 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -167,6 +167,7 @@ typedef enum {
VV_COLLATE,
VV_EXITING,
VV_MAXCOL,
+ VV_STACKTRACE,
// Nvim
VV_STDERR,
VV_MSGPACK_TYPES,
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index a418b34909..9d787c68ea 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -17,6 +17,7 @@
--- @field deprecated? true
--- @field returns? string|false
--- @field returns_desc? string
+--- @field generics? string[] Used to write `---@generic` annotations over a function.
--- @field signature? string
--- @field desc? string
--- @field params [string, string, string][]
@@ -1383,16 +1384,22 @@ M.funcs = {
See |complete_info_mode| for the values.
pum_visible |TRUE| if popup menu is visible.
See |pumvisible()|.
- items List of completion matches. Each item is a
- dictionary containing the entries "word",
+ items List of all completion candidates. Each item
+ is a dictionary containing the entries "word",
"abbr", "menu", "kind", "info" and "user_data".
See |complete-items|.
+ matches Same as "items", but only returns items that
+ are matching current query. If both "matches"
+ and "items" are in "what", the returned list
+ will still be named "items", but each item
+ will have an additional "match" field.
selected Selected item index. First index is zero.
Index is -1 if no item is selected (showing
typed text only, or the last completion after
no item is selected when using the <Up> or
<Down> keys)
- inserted Inserted string. [NOT IMPLEMENTED YET]
+ completed Return a dictionary containing the entries of
+ the currently selected index item.
preview_winid Info floating preview window id.
preview_bufnr Info floating preview buffer id.
@@ -1521,9 +1528,10 @@ M.funcs = {
A |Dictionary| is copied in a similar way as a |List|.
Also see |deepcopy()|.
]=],
+ generics = { 'T' },
name = 'copy',
- params = { { 'expr', 'any' } },
- returns = 'any',
+ params = { { 'expr', 'T' } },
+ returns = 'T',
signature = 'copy({expr})',
},
cos = {
@@ -1639,6 +1647,7 @@ M.funcs = {
]=],
name = 'ctxset',
params = { { 'context', 'table' }, { 'index', 'integer' } },
+ returns = 'integer',
signature = 'ctxset({context} [, {index}])',
},
ctxsize = {
@@ -1738,8 +1747,10 @@ M.funcs = {
Also see |copy()|.
]=],
+ generics = { 'T' },
name = 'deepcopy',
- params = { { 'expr', 'any' }, { 'noref', 'boolean' } },
+ params = { { 'expr', 'T' }, { 'noref', 'boolean' } },
+ returns = 'T',
signature = 'deepcopy({expr} [, {noref}])',
},
delete = {
@@ -1869,6 +1880,7 @@ M.funcs = {
fast = true,
name = 'did_filetype',
params = {},
+ returns = 'integer',
signature = 'did_filetype()',
},
diff_filler = {
@@ -1886,6 +1898,7 @@ M.funcs = {
]=],
name = 'diff_filler',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'diff_filler({lnum})',
},
diff_hlID = {
@@ -1930,6 +1943,7 @@ M.funcs = {
]=],
name = 'digraph_get',
params = { { 'chars', 'string' } },
+ returns = 'string',
signature = 'digraph_get({chars})',
},
digraph_getlist = {
@@ -1952,6 +1966,7 @@ M.funcs = {
]=],
name = 'digraph_getlist',
params = { { 'listall', 'boolean' } },
+ returns = 'string[][]',
signature = 'digraph_getlist([{listall}])',
},
digraph_set = {
@@ -2016,6 +2031,7 @@ M.funcs = {
]=],
name = 'empty',
params = { { 'expr', 'any' } },
+ returns = 'integer',
signature = 'empty({expr})',
},
environ = {
@@ -2048,6 +2064,7 @@ M.funcs = {
fast = true,
name = 'escape',
params = { { 'string', 'string' }, { 'chars', 'string' } },
+ returns = 'string',
signature = 'escape({string}, {chars})',
},
eval = {
@@ -3018,6 +3035,7 @@ M.funcs = {
]=],
name = 'foreach',
params = { { 'expr1', 'string|table' }, { 'expr2', 'string|function' } },
+ returns = 'string|table',
signature = 'foreach({expr1}, {expr2})',
},
foreground = {
@@ -3372,6 +3390,7 @@ M.funcs = {
]=],
name = 'getbufline',
params = { { 'buf', 'integer|string' }, { 'lnum', 'integer' }, { 'end', 'integer' } },
+ returns = 'string[]',
signature = 'getbufline({buf}, {lnum} [, {end}])',
},
getbufoneline = {
@@ -3452,15 +3471,17 @@ M.funcs = {
signature = 'getchangelist([{buf}])',
},
getchar = {
- args = { 0, 1 },
+ args = { 0, 2 },
desc = [=[
Get a single character from the user or input stream.
- If {expr} is omitted, wait until a character is available.
+ If {expr} is omitted or is -1, wait until a character is
+ available.
If {expr} is 0, only get a character when one is available.
Return zero otherwise.
If {expr} is 1, only check if a character is available, it is
not consumed. Return zero if no character available.
- If you prefer always getting a string use |getcharstr()|.
+ If you prefer always getting a string use |getcharstr()|, or
+ specify |FALSE| as "number" in {opts}.
Without {expr} and when {expr} is 0 a whole character or
special key is returned. If it is a single character, the
@@ -3470,7 +3491,8 @@ M.funcs = {
starting with 0x80 (decimal: 128). This is the same value as
the String "\<Key>", e.g., "\<Left>". The returned value is
also a String when a modifier (shift, control, alt) was used
- that is not included in the character.
+ that is not included in the character. |keytrans()| can also
+ be used to convert a returned String into a readable form.
When {expr} is 0 and Esc is typed, there will be a short delay
while Vim waits to see if this is the start of an escape
@@ -3482,6 +3504,32 @@ M.funcs = {
Use getcharmod() to obtain any additional modifiers.
+ The optional argument {opts} is a Dict and supports the
+ following items:
+
+ cursor A String specifying cursor behavior
+ when waiting for a character.
+ "hide": hide the cursor.
+ "keep": keep current cursor unchanged.
+ "msg": move cursor to message area.
+ (default: automagically decide
+ between "keep" and "msg")
+
+ number If |TRUE|, return a Number when getting
+ a single character.
+ If |FALSE|, the return value is always
+ converted to a String, and an empty
+ String (instead of 0) is returned when
+ no character is available.
+ (default: |TRUE|)
+
+ simplify If |TRUE|, include modifiers in the
+ character if possible. E.g., return
+ the same value for CTRL-I and <Tab>.
+ If |FALSE|, don't include modifiers in
+ the character.
+ (default: |TRUE|)
+
When the user clicks a mouse button, the mouse event will be
returned. The position can then be found in |v:mouse_col|,
|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -3519,9 +3567,9 @@ M.funcs = {
<
]=],
name = 'getchar',
- params = { { 'expr', '0|1' } },
- returns = 'integer',
- signature = 'getchar([{expr}])',
+ params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
+ returns = 'integer|string',
+ signature = 'getchar([{expr} [, {opts}]])',
},
getcharmod = {
desc = [=[
@@ -3594,21 +3642,13 @@ M.funcs = {
signature = 'getcharsearch()',
},
getcharstr = {
- args = { 0, 1 },
+ args = { 0, 2 },
desc = [=[
- Get a single character from the user or input stream as a
- string.
- If {expr} is omitted, wait until a character is available.
- If {expr} is 0 or false, only get a character when one is
- available. Return an empty string otherwise.
- If {expr} is 1 or true, only check if a character is
- available, it is not consumed. Return an empty string
- if no character is available.
- Otherwise this works like |getchar()|, except that a number
- result is converted to a string.
+ The same as |getchar()|, except that this always returns a
+ String, and "number" isn't allowed in {opts}.
]=],
name = 'getcharstr',
- params = { { 'expr', '0|1' } },
+ params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
returns = 'string',
signature = 'getcharstr([{expr}])',
},
@@ -3700,6 +3740,7 @@ M.funcs = {
]=],
name = 'getcmdscreenpos',
params = {},
+ returns = 'integer',
signature = 'getcmdscreenpos()',
},
getcmdtype = {
@@ -4651,6 +4692,25 @@ M.funcs = {
returns = 'vim.fn.getscriptinfo.ret[]',
signature = 'getscriptinfo([{opts}])',
},
+ getstacktrace = {
+ args = 0,
+ desc = [=[
+ Returns the current stack trace of Vim scripts.
+ Stack trace is a |List|, of which each item is a |Dictionary|
+ with the following items:
+ funcref The funcref if the stack is at a function,
+ otherwise this item is omitted.
+ event The string of the event description if the
+ stack is at an autocmd event, otherwise this
+ item is omitted.
+ lnum The line number in the script on the stack.
+ filepath The file path of the script on the stack.
+ ]=],
+ name = 'getstacktrace',
+ params = {},
+ returns = 'table[]',
+ signature = 'getstacktrace()',
+ },
gettabinfo = {
args = { 0, 1 },
base = 1,
@@ -4781,6 +4841,7 @@ M.funcs = {
]=],
name = 'gettext',
params = { { 'text', 'string' } },
+ returns = 'string',
signature = 'gettext({text})',
},
getwininfo = {
@@ -4800,6 +4861,8 @@ M.funcs = {
botline last complete displayed buffer line
bufnr number of buffer in the window
height window height (excluding winbar)
+ leftcol first column displayed; only used when
+ 'wrap' is off
loclist 1 if showing a location list
quickfix 1 if quickfix or location list window
terminal 1 if a terminal window
@@ -4963,6 +5026,7 @@ M.funcs = {
]=],
name = 'glob2regpat',
params = { { 'string', 'string' } },
+ returns = 'string',
signature = 'glob2regpat({string})',
},
globpath = {
@@ -5370,6 +5434,7 @@ M.funcs = {
fast = true,
name = 'iconv',
params = { { 'string', 'string' }, { 'from', 'string' }, { 'to', 'string' } },
+ returns = 'string',
signature = 'iconv({string}, {from}, {to})',
},
id = {
@@ -5393,6 +5458,7 @@ M.funcs = {
]=],
name = 'id',
params = { { 'expr', 'any' } },
+ returns = 'string',
signature = 'id({expr})',
},
indent = {
@@ -5445,6 +5511,7 @@ M.funcs = {
]=],
name = 'index',
params = { { 'object', 'any' }, { 'expr', 'any' }, { 'start', 'integer' }, { 'ic', 'boolean' } },
+ returns = 'integer',
signature = 'index({object}, {expr} [, {start} [, {ic}]])',
},
indexof = {
@@ -5492,6 +5559,7 @@ M.funcs = {
]=],
name = 'indexof',
params = { { 'object', 'any' }, { 'expr', 'any' }, { 'opts', 'table' } },
+ returns = 'integer',
signature = 'indexof({object}, {expr} [, {opts}])',
},
input = {
@@ -5500,6 +5568,7 @@ M.funcs = {
desc = '',
name = 'input',
params = { { 'prompt', 'string' }, { 'text', 'string' }, { 'completion', 'string' } },
+ returns = 'string',
signature = 'input({prompt} [, {text} [, {completion}]])',
},
input__1 = {
@@ -5619,6 +5688,7 @@ M.funcs = {
]=],
name = 'input',
params = { { 'opts', 'table' } },
+ returns = 'string',
signature = 'input({opts})',
},
inputdialog = {
@@ -5665,6 +5735,7 @@ M.funcs = {
]=],
name = 'inputrestore',
params = {},
+ returns = 'integer',
signature = 'inputrestore()',
},
inputsave = {
@@ -5678,6 +5749,7 @@ M.funcs = {
]=],
name = 'inputsave',
params = {},
+ returns = 'integer',
signature = 'inputsave()',
},
inputsecret = {
@@ -5697,6 +5769,7 @@ M.funcs = {
]=],
name = 'inputsecret',
params = { { 'prompt', 'string' }, { 'text', 'string' } },
+ returns = 'string',
signature = 'inputsecret({prompt} [, {text}])',
},
insert = {
@@ -5754,7 +5827,8 @@ M.funcs = {
<
]=],
name = 'invert',
- params = { { 'expr', 'number' } },
+ params = { { 'expr', 'integer' } },
+ returns = 'integer',
signature = 'invert({expr})',
},
isabsolutepath = {
@@ -5870,7 +5944,7 @@ M.funcs = {
the index.
]=],
name = 'items',
- params = { { 'dict', 'any' } },
+ params = { { 'dict', 'table' } },
signature = 'items({dict})',
},
jobclose = {
@@ -5917,7 +5991,7 @@ M.funcs = {
jobstart = {
args = { 1, 2 },
desc = [=[
- Note: Prefer |vim.system()| in Lua (unless using the `pty` option).
+ Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`).
Spawns {cmd} as a job.
If {cmd} is a List it runs directly (no 'shell').
@@ -5925,8 +5999,11 @@ M.funcs = {
call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
<(See |shell-unquoting| for details.)
- Example: >vim
- call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
+ Example: start a job and handle its output: >vim
+ call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}})
+ <
+ Example: start a job in a |terminal| connected to the current buffer: >vim
+ call jobstart(['nvim', '-h'], {'term':v:true})
<
Returns |job-id| on success, 0 on invalid arguments (or job
table is full), -1 if {cmd}[0] or 'shell' is not executable.
@@ -5991,6 +6068,10 @@ M.funcs = {
stdin: (string) Either "pipe" (default) to connect the
job's stdin to a channel or "null" to disconnect
stdin.
+ term: (boolean) Spawns {cmd} in a new pseudo-terminal session
+ connected to the current (unmodified) buffer. Implies "pty".
+ Default "height" and "width" are set to the current window
+ dimensions. |jobstart()|. Defaults $TERM to "xterm-256color".
width: (number) Width of the `pty` terminal.
{opts} is passed as |self| dictionary to the callback; the
@@ -6004,6 +6085,7 @@ M.funcs = {
]=],
name = 'jobstart',
params = { { 'cmd', 'string|string[]' }, { 'opts', 'table' } },
+ returns = 'integer',
signature = 'jobstart({cmd} [, {opts}])',
},
jobstop = {
@@ -6020,6 +6102,7 @@ M.funcs = {
]=],
name = 'jobstop',
params = { { 'id', 'integer' } },
+ returns = 'integer',
signature = 'jobstop({id})',
},
jobwait = {
@@ -6047,6 +6130,7 @@ M.funcs = {
]=],
name = 'jobwait',
params = { { 'jobs', 'integer[]' }, { 'timeout', 'integer' } },
+ returns = 'integer[]',
signature = 'jobwait({jobs} [, {timeout}])',
},
join = {
@@ -6066,6 +6150,7 @@ M.funcs = {
]=],
name = 'join',
params = { { 'list', 'any[]' }, { 'sep', 'string' } },
+ returns = 'string',
signature = 'join({list} [, {sep}])',
},
json_decode = {
@@ -6109,6 +6194,7 @@ M.funcs = {
]=],
name = 'json_encode',
params = { { 'expr', 'any' } },
+ returns = 'string',
signature = 'json_encode({expr})',
},
keys = {
@@ -6121,6 +6207,7 @@ M.funcs = {
]=],
name = 'keys',
params = { { 'dict', 'table' } },
+ returns = 'string[]',
signature = 'keys({dict})',
},
keytrans = {
@@ -6136,6 +6223,7 @@ M.funcs = {
]=],
name = 'keytrans',
params = { { 'string', 'string' } },
+ returns = 'string',
signature = 'keytrans({string})',
},
last_buffer_nr = {
@@ -6162,7 +6250,8 @@ M.funcs = {
]=],
name = 'len',
- params = { { 'expr', 'any' } },
+ params = { { 'expr', 'any[]' } },
+ returns = 'integer',
signature = 'len({expr})',
tags = { 'E701' },
},
@@ -6297,6 +6386,7 @@ M.funcs = {
]=],
name = 'lispindent',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'lispindent({lnum})',
},
list2blob = {
@@ -6315,6 +6405,7 @@ M.funcs = {
]=],
name = 'list2blob',
params = { { 'list', 'any[]' } },
+ returns = 'string',
signature = 'list2blob({list})',
},
list2str = {
@@ -6339,6 +6430,7 @@ M.funcs = {
]=],
name = 'list2str',
params = { { 'list', 'any[]' }, { 'utf8', 'boolean' } },
+ returns = 'string',
signature = 'list2str({list} [, {utf8}])',
},
localtime = {
@@ -6348,6 +6440,7 @@ M.funcs = {
]=],
name = 'localtime',
params = {},
+ returns = 'integer',
signature = 'localtime()',
},
log = {
@@ -6368,6 +6461,7 @@ M.funcs = {
float_func = 'log',
name = 'log',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'log({expr})',
},
log10 = {
@@ -6387,6 +6481,7 @@ M.funcs = {
float_func = 'log10',
name = 'log10',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'log10({expr})',
},
luaeval = {
@@ -7271,6 +7366,7 @@ M.funcs = {
]=],
name = 'max',
params = { { 'expr', 'any' } },
+ returns = 'number',
signature = 'max({expr})',
},
menu_get = {
@@ -7419,6 +7515,7 @@ M.funcs = {
]=],
name = 'min',
params = { { 'expr', 'any' } },
+ returns = 'number',
signature = 'min({expr})',
},
mkdir = {
@@ -7467,6 +7564,7 @@ M.funcs = {
]=],
name = 'mkdir',
params = { { 'name', 'string' }, { 'flags', 'string' }, { 'prot', 'string' } },
+ returns = 'integer',
signature = 'mkdir({name} [, {flags} [, {prot}]])',
tags = { 'E739' },
},
@@ -7646,6 +7744,7 @@ M.funcs = {
]=],
name = 'nextnonblank',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'nextnonblank({lnum})',
},
nr2char = {
@@ -7669,6 +7768,7 @@ M.funcs = {
]=],
name = 'nr2char',
params = { { 'expr', 'integer' }, { 'utf8', 'boolean' } },
+ returns = 'string',
signature = 'nr2char({expr} [, {utf8}])',
},
nvim_api__ = {
@@ -7730,6 +7830,7 @@ M.funcs = {
]=],
name = 'pathshorten',
params = { { 'path', 'string' }, { 'len', 'integer' } },
+ returns = 'string',
signature = 'pathshorten({path} [, {len}])',
},
perleval = {
@@ -7773,6 +7874,7 @@ M.funcs = {
]=],
name = 'pow',
params = { { 'x', 'number' }, { 'y', 'number' } },
+ returns = 'number',
signature = 'pow({x}, {y})',
},
prevnonblank = {
@@ -7790,6 +7892,7 @@ M.funcs = {
]=],
name = 'prevnonblank',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'prevnonblank({lnum})',
},
printf = {
@@ -8475,7 +8578,13 @@ M.funcs = {
<
]=],
name = 'reduce',
- params = { { 'object', 'any' }, { 'func', 'function' }, { 'initial', 'any' } },
+ generics = { 'T' },
+ params = {
+ { 'object', 'any' },
+ { 'func', 'fun(accumulator: T, current: any): any' },
+ { 'initial', 'any' },
+ },
+ returns = 'T',
signature = 'reduce({object}, {func} [, {initial}])',
},
reg_executing = {
@@ -8679,6 +8788,7 @@ M.funcs = {
]=],
name = 'rename',
params = { { 'from', 'string' }, { 'to', 'string' } },
+ returns = 'integer',
signature = 'rename({from}, {to})',
},
['repeat'] = {
@@ -8721,6 +8831,7 @@ M.funcs = {
fast = true,
name = 'resolve',
params = { { 'filename', 'string' } },
+ returns = 'string',
signature = 'resolve({filename})',
},
reverse = {
@@ -8738,7 +8849,9 @@ M.funcs = {
<
]=],
name = 'reverse',
- params = { { 'object', 'any' } },
+ generics = { 'T' },
+ params = { { 'object', 'T[]' } },
+ returns = 'T[]',
signature = 'reverse({object})',
},
round = {
@@ -8762,6 +8875,7 @@ M.funcs = {
float_func = 'round',
name = 'round',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'round({expr})',
},
rpcnotify = {
@@ -8774,7 +8888,8 @@ M.funcs = {
<
]=],
name = 'rpcnotify',
- params = { { 'channel', 'integer' }, { 'event', 'string' }, { 'args', 'any' } },
+ params = { { 'channel', 'integer' }, { 'event', 'string' }, { '...', 'any' } },
+ returns = 'integer',
signature = 'rpcnotify({channel}, {event} [, {args}...])',
},
rpcrequest = {
@@ -8787,7 +8902,7 @@ M.funcs = {
<
]=],
name = 'rpcrequest',
- params = { { 'channel', 'integer' }, { 'method', 'string' }, { 'args', 'any' } },
+ params = { { 'channel', 'integer' }, { 'method', 'string' }, { '...', 'any' } },
signature = 'rpcrequest({channel}, {method} [, {args}...])',
},
rpcstart = {
@@ -8846,6 +8961,7 @@ M.funcs = {
]=],
name = 'screenattr',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer',
signature = 'screenattr({row}, {col})',
},
screenchar = {
@@ -8864,6 +8980,7 @@ M.funcs = {
]=],
name = 'screenchar',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer',
signature = 'screenchar({row}, {col})',
},
screenchars = {
@@ -8879,6 +8996,7 @@ M.funcs = {
]=],
name = 'screenchars',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer[]',
signature = 'screenchars({row}, {col})',
},
screencol = {
@@ -8899,6 +9017,7 @@ M.funcs = {
]=],
name = 'screencol',
params = {},
+ returns = 'integer[]',
signature = 'screencol()',
},
screenpos = {
@@ -8944,6 +9063,7 @@ M.funcs = {
]=],
name = 'screenrow',
params = {},
+ returns = 'integer',
signature = 'screenrow()',
},
screenstring = {
@@ -8960,6 +9080,7 @@ M.funcs = {
]=],
name = 'screenstring',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'string',
signature = 'screenstring({row}, {col})',
},
search = {
@@ -9079,6 +9200,7 @@ M.funcs = {
{ 'timeout', 'integer' },
{ 'skip', 'string|function' },
},
+ returns = 'integer',
signature = 'search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])',
},
searchcount = {
@@ -9401,6 +9523,7 @@ M.funcs = {
]=],
name = 'serverlist',
params = {},
+ returns = 'string[]',
signature = 'serverlist()',
},
serverstart = {
@@ -9441,6 +9564,7 @@ M.funcs = {
]=],
name = 'serverstart',
params = { { 'address', 'string' } },
+ returns = 'string',
signature = 'serverstart([{address}])',
},
serverstop = {
@@ -9453,6 +9577,7 @@ M.funcs = {
]=],
name = 'serverstop',
params = { { 'address', 'string' } },
+ returns = 'integer',
signature = 'serverstop({address})',
},
setbufline = {
@@ -9486,6 +9611,7 @@ M.funcs = {
]=],
name = 'setbufline',
params = { { 'buf', 'integer|string' }, { 'lnum', 'integer' }, { 'text', 'string|string[]' } },
+ returns = 'integer',
signature = 'setbufline({buf}, {lnum}, {text})',
},
setbufvar = {
@@ -9611,6 +9737,7 @@ M.funcs = {
]=],
name = 'setcmdline',
params = { { 'str', 'string' }, { 'pos', 'integer' } },
+ returns = 'integer',
signature = 'setcmdline({str} [, {pos}])',
},
setcmdpos = {
@@ -9961,6 +10088,7 @@ M.funcs = {
{ 'action', 'string' },
{ 'what', 'vim.fn.setqflist.what' },
},
+ returns = 'integer',
signature = 'setqflist({list} [, {action} [, {what}]])',
},
setreg = {
@@ -10137,6 +10265,7 @@ M.funcs = {
]=],
name = 'sha256',
params = { { 'string', 'string' } },
+ returns = 'string',
signature = 'sha256({string})',
},
shellescape = {
@@ -10177,6 +10306,7 @@ M.funcs = {
]=],
name = 'shellescape',
params = { { 'string', 'string' }, { 'special', 'boolean' } },
+ returns = 'string',
signature = 'shellescape({string} [, {special}])',
},
shiftwidth = {
@@ -10691,6 +10821,7 @@ M.funcs = {
]=],
name = 'simplify',
params = { { 'filename', 'string' } },
+ returns = 'string',
signature = 'simplify({filename})',
},
sin = {
@@ -10710,6 +10841,7 @@ M.funcs = {
float_func = 'sin',
name = 'sin',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'sin({expr})',
},
sinh = {
@@ -10859,7 +10991,9 @@ M.funcs = {
<
]=],
name = 'sort',
- params = { { 'list', 'any' }, { 'how', 'string|function' }, { 'dict', 'any' } },
+ generics = { 'T' },
+ params = { { 'list', 'T[]' }, { 'how', 'string|function' }, { 'dict', 'any' } },
+ returns = 'T[]',
signature = 'sort({list} [, {how} [, {dict}]])',
},
soundfold = {
@@ -10876,6 +11010,7 @@ M.funcs = {
]=],
name = 'soundfold',
params = { { 'word', 'string' } },
+ returns = 'string',
signature = 'soundfold({word})',
},
spellbadword = {
@@ -10937,6 +11072,7 @@ M.funcs = {
]=],
name = 'spellsuggest',
params = { { 'word', 'string' }, { 'max', 'integer' }, { 'capital', 'boolean' } },
+ returns = 'string[]',
signature = 'spellsuggest({word} [, {max} [, {capital}]])',
},
split = {
@@ -10970,6 +11106,7 @@ M.funcs = {
]=],
name = 'split',
params = { { 'string', 'string' }, { 'pattern', 'string' }, { 'keepempty', 'boolean' } },
+ returns = 'string[]',
signature = 'split({string} [, {pattern} [, {keepempty}]])',
},
sqrt = {
@@ -11153,7 +11290,7 @@ M.funcs = {
and exists only for backwards-compatibility.
With UTF-8 composing characters are handled properly: >vim
echo str2list("á") " returns [97, 769]
-
+ <
]=],
name = 'str2list',
params = { { 'string', 'string' }, { 'utf8', 'boolean' } },
@@ -12188,24 +12325,14 @@ M.funcs = {
signature = 'tempname()',
},
termopen = {
+ deprecated = true,
args = { 1, 2 },
desc = [=[
- Spawns {cmd} in a new pseudo-terminal session connected
- to the current (unmodified) buffer. Parameters and behavior
- are the same as |jobstart()| except "pty", "width", "height",
- and "TERM" are ignored: "height" and "width" are taken from
- the current window. Note that termopen() implies a "pty" arg
- to jobstart(), and thus has the implications documented at
- |jobstart()|.
-
- Returns the same values as jobstart().
-
- Terminal environment is initialized as in |jobstart-env|,
- except $TERM is set to "xterm-256color". Full behavior is
- described in |terminal|.
+ Use |jobstart()| with `{term: v:true}` instead.
]=],
name = 'termopen',
params = { { 'cmd', 'string|string[]' }, { 'opts', 'table' } },
+ returns = 'integer',
signature = 'termopen({cmd} [, {opts}])',
},
test_garbagecollect_now = {
@@ -12715,6 +12842,7 @@ M.funcs = {
]=],
name = 'virtcol2col',
params = { { 'winid', 'integer' }, { 'lnum', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer',
signature = 'virtcol2col({winid}, {lnum}, {col})',
},
visualmode = {
@@ -12739,6 +12867,7 @@ M.funcs = {
]=],
name = 'visualmode',
params = { { 'expr', 'boolean' } },
+ returns = 'string',
signature = 'visualmode([{expr}])',
},
wait = {
@@ -12896,6 +13025,7 @@ M.funcs = {
]=],
name = 'win_id2win',
params = { { 'expr', 'integer' } },
+ returns = 'integer',
signature = 'win_id2win({expr})',
},
win_move_separator = {
@@ -13089,6 +13219,7 @@ M.funcs = {
]=],
name = 'winlayout',
params = { { 'tabnr', 'integer' } },
+ returns = 'any[]',
signature = 'winlayout([{tabnr}])',
},
winline = {
@@ -13141,6 +13272,7 @@ M.funcs = {
]=],
name = 'winnr',
params = { { 'arg', 'string|integer' } },
+ returns = 'integer',
signature = 'winnr([{arg}])',
},
winrestcmd = {
@@ -13157,6 +13289,7 @@ M.funcs = {
]=],
name = 'winrestcmd',
params = {},
+ returns = 'string',
signature = 'winrestcmd()',
},
winrestview = {
@@ -13236,6 +13369,7 @@ M.funcs = {
]=],
name = 'winwidth',
params = { { 'nr', 'integer' } },
+ returns = 'integer',
signature = 'winwidth({nr})',
},
wordcount = {
@@ -13331,7 +13465,8 @@ M.funcs = {
<
]=],
name = 'xor',
- params = { { 'expr', 'number' }, { 'expr', 'number' } },
+ params = { { 'expr', 'integer' }, { 'expr', 'integer' } },
+ returns = 'integer',
signature = 'xor({expr}, {expr})',
},
}
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
index 73bfd6db2a..41ed17598b 100644
--- a/src/nvim/eval/buffer.c
+++ b/src/nvim/eval/buffer.c
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -66,12 +67,11 @@ buf_T *find_buffer(typval_T *avar)
/// 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) {
+ for (size_t i = 0; i < kv_size(curbuf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(curbuf->b_wininfo, i);
if (wip->wi_win != NULL && wip->wi_win->w_buffer == curbuf) {
curwin = wip->wi_win;
break;
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index afc2efddf6..cfcd415219 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -6,6 +6,8 @@
#include <string.h>
#include "klib/kvec.h"
+#include "mpack/conv.h"
+#include "mpack/mpack_core.h"
#include "mpack/object.h"
#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
@@ -21,7 +23,6 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
/// Helper structure for container_struct
diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h
index 485cc65561..af5fd3979c 100644
--- a/src/nvim/eval/decode.h
+++ b/src/nvim/eval/decode.h
@@ -2,7 +2,7 @@
#include <stddef.h> // IWYU pragma: keep
-#include "mpack/object.h"
+#include "mpack/object.h" // IWYU pragma: keep
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/eval/deprecated.c b/src/nvim/eval/deprecated.c
new file mode 100644
index 0000000000..0fc16b605d
--- /dev/null
+++ b/src/nvim/eval/deprecated.c
@@ -0,0 +1,158 @@
+#include <stdbool.h> // for true
+
+#include "nvim/channel.h"
+#include "nvim/errors.h"
+#include "nvim/eval.h"
+#include "nvim/eval/deprecated.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/gettext_defs.h" // for _
+#include "nvim/globals.h"
+#include "nvim/macros_defs.h" // for S_LEN
+#include "nvim/message.h" // for semsg
+#include "nvim/types_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/deprecated.c.generated.h" // IWYU pragma: keep
+#endif
+
+/// "rpcstart()" function (DEPRECATED)
+void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_STRING
+ || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) {
+ // Wrong argument types
+ emsg(_(e_invarg));
+ return;
+ }
+
+ list_T *args = NULL;
+ int argsl = 0;
+ if (argvars[1].v_type == VAR_LIST) {
+ args = argvars[1].vval.v_list;
+ argsl = tv_list_len(args);
+ // Assert that all list items are strings
+ int i = 0;
+ TV_LIST_ITER_CONST(args, arg, {
+ if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) {
+ semsg(_("E5010: List item %d of the second argument is not a string"),
+ i);
+ return;
+ }
+ i++;
+ });
+ }
+
+ if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) {
+ emsg(_(e_api_spawn_failed));
+ return;
+ }
+
+ // Allocate extra memory for the argument vector and the NULL pointer
+ int argvl = argsl + 2;
+ char **argv = xmalloc(sizeof(char *) * (size_t)argvl);
+
+ // Copy program name
+ argv[0] = xstrdup(argvars[0].vval.v_string);
+
+ int i = 1;
+ // Copy arguments to the vector
+ if (argsl > 0) {
+ TV_LIST_ITER_CONST(args, arg, {
+ argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg)));
+ });
+ }
+
+ // The last item of argv must be NULL
+ argv[i] = NULL;
+
+ Channel *chan = channel_job_start(argv, NULL, CALLBACK_READER_INIT,
+ CALLBACK_READER_INIT, CALLBACK_NONE,
+ false, true, false, false,
+ kChannelStdinPipe, NULL, 0, 0, NULL,
+ &rettv->vval.v_number);
+ if (chan) {
+ channel_create_event(chan, NULL);
+ }
+}
+
+/// "rpcstop()" function
+void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER) {
+ // Wrong argument types
+ emsg(_(e_invarg));
+ return;
+ }
+
+ // if called with a job, stop it, else closes the channel
+ uint64_t id = (uint64_t)argvars[0].vval.v_number;
+ if (find_job(id, false)) {
+ f_jobstop(argvars, rettv, fptr);
+ } else {
+ const char *error;
+ rettv->vval.v_number =
+ channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error);
+ if (!rettv->vval.v_number) {
+ emsg(error);
+ }
+ }
+}
+
+/// "last_buffer_nr()" function.
+void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int n = 0;
+
+ FOR_ALL_BUFFERS(buf) {
+ if (n < buf->b_fnum) {
+ n = buf->b_fnum;
+ }
+ }
+
+ rettv->vval.v_number = n;
+}
+
+/// "termopen(cmd[, cwd])" function
+void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (check_secure()) {
+ return;
+ }
+
+ bool must_free = false;
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ must_free = true;
+ argvars[1].v_type = VAR_DICT;
+ argvars[1].vval.v_dict = tv_dict_alloc();
+ }
+
+ if (argvars[1].v_type != VAR_DICT) {
+ // Wrong argument types
+ semsg(_(e_invarg2), "expected dictionary");
+ return;
+ }
+
+ tv_dict_add_bool(argvars[1].vval.v_dict, S_LEN("term"), true);
+ f_jobstart(argvars, rettv, fptr);
+ if (must_free) {
+ tv_dict_free(argvars[1].vval.v_dict);
+ }
+}
diff --git a/src/nvim/eval/deprecated.h b/src/nvim/eval/deprecated.h
new file mode 100644
index 0000000000..e2e3ee436e
--- /dev/null
+++ b/src/nvim/eval/deprecated.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h" // IWYU pragma: keep
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/deprecated.h.generated.h"
+#endif
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index 5b92f217d1..691fd405e9 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -8,7 +8,6 @@
#include "nvim/eval/typval_defs.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/message.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 9e9e36d3c5..8db50a53b3 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -13,6 +13,8 @@
#include <uv.h>
#include "auto/config.h"
+#include "klib/kvec.h"
+#include "mpack/mpack_core.h"
#include "mpack/object.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
@@ -89,6 +91,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/normal.h"
#include "nvim/normal_defs.h"
@@ -545,8 +548,7 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "call(func, arglist [, dict])" function
static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[1].v_type != VAR_LIST) {
- emsg(_(e_listreq));
+ if (tv_check_for_list_arg(argvars, 1) == FAIL) {
return;
}
if (argvars[1].vval.v_list == NULL) {
@@ -572,22 +574,32 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (func == NULL || *func == NUL) {
return; // type error, empty name or null function
}
+ char *tofree = NULL;
+ if (argvars[0].v_type == VAR_STRING) {
+ char *p = func;
+ tofree = trans_function_name(&p, false, TFN_INT|TFN_QUIET, NULL, NULL);
+ if (tofree == NULL) {
+ emsg_funcname(e_unknown_function_str, func);
+ return;
+ }
+ func = tofree;
+ }
dict_T *selfdict = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
- if (owned) {
- func_unref(func);
- }
- return;
+ goto done;
}
selfdict = argvars[2].vval.v_dict;
}
func_call(func, &argvars[1], partial, selfdict, rettv);
+
+done:
if (owned) {
func_unref(func);
}
+ xfree(tofree);
}
/// "changenr()" function
@@ -2402,14 +2414,15 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
if (buf == curwin->w_buffer) {
changelistindex = curwin->w_changelistidx;
} else {
- wininfo_T *wip;
+ changelistindex = buf->b_changelistlen;
- FOR_ALL_BUF_WININFO(buf, wip) {
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == curwin) {
+ changelistindex = wip->wi_changelistidx;
break;
}
}
- changelistindex = wip != NULL ? wip->wi_changelistidx : buf->b_changelistlen;
}
tv_list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex);
@@ -3541,6 +3554,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
+ msg_ext_set_kind("list_cmd");
msg_start();
msg_row = Rows - 1; // for when 'cmdheight' > 1
lines_left = Rows; // avoid more prompt
@@ -3553,10 +3567,10 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
});
// Ask for choice.
- bool mouse_used;
- int selected = prompt_for_number(&mouse_used);
+ bool mouse_used = false;
+ int selected = prompt_for_input(NULL, 0, false, &mouse_used);
if (mouse_used) {
- selected -= lines_left;
+ selected = tv_list_len(argvars[0].vval.v_list) - (cmdline_row - mouse_row);
}
rettv->vval.v_number = selected;
@@ -3839,8 +3853,8 @@ static const char *required_env_vars[] = {
NULL
};
-static dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty,
- const char * const pty_term_name)
+dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty,
+ const char * const pty_term_name)
{
dict_T *env = tv_dict_alloc();
@@ -3932,7 +3946,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
}
/// "jobstart()" function
-static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -3941,9 +3955,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
+ const char *cmd;
bool executable = true;
- char **argv = tv_to_argv(&argvars[0], NULL, &executable);
- dict_T *env = NULL;
+ char **argv = tv_to_argv(&argvars[0], &cmd, &executable);
if (!argv) {
rettv->vval.v_number = executable ? 0 : -1;
return; // Did error message in tv_to_argv.
@@ -3960,6 +3974,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool detach = false;
bool rpc = false;
bool pty = false;
+ bool term = false;
bool clear_env = false;
bool overlapped = false;
ChannelStdinMode stdin_mode = kChannelStdinPipe;
@@ -3973,7 +3988,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
detach = tv_dict_get_number(job_opts, "detach") != 0;
rpc = tv_dict_get_number(job_opts, "rpc") != 0;
- pty = tv_dict_get_number(job_opts, "pty") != 0;
+ term = tv_dict_get_number(job_opts, "term") != 0;
+ pty = term || tv_dict_get_number(job_opts, "pty") != 0;
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
@@ -3988,6 +4004,14 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
+ dictitem_T *const job_term = tv_dict_find(job_opts, S_LEN("term"));
+ if (job_term && VAR_BOOL != job_term->di_tv.v_type) {
+ // Restrict "term" field to boolean, in case we want to allow buffer numbers in the future.
+ semsg(_(e_invarg2), "'term' must be Boolean");
+ shell_free_argv(argv);
+ return;
+ }
+
if (pty && rpc) {
semsg(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
shell_free_argv(argv);
@@ -4031,29 +4055,92 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
uint16_t height = 0;
char *term_name = NULL;
- if (pty) {
- width = (uint16_t)tv_dict_get_number(job_opts, "width");
- height = (uint16_t)tv_dict_get_number(job_opts, "height");
- // Legacy method, before env option existed, to specify $TERM. No longer
- // documented, but still usable to avoid breaking scripts.
- term_name = tv_dict_get_string(job_opts, "TERM", false);
- if (!term_name) {
- term_name = "ansi";
+ if (term) {
+ if (text_locked()) {
+ text_locked_msg();
+ shell_free_argv(argv);
+ return;
}
+ if (curbuf->b_changed) {
+ emsg(_("jobstart(...,{term=true}) requires unmodified buffer"));
+ shell_free_argv(argv);
+ return;
+ }
+ assert(!rpc);
+ term_name = "xterm-256color";
+ cwd = cwd ? cwd : ".";
+ overlapped = false;
+ detach = false;
+ stdin_mode = kChannelStdinPipe;
+ width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin));
+ height = (uint16_t)curwin->w_height_inner;
}
- env = create_environment(job_env, clear_env, pty, term_name);
+ if (pty) {
+ width = width ? width : (uint16_t)tv_dict_get_number(job_opts, "width");
+ height = height ? height : (uint16_t)tv_dict_get_number(job_opts, "height");
+ // Deprecated TERM field is from before `env` option existed.
+ term_name = term_name ? term_name : tv_dict_get_string(job_opts, "TERM", false);
+ term_name = term_name ? term_name : "ansi";
+ }
+ dict_T *env = create_environment(job_env, clear_env, pty, term_name);
Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit, pty,
rpc, overlapped, detach, stdin_mode, cwd,
width, height, env, &rettv->vval.v_number);
- if (chan) {
+ if (!chan) {
+ return;
+ } else if (!term) {
channel_create_event(chan, NULL);
+ } else {
+ if (rettv->vval.v_number <= 0) {
+ return;
+ }
+
+ int pid = chan->stream.pty.proc.pid;
+
+ // "./…" => "/home/foo/…"
+ vim_FullName(cwd, NameBuff, sizeof(NameBuff), false);
+ // "/home/foo/…" => "~/…"
+ 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] = NUL;
+ }
+
+ if (len == 1 && IObuff[0] == '/') {
+ // Avoid ambiguity in the URI when CWD is root directory.
+ IObuff[1] = '.';
+ IObuff[2] = NUL;
+ }
+
+ // Terminal URI: "term://$CWD//$PID:$CMD"
+ snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s", IObuff, pid, cmd);
+ // Buffer has no terminal associated yet; unset 'swapfile' to ensure no swapfile is created.
+ curbuf->b_p_swf = false;
+
+ apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
+ setfname(curbuf, NameBuff, NULL, true);
+ apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
+
+ Error err = ERROR_INIT;
+ // Set (deprecated) buffer-local vars (prefer 'channel' buffer-local option).
+ dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
+ INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err);
+ api_clear_error(&err);
+ dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
+ INTEGER_OBJ(pid), false, false, NULL, &err);
+ api_clear_error(&err);
+
+ channel_incref(chan);
+ channel_terminal_open(curbuf, chan);
+ channel_create_event(chan, NULL);
+ channel_decref(chan);
}
}
/// "jobstop()" function
-static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4100,8 +4187,6 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- ui_busy_start();
- ui_flush();
list_T *args = argvars[0].vval.v_list;
Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs));
MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop);
@@ -4138,6 +4223,13 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
before = os_hrtime();
}
+ // Only mark the UI as busy when jobwait() blocks
+ const bool busy = remaining != 0;
+ if (busy) {
+ ui_busy_start();
+ ui_flush();
+ }
+
for (i = 0; i < tv_list_len(args); i++) {
if (remaining == 0) {
break; // Timeout.
@@ -4179,7 +4271,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
multiqueue_free(waiting_jobs);
xfree(jobs);
- ui_busy_stop();
+ if (busy) {
+ ui_busy_stop();
+ }
tv_list_ref(rv);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = rv;
@@ -4240,20 +4334,6 @@ static void f_keytrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(escaped);
}
-/// "last_buffer_nr()" function.
-static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int n = 0;
-
- FOR_ALL_BUFFERS(buf) {
- if (n < buf->b_fnum) {
- n = buf->b_fnum;
- }
- }
-
- rettv->vval.v_number = n;
-}
-
/// "len()" function
static void f_len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -6348,103 +6428,6 @@ end:
api_clear_error(&err);
}
-/// "rpcstart()" function (DEPRECATED)
-static void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_STRING
- || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) {
- // Wrong argument types
- emsg(_(e_invarg));
- return;
- }
-
- list_T *args = NULL;
- int argsl = 0;
- if (argvars[1].v_type == VAR_LIST) {
- args = argvars[1].vval.v_list;
- argsl = tv_list_len(args);
- // Assert that all list items are strings
- int i = 0;
- TV_LIST_ITER_CONST(args, arg, {
- if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) {
- semsg(_("E5010: List item %d of the second argument is not a string"),
- i);
- return;
- }
- i++;
- });
- }
-
- if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) {
- emsg(_(e_api_spawn_failed));
- return;
- }
-
- // Allocate extra memory for the argument vector and the NULL pointer
- int argvl = argsl + 2;
- char **argv = xmalloc(sizeof(char *) * (size_t)argvl);
-
- // Copy program name
- argv[0] = xstrdup(argvars[0].vval.v_string);
-
- int i = 1;
- // Copy arguments to the vector
- if (argsl > 0) {
- TV_LIST_ITER_CONST(args, arg, {
- argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg)));
- });
- }
-
- // The last item of argv must be NULL
- argv[i] = NULL;
-
- Channel *chan = channel_job_start(argv, NULL, CALLBACK_READER_INIT,
- CALLBACK_READER_INIT, CALLBACK_NONE,
- false, true, false, false,
- kChannelStdinPipe, NULL, 0, 0, NULL,
- &rettv->vval.v_number);
- if (chan) {
- channel_create_event(chan, NULL);
- }
-}
-
-/// "rpcstop()" function
-static void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_NUMBER) {
- // Wrong argument types
- emsg(_(e_invarg));
- return;
- }
-
- // if called with a job, stop it, else closes the channel
- uint64_t id = (uint64_t)argvars[0].vval.v_number;
- if (find_job(id, false)) {
- f_jobstop(argvars, rettv, fptr);
- } else {
- const char *error;
- rettv->vval.v_number =
- channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error);
- if (!rettv->vval.v_number) {
- emsg(error);
- }
- }
-}
-
static void screenchar_adjust(ScreenGrid **grid, int *row, int *col)
{
// TODO(bfredl): this is a hack for legacy tests which use screenchar()
@@ -8132,134 +8115,6 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
(char *)tag_pattern, (char *)fname);
}
-/// "termopen(cmd[, cwd])" function
-static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- if (check_secure()) {
- return;
- }
- if (text_locked()) {
- text_locked_msg();
- return;
- }
- if (curbuf->b_changed) {
- emsg(_("Can only call this function in an unmodified buffer"));
- return;
- }
-
- const char *cmd;
- bool executable = true;
- char **argv = tv_to_argv(&argvars[0], &cmd, &executable);
- if (!argv) {
- rettv->vval.v_number = executable ? 0 : -1;
- return; // Did error message in tv_to_argv.
- }
-
- if (argvars[1].v_type != VAR_DICT && argvars[1].v_type != VAR_UNKNOWN) {
- // Wrong argument type
- semsg(_(e_invarg2), "expected dictionary");
- shell_free_argv(argv);
- return;
- }
-
- CallbackReader on_stdout = CALLBACK_READER_INIT;
- CallbackReader on_stderr = CALLBACK_READER_INIT;
- Callback on_exit = CALLBACK_NONE;
- dict_T *job_opts = NULL;
- const char *cwd = ".";
- dict_T *env = NULL;
- const bool pty = true;
- bool clear_env = false;
- dictitem_T *job_env = NULL;
-
- if (argvars[1].v_type == VAR_DICT) {
- job_opts = argvars[1].vval.v_dict;
-
- const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false);
- if (new_cwd && *new_cwd != NUL) {
- cwd = new_cwd;
- // The new cwd must be a directory.
- if (!os_isdir(cwd)) {
- semsg(_(e_invarg2), "expected valid directory");
- shell_free_argv(argv);
- return;
- }
- }
-
- job_env = tv_dict_find(job_opts, S_LEN("env"));
- if (job_env && job_env->di_tv.v_type != VAR_DICT) {
- semsg(_(e_invarg2), "env");
- shell_free_argv(argv);
- return;
- }
-
- clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
-
- if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
- shell_free_argv(argv);
- return;
- }
- }
-
- env = create_environment(job_env, clear_env, pty, "xterm-256color");
-
- const bool rpc = false;
- const bool overlapped = false;
- const bool detach = false;
- ChannelStdinMode stdin_mode = kChannelStdinPipe;
- uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin));
- Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit,
- pty, rpc, overlapped, detach, stdin_mode,
- cwd, term_width, (uint16_t)curwin->w_height_inner,
- env, &rettv->vval.v_number);
- if (rettv->vval.v_number <= 0) {
- return;
- }
-
- int pid = chan->stream.pty.proc.pid;
-
- // "./…" => "/home/foo/…"
- vim_FullName(cwd, NameBuff, sizeof(NameBuff), false);
- // "/home/foo/…" => "~/…"
- 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] = NUL;
- }
-
- if (len == 1 && IObuff[0] == '/') {
- // Avoid ambiguity in the URI when CWD is root directory.
- IObuff[1] = '.';
- IObuff[2] = NUL;
- }
-
- // Terminal URI: "term://$CWD//$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);
- 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}
- Error err = ERROR_INIT;
- // deprecated: use 'channel' buffer option
- dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
- INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err);
- api_clear_error(&err);
- dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
- INTEGER_OBJ(pid), false, false, NULL, &err);
- api_clear_error(&err);
-
- channel_incref(chan);
- channel_terminal_open(curbuf, chan);
- channel_create_event(chan, NULL);
- channel_decref(chan);
-}
-
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index e7b6a0feee..f9cf245e50 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -9,6 +9,7 @@
#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
@@ -32,6 +33,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/os/input.h"
#include "nvim/pos_defs.h"
@@ -2232,6 +2234,18 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptr
return TV_DICT_HI2DI(hi);
}
+/// Check if a key is present in a dictionary.
+///
+/// @param[in] d Dictionary to check.
+/// @param[in] key Dictionary key.
+///
+/// @return whether the key is present in the dictionary.
+bool tv_dict_has_key(const dict_T *const d, const char *const key)
+ FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return tv_dict_find(d, key, -1) != NULL;
+}
+
/// Get a typval item from a dictionary and copy it into "rettv".
///
/// @param[in] d Dictionary to check.
@@ -2631,6 +2645,30 @@ int tv_dict_add_allocated_str(dict_T *const d, const char *const key, const size
return OK;
}
+/// Add a function entry to dictionary.
+///
+/// @param[out] d Dictionary to add entry to.
+/// @param[in] key Key to add.
+/// @param[in] key_len Key length.
+/// @param[in] fp Function to add.
+///
+/// @return OK in case of success, FAIL when key already exists.
+int tv_dict_add_func(dict_T *const d, const char *const key, const size_t key_len,
+ ufunc_T *const fp)
+ FUNC_ATTR_NONNULL_ARG(1, 2, 4)
+{
+ dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
+
+ item->di_tv.v_type = VAR_FUNC;
+ item->di_tv.vval.v_string = xmemdupz(fp->uf_name, fp->uf_namelen);
+ if (tv_dict_add(d, item) == FAIL) {
+ tv_dict_item_free(item);
+ return FAIL;
+ }
+ func_ref(item->di_tv.vval.v_string);
+ return OK;
+}
+
//{{{2 Operations on the whole dict
/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
index 1ec7631c4b..90b0d4e4a9 100644
--- a/src/nvim/eval/typval_defs.h
+++ b/src/nvim/eval/typval_defs.h
@@ -357,9 +357,10 @@ struct ufunc {
funccall_T *uf_scoped; ///< l: local variables for closure
char *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
///< "<SNR>" as a string, otherwise NULL
- char uf_name[]; ///< Name of function (actual size equals name);
- ///< can start with <SNR>123_
- ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
+ size_t uf_namelen; ///< Length of uf_name (excluding the NUL)
+ 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 {
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 0050a77fe3..84ee27493c 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -78,7 +78,6 @@ static funccall_T *current_funccal = NULL;
// item in it is still being used.
static funccall_T *previous_funccal = NULL;
-static const char *e_unknown_function_str = N_("E117: Unknown function: %s");
static const char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
static const char *e_funcdict = N_("E717: Dictionary entry already exists");
static const char *e_funcref = N_("E718: Funcref required");
@@ -105,6 +104,48 @@ hashtab_T *func_tbl_get(void)
return &func_hashtab;
}
+/// Get one function argument.
+/// Return a pointer to after the type.
+/// When something is wrong return "arg".
+static char *one_function_arg(char *arg, garray_T *newargs, bool skip)
+{
+ char *p = arg;
+
+ while (ASCII_ISALNUM(*p) || *p == '_') {
+ p++;
+ }
+ if (arg == p || isdigit((uint8_t)(*arg))
+ || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
+ || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
+ if (!skip) {
+ semsg(_("E125: Illegal argument: %s"), arg);
+ }
+ return arg;
+ }
+
+ if (newargs != NULL) {
+ ga_grow(newargs, 1);
+ uint8_t c = (uint8_t)(*p);
+ *p = NUL;
+ char *arg_copy = xstrdup(arg);
+
+ // Check for duplicate argument name.
+ for (int i = 0; i < newargs->ga_len; i++) {
+ if (strcmp(((char **)(newargs->ga_data))[i], arg_copy) == 0) {
+ semsg(_("E853: Duplicate argument name: %s"), arg_copy);
+ xfree(arg_copy);
+ return arg;
+ }
+ }
+ ((char **)(newargs->ga_data))[newargs->ga_len] = arg_copy;
+ newargs->ga_len++;
+
+ *p = (char)c;
+ }
+
+ return p;
+}
+
/// Get function arguments.
static int get_function_args(char **argp, char endchar, garray_T *newargs, int *varargs,
garray_T *default_args, bool skip)
@@ -135,36 +176,11 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
mustend = true;
} else {
arg = p;
- while (ASCII_ISALNUM(*p) || *p == '_') {
- p++;
- }
- if (arg == p || isdigit((uint8_t)(*arg))
- || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
- || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
- if (!skip) {
- semsg(_("E125: Illegal argument: %s"), arg);
- }
+ p = one_function_arg(p, newargs, skip);
+ if (p == arg) {
break;
}
- if (newargs != NULL) {
- ga_grow(newargs, 1);
- uint8_t c = (uint8_t)(*p);
- *p = NUL;
- arg = xstrdup(arg);
-
- // Check for duplicate argument name.
- for (int i = 0; i < newargs->ga_len; i++) {
- if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) {
- semsg(_("E853: Duplicate argument name: %s"), arg);
- xfree(arg);
- goto err_ret;
- }
- }
- ((char **)(newargs->ga_data))[newargs->ga_len] = arg;
- newargs->ga_len++;
- *p = (char)c;
- }
if (*skipwhite(p) == '=' && default_args != NULL) {
typval_T rettv;
@@ -248,25 +264,47 @@ static void register_closure(ufunc_T *fp)
[current_funccal->fc_ufuncs.ga_len++] = fp;
}
+static char lambda_name[8 + NUMBUFLEN];
+static size_t lambda_namelen = 0;
+
/// @return a name for a lambda. Returned in static memory.
char *get_lambda_name(void)
{
- static char name[30];
static int lambda_no = 0;
- snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no);
- return name;
+ int n = snprintf(lambda_name, sizeof(lambda_name), "<lambda>%d", ++lambda_no);
+ if (n < 1) {
+ lambda_namelen = 0;
+ } else if (n >= (int)sizeof(lambda_name)) {
+ lambda_namelen = sizeof(lambda_name) - 1;
+ } else {
+ lambda_namelen = (size_t)n;
+ }
+
+ return lambda_name;
}
-static void set_ufunc_name(ufunc_T *fp, char *name)
+/// Get the length of the last lambda name.
+size_t get_lambda_name_len(void)
{
+ return lambda_namelen;
+}
+
+/// Allocate a "ufunc_T" for a function called "name".
+static ufunc_T *alloc_ufunc(const char *name, size_t namelen)
+{
+ size_t len = offsetof(ufunc_T, uf_name) + namelen + 1;
+ ufunc_T *fp = xcalloc(1, len);
STRCPY(fp->uf_name, name);
+ fp->uf_namelen = namelen;
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);
+ len = namelen + 3;
+ fp->uf_name_exp = xmalloc(len);
+ snprintf(fp->uf_name_exp, len, "<SNR>%s", fp->uf_name + 3);
}
+
+ return fp;
}
/// Parse a lambda expression and get a Funcref from "*arg".
@@ -334,8 +372,9 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
garray_T newlines;
char *name = get_lambda_name();
+ size_t namelen = get_lambda_name_len();
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
+ fp = alloc_ufunc(name, namelen);
pt = xcalloc(1, sizeof(partial_T));
ga_init(&newlines, (int)sizeof(char *), 1);
@@ -353,7 +392,6 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
}
fp->uf_refcount = 1;
- set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
@@ -393,7 +431,10 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
errret:
ga_clear_strings(&newargs);
- xfree(fp);
+ if (fp != NULL) {
+ xfree(fp->uf_name_exp);
+ xfree(fp);
+ }
xfree(pt);
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
evalarg->eval_tofree = tofree;
@@ -611,33 +652,36 @@ static char *fname_trans_sid(const char *const name, char *const fname_buf, char
int *const error)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- const int llen = eval_fname_script(name);
- if (llen == 0) {
+ const char *script_name = name + eval_fname_script(name);
+ if (script_name == name) {
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:"
+ size_t fname_buflen = 3;
+ if (!eval_fname_sid(name)) { // "<SID>" or "s:"
+ fname_buf[fname_buflen] = NUL;
+ } else {
if (current_sctx.sc_sid <= 0) {
*error = FCERR_SCRIPT;
} else {
- snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
- i = (int)strlen(fname_buf);
+ fname_buflen += (size_t)snprintf(fname_buf + fname_buflen,
+ FLEN_FIXED + 1 - fname_buflen,
+ "%" PRIdSCID "_",
+ current_sctx.sc_sid);
}
}
+ size_t fnamelen = fname_buflen + strlen(script_name);
char *fname;
- if ((size_t)i + strlen(name + llen) < FLEN_FIXED) {
- STRCPY(fname_buf + i, name + llen);
+ if (fnamelen < FLEN_FIXED) {
+ STRCPY(fname_buf + fname_buflen, script_name);
fname = fname_buf;
} else {
- fname = xmalloc((size_t)i + strlen(name + llen) + 1);
+ fname = xmalloc(fnamelen + 1);
*tofree = fname;
- memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
+ snprintf(fname, fnamelen + 1, "%s%s", fname_buf, script_name);
}
return fname;
}
@@ -695,20 +739,20 @@ ufunc_T *find_func(const char *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 *buf, size_t buflen, ufunc_T *fp)
+static int cat_func_name(char *buf, size_t bufsize, ufunc_T *fp)
{
int len = -1;
- size_t uflen = strlen(fp->uf_name);
+ size_t uflen = fp->uf_namelen;
assert(uflen > 0);
if ((uint8_t)fp->uf_name[0] == K_SPECIAL && uflen > 3) {
- len = snprintf(buf, buflen, "<SNR>%s", fp->uf_name + 3);
+ len = snprintf(buf, bufsize, "<SNR>%s", fp->uf_name + 3);
} else {
- len = snprintf(buf, buflen, "%s", fp->uf_name);
+ len = snprintf(buf, bufsize, "%s", fp->uf_name);
}
- (void)len; // Avoid unused warning on release builds
assert(len > 0);
+ return (len >= (int)bufsize) ? (int)bufsize - 1 : len;
}
/// Add a number variable "name" to dict "dp" with value "nr".
@@ -875,7 +919,6 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_def_args));
ga_clear_strings(&(fp->uf_lines));
- XFREE_CLEAR(fp->uf_name_exp);
if (fp->uf_flags & FC_LUAREF) {
api_free_luaref(fp->uf_luaref);
@@ -914,6 +957,8 @@ static void func_free(ufunc_T *fp)
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) {
func_remove(fp);
}
+
+ XFREE_CLEAR(fp->uf_name_exp);
xfree(fp);
}
@@ -967,6 +1012,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool islambda = false;
char numbuf[NUMBUFLEN];
char *name;
+ size_t namelen;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
proftime_T wait_start;
@@ -1092,23 +1138,25 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
break;
}
}
+
+ namelen = strlen(name);
} else {
if ((fp->uf_flags & FC_NOARGS) != 0) {
// Bail out if no a: arguments used (in lambda).
break;
}
// "..." argument a:1, a:2, etc.
- snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
+ namelen = (size_t)snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
name = numbuf;
}
- if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
+ if (fixvar_idx < FIXVAR_CNT && namelen <= VAR_SHORT_LEN) {
v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ STRCPY(v->di_key, name);
} else {
- v = xmalloc(sizeof(dictitem_T) + strlen(name));
- v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
+ v = tv_dict_item_alloc_len(name, namelen);
+ v->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
}
- STRCPY(v->di_key, name);
// Note: the values are copied directly to avoid alloc/free.
// "argvars" must have VAR_FIXED for v_lock.
@@ -2085,7 +2133,7 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
len = (int)(end - lv.ll_name);
}
- size_t sid_buf_len = 0;
+ size_t sid_buflen = 0;
char sid_buf[20];
// Copy the function name to allocated memory.
@@ -2095,15 +2143,16 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
lead = 0; // do nothing
} else if (lead > 0) {
lead = 3;
- if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid(*pp)) {
+ if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name))
+ || eval_fname_sid(*pp)) {
// It's "s:" or "<SID>".
if (current_sctx.sc_sid <= 0) {
emsg(_(e_usingsid));
goto theend;
}
- sid_buf_len =
- (size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_", current_sctx.sc_sid);
- lead += (int)sid_buf_len;
+ sid_buflen = (size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_",
+ current_sctx.sc_sid);
+ lead += (int)sid_buflen;
}
} else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, (int)lv.ll_name_len)) {
semsg(_("E128: Function name must start with a capital or \"s:\": %s"),
@@ -2125,8 +2174,8 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
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);
+ if (sid_buflen > 0) { // If it's "<SID>"
+ memcpy(name + 3, sid_buf, sid_buflen);
}
}
memmove(name + lead, lv.ll_name, (size_t)len);
@@ -2162,12 +2211,12 @@ char *get_scriptlocal_funcname(char *funcname)
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);
+ size_t sid_buflen = (size_t)snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRIdSCID "_",
+ 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);
+ size_t newnamesize = sid_buflen + strlen(funcname + off) + 1;
+ char *newname = xmalloc(newnamesize);
+ snprintf(newname, newnamesize, "%s%s", sid_buf, funcname + off);
return newname;
}
@@ -2193,8 +2242,6 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
return saved;
}
-#define MAX_FUNC_NESTING 50
-
/// List functions.
///
/// @param regmatch When NULL, all of them.
@@ -2225,12 +2272,291 @@ static void list_functions(regmatch_T *regmatch)
}
}
+#define MAX_FUNC_NESTING 50
+
+/// Read the body of a function, put every line in "newlines".
+/// This stops at "endfunction".
+/// "newlines" must already have been initialized.
+static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in,
+ char **line_to_free, bool show_block)
+{
+ bool saved_wait_return = need_wait_return;
+ char *line_arg = line_arg_in;
+ int indent = 2;
+ int nesting = 0;
+ char *skip_until = NULL;
+ int ret = FAIL;
+ bool is_heredoc = false;
+ char *heredoc_trimmed = NULL;
+ size_t heredoc_trimmedlen = 0;
+ bool do_concat = true;
+
+ while (true) {
+ if (KeyTyped) {
+ msg_scroll = true;
+ saved_wait_return = false;
+ }
+ need_wait_return = false;
+
+ char *theline;
+ char *p;
+ char *arg;
+
+ if (line_arg != NULL) {
+ // Use eap->arg, split up in parts by line breaks.
+ theline = line_arg;
+ p = vim_strchr(theline, '\n');
+ if (p == NULL) {
+ line_arg += strlen(line_arg);
+ } else {
+ *p = NUL;
+ line_arg = p + 1;
+ }
+ } else {
+ xfree(*line_to_free);
+ if (eap->ea_getline == NULL) {
+ theline = getcmdline(':', 0, indent, do_concat);
+ } else {
+ theline = eap->ea_getline(':', eap->cookie, indent, do_concat);
+ }
+ *line_to_free = theline;
+ }
+ if (KeyTyped) {
+ lines_left = Rows - 1;
+ }
+ if (theline == NULL) {
+ if (skip_until != NULL) {
+ semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+ } else {
+ emsg(_("E126: Missing :endfunction"));
+ }
+ goto theend;
+ }
+ if (show_block) {
+ assert(indent >= 0);
+ ui_ext_cmdline_block_append((size_t)indent, theline);
+ }
+
+ // Detect line continuation: SOURCING_LNUM increased more than one.
+ linenr_T sourcing_lnum_off = get_sourced_lnum(eap->ea_getline, eap->cookie);
+ if (SOURCING_LNUM < sourcing_lnum_off) {
+ sourcing_lnum_off -= SOURCING_LNUM;
+ } else {
+ sourcing_lnum_off = 0;
+ }
+
+ if (skip_until != NULL) {
+ // Don't check for ":endfunc" between
+ // * ":append" and "."
+ // * ":python <<EOF" and "EOF"
+ // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
+ if (heredoc_trimmed == NULL
+ || (is_heredoc && skipwhite(theline) == theline)
+ || strncmp(theline, heredoc_trimmed, heredoc_trimmedlen) == 0) {
+ if (heredoc_trimmed == NULL) {
+ p = theline;
+ } else if (is_heredoc) {
+ p = skipwhite(theline) == theline ? theline : theline + heredoc_trimmedlen;
+ } else {
+ p = theline + heredoc_trimmedlen;
+ }
+ if (strcmp(p, skip_until) == 0) {
+ XFREE_CLEAR(skip_until);
+ XFREE_CLEAR(heredoc_trimmed);
+ heredoc_trimmedlen = 0;
+ do_concat = true;
+ is_heredoc = false;
+ }
+ }
+ } else {
+ // skip ':' and blanks
+ for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
+
+ // Check for "endfunction".
+ if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
+ if (*p == '!') {
+ p++;
+ }
+ char *nextcmd = NULL;
+ if (*p == '|') {
+ nextcmd = p + 1;
+ } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
+ nextcmd = line_arg;
+ } else if (*p != NUL && *p != '"' && p_verbose > 0) {
+ swmsg(true, _("W22: Text found after :endfunction: %s"), p);
+ }
+ 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 = nextcmd;
+ if (*line_to_free != NULL) {
+ xfree(*eap->cmdlinep);
+ *eap->cmdlinep = *line_to_free;
+ *line_to_free = NULL;
+ }
+ }
+ break;
+ }
+
+ // Increase indent inside "if", "while", "for" and "try", decrease
+ // at "end".
+ 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) {
+ indent += 2;
+ }
+
+ // Check for defining a function inside this function.
+ if (checkforcmd(&p, "function", 2)) {
+ if (*p == '!') {
+ p = skipwhite(p + 1);
+ }
+ p += eval_fname_script(p);
+ xfree(trans_function_name(&p, true, 0, NULL, NULL));
+ if (*skipwhite(p) == '(') {
+ if (nesting == MAX_FUNC_NESTING - 1) {
+ emsg(_(e_function_nesting_too_deep));
+ } else {
+ nesting++;
+ indent += 2;
+ }
+ }
+ }
+
+ // Check for ":append", ":change", ":insert".
+ 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
+ || !ASCII_ISALPHA(p[6])))))))
+ || (p[0] == 'i'
+ && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
+ && (!ASCII_ISALPHA(p[2])
+ || (p[2] == 's')))))) {
+ skip_until = xmemdupz(".", 1);
+ }
+
+ // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
+ arg = skipwhite(skiptowhite(p));
+ if (arg[0] == '<' && arg[1] == '<'
+ && ((p[0] == 'p' && p[1] == 'y'
+ && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
+ || ((p[2] == '3' || p[2] == 'x')
+ && !ASCII_ISALPHA(p[3]))))
+ || (p[0] == 'p' && p[1] == 'e'
+ && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
+ || (p[0] == 't' && p[1] == 'c'
+ && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
+ || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
+ && !ASCII_ISALPHA(p[3]))
+ || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
+ && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
+ || (p[0] == 'm' && p[1] == 'z'
+ && (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
+ // ":python <<" continues until a dot, like ":append"
+ p = skipwhite(arg + 2);
+ if (strncmp(p, "trim", 4) == 0
+ && (p[4] == NUL || ascii_iswhite(p[4]))) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ heredoc_trimmedlen = (size_t)(skipwhite(theline) - theline);
+ heredoc_trimmed = xmemdupz(theline, heredoc_trimmedlen);
+ }
+ if (*p == NUL) {
+ skip_until = xmemdupz(".", 1);
+ } else {
+ skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
+ }
+ do_concat = false;
+ is_heredoc = true;
+ }
+
+ if (!is_heredoc) {
+ // Check for ":let v =<< [trim] EOF"
+ // and ":let [a, b] =<< [trim] EOF"
+ arg = p;
+ if (checkforcmd(&arg, "let", 2)) {
+ int var_count = 0;
+ int semicolon = 0;
+ arg = (char *)skip_var_list(arg, &var_count, &semicolon, true);
+ if (arg != NULL) {
+ arg = skipwhite(arg);
+ }
+ if (arg != NULL && strncmp(arg, "=<<", 3) == 0) {
+ p = skipwhite(arg + 3);
+ bool has_trim = false;
+ while (true) {
+ if (strncmp(p, "trim", 4) == 0
+ && (p[4] == NUL || ascii_iswhite(p[4]))) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ has_trim = true;
+ continue;
+ }
+ if (strncmp(p, "eval", 4) == 0
+ && (p[4] == NUL || ascii_iswhite(p[4]))) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ continue;
+ }
+ break;
+ }
+ if (has_trim) {
+ heredoc_trimmedlen = (size_t)(skipwhite(theline) - theline);
+ heredoc_trimmed = xmemdupz(theline, heredoc_trimmedlen);
+ }
+ skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
+ do_concat = false;
+ is_heredoc = true;
+ }
+ }
+ }
+ }
+
+ // Add the line to the function.
+ ga_grow(newlines, 1 + (int)sourcing_lnum_off);
+
+ // Copy the line to newly allocated memory. get_one_sourceline()
+ // allocates 250 bytes per line, this saves 80% on average. The cost
+ // is an extra alloc/free.
+ 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.
+ while (sourcing_lnum_off-- > 0) {
+ ((char **)(newlines->ga_data))[newlines->ga_len++] = NULL;
+ }
+
+ // Check for end of eap->arg.
+ if (line_arg != NULL && *line_arg == NUL) {
+ line_arg = NULL;
+ }
+ }
+
+ // Return OK when no error was detected.
+ if (!did_emsg) {
+ ret = OK;
+ }
+
+theend:
+ xfree(skip_until);
+ xfree(heredoc_trimmed);
+ need_wait_return |= saved_wait_return;
+ return ret;
+}
+
/// ":function"
void ex_function(exarg_T *eap)
{
- char *theline;
char *line_to_free = NULL;
- bool saved_wait_return = need_wait_return;
char *arg;
char *line_arg = NULL;
garray_T newargs;
@@ -2238,16 +2564,13 @@ void ex_function(exarg_T *eap)
garray_T newlines;
int varargs = false;
int flags = 0;
- ufunc_T *fp;
+ ufunc_T *fp = NULL;
+ bool free_fp = false;
bool overwrite = false;
funcdict_T fudi;
static int func_nr = 0; // number for nameless function
hashtab_T *ht;
- bool is_heredoc = false;
- char *skip_until = NULL;
- char *heredoc_trimmed = NULL;
bool show_block = false;
- bool do_concat = true;
// ":function" without argument: list functions.
if (ends_excmd(*eap->arg)) {
@@ -2494,251 +2817,14 @@ void ex_function(exarg_T *eap)
// Save the starting line number.
linenr_T sourcing_lnum_top = SOURCING_LNUM;
- int indent = 2;
- int nesting = 0;
- while (true) {
- if (KeyTyped) {
- msg_scroll = true;
- saved_wait_return = false;
- }
- need_wait_return = false;
-
- if (line_arg != NULL) {
- // Use eap->arg, split up in parts by line breaks.
- theline = line_arg;
- p = vim_strchr(theline, '\n');
- if (p == NULL) {
- line_arg += strlen(line_arg);
- } else {
- *p = NUL;
- line_arg = p + 1;
- }
- } else {
- xfree(line_to_free);
- if (eap->ea_getline == NULL) {
- theline = getcmdline(':', 0, indent, do_concat);
- } else {
- theline = eap->ea_getline(':', eap->cookie, indent, do_concat);
- }
- line_to_free = theline;
- }
- if (KeyTyped) {
- lines_left = Rows - 1;
- }
- if (theline == NULL) {
- if (skip_until != NULL) {
- semsg(_(e_missing_heredoc_end_marker_str), skip_until);
- } else {
- emsg(_("E126: Missing :endfunction"));
- }
- goto erret;
- }
- if (show_block) {
- assert(indent >= 0);
- ui_ext_cmdline_block_append((size_t)indent, theline);
- }
-
- // Detect line continuation: SOURCING_LNUM increased more than one.
- linenr_T sourcing_lnum_off = get_sourced_lnum(eap->ea_getline, eap->cookie);
- if (SOURCING_LNUM < sourcing_lnum_off) {
- sourcing_lnum_off -= SOURCING_LNUM;
- } else {
- sourcing_lnum_off = 0;
- }
-
- if (skip_until != NULL) {
- // Don't check for ":endfunc" between
- // * ":append" and "."
- // * ":python <<EOF" and "EOF"
- // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
- if (heredoc_trimmed == NULL
- || (is_heredoc && skipwhite(theline) == theline)
- || strncmp(theline, heredoc_trimmed,
- strlen(heredoc_trimmed)) == 0) {
- if (heredoc_trimmed == NULL) {
- p = theline;
- } else if (is_heredoc) {
- p = skipwhite(theline) == theline
- ? theline : theline + strlen(heredoc_trimmed);
- } else {
- p = theline + strlen(heredoc_trimmed);
- }
- if (strcmp(p, skip_until) == 0) {
- XFREE_CLEAR(skip_until);
- XFREE_CLEAR(heredoc_trimmed);
- do_concat = true;
- is_heredoc = false;
- }
- }
- } else {
- // skip ':' and blanks
- for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
-
- // Check for "endfunction".
- if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
- if (*p == '!') {
- p++;
- }
- char *nextcmd = NULL;
- if (*p == '|') {
- nextcmd = p + 1;
- } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
- nextcmd = line_arg;
- } else if (*p != NUL && *p != '"' && p_verbose > 0) {
- swmsg(true, _("W22: Text found after :endfunction: %s"), p);
- }
- 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 = nextcmd;
- if (line_to_free != NULL) {
- xfree(*eap->cmdlinep);
- *eap->cmdlinep = line_to_free;
- line_to_free = NULL;
- }
- }
- break;
- }
-
- // Increase indent inside "if", "while", "for" and "try", decrease
- // at "end".
- 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) {
- indent += 2;
- }
-
- // Check for defining a function inside this function.
- if (checkforcmd(&p, "function", 2)) {
- if (*p == '!') {
- p = skipwhite(p + 1);
- }
- p += eval_fname_script(p);
- xfree(trans_function_name(&p, true, 0, NULL, NULL));
- if (*skipwhite(p) == '(') {
- if (nesting == MAX_FUNC_NESTING - 1) {
- emsg(_(e_function_nesting_too_deep));
- } else {
- nesting++;
- indent += 2;
- }
- }
- }
-
- // Check for ":append", ":change", ":insert".
- 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
- || !ASCII_ISALPHA(p[6])))))))
- || (p[0] == 'i'
- && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
- && (!ASCII_ISALPHA(p[2])
- || (p[2] == 's')))))) {
- skip_until = xstrdup(".");
- }
-
- // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
- arg = skipwhite(skiptowhite(p));
- if (arg[0] == '<' && arg[1] == '<'
- && ((p[0] == 'p' && p[1] == 'y'
- && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
- || ((p[2] == '3' || p[2] == 'x')
- && !ASCII_ISALPHA(p[3]))))
- || (p[0] == 'p' && p[1] == 'e'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
- || (p[0] == 't' && p[1] == 'c'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
- || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
- && !ASCII_ISALPHA(p[3]))
- || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
- && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
- || (p[0] == 'm' && p[1] == 'z'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
- // ":python <<" continues until a dot, like ":append"
- p = skipwhite(arg + 2);
- if (strncmp(p, "trim", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
- }
- if (*p == NUL) {
- skip_until = xstrdup(".");
- } else {
- skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
- }
- do_concat = false;
- is_heredoc = true;
- }
-
- // Check for ":let v =<< [trim] EOF"
- // and ":let [a, b] =<< [trim] EOF"
- arg = p;
- if (checkforcmd(&arg, "let", 2)) {
- int var_count = 0;
- int semicolon = 0;
- arg = (char *)skip_var_list(arg, &var_count, &semicolon, true);
- if (arg != NULL) {
- arg = skipwhite(arg);
- }
- if (arg != NULL && strncmp(arg, "=<<", 3) == 0) {
- p = skipwhite(arg + 3);
- while (true) {
- if (strncmp(p, "trim", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
- continue;
- }
- if (strncmp(p, "eval", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- continue;
- }
- break;
- }
- skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
- do_concat = false;
- is_heredoc = true;
- }
- }
- }
-
- // Add the line to the function.
- ga_grow(&newlines, 1 + (int)sourcing_lnum_off);
-
- // Copy the line to newly allocated memory. get_one_sourceline()
- // allocates 250 bytes per line, this saves 80% on average. The cost
- // is an extra alloc/free.
- 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.
- while (sourcing_lnum_off-- > 0) {
- ((char **)(newlines.ga_data))[newlines.ga_len++] = NULL;
- }
-
- // Check for end of eap->arg.
- if (line_arg != NULL && *line_arg == NUL) {
- line_arg = NULL;
- }
- }
-
- // Don't define the function when skipping commands or when an error was
- // detected.
- if (eap->skip || did_emsg) {
+ // Do not define the function when getting the body fails and when skipping.
+ if (get_function_body(eap, &newlines, line_arg, &line_to_free, show_block) == FAIL
+ || eap->skip) {
goto erret;
}
// If there are no errors, add the function
+ size_t namelen = 0;
if (fudi.fd_dict == NULL) {
dictitem_T *v = find_var(name, strlen(name), &ht, false);
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
@@ -2754,11 +2840,11 @@ void ex_function(exarg_T *eap)
&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
|| fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) {
emsg_funcname(e_funcexts, name);
- goto erret;
+ goto errret_keep;
}
if (fp->uf_calls > 0) {
emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name);
- goto erret;
+ goto errret_keep;
}
if (fp->uf_refcount > 1) {
// This function is referenced somewhere, don't redefine it but
@@ -2779,7 +2865,7 @@ void ex_function(exarg_T *eap)
}
}
} else {
- char numbuf[20];
+ char numbuf[NUMBUFLEN];
fp = NULL;
if (fudi.fd_newkey == NULL && !eap->forceit) {
@@ -2799,8 +2885,8 @@ void ex_function(exarg_T *eap)
// Give the function a sequential number. Can only be used with a
// Funcref!
xfree(name);
- snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
- name = xstrdup(numbuf);
+ namelen = (size_t)snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
+ name = xmemdupz(numbuf, namelen);
}
if (fp == NULL) {
@@ -2824,7 +2910,10 @@ void ex_function(exarg_T *eap)
}
}
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
+ if (namelen == 0) {
+ namelen = strlen(name);
+ }
+ fp = alloc_ufunc(name, namelen);
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
@@ -2832,7 +2921,7 @@ void ex_function(exarg_T *eap)
fudi.fd_di = tv_dict_item_alloc(fudi.fd_newkey);
if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
xfree(fudi.fd_di);
- xfree(fp);
+ XFREE_CLEAR(fp);
goto erret;
}
} else {
@@ -2840,19 +2929,18 @@ 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 = xstrdup(name);
+ fudi.fd_di->di_tv.vval.v_string = xmemdupz(name, namelen);
// behave like "dict" was used
flags |= FC_DICT;
}
// insert the new function in the function list
- set_ufunc_name(fp, name);
if (overwrite) {
hashitem_T *hi = hash_find(&func_hashtab, name);
hi->hi_key = UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
- xfree(fp);
+ free_fp = true;
goto erret;
}
fp->uf_refcount = 1;
@@ -2881,18 +2969,27 @@ void ex_function(exarg_T *eap)
goto ret_free;
erret:
+ if (fp != NULL) {
+ // these were set to "newargs" and "default_args", which are cleared below
+ ga_init(&fp->uf_args, (int)sizeof(char *), 1);
+ ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
+ }
+errret_2:
+ if (fp != NULL) {
+ XFREE_CLEAR(fp->uf_name_exp);
+ }
+ if (free_fp) {
+ XFREE_CLEAR(fp);
+ }
+errret_keep:
ga_clear_strings(&newargs);
ga_clear_strings(&default_args);
-errret_2:
ga_clear_strings(&newlines);
ret_free:
- xfree(skip_until);
- xfree(heredoc_trimmed);
xfree(line_to_free);
xfree(fudi.fd_newkey);
xfree(name);
did_emsg |= saved_did_emsg;
- need_wait_return |= saved_wait_return;
if (show_block) {
ui_ext_cmdline_block_leave();
}
@@ -2979,15 +3076,16 @@ char *get_user_func_name(expand_T *xp, int idx)
return ""; // don't show dict and lambda functions
}
- if (strlen(fp->uf_name) + 4 >= IOSIZE) {
+ if (fp->uf_namelen + 4 >= IOSIZE) {
return fp->uf_name; // Prevent overflow.
}
- cat_func_name(IObuff, IOSIZE, fp);
+ int len = cat_func_name(IObuff, IOSIZE, fp);
if (xp->xp_context != EXPAND_USER_FUNC) {
- xstrlcat(IObuff, "(", IOSIZE);
+ xstrlcpy(IObuff + len, "(", IOSIZE - (size_t)len);
if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) {
- xstrlcat(IObuff, ")", IOSIZE);
+ len++;
+ xstrlcpy(IObuff + len, ")", IOSIZE - (size_t)len);
}
}
return IObuff;
@@ -3589,21 +3687,26 @@ char *get_return_cmd(void *rettv)
{
char *s = NULL;
char *tofree = NULL;
+ size_t slen = 0;
if (rettv != NULL) {
tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
}
if (s == NULL) {
s = "";
+ } else {
+ slen = strlen(s);
}
xstrlcpy(IObuff, ":return ", IOSIZE);
xstrlcpy(IObuff + 8, s, IOSIZE - 8);
- if (strlen(s) + 8 >= IOSIZE) {
+ size_t IObufflen = 8 + slen;
+ if (IObufflen >= IOSIZE) {
STRCPY(IObuff + IOSIZE - 4, "...");
+ IObufflen = IOSIZE - 1;
}
xfree(tofree);
- return xstrdup(IObuff);
+ return xstrnsave(IObuff, IObufflen);
}
/// Get next function line.
@@ -4046,7 +4149,8 @@ bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
char *register_luafunc(LuaRef ref)
{
char *name = get_lambda_name();
- ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
+ size_t namelen = get_lambda_name_len();
+ ufunc_T *fp = alloc_ufunc(name, namelen);
fp->uf_refcount = 1;
fp->uf_varargs = true;
@@ -4055,7 +4159,6 @@ char *register_luafunc(LuaRef ref)
fp->uf_script_ctx = current_sctx;
fp->uf_luaref = ref;
- STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
// coverity[leaked_storage]
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index a8358aab51..c64477741d 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -40,7 +40,6 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
-#include "nvim/option_vars.h"
#include "nvim/os/os.h"
#include "nvim/search.h"
#include "nvim/strings.h"
@@ -811,9 +810,9 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
// Find the end of the name.
char *arg_end = NULL;
OptIndex opt_idx;
- int scope;
+ int opt_flags;
- char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &scope);
+ char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &opt_flags);
if (p == NULL || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
emsg(_(e_letunexp));
@@ -825,7 +824,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
bool is_tty_opt = is_tty_option(arg);
bool hidden = is_option_hidden(opt_idx);
- OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, scope);
+ OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, opt_flags);
OptVal newval = NIL_OPTVAL;
if (curval.type == kOptValTypeNil) {
@@ -845,11 +844,10 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
goto theend;
}
- // Don't assume current and new values are of the same type in order to future-proof the code for
- // when an option can have multiple types.
- const bool is_num = ((curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean)
- && (newval.type == kOptValTypeNumber || newval.type == kOptValTypeBoolean));
- const bool is_string = curval.type == kOptValTypeString && newval.type == kOptValTypeString;
+ // Current value and new value must have the same type.
+ assert(curval.type == newval.type);
+ const bool is_num = curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean;
+ const bool is_string = curval.type == kOptValTypeString;
if (op != NULL && *op != '=') {
if (!hidden && is_num) { // number or bool
@@ -874,15 +872,19 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
} else {
newval = BOOLEAN_OPTVAL(TRISTATE_FROM_INT(new_n));
}
- } else if (!hidden && is_string
- && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string
- OptVal newval_old = newval;
- newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data));
- optval_free(newval_old);
+ } else if (!hidden && is_string) { // string
+ const char *curval_data = curval.data.string.data;
+ const char *newval_data = newval.data.string.data;
+
+ if (curval_data != NULL && newval_data != NULL) {
+ OptVal newval_old = newval;
+ newval = CSTR_AS_OPTVAL(concat_str(curval_data, newval_data));
+ optval_free(newval_old);
+ }
}
}
- const char *err = set_option_value_handle_tty(arg, opt_idx, newval, scope);
+ const char *err = set_option_value_handle_tty(arg, opt_idx, newval, opt_flags);
arg_end = p;
if (err != NULL) {
emsg(_(err));
@@ -1409,6 +1411,7 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first)
static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len,
const VarType type, const char *string, int *first)
{
+ msg_ext_set_kind("list_cmd");
// don't use msg() to avoid overwriting "v:statusmsg"
msg_start();
msg_puts(prefix);
@@ -1902,8 +1905,6 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off)
///
/// @return Typval converted to OptVal. Must be freed by caller.
/// Returns NIL_OPTVAL for invalid option name.
-///
-/// TODO(famiu): Refactor this to support multitype options.
static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, bool *error)
{
OptVal value = NIL_OPTVAL;
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
index 86495f1cb6..2e2758d3bc 100644
--- a/src/nvim/eval/window.c
+++ b/src/nvim/eval/window.c
@@ -31,7 +31,6 @@
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/window.c.generated.h"
@@ -326,6 +325,7 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1);
tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
+ tv_dict_add_nr(dict, S_LEN("leftcol"), wp->w_leftcol);
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);
diff --git a/src/nvim/event/proc.c b/src/nvim/event/proc.c
index 5ae3bd8c2d..37cb102d11 100644
--- a/src/nvim/event/proc.c
+++ b/src/nvim/event/proc.c
@@ -1,8 +1,10 @@
#include <assert.h>
#include <inttypes.h>
#include <signal.h>
+#include <string.h>
#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/event/libuv_proc.h"
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
@@ -13,6 +15,7 @@
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/main.h"
+#include "nvim/memory_defs.h"
#include "nvim/os/proc.h"
#include "nvim/os/pty_proc.h"
#include "nvim/os/shell.h"
diff --git a/src/nvim/event/proc.h b/src/nvim/event/proc.h
index f525d46f87..cd4d5913ac 100644
--- a/src/nvim/event/proc.h
+++ b/src/nvim/event/proc.h
@@ -4,6 +4,7 @@
#include <stddef.h>
#include "nvim/event/defs.h" // IWYU pragma: keep
+#include "nvim/os/os_defs.h"
#include "nvim/types_defs.h"
static inline Proc proc_init(Loop *loop, ProcType type, void *data)
@@ -21,8 +22,8 @@ static inline Proc proc_init(Loop *loop, ProcType type, void *data)
.argv = NULL,
.exepath = NULL,
.in = { .closed = false },
- .out = { .s.closed = false },
- .err = { .s.closed = false },
+ .out = { .s.closed = false, .s.fd = STDOUT_FILENO },
+ .err = { .s.closed = false, .s.fd = STDERR_FILENO },
.cb = NULL,
.closed = false,
.internal_close_cb = NULL,
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 15bdc547d5..6304953029 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -1,7 +1,6 @@
#include <assert.h>
#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
+#include <string.h>
#include <uv.h>
#include "nvim/event/multiqueue.h"
@@ -9,7 +8,8 @@
#include "nvim/event/stream.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
-#include "nvim/main.h"
+#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/os/os_defs.h"
#include "nvim/types_defs.h"
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index 1214c3e336..c340ef2826 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -33,7 +33,7 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint
char *host_end = strrchr(addr, ':');
if (host_end && addr != host_end) {
- // Split user specified address into two strings, addr(hostname) and port.
+ // Split user specified address into two strings, addr (hostname) and port.
// The port part in watcher->addr will be updated later.
*host_end = NUL;
char *port = host_end + 1;
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 71de6ee1ba..9c155b55ea 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -8,6 +8,7 @@
#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
#include "nvim/log.h"
+#include "nvim/memory.h"
#include "nvim/types_defs.h"
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 8cccf08e11..34217107aa 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -15,6 +15,7 @@
#include "auto/config.h"
#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
@@ -52,7 +53,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -73,7 +73,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
@@ -3708,12 +3707,9 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
// Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
while (subflags.do_ask) {
if (exmode_active) {
- char *prompt;
- char *resp;
- colnr_T sc, ec;
-
print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
+ colnr_T sc, ec;
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
curwin->w_cursor.col = MAX(regmatch.endpos[0].col - 1, 0);
@@ -3725,10 +3721,11 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
ec += numw;
}
- prompt = xmallocz((size_t)ec + 1);
+ char *prompt = xmallocz((size_t)ec + 1);
memset(prompt, ' ', (size_t)sc);
memset(prompt + sc, '^', (size_t)(ec - sc) + 1);
- resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);
+ char *resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE, false, NULL);
msg_putchar('\n');
xfree(prompt);
if (resp != NULL) {
@@ -3795,35 +3792,17 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
redraw_later(curwin, UPD_SOME_VALID);
curwin->w_p_fen = save_p_fen;
- if (msg_row == Rows - 1) {
- msg_didout = false; // avoid a scroll-up
- }
- msg_starthere();
- i = msg_scroll;
- msg_scroll = 0; // truncate msg when
- // needed
- msg_no_more = true;
- msg_ext_set_kind("confirm_sub");
- // Same highlight as wait_return().
- smsg(HLF_R, _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
- msg_no_more = false;
- msg_scroll = i;
- if (!ui_has(kUIMessages)) {
- ui_cursor_goto(msg_row, msg_col);
- }
- RedrawingDisabled = temp;
- no_mapping++; // don't map this key
- allow_keys++; // allow special keys
- typed = plain_vgetc();
- no_mapping--;
- allow_keys--;
+ char *p = _("replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)");
+ snprintf(IObuff, IOSIZE, p, sub);
+ p = xstrdup(IObuff);
+ typed = prompt_for_input(p, HLF_R, true, NULL);
+ xfree(p);
- // clear the question
- msg_didout = false; // don't scroll up
- msg_col = 0;
+ msg_didout = false; // don't scroll up
gotocmdline(true);
p_lz = save_p_lz;
+ RedrawingDisabled = temp;
// restore the line
if (orig_line != NULL) {
@@ -4809,7 +4788,7 @@ void ex_oldfiles(exarg_T *eap)
// File selection prompt on ":browse oldfiles"
if (cmdmod.cmod_flags & CMOD_BROWSE) {
quit_more = false;
- nr = prompt_for_number(false);
+ nr = prompt_for_input(NULL, 0, false, NULL);
msg_starthere();
if (nr > 0 && nr <= tv_list_len(l)) {
const char *const p = tv_list_find_str(l, nr - 1);
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index c8574c805c..0cdc397e9c 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -1947,6 +1947,12 @@ M.cmds = {
func = 'ex_packloadall',
},
{
+ command = 'pbuffer',
+ flags = bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR),
+ addr_type = 'ADDR_BUFFERS',
+ func = 'ex_pbuffer',
+ },
+ {
command = 'pclose',
flags = bit.bor(BANG, TRLBAR),
addr_type = 'ADDR_NONE',
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index e37c37e8e6..61a6dab897 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -30,10 +30,10 @@
#include "nvim/fileio.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/macros_defs.h"
#include "nvim/mark.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 4924e86470..9dc922b0d9 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -94,35 +94,6 @@ typedef struct exarg exarg_T;
typedef void (*ex_func_T)(exarg_T *eap);
typedef int (*ex_preview_func_T)(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr);
-// NOTE: These possible could be removed and changed so that
-// Callback could take a "command" style string, and simply
-// execute that (instead of it being a function).
-//
-// But it's still a bit weird to do that.
-//
-// Another option would be that we just make a callback reference to
-// "execute($INPUT)" or something like that, so whatever the user
-// sends in via autocmds is just executed via this.
-//
-// However, that would probably have some performance cost (probably
-// very marginal, but still some cost either way).
-typedef enum {
- CALLABLE_NONE,
- CALLABLE_EX,
- CALLABLE_CB,
-} AucmdExecutableType;
-
-typedef struct aucmd_executable_t AucmdExecutable;
-struct aucmd_executable_t {
- AucmdExecutableType type;
- union {
- char *cmd;
- Callback cb;
- } callable;
-};
-
-#define AUCMD_EXECUTABLE_INIT { .type = CALLABLE_NONE }
-
typedef char *(*LineGetter)(int, void *, int, bool);
/// Structure for command definition.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index f5ecedf827..ceb17a995d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -51,7 +51,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/input.h"
@@ -980,12 +979,10 @@ void handle_did_throw(void)
force_abort = true;
}
- msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993
-
if (messages != NULL) {
do {
msglist_T *next = messages->next;
- emsg_multiline(messages->msg, messages->multiline);
+ emsg_multiline(messages->msg, "emsg", HLF_E, messages->multiline);
xfree(messages->msg);
xfree(messages->sfile);
xfree(messages);
@@ -2204,7 +2201,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
errormsg = _("E493: Backwards range given");
goto doend;
}
- if (ask_yesno(_("Backwards range given, OK to swap"), false) != 'y') {
+ if (ask_yesno(_("Backwards range given, OK to swap")) != 'y') {
goto doend;
}
}
@@ -4504,6 +4501,12 @@ static void ex_bunload(exarg_T *eap)
/// :[N]sbuffer [N] to buffer N
static void ex_buffer(exarg_T *eap)
{
+ do_exbuffer(eap);
+}
+
+/// ":buffer" command and alike.
+static void do_exbuffer(exarg_T *eap)
+{
if (*eap->arg) {
eap->errmsg = ex_errmsg(e_trailing_arg, eap->arg);
} else {
@@ -6103,12 +6106,20 @@ static void ex_sleep(exarg_T *eap)
default:
semsg(_(e_invarg2), eap->arg); return;
}
- do_sleep(len);
+
+ // Hide the cursor if invoked with !
+ do_sleep(len, eap->forceit);
}
/// Sleep for "msec" milliseconds, but return early on CTRL-C.
-void do_sleep(int64_t msec)
+///
+/// @param hide_cursor hide the cursor if true
+void do_sleep(int64_t msec, bool hide_cursor)
{
+ if (hide_cursor) {
+ ui_busy_start();
+ }
+
ui_flush(); // flush before waiting
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, msec, got_int);
@@ -6117,6 +6128,10 @@ void do_sleep(int64_t msec)
if (got_int) {
vpeekc();
}
+
+ if (hide_cursor) {
+ ui_busy_stop();
+ }
}
/// ":winsize" command (obsolete).
@@ -6992,14 +7007,35 @@ static void ex_ptag(exarg_T *eap)
static void ex_pedit(exarg_T *eap)
{
win_T *curwin_save = curwin;
+ prepare_preview_window();
+ // Edit the file.
+ do_exedit(eap, NULL);
+
+ back_to_current_window(curwin_save);
+}
+
+/// ":pbuffer"
+static void ex_pbuffer(exarg_T *eap)
+{
+ win_T *curwin_save = curwin;
+ prepare_preview_window();
+
+ // Go to the buffer.
+ do_exbuffer(eap);
+
+ back_to_current_window(curwin_save);
+}
+
+static void prepare_preview_window(void)
+{
// Open the preview window or popup and make it the current window.
g_do_tagpreview = (int)p_pvh;
prepare_tagpreview(true);
+}
- // Edit the file.
- do_exedit(eap, NULL);
-
+static void back_to_current_window(win_T *curwin_save)
+{
if (curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor(curwin);
@@ -7364,8 +7400,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum
*errormsg = _(e_usingsid);
return NULL;
}
- snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_",
- current_sctx.sc_sid);
+ snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_", current_sctx.sc_sid);
result = strbuf;
break;
@@ -7738,7 +7773,7 @@ static void ex_terminal(exarg_T *eap)
if (*eap->arg != NUL) { // Run {cmd} in 'shell'.
char *name = vim_strsave_escaped(eap->arg, "\"\\");
snprintf(ex_cmd + len, sizeof(ex_cmd) - len,
- " | call termopen(\"%s\")", name);
+ " | call jobstart(\"%s\",{'term':v:true})", name);
xfree(name);
} else { // No {cmd}: run the job with tokenized 'shell'.
if (*p_sh == NUL) {
@@ -7761,7 +7796,7 @@ static void ex_terminal(exarg_T *eap)
shell_free_argv(argv);
snprintf(ex_cmd + len, sizeof(ex_cmd) - len,
- " | call termopen([%s])", shell_argv + 1);
+ " | call jobstart([%s], {'term':v:true})", shell_argv + 1);
}
do_cmdline_cmd(ex_cmd);
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index f9936dd88e..18c691d076 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -479,6 +479,9 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
excp->throw_lnum = SOURCING_LNUM;
}
+ excp->stacktrace = stacktrace_create();
+ tv_list_ref(excp->stacktrace);
+
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
@@ -563,6 +566,7 @@ static void discard_exception(except_T *excp, bool was_finished)
free_msglist(excp->messages);
}
xfree(excp->throw_name);
+ tv_list_unref(excp->stacktrace);
xfree(excp);
}
@@ -584,6 +588,7 @@ static void catch_exception(except_T *excp)
excp->caught = caught_stack;
caught_stack = excp;
set_vim_var_string(VV_EXCEPTION, excp->value, -1);
+ set_vim_var_list(VV_STACKTRACE, excp->stacktrace);
if (*excp->throw_name != NUL) {
if (excp->throw_lnum != 0) {
vim_snprintf(IObuff, IOSIZE, _("%s, line %" PRId64),
@@ -633,6 +638,7 @@ static void finish_exception(except_T *excp)
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
+ set_vim_var_list(VV_STACKTRACE, caught_stack->stacktrace);
if (*caught_stack->throw_name != NUL) {
if (caught_stack->throw_lnum != 0) {
vim_snprintf(IObuff, IOSIZE,
@@ -651,6 +657,7 @@ static void finish_exception(except_T *excp)
} else {
set_vim_var_string(VV_EXCEPTION, NULL, -1);
set_vim_var_string(VV_THROWPOINT, NULL, -1);
+ set_vim_var_list(VV_STACKTRACE, NULL);
}
// Discard the exception, but use the finish message for 'verbose'.
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 3f5e510a20..e0d06f3e93 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
+#include "nvim/eval/typval_defs.h"
#include "nvim/pos_defs.h"
/// A list used for saving values of "emsg_silent". Used by ex_try() to save the
@@ -107,6 +108,7 @@ struct vim_exception {
msglist_T *messages; ///< message(s) causing error exception
char *throw_name; ///< name of the throw point
linenr_T throw_lnum; ///< line number of the throw point
+ list_T *stacktrace; ///< stacktrace
except_T *caught; ///< next exception on the caught stack
};
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ace62ea729..fc20748309 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -14,7 +14,6 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/arabic.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -43,7 +42,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
@@ -64,7 +62,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
@@ -123,7 +120,7 @@ typedef struct {
int indent;
int c;
bool gotesc; // true when <ESC> just typed
- int do_abbr; // when true check for abbr.
+ bool do_abbr; // when true check for abbr.
char *lookfor; // string to match
int lookforlen;
int hiscnt; // current history line in use
@@ -131,17 +128,17 @@ typedef struct {
// to jump to next match
int histype; // history type to be used
incsearch_state_T is_state;
- int did_wild_list; // did wild_list() recently
+ bool did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
int save_msg_scroll;
int save_State; // remember State when called
int prev_cmdpos;
char *save_p_icm;
- int some_key_typed; // one of the keys was typed
+ bool 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
- int ignore_drag_release;
- int break_ctrl_c;
+ bool ignore_drag_release;
+ bool break_ctrl_c;
expand_T xpc;
OptInt *b_im_ptr;
buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid
@@ -166,6 +163,7 @@ typedef struct {
typedef struct {
buf_T *buf;
OptInt save_b_p_ul;
+ int save_b_p_ma;
int save_b_changed;
pos_T save_b_op_start;
pos_T save_b_op_end;
@@ -403,7 +401,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
parse_cmd_address(&ea, &dummy, true);
if (ea.addr_count > 0) {
// Allow for reverse match.
- search_first_line = MIN(ea.line1, ea.line1);
+ search_first_line = MIN(ea.line2, ea.line1);
search_last_line = MAX(ea.line2, ea.line1);
} else if (cmd[0] == 's' && cmd[1] != 'o') {
// :s defaults to the current line
@@ -681,6 +679,15 @@ static void init_ccline(int firstc, int indent)
}
}
+static void ui_ext_cmdline_hide(bool abort)
+{
+ if (ui_has(kUICmdline)) {
+ cmdline_was_last_drawn = false;
+ ccline.redraw_state = kCmdRedrawNone;
+ ui_call_cmdline_hide(ccline.level, abort);
+ }
+}
+
/// Internal entry point for cmdline mode.
///
/// @param count only used for incremental search
@@ -787,9 +794,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
setmouse();
setcursor();
- TryState tstate;
Error err = ERROR_INIT;
- bool tl_ret = true;
char firstcbuf[2];
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
firstcbuf[1] = 0;
@@ -802,20 +807,19 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
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);
- try_enter(&tstate);
-
- apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf);
- restore_v_event(dict, &save_v_event);
+ TRY_WRAP(&err, {
+ apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf);
+ restore_v_event(dict, &save_v_event);
+ });
- tl_ret = try_leave(&tstate, &err);
- if (!tl_ret && ERROR_SET(&err)) {
+ if (ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
msg_puts_hl(err.msg, HLF_E, true);
api_clear_error(&err);
redrawcmd();
}
- tl_ret = true;
+ err = ERROR_INIT;
}
may_trigger_modechanged();
@@ -847,6 +851,10 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
found_one = true;
}
+ if (redraw_custom_title_later()) {
+ found_one = true;
+ }
+
if (found_one) {
redraw_statuslines();
}
@@ -869,10 +877,10 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
// not readonly:
tv_dict_add_bool(dict, S_LEN("abort"),
s->gotesc ? kBoolVarTrue : kBoolVarFalse);
- try_enter(&tstate);
- apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf);
- // error printed below, to avoid redraw issues
- tl_ret = try_leave(&tstate, &err);
+ TRY_WRAP(&err, {
+ apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf);
+ // error printed below, to avoid redraw issues
+ });
if (tv_dict_get_number(dict, "abort") != 0) {
s->gotesc = true;
}
@@ -925,7 +933,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
msg_scroll = s->save_msg_scroll;
redir_off = false;
- if (!tl_ret && ERROR_SET(&err)) {
+ if (ERROR_SET(&err)) {
msg_putchar('\n');
emsg(err.msg);
did_emsg = false;
@@ -933,7 +941,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
}
// When the command line was typed, no need for a wait-return prompt.
- if (s->some_key_typed && tl_ret) {
+ if (s->some_key_typed && !ERROR_SET(&err)) {
need_wait_return = false;
}
@@ -955,10 +963,11 @@ theend:
char *p = ccline.cmdbuff;
if (ui_has(kUICmdline)) {
- ui_call_cmdline_hide(ccline.level);
+ ui_ext_cmdline_hide(s->gotesc);
msg_ext_clear_later();
}
if (!cmd_silent) {
+ redraw_custom_title_later();
status_redraw_all(); // redraw to show mode change
}
@@ -1070,23 +1079,23 @@ static int command_line_wildchar_complete(CommandLineState *s)
{
int res;
int options = WILD_NO_BEEP;
- if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
+ if (wim_flags[s->wim_index] & kOptWimFlagLastused) {
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))) {
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ && ((wim_flags[s->wim_index] & kOptWimFlagList)
+ || (p_wmnu && (wim_flags[s->wim_index] & kOptWimFlagFull) != 0))) {
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
redrawcmd();
s->did_wild_list = true;
}
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ } else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
res = nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
} else {
res = OK; // don't insert 'wildchar' now
@@ -1097,7 +1106,7 @@ static int command_line_wildchar_complete(CommandLineState *s)
// if 'wildmode' first contains "longest", get longest
// common part
- if (wim_flags[0] & WIM_LONGEST) {
+ if (wim_flags[0] & kOptWimFlagLongest) {
res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
} else {
res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, s->firstc != '@');
@@ -1118,12 +1127,12 @@ static int command_line_wildchar_complete(CommandLineState *s)
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) {
+ if (wim_flags[0] == kOptWimFlagLongest && 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)) {
+ if ((wim_flags[s->wim_index] & kOptWimFlagList)
+ || (p_wmnu && (wim_flags[s->wim_index] & kOptWimFlagFull) != 0)) {
+ if (!(wim_flags[0] & kOptWimFlagLongest)) {
int p_wmnu_save = p_wmnu;
p_wmnu = 0;
// remove match
@@ -1131,17 +1140,17 @@ static int command_line_wildchar_complete(CommandLineState *s)
p_wmnu = p_wmnu_save;
}
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
redrawcmd();
s->did_wild_list = true;
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ } else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
}
} else {
- vim_beep(BO_WILD);
+ vim_beep(kOptBoFlagWildmode);
}
} else if (s->xpc.xp_numfiles == -1) {
s->xpc.xp_context = EXPAND_NOTHING;
@@ -1380,9 +1389,9 @@ static int command_line_execute(VimState *state, int key)
if (s->c == K_S_TAB && KeyTyped) {
if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) {
if (s->xpc.xp_numfiles > 1
- && ((!s->did_wild_list && (wim_flags[s->wim_index] & WIM_LIST)) || p_wmnu)) {
+ && ((!s->did_wild_list && (wim_flags[s->wim_index] & kOptWimFlagList)) || p_wmnu)) {
// Trigger the popup menu when wildoptions=pum
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
}
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
@@ -1511,7 +1520,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s
redrawcmdline();
curwin->w_cursor = s->match_end;
} else {
- vim_beep(BO_ERROR);
+ vim_beep(kOptBoFlagError);
}
restore_last_search_pattern();
return FAIL;
@@ -1849,6 +1858,12 @@ static int command_line_browse_history(CommandLineState *s)
static int command_line_handle_key(CommandLineState *s)
{
+ // For one key prompt, avoid putting ESC and Ctrl_C onto cmdline.
+ // For all other keys, just put onto cmdline and exit.
+ if (ccline.one_key && s->c != ESC && s->c != Ctrl_C) {
+ goto end;
+ }
+
// Big switch for a typed command line character.
switch (s->c) {
case K_BS:
@@ -1999,6 +2014,12 @@ static int command_line_handle_key(CommandLineState *s)
}
FALLTHROUGH;
case K_LEFTMOUSE:
+ // Return on left click above number prompt
+ if (ccline.mouse_used && mouse_row < cmdline_row) {
+ *ccline.mouse_used = true;
+ return 0;
+ }
+ FALLTHROUGH;
case K_RIGHTMOUSE:
command_line_left_right_mouse(s);
return command_line_not_changed(s);
@@ -2156,6 +2177,14 @@ static int command_line_handle_key(CommandLineState *s)
}
return command_line_not_changed(s);
+ case 'q':
+ // Number prompts use the mouse and return on 'q' press
+ if (ccline.mouse_used) {
+ *ccline.cmdbuff = NUL;
+ return 0;
+ }
+ FALLTHROUGH;
+
default:
// Normal character with no special meaning. Just set mod_mask
// to 0x0 so that typing Shift-Space in the GUI doesn't enter
@@ -2176,6 +2205,7 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_changed(s);
}
+end:
// 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);
@@ -2184,17 +2214,22 @@ static int command_line_handle_key(CommandLineState *s)
IObuff[j] = NUL; // exclude composing chars
put_on_cmdline(IObuff, j, true);
}
- return command_line_changed(s);
+ return ccline.one_key ? 0 : command_line_changed(s);
}
-static int command_line_not_changed(CommandLineState *s)
+/// Trigger CursorMovedC autocommands.
+static void may_trigger_cursormovedc(CommandLineState *s)
{
- // Trigger CursorMovedC autocommands.
if (ccline.cmdpos != s->prev_cmdpos) {
trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC);
s->prev_cmdpos = ccline.cmdpos;
+ ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos);
}
+}
+static int command_line_not_changed(CommandLineState *s)
+{
+ may_trigger_cursormovedc(s);
// Incremental searches for "/" and "?":
// Enter command_line_not_changed() when a character has been read but the
// command line did not change. Then we only search and redraw if something
@@ -2310,11 +2345,13 @@ static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf)
win_T *preview_win = curwin;
Error err = ERROR_INIT;
+ int result = OK;
// Switch to preview buffer
- try_start();
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0);
- if (try_end(&err) || result == FAIL) {
+ TRY_WRAP(&err, {
+ result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0);
+ });
+ if (ERROR_SET(&err) || result == FAIL) {
api_clear_error(&err);
return NULL;
}
@@ -2396,6 +2433,7 @@ static void cmdpreview_prepare(CpInfo *cpinfo)
if (!set_has(ptr_t, &saved_bufs, buf)) {
CpBufInfo cp_bufinfo;
cp_bufinfo.buf = buf;
+ cp_bufinfo.save_b_p_ma = buf->b_p_ma;
cp_bufinfo.save_b_p_ul = buf->b_p_ul;
cp_bufinfo.save_b_changed = buf->b_changed;
cp_bufinfo.save_b_op_start = buf->b_op_start;
@@ -2486,6 +2524,7 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
}
buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels'
+ buf->b_p_ma = cp_bufinfo.save_b_p_ma; // Restore 'modifiable'
}
for (size_t i = 0; i < cpinfo->win_info.size; i++) {
@@ -2554,7 +2593,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
// Place it there in case preview callback flushes it. #30696
cursorcmd();
// Flush now: external cmdline may itself wish to update the screen which is
- // currently disallowed during cmdpreview(no longer needed in case that changes).
+ // currently disallowed during cmdpreview (no longer needed in case that changes).
cmdline_ui_flush();
// Swap invalid command range if needed
@@ -2595,9 +2634,10 @@ static bool cmdpreview_may_show(CommandLineState *s)
// open the preview window. The preview callback also handles doing the changes and highlights for
// the preview.
Error err = ERROR_INIT;
- try_start();
- cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
- if (try_end(&err)) {
+ TRY_WRAP(&err, {
+ cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
+ });
+ if (ERROR_SET(&err)) {
api_clear_error(&err);
cmdpreview_type = 0;
}
@@ -2638,7 +2678,6 @@ end:
static void do_autocmd_cmdlinechanged(int firstc)
{
if (has_event(EVENT_CMDLINECHANGED)) {
- TryState tstate;
Error err = ERROR_INIT;
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
@@ -2651,13 +2690,11 @@ static void do_autocmd_cmdlinechanged(int firstc)
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);
- try_enter(&tstate);
-
- apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf);
- restore_v_event(dict, &save_v_event);
-
- bool tl_ret = try_leave(&tstate, &err);
- if (!tl_ret && ERROR_SET(&err)) {
+ TRY_WRAP(&err, {
+ apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf);
+ restore_v_event(dict, &save_v_event);
+ });
+ if (ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
msg_puts_hl(err.msg, HLF_E, true);
@@ -2672,18 +2709,13 @@ static int command_line_changed(CommandLineState *s)
// Trigger CmdlineChanged autocommands.
do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-');
- // Trigger CursorMovedC autocommands.
- if (ccline.cmdpos != s->prev_cmdpos) {
- trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC);
- s->prev_cmdpos = ccline.cmdpos;
- }
+ may_trigger_cursormovedc(s);
const bool prev_cmdpreview = cmdpreview;
if (s->firstc == ':'
&& current_sctx.sc_sid == 0 // only if interactive
&& *p_icm != NUL // 'inccommand' is set
&& !exmode_active // not in ex mode
- && curbuf->b_p_ma // buffer is modifiable
&& cmdline_star == 0 // not typing a password
&& !vpeekc_any()
&& cmdpreview_may_show(s)) {
@@ -2718,12 +2750,14 @@ static int command_line_changed(CommandLineState *s)
static void abandon_cmdline(void)
{
dealloc_cmdbuff();
- ccline.redraw_state = kCmdRedrawNone;
if (msg_scrolled == 0) {
compute_cmdrow();
}
- msg("", 0);
- redraw_cmdline = true;
+ // Avoid overwriting key prompt
+ if (!ccline.one_key) {
+ msg("", 0);
+ redraw_cmdline = true;
+ }
}
/// getcmdline() - accept a command line starting with firstc.
@@ -2762,11 +2796,13 @@ char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNU
/// @param[in] xp_context Type of expansion.
/// @param[in] xp_arg User-defined expansion argument.
/// @param[in] highlight_callback Callback used for highlighting user input.
+/// @param[in] one_key Return after one key press for button prompt.
+/// @param[in] mouse_used Set to true when returning after right mouse click.
///
/// @return [allocated] Command line or NULL.
char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id,
const int xp_context, const char *const xp_arg,
- const Callback highlight_callback)
+ const Callback highlight_callback, bool one_key, bool *mouse_used)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
const int msg_col_save = msg_col;
@@ -2787,16 +2823,22 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl
ccline.xp_arg = (char *)xp_arg;
ccline.input_fn = (firstc == '@');
ccline.highlight_callback = highlight_callback;
+ ccline.one_key = one_key;
+ ccline.mouse_used = mouse_used;
+ const bool cmd_silent_saved = cmd_silent;
int msg_silent_saved = msg_silent;
msg_silent = 0;
+ cmd_silent = false; // Want to see the prompt.
char *const ret = (char *)command_line_enter(firstc, 1, 0, false);
+ ccline.redraw_state = kCmdRedrawNone;
if (did_save_ccline) {
restore_cmdline(&save_ccline);
}
msg_silent = msg_silent_saved;
+ cmd_silent = cmd_silent_saved;
// Restore msg_col, the prompt from input() may have changed it.
// But only if called recursively and the commandline is therefore being
// restored to an old one; if not, the input() prompt stays on the screen,
@@ -2820,19 +2862,19 @@ int check_opt_wim(void)
}
for (char *p = p_wim; *p; p++) {
- // Note: Keep this in sync with p_wim_values.
+ // Note: Keep this in sync with opt_wim_values.
for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
return FAIL;
}
if (i == 7 && strncmp(p, "longest", 7) == 0) {
- new_wim_flags[idx] |= WIM_LONGEST;
+ new_wim_flags[idx] |= kOptWimFlagLongest;
} else if (i == 4 && strncmp(p, "full", 4) == 0) {
- new_wim_flags[idx] |= WIM_FULL;
+ new_wim_flags[idx] |= kOptWimFlagFull;
} else if (i == 4 && strncmp(p, "list", 4) == 0) {
- new_wim_flags[idx] |= WIM_LIST;
+ new_wim_flags[idx] |= kOptWimFlagList;
} else if (i == 8 && strncmp(p, "lastused", 8) == 0) {
- new_wim_flags[idx] |= WIM_BUFLASTUSED;
+ new_wim_flags[idx] |= kOptWimFlagLastused;
} else {
return FAIL;
}
@@ -3140,8 +3182,9 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
#define PRINT_ERRMSG(...) \
do { \
+ msg_scroll = true; \
msg_putchar('\n'); \
- msg_printf_hl(HLF_E, __VA_ARGS__); \
+ smsg(HLF_E, __VA_ARGS__); \
printed_errmsg = true; \
} while (0)
bool ret = true;
@@ -3174,11 +3217,9 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
static int prev_prompt_errors = 0;
Callback color_cb = CALLBACK_NONE;
bool can_free_cb = false;
- TryState tstate;
Error err = ERROR_INIT;
const char *err_errmsg = e_intern2;
bool dgc_ret = true;
- bool tl_ret = true;
if (colored_ccline->prompt_id != prev_prompt_id) {
prev_prompt_errors = 0;
@@ -3191,16 +3232,16 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
assert(colored_ccline->input_fn);
color_cb = colored_ccline->highlight_callback;
} else if (colored_ccline->cmdfirstc == ':') {
- try_enter(&tstate);
- err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s");
- dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
- &color_cb);
- tl_ret = try_leave(&tstate, &err);
+ TRY_WRAP(&err, {
+ err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s");
+ dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
+ &color_cb);
+ });
can_free_cb = true;
} else if (colored_ccline->cmdfirstc == '=') {
color_expr_cmdline(colored_ccline, ccline_colors);
}
- if (!tl_ret || !dgc_ret) {
+ if (ERROR_SET(&err) || !dgc_ret) {
goto color_cmdline_error;
}
@@ -3221,20 +3262,22 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
// correct, with msg_col it just misses leading `:`. Since `redraw!` in
// callback lags this is least of the user problems.
//
- // Also using try_enter() because error messages may overwrite typed
+ // Also using TRY_WRAP because error messages may overwrite typed
// command-line which is not expected.
getln_interrupted_highlight = false;
- try_enter(&tstate);
- err_errmsg = N_("E5407: Callback has thrown an exception: %s");
- const int saved_msg_col = msg_col;
- msg_silent++;
- const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
- msg_silent--;
- msg_col = saved_msg_col;
- if (got_int) {
- getln_interrupted_highlight = true;
- }
- if (!try_leave(&tstate, &err) || !cbcall_ret) {
+ bool cbcall_ret = true;
+ TRY_WRAP(&err, {
+ err_errmsg = N_("E5407: Callback has thrown an exception: %s");
+ const int saved_msg_col = msg_col;
+ msg_silent++;
+ cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
+ msg_silent--;
+ msg_col = saved_msg_col;
+ if (got_int) {
+ getln_interrupted_highlight = true;
+ }
+ });
+ if (ERROR_SET(&err) || !cbcall_ret) {
goto color_cmdline_error;
}
if (tv.v_type != VAR_LIST) {
@@ -3350,7 +3393,7 @@ color_cmdline_error:
// when cmdline_star is true.
static void draw_cmdline(int start, int len)
{
- if (!color_cmdline(&ccline)) {
+ if (ccline.cmdbuff == NULL || !color_cmdline(&ccline)) {
return;
}
@@ -3420,8 +3463,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
ui_call_cmdline_show(content, line->cmdpos,
cstr_as_string(charbuf),
cstr_as_string((line->cmdprompt)),
- line->cmdindent,
- line->level);
+ line->cmdindent, line->level, line->hl_id);
if (line->special_char) {
charbuf[0] = line->special_char;
ui_call_cmdline_special_char(cstr_as_string(charbuf),
@@ -3457,8 +3499,7 @@ void ui_ext_cmdline_block_leave(void)
ui_call_cmdline_block_hide();
}
-/// Extra redrawing needed for redraw! and on ui_attach
-/// assumes "redrawcmdline()" will already be invoked
+/// Extra redrawing needed for redraw! and on ui_attach.
void cmdline_screen_cleared(void)
{
if (!ui_has(kUICmdline)) {
@@ -3481,6 +3522,7 @@ void cmdline_screen_cleared(void)
}
line = line->prev_ccline;
}
+ redrawcmd();
}
/// called by ui_flush, do what redraws necessary to keep cmdline updated.
@@ -3493,12 +3535,14 @@ void cmdline_ui_flush(void)
CmdlineInfo *line = &ccline;
while (level > 0 && line) {
if (line->level == level) {
- if (line->redraw_state == kCmdRedrawAll) {
+ CmdRedraw redraw_state = line->redraw_state;
+ line->redraw_state = kCmdRedrawNone;
+ if (redraw_state == kCmdRedrawAll) {
+ cmdline_was_last_drawn = true;
ui_ext_cmdline_show(line);
- } else if (line->redraw_state == kCmdRedrawPos) {
+ } else if (redraw_state == kCmdRedrawPos && cmdline_was_last_drawn) {
ui_call_cmdline_pos(line->cmdpos, line->level);
}
- line->redraw_state = kCmdRedrawNone;
level--;
}
line = line->prev_ccline;
@@ -3866,12 +3910,7 @@ void compute_cmdrow(void)
void cursorcmd(void)
{
- if (cmd_silent) {
- return;
- }
-
- if (ui_has(kUICmdline)) {
- ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos);
+ if (cmd_silent || ui_has(kUICmdline)) {
return;
}
@@ -4473,10 +4512,7 @@ static int open_cmdwin(void)
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
invalidate_botline(curwin);
- if (ui_has(kUICmdline)) {
- ccline.redraw_state = kCmdRedrawNone;
- ui_call_cmdline_hide(ccline.level);
- }
+ ui_ext_cmdline_hide(false);
redraw_later(curwin, UPD_SOME_VALID);
// No Ex mode here!
@@ -4764,9 +4800,6 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
}
}
- const bool cmd_silent_save = cmd_silent;
-
- cmd_silent = false; // Want to see the prompt.
// Only the part of the message after the last NL is considered as
// prompt for the command line, unlsess cmdline is externalized
const char *p = prompt;
@@ -4788,7 +4821,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(),
- xp_type, xp_arg, input_callback);
+ xp_type, xp_arg, input_callback, false, NULL);
ex_normal_busy = save_ex_normal_busy;
callback_free(&input_callback);
@@ -4801,5 +4834,4 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
// Since the user typed this, no need to wait for return.
need_wait_return = false;
msg_didout = false;
- cmd_silent = cmd_silent_save;
}
diff --git a/src/nvim/ex_getln_defs.h b/src/nvim/ex_getln_defs.h
index 584c360450..e05d8f27db 100644
--- a/src/nvim/ex_getln_defs.h
+++ b/src/nvim/ex_getln_defs.h
@@ -65,4 +65,6 @@ struct cmdline_info {
char special_char; ///< last putcmdline char (used for redraws)
bool special_shift; ///< shift of last putcmdline char
CmdRedraw redraw_state; ///< needed redraw for external cmdline
+ bool one_key; ///< return after one key press for button prompt
+ bool *mouse_used; ///< mouse clicked in prompt
};
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 50ee197ef4..ab7585e90a 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -9,10 +9,12 @@
#include <stdio.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/arglist.h"
#include "nvim/arglist_defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/errors.h"
@@ -71,7 +73,7 @@ static int put_view_curpos(FILE *fd, const win_T *wp, char *spaces)
static int ses_winsizes(FILE *fd, bool restore_size, win_T *tab_firstwin)
{
- if (restore_size && (ssop_flags & SSOP_WINSIZE)) {
+ if (restore_size && (ssop_flags & kOptSsopFlagWinsize)) {
int n = 0;
for (win_T *wp = tab_firstwin; wp != NULL; wp = wp->w_next) {
if (!ses_do_win(wp)) {
@@ -198,13 +200,13 @@ static int ses_do_win(win_T *wp)
if (wp->w_buffer->b_fname == NULL
// When 'buftype' is "nofile" can't restore the window contents.
|| (!wp->w_buffer->terminal && bt_nofilename(wp->w_buffer))) {
- return ssop_flags & SSOP_BLANK;
+ return ssop_flags & kOptSsopFlagBlank;
}
if (bt_help(wp->w_buffer)) {
- return ssop_flags & SSOP_HELP;
+ return ssop_flags & kOptSsopFlagHelp;
}
if (bt_terminal(wp->w_buffer)) {
- return ssop_flags & SSOP_TERMINAL;
+ return ssop_flags & kOptSsopFlagTerminal;
}
return true;
}
@@ -257,7 +259,7 @@ static char *ses_get_fname(buf_T *buf, const unsigned *flagp)
// directory is.
if (buf->b_sfname != NULL
&& flagp == &ssop_flags
- && (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR))
+ && (ssop_flags & (kOptSsopFlagCurdir | kOptSsopFlagSesdir))
&& !p_acd
&& !did_lcd) {
return buf->b_sfname;
@@ -289,7 +291,7 @@ static char *ses_escape_fname(char *name, unsigned *flagp)
char *p;
char *sname = home_replace_save(NULL, name);
- // Always SSOP_SLASH: change all backslashes to forward slashes.
+ // Always kOptSsopFlagSlash: change all backslashes to forward slashes.
for (p = sname; *p != NUL; MB_PTR_ADV(p)) {
if (*p == '\\') {
*p = '/';
@@ -328,7 +330,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// Always restore cursor position for ":mksession". For ":mkview" only
// when 'viewoptions' contains "cursor".
- bool do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR);
+ bool do_cursor = (flagp == &ssop_flags || *flagp & kOptSsopFlagCursor);
// Local argument list.
if (wp->w_alist == &global_alist) {
@@ -336,7 +338,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
} else {
if (ses_arglist(fd, "arglocal", &wp->w_alist->al_ga,
flagp == &vop_flags
- || !(*flagp & SSOP_CURDIR)
+ || !(*flagp & kOptSsopFlagCurdir)
|| wp->w_localdir != NULL, flagp) == FAIL) {
return FAIL;
}
@@ -417,7 +419,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
&& *alt->b_fname != NUL
&& alt->b_p_bl
// do not set balt if buffer is terminal and "terminal" is not set in options
- && !(bt_terminal(alt) && !(ssop_flags & SSOP_TERMINAL))
+ && !(bt_terminal(alt) && !(ssop_flags & kOptSsopFlagTerminal))
&& (fputs("balt ", fd) < 0
|| ses_fname(fd, alt, flagp, true) == FAIL)) {
return FAIL;
@@ -425,7 +427,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
}
// Local mappings and abbreviations.
- if ((*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS))
+ if ((*flagp & (kOptSsopFlagOptions | kOptSsopFlagLocaloptions))
&& makemap(fd, wp->w_buffer) == FAIL) {
return FAIL;
}
@@ -438,10 +440,10 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
win_T *save_curwin = curwin;
curwin = wp;
curbuf = curwin->w_buffer;
- if (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) {
+ if (*flagp & (kOptSsopFlagOptions | kOptSsopFlagLocaloptions)) {
f = makeset(fd, OPT_LOCAL,
- flagp == &vop_flags || !(*flagp & SSOP_OPTIONS));
- } else if (*flagp & SSOP_FOLDS) {
+ flagp == &vop_flags || !(*flagp & kOptSsopFlagOptions));
+ } else if (*flagp & kOptSsopFlagFolds) {
f = makefoldset(fd);
} else {
f = OK;
@@ -453,7 +455,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
}
// Save Folds when 'buftype' is empty and for help files.
- if ((*flagp & SSOP_FOLDS)
+ if ((*flagp & kOptSsopFlagFolds)
&& wp->w_buffer->b_ffname != NULL
&& (bt_normal(wp->w_buffer)
|| bt_help(wp->w_buffer))) {
@@ -516,7 +518,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// Local directory, if the current flag is not view options or the "curdir"
// option is included.
if (wp->w_localdir != NULL
- && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) {
+ && (flagp != &vop_flags || (*flagp & kOptSsopFlagCurdir))) {
if (fputs("lcd ", fd) < 0
|| ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
|| fprintf(fd, "\n") < 0) {
@@ -574,7 +576,7 @@ static int store_session_globals(FILE *fd)
/// Writes commands for restoring the current buffers, for :mksession.
///
-/// Legacy 'sessionoptions'/'viewoptions' flags SSOP_UNIX, SSOP_SLASH are
+/// Legacy 'sessionoptions'/'viewoptions' flags kOptSsopFlagUnix, kOptSsopFlagSlash are
/// always enabled.
///
/// @param dirnow Current directory name
@@ -591,13 +593,13 @@ static int makeopens(FILE *fd, char *dirnow)
int cur_arg_idx = 0;
int next_arg_idx = 0;
- if (ssop_flags & SSOP_BUFFERS) {
+ if (ssop_flags & kOptSsopFlagBuffers) {
only_save_windows = false; // Save ALL buffers
}
// Begin by setting v:this_session, and then other sessionable variables.
PUTLINE_FAIL("let v:this_session=expand(\"<sfile>:p\")");
- if (ssop_flags & SSOP_GLOBALS) {
+ if (ssop_flags & kOptSsopFlagGlobals) {
if (store_session_globals(fd) == FAIL) {
return FAIL;
}
@@ -605,15 +607,15 @@ static int makeopens(FILE *fd, char *dirnow)
// Close all windows and tabs but one.
PUTLINE_FAIL("silent only");
- if ((ssop_flags & SSOP_TABPAGES)
+ if ((ssop_flags & kOptSsopFlagTabpages)
&& put_line(fd, "silent tabonly") == FAIL) {
return FAIL;
}
// Now a :cd command to the session directory or the current directory
- if (ssop_flags & SSOP_SESDIR) {
+ if (ssop_flags & kOptSsopFlagSesdir) {
PUTLINE_FAIL("exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')");
- } else if (ssop_flags & SSOP_CURDIR) {
+ } else if (ssop_flags & kOptSsopFlagCurdir) {
char *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) {
@@ -637,7 +639,7 @@ static int makeopens(FILE *fd, char *dirnow)
}
// save 'shortmess' if not storing options
- if ((ssop_flags & SSOP_OPTIONS) == 0) {
+ if ((ssop_flags & kOptSsopFlagOptions) == 0) {
PUTLINE_FAIL("let s:shortmess_save = &shortmess");
}
@@ -654,14 +656,13 @@ static int makeopens(FILE *fd, char *dirnow)
// can be disrupted by prior `edit` or `tabedit` calls).
FOR_ALL_BUFFERS(buf) {
if (!(only_save_windows && buf->b_nwindows == 0)
- && !(buf->b_help && !(ssop_flags & SSOP_HELP))
- && !(bt_terminal(buf) && !(ssop_flags & SSOP_TERMINAL))
+ && !(buf->b_help && !(ssop_flags & kOptSsopFlagHelp))
+ && !(bt_terminal(buf) && !(ssop_flags & kOptSsopFlagTerminal))
&& buf->b_fname != NULL
&& buf->b_p_bl) {
if (fprintf(fd, "badd +%" PRId64 " ",
- buf->b_wininfo == NULL
- ? 1
- : (int64_t)buf->b_wininfo->wi_mark.mark.lnum) < 0
+ kv_size(buf->b_wininfo) == 0
+ ? 1 : (int64_t)kv_A(buf->b_wininfo, 0)->wi_mark.mark.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
return FAIL;
}
@@ -670,11 +671,11 @@ static int makeopens(FILE *fd, char *dirnow)
// the global argument list
if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
- !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) {
+ !(ssop_flags & kOptSsopFlagCurdir), &ssop_flags) == FAIL) {
return FAIL;
}
- if (ssop_flags & SSOP_RESIZE) {
+ if (ssop_flags & kOptSsopFlagResize) {
// Note: after the restore we still check it worked!
if (fprintf(fd, "set lines=%" PRId64 " columns=%" PRId64 "\n",
(int64_t)Rows, (int64_t)Columns) < 0) {
@@ -692,7 +693,7 @@ static int makeopens(FILE *fd, char *dirnow)
restore_stal = true;
}
- if ((ssop_flags & SSOP_TABPAGES)) {
+ if ((ssop_flags & kOptSsopFlagTabpages)) {
// "tabpages" is in 'sessionoptions': Similar to ses_win_rec() below,
// populate the tab pages first so later local options won't be copied
// to the new tabs.
@@ -720,7 +721,7 @@ static int makeopens(FILE *fd, char *dirnow)
// 'sessionoptions'.
// Don't use goto_tabpage(), it may change directory and trigger
// autocommands.
- if ((ssop_flags & SSOP_TABPAGES)) {
+ if ((ssop_flags & kOptSsopFlagTabpages)) {
if (tp == curtab) {
tab_firstwin = firstwin;
tab_topframe = topframe;
@@ -820,7 +821,7 @@ static int makeopens(FILE *fd, char *dirnow)
// Restore the tab-local working directory if specified
// Do this before the windows, so that the window-local directory can
// override the tab-local directory.
- if ((ssop_flags & SSOP_CURDIR) && tp->tp_localdir != NULL) {
+ if ((ssop_flags & kOptSsopFlagCurdir) && tp->tp_localdir != NULL) {
if (fputs("tcd ", fd) < 0
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| put_eol(fd) == FAIL) {
@@ -862,12 +863,12 @@ static int makeopens(FILE *fd, char *dirnow)
// Don't continue in another tab page when doing only the current one
// or when at the last tab page.
- if (!(ssop_flags & SSOP_TABPAGES)) {
+ if (!(ssop_flags & kOptSsopFlagTabpages)) {
break;
}
}
- if (ssop_flags & SSOP_TABPAGES) {
+ if (ssop_flags & kOptSsopFlagTabpages) {
if (fprintf(fd, "tabnext %d\n", tabpage_index(curtab)) < 0) {
return FAIL;
}
@@ -894,7 +895,7 @@ static int makeopens(FILE *fd, char *dirnow)
}
// Restore 'shortmess'.
- if (ssop_flags & SSOP_OPTIONS) {
+ if (ssop_flags & kOptSsopFlagOptions) {
if (fprintf(fd, "set shortmess=%s\n", p_shm) < 0) {
return FAIL;
}
@@ -937,8 +938,8 @@ void ex_loadview(exarg_T *eap)
/// ":mkexrc", ":mkvimrc", ":mkview", ":mksession".
///
/// Legacy 'sessionoptions'/'viewoptions' flags are always enabled:
-/// - SSOP_UNIX: line-endings are LF
-/// - SSOP_SLASH: filenames are written with "/" slash
+/// - kOptSsopFlagUnix: line-endings are LF
+/// - kOptSsopFlagSlash: filenames are written with "/" slash
void ex_mkrc(exarg_T *eap)
{
bool view_session = false; // :mkview, :mksession
@@ -1002,10 +1003,10 @@ void ex_mkrc(exarg_T *eap)
}
if (!view_session || (eap->cmdidx == CMD_mksession
- && (*flagp & SSOP_OPTIONS))) {
+ && (*flagp & kOptSsopFlagOptions))) {
int flags = OPT_GLOBAL;
- if (eap->cmdidx == CMD_mksession && (*flagp & SSOP_SKIP_RTP)) {
+ if (eap->cmdidx == CMD_mksession && (*flagp & kOptSsopFlagSkiprtp)) {
flags |= OPT_SKIPRTP;
}
failed |= (makemap(fd, NULL) == FAIL
@@ -1028,12 +1029,12 @@ void ex_mkrc(exarg_T *eap)
|| os_chdir(dirnow) != 0) {
*dirnow = NUL;
}
- if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
+ if (*dirnow != NUL && (ssop_flags & kOptSsopFlagSesdir)) {
if (vim_chdirfile(fname, kCdCauseOther) == OK) {
shorten_fnames(true);
}
} else if (*dirnow != NUL
- && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) {
+ && (ssop_flags & kOptSsopFlagCurdir) && globaldir != NULL) {
if (os_chdir(globaldir) == 0) {
shorten_fnames(true);
}
@@ -1042,8 +1043,8 @@ void ex_mkrc(exarg_T *eap)
failed |= (makeopens(fd, dirnow) == FAIL);
// restore original dir
- if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR)
- || ((ssop_flags & SSOP_CURDIR) && globaldir !=
+ if (*dirnow != NUL && ((ssop_flags & kOptSsopFlagSesdir)
+ || ((ssop_flags & kOptSsopFlagCurdir) && globaldir !=
NULL))) {
if (os_chdir(dirnow) != 0) {
emsg(_(e_prev_dir));
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index c20c7dea23..7e6dfb8dea 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -95,6 +95,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
MTKey mark = { { row, col }, ns_id, id, flags, decor.data };
marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity);
+ decor_state_invalidate(buf);
revised:
if (decor_flags || decor.ext) {
@@ -111,10 +112,10 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
{
MarkTreeIter itr[1] = { 0 };
MTKey key = marktree_lookup(buf->b_marktree, mark, itr);
- bool move = key.pos.row >= 0 && (key.pos.row != row || key.pos.col != col);
- // Already valid keys were being revalidated, presumably when encountering a
- // SavePos from a modified mark. Avoid adding that to the decor again.
- invalid = invalid && mt_invalid(key);
+ bool move = key.pos.row != row || key.pos.col != col;
+ if (key.pos.row < 0 || (!move && !invalid)) {
+ return; // Mark was deleted or no change needed
+ }
// Only the position before undo needs to be redrawn here,
// as the position after undo should be marked as changed.
@@ -124,13 +125,16 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
int row1 = 0;
int row2 = 0;
+ MarkTreeIter altitr[1] = { *itr };
+ MTKey alt = marktree_get_alt(buf->b_marktree, key, altitr);
+
if (invalid) {
mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID;
- marktree_revise_meta(buf->b_marktree, itr, key);
- } else if (move && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
- MTPos end = marktree_get_altpos(buf->b_marktree, key, NULL);
- row1 = MIN(end.row, MIN(key.pos.row, row));
- row2 = MAX(end.row, MAX(key.pos.row, row));
+ mt_itr_rawkey(altitr).flags &= (uint16_t) ~MT_FLAG_INVALID;
+ marktree_revise_meta(buf->b_marktree, mt_end(key) ? altitr : itr, mt_end(key) ? alt : key);
+ } else if (!mt_invalid(key) && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
+ row1 = MIN(alt.pos.row, MIN(key.pos.row, row));
+ row2 = MAX(alt.pos.row, MAX(key.pos.row, row));
buf_signcols_count_range(buf, row1, row2, 0, kTrue);
}
@@ -139,9 +143,8 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
}
if (invalid) {
- row2 = mt_paired(key) ? marktree_get_altpos(buf->b_marktree, key, NULL).row : row;
- buf_put_decor(buf, mt_decor(key), row, row2);
- } else if (move && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
+ buf_put_decor(buf, mt_decor(key), MIN(row, key.pos.row), MAX(row, key.pos.row));
+ } else if (!mt_invalid(key) && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
buf_signcols_count_range(buf, row1, row2, 0, kNone);
}
}
@@ -184,6 +187,8 @@ void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
}
}
+ decor_state_invalidate(buf);
+
// TODO(bfredl): delete it from current undo header, opportunistically?
}
@@ -237,6 +242,10 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
}
}
+ if (marks_cleared_any) {
+ decor_state_invalidate(buf);
+ }
+
return marks_cleared_any;
}
@@ -367,16 +376,28 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
marktree_itr_get(buf->b_marktree, (int32_t)l_row, l_col, itr);
while (true) {
MTKey mark = marktree_itr_current(itr);
- if (mark.pos.row < 0
- || mark.pos.row > u_row
- || (mark.pos.row == u_row && mark.pos.col > u_col)) {
+ if (mark.pos.row < 0 || mark.pos.row > u_row) {
break;
}
+ bool copy = true;
+ // No need to copy left gravity marks at the beginning of the range,
+ // and right gravity marks at the end of the range, unless invalidated.
+ if (mark.pos.row == l_row && mark.pos.col - !mt_right(mark) < l_col) {
+ copy = false;
+ } else if (mark.pos.row == u_row) {
+ if (mark.pos.col > u_col + 1) {
+ break;
+ } else if (mark.pos.col + mt_right(mark) > u_col) {
+ copy = false;
+ }
+ }
+
bool invalidated = false;
// Invalidate/delete mark
if (!only_copy && !mt_invalid(mark) && mt_invalidate(mark) && !mt_end(mark)) {
- MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
+ MarkTreeIter enditr[1] = { *itr };
+ MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, enditr);
// Invalidate unpaired marks in deleted lines and paired marks whose entire
// range has been deleted.
if ((!mt_paired(mark) && mark.pos.row < u_row)
@@ -388,8 +409,10 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
extmark_del(buf, itr, mark, true);
continue;
} else {
+ copy = true;
invalidated = true;
mt_itr_rawkey(itr).flags |= MT_FLAG_INVALID;
+ mt_itr_rawkey(enditr).flags |= MT_FLAG_INVALID;
marktree_revise_meta(buf->b_marktree, itr, mark);
buf_decor_remove(buf, mark.pos.row, endpos.row, mark.pos.col, mt_decor(mark), false);
}
@@ -397,7 +420,7 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
}
// Push mark to undo header
- if (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark))) {
+ if (copy && (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark)))) {
ExtmarkSavePos pos = {
.mark = mt_lookup_key(mark),
.invalidated = invalidated,
@@ -541,10 +564,8 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
if (old_row > 0 || old_col > 0) {
// Copy and invalidate marks that would be effected by delete
- // TODO(bfredl): Be "smart" about gravity here, left-gravity at the
- // beginning and right-gravity at the end need not be preserved.
- // Also be smart about marks that already have been saved (important for
- // merge!)
+ // TODO(bfredl): Be smart about marks that already have been
+ // saved (important for merge!)
int end_row = start_row + old_row;
int end_col = (old_row ? 0 : start_col) + old_col;
u_header_T *uhp = u_force_get_undo_header(buf);
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index d183978d2d..1e6153bf8d 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -41,6 +41,7 @@
// functions.
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
@@ -52,6 +53,7 @@
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/errors.h"
@@ -67,6 +69,7 @@
#include "nvim/message.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
@@ -76,7 +79,6 @@
#include "nvim/path.h"
#include "nvim/strings.h"
#include "nvim/vim_defs.h"
-#include "nvim/window.h"
static char *ff_expand_buffer = NULL; // used for expanding filenames
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index fc7fabc009..61b252f823 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -38,7 +38,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/iconv_defs.h"
#include "nvim/log.h"
@@ -55,7 +54,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
@@ -3278,7 +3276,11 @@ static void vim_mktempdir(void)
expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64);
if (!os_isdir(tmp)) {
if (strequal("$TMPDIR", temp_dirs[i])) {
- WLOG("$TMPDIR tempdir not a directory (or does not exist): %s", tmp);
+ if (!os_getenv("TMPDIR")) {
+ DLOG("$TMPDIR is unset");
+ } else {
+ WLOG("$TMPDIR tempdir not a directory (or does not exist): \"%s\"", tmp);
+ }
}
continue;
}
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index c9699cb161..b59933d600 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1658,7 +1658,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker
if (*cms != NUL) {
// Also delete 'commentstring' if it matches.
char *cms2 = strstr(cms, "%s");
- if (p - line >= cms2 - cms
+ if (cms2 != NULL && p - line >= cms2 - cms
&& strncmp(p - (cms2 - cms), cms, (size_t)(cms2 - cms)) == 0
&& strncmp(p + len, cms2 + 2, strlen(cms2 + 2)) == 0) {
p -= cms2 - cms;
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 4f8ba30522..a8d15a1fa8 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -7,12 +7,13 @@
#include "nvim/garray.h"
#include "nvim/log.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/path.h"
#include "nvim/strings.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "garray.c.generated.h"
+# include "garray.c.generated.h" // IWYU pragma: keep
#endif
/// Clear an allocated growing array.
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index ed6e30ea10..890c260843 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -1,10 +1,37 @@
-local lpeg = vim.lpeg
-
-- lpeg grammar for building api metadata from a set of header files. It
-- ignores comments and preprocessor commands and parses a very small subset
-- of C prototypes with a limited set of types
-local P, R, S = lpeg.P, lpeg.R, lpeg.S
-local C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg
+
+--- @class nvim.c_grammar.Proto
+--- @field [1] 'proto'
+--- @field pos integer
+--- @field endpos integer
+--- @field fast boolean
+--- @field name string
+--- @field return_type string
+--- @field parameters [string, string][]
+--- @field static true?
+--- @field inline true?
+
+--- @class nvim.c_grammar.Preproc
+--- @field [1] 'preproc'
+--- @field content string
+
+--- @class nvim.c_grammar.Empty
+--- @field [1] 'empty'
+
+--- @alias nvim.c_grammar.result
+--- | nvim.c_grammar.Proto
+--- | nvim.c_grammar.Preproc
+--- | nvim.c_grammar.Empty
+
+--- @class nvim.c_grammar
+--- @field match fun(self, input: string): nvim.c_grammar.result[]
+
+local lpeg = vim.lpeg
+
+local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
+local C, Ct, Cc, Cg, Cp = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cp
--- @param pat vim.lpeg.Pattern
local function rep(pat)
@@ -21,95 +48,253 @@ local function opt(pat)
return pat ^ -1
end
-local any = P(1) -- (consume one character)
+local any = P(1)
local letter = R('az', 'AZ') + S('_$')
local num = R('09')
local alpha = letter + num
local nl = P('\r\n') + P('\n')
-local not_nl = any - nl
local space = S(' \t')
+local str = P('"') * rep((P('\\') * any) + (1 - P('"'))) * P('"')
+local char = P("'") * (any - P("'")) * P("'")
local ws = space + nl
-local fill = rep(ws)
-local c_comment = P('//') * rep(not_nl)
-local cdoc_comment = P('///') * opt(Ct(Cg(rep(space) * rep(not_nl), 'comment')))
-local c_preproc = P('#') * rep(not_nl)
-local dllexport = P('DLLEXPORT') * rep1(ws)
-
-local typed_container = ((P('ArrayOf(') + P('DictOf(') + P('Dict(')) * rep1(any - P(')')) * P(')'))
-
-local c_id = (typed_container + (letter * rep(alpha)))
-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(opt(P('const ')) * c_id * rep1(ws) * rep1(P('*')))
- + (C(c_id) * rep1(ws))
+local wb = #-alpha -- word boundary
+local id = letter * rep(alpha)
+
+local comment_inline = P('/*') * rep(1 - P('*/')) * P('*/')
+local comment = P('//') * rep(1 - nl) * nl
+local preproc = Ct(Cc('preproc') * P('#') * Cg(rep(1 - nl) * nl, 'content'))
+
+local fill = rep(ws + comment_inline + comment + preproc)
+
+--- @param s string
+--- @return vim.lpeg.Pattern
+local function word(s)
+ return fill * P(s) * wb * fill
+end
+
+--- @param x vim.lpeg.Pattern
+local function comma1(x)
+ return x * rep(fill * P(',') * fill * x)
+end
+
+--- @param v string
+local function Pf(v)
+ return fill * P(v) * fill
+end
+
+--- @param x vim.lpeg.Pattern
+local function paren(x)
+ return P('(') * fill * x * fill * P(')')
+end
+
+local cdoc_comment = P('///') * opt(Ct(Cg(rep(space) * rep(1 - nl), 'comment')))
+
+local braces = P({
+ 'S',
+ A = comment_inline + comment + preproc + str + char + (any - S('{}')),
+ S = P('{') * rep(V('A')) * rep(V('S') + V('A')) * P('}'),
+})
+
+-- stylua: ignore start
+local typed_container = P({
+ 'S',
+ S = (
+ (P('Union') * paren(comma1(V('ID'))))
+ + (P('ArrayOf') * paren(id * opt(P(',') * fill * rep1(num))))
+ + (P('DictOf') * paren(id))
+ + (P('LuaRefOf') * paren(
+ paren(comma1((V('ID') + str) * rep1(ws) * opt(P('*')) * id))
+ * opt(P(',') * fill * opt(P('*')) * V('ID'))
+ ))
+ + (P('Dict') * paren(id))),
+ ID = V('S') + id,
+})
+-- stylua: ignore end
+
+local ptr_mod = word('restrict') + word('__restrict') + word('const')
+local opt_ptr = rep(Pf('*') * opt(ptr_mod))
+
+--- @param name string
+--- @param var string
+--- @return vim.lpeg.Pattern
+local function attr(name, var)
+ return Cg((P(name) * Cc(true)), var)
+end
+
+--- @param name string
+--- @param var string
+--- @return vim.lpeg.Pattern
+local function attr_num(name, var)
+ return Cg((P(name) * paren(C(rep1(num)))), var)
+end
+
+local fattr = (
+ attr_num('FUNC_API_SINCE', 'since')
+ + attr_num('FUNC_API_DEPRECATED_SINCE', 'deprecated_since')
+ + attr('FUNC_API_FAST', 'fast')
+ + attr('FUNC_API_RET_ALLOC', 'ret_alloc')
+ + attr('FUNC_API_NOEXPORT', 'noexport')
+ + attr('FUNC_API_REMOTE_ONLY', 'remote_only')
+ + attr('FUNC_API_LUA_ONLY', 'lua_only')
+ + attr('FUNC_API_TEXTLOCK_ALLOW_CMDWIN', 'textlock_allow_cmdwin')
+ + attr('FUNC_API_TEXTLOCK', 'textlock')
+ + attr('FUNC_API_REMOTE_IMPL', 'remote_impl')
+ + attr('FUNC_API_COMPOSITOR_IMPL', 'compositor_impl')
+ + attr('FUNC_API_CLIENT_IMPL', 'client_impl')
+ + attr('FUNC_API_CLIENT_IGNORE', 'client_ignore')
+ + (P('FUNC_') * rep(alpha) * opt(fill * paren(rep(1 - P(')') * any))))
)
-local c_type = (C(c_void) * (ws ^ 1)) + c_param_type
-local c_param = Ct(c_param_type * C(c_id))
-local c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0)
-local c_params = Ct(c_void + c_param_list)
+local void = P('void') * wb
-local impl_line = (any - P('}')) * opt(rep(not_nl)) * nl
+local api_param_type = (
+ (word('Error') * opt_ptr * Cc('error'))
+ + (word('Arena') * opt_ptr * Cc('arena'))
+ + (word('lua_State') * opt_ptr * Cc('lstate'))
+)
-local ignore_line = rep1(not_nl) * nl
+local ctype = C(
+ opt(word('const'))
+ * (
+ typed_container
+ -- 'unsigned' is a type modifier, and a type itself
+ + (word('unsigned char') + word('unsigned'))
+ + (word('struct') * fill * id)
+ + id
+ )
+ * opt(word('const'))
+ * opt_ptr
+)
+
+local return_type = (C(void) * fill) + ctype
+-- stylua: ignore start
+local params = Ct(
+ (void * #P(')'))
+ + comma1(Ct(
+ (api_param_type + ctype)
+ * fill
+ * C(id)
+ * rep(Pf('[') * rep(alpha) * Pf(']'))
+ * rep(fill * fattr)
+ ))
+ * opt(Pf(',') * P('...'))
+)
+-- stylua: ignore end
+
+local ignore_line = rep1(1 - nl) * nl
local empty_line = Ct(Cc('empty') * nl * nl)
-local c_proto = Ct(
- Cc('proto')
- * opt(dllexport)
- * opt(Cg(P('static') * fill * Cc(true), 'static'))
- * Cg(c_type, 'return_type')
- * Cg(c_id, 'name')
- * fill
- * (P('(') * fill * Cg(c_params, 'parameters') * fill * P(')'))
- * Cg(Cc(false), 'fast')
- * (fill * Cg((P('FUNC_API_SINCE(') * C(rep1(num))) * P(')'), 'since') ^ -1)
- * (fill * Cg((P('FUNC_API_DEPRECATED_SINCE(') * C(rep1(num))) * P(')'), 'deprecated_since') ^ -1)
- * (fill * Cg((P('FUNC_API_FAST') * Cc(true)), 'fast') ^ -1)
- * (fill * Cg((P('FUNC_API_RET_ALLOC') * Cc(true)), 'ret_alloc') ^ -1)
- * (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1)
- * (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1)
- * (fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1)
- * (fill * (Cg(P('FUNC_API_TEXTLOCK_ALLOW_CMDWIN') * Cc(true), 'textlock_allow_cmdwin') + Cg(
- P('FUNC_API_TEXTLOCK') * Cc(true),
- 'textlock'
- )) ^ -1)
- * (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_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(';') + (P('{') * nl + (impl_line ^ 0) * P('}')))
+local proto_name = opt_ptr * fill * id
+
+-- __inline is used in MSVC
+local decl_mod = (
+ Cg(word('static') * Cc(true), 'static')
+ + Cg((word('inline') + word('__inline')) * Cc(true), 'inline')
)
-local dict_key = P('DictKey(') * Cg(rep1(any - P(')')), 'dict_key') * P(')')
-local keyset_field =
- Ct(Cg(c_id, 'type') * ws * Cg(c_id, 'name') * fill * (dict_key ^ -1) * fill * P(';') * fill)
-local c_keyset = Ct(
- P('typedef')
- * ws
- * P('struct')
+local proto = Ct(
+ Cg(Cp(), 'pos')
+ * Cc('proto')
+ * -#P('typedef')
+ * #alpha
+ * opt(P('DLLEXPORT') * rep1(ws))
+ * rep(decl_mod)
+ * Cg(return_type, 'return_type')
* fill
- * P('{')
+ * Cg(proto_name, 'name')
* fill
- * Cg(Ct(keyset_field ^ 1), 'fields')
- * P('}')
- * fill
- * P('Dict')
+ * paren(Cg(params, 'parameters'))
+ * Cg(Cc(false), 'fast')
+ * rep(fill * fattr)
+ * Cg(Cp(), 'endpos')
+ * (fill * (S(';') + braces))
+)
+
+local keyset_field = Ct(
+ Cg(ctype, 'type')
* fill
- * P('(')
- * Cg(c_id, 'keyset_name')
+ * Cg(id, 'name')
* fill
- * P(')')
- * P(';')
+ * opt(P('DictKey') * paren(Cg(rep1(1 - P(')')), 'dict_key')))
+ * Pf(';')
)
-local grammar = Ct(
- rep1(empty_line + c_proto + cdoc_comment + c_comment + c_preproc + ws + c_keyset + ignore_line)
+local keyset = Ct(
+ P('typedef')
+ * word('struct')
+ * Pf('{')
+ * Cg(Ct(rep1(keyset_field)), 'fields')
+ * Pf('}')
+ * P('Dict')
+ * paren(Cg(id, 'keyset_name'))
+ * Pf(';')
)
-return { grammar = grammar, typed_container = typed_container }
+
+local grammar =
+ Ct(rep1(empty_line + proto + cdoc_comment + comment + preproc + ws + keyset + ignore_line))
+
+if arg[1] == '--test' then
+ for i, t in ipairs({
+ 'void multiqueue_put_event(MultiQueue *self, Event event) {} ',
+ 'void *xmalloc(size_t size) {} ',
+ {
+ 'struct tm *os_localtime_r(const time_t *restrict clock,',
+ ' struct tm *restrict result) FUNC_ATTR_NONNULL_ALL {}',
+ },
+ {
+ '_Bool',
+ '# 163 "src/nvim/event/multiqueue.c"',
+ ' multiqueue_empty(MultiQueue *self)',
+ '{}',
+ },
+ 'const char *find_option_end(const char *arg, OptIndex *opt_idxp) {}',
+ 'bool semsg(const char *const fmt, ...) {}',
+ 'int32_t utf_ptr2CharInfo_impl(uint8_t const *p, uintptr_t const len) {}',
+ 'void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED) {}',
+ 'static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name) {}',
+ 'unsigned get_bkc_flags(buf_T *buf) {}',
+ 'char *xstpcpy(char *restrict dst, const char *restrict src) {}',
+ 'bool try_leave(const TryState *const tstate, Error *const err) {}',
+ 'void api_set_error(ErrorType errType) {}',
+ {
+ 'void nvim_subscribe(uint64_t channel_id, String event)',
+ 'FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY',
+ '{}',
+ },
+
+ -- Do not consume leading preproc statements
+ {
+ '#line 1 "D:/a/neovim/neovim/src\\nvim/mark.h"',
+ 'static __inline int mark_global_index(const char name)',
+ ' FUNC_ATTR_CONST',
+ '{}',
+ },
+ {
+ '',
+ '#line 1 "D:/a/neovim/neovim/src\\nvim/mark.h"',
+ 'static __inline int mark_global_index(const char name)',
+ '{}',
+ },
+ {
+ 'size_t xstrlcpy(char *__restrict dst, const char *__restrict src, size_t dsize)',
+ ' FUNC_ATTR_NONNULL_ALL',
+ ' {}',
+ },
+ }) do
+ if type(t) == 'table' then
+ t = table.concat(t, '\n') .. '\n'
+ end
+ t = t:gsub(' +', ' ')
+ local r = grammar:match(t)
+ if not r then
+ print('Test ' .. i .. ' failed')
+ print(' |' .. table.concat(vim.split(t, '\n'), '\n |'))
+ end
+ end
+end
+
+return {
+ grammar = grammar --[[@as nvim.c_grammar]],
+ typed_container = typed_container,
+}
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index a78f746fee..c987037324 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -1,3 +1,10 @@
+-- Example (manual) invocation:
+--
+-- make
+-- cp build/nvim_version.lua src/nvim/
+-- cd src/nvim
+-- nvim -l generators/gen_api_dispatch.lua "../../build/src/nvim/auto/api/private/dispatch_wrappers.generated.h" "../../build/src/nvim/auto/api/private/api_metadata.generated.h" "../../build/funcs_metadata.mpack" "../../build/src/nvim/auto/lua_api_c_bindings.generated.h" "../../build/src/nvim/auto/keysets_defs.generated.h" "../../build/ui_metadata.mpack" "../../build/cmake.config/auto/versiondef_git.h" "./api/autocmd.h" "./api/buffer.h" "./api/command.h" "./api/deprecated.h" "./api/extmark.h" "./api/keysets_defs.h" "./api/options.h" "./api/tabpage.h" "./api/ui.h" "./api/vim.h" "./api/vimscript.h" "./api/win_config.h" "./api/window.h" "../../build/include/api/autocmd.h.generated.h" "../../build/include/api/buffer.h.generated.h" "../../build/include/api/command.h.generated.h" "../../build/include/api/deprecated.h.generated.h" "../../build/include/api/extmark.h.generated.h" "../../build/include/api/options.h.generated.h" "../../build/include/api/tabpage.h.generated.h" "../../build/include/api/ui.h.generated.h" "../../build/include/api/vim.h.generated.h" "../../build/include/api/vimscript.h.generated.h" "../../build/include/api/win_config.h.generated.h" "../../build/include/api/window.h.generated.h"
+
local mpack = vim.mpack
local hashy = require 'generators.hashy'
@@ -8,7 +15,7 @@ assert(#arg >= pre_args)
local dispatch_outputf = arg[1]
-- output h file with packed metadata (api_metadata.generated.h)
local api_metadata_outputf = arg[2]
--- output metadata mpack file, for use by other build scripts (api_metadata.mpack)
+-- output metadata mpack file, for use by other build scripts (funcs_metadata.mpack)
local mpack_outputf = arg[3]
local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c
local keysets_outputf = arg[5] -- keysets_defs.generated.h
@@ -235,7 +242,7 @@ for x in string.gmatch(ui_options_text, '"([a-z][a-z_]+)"') do
table.insert(ui_options, x)
end
-local version = require 'nvim_version'
+local version = require 'nvim_version' -- `build/nvim_version.lua` file.
local git_version = io.open(git_version_inputf):read '*a'
local version_build = string.match(git_version, '#define NVIM_VERSION_BUILD "([^"]+)"') or vim.NIL
@@ -266,10 +273,7 @@ fixdict(1 + #version)
for _, item in ipairs(version) do
-- NB: all items are mandatory. But any error will be less confusing
-- with placeholder vim.NIL (than invalid mpack data)
- local val = item[2]
- if val == nil then
- val = vim.NIL
- end
+ local val = item[2] == nil and vim.NIL or item[2]
put(item[1], val)
end
put('build', version_build)
@@ -347,12 +351,16 @@ for _, k in ipairs(keysets) do
local function typename(type)
if type == 'HLGroupID' then
return 'kObjectTypeInteger'
+ elseif not type or vim.startswith(type, 'Union') then
+ return 'kObjectTypeNil'
+ elseif vim.startswith(type, 'LuaRefOf') then
+ return 'kObjectTypeLuaRef'
elseif type == 'StringArray' then
return 'kUnpackTypeStringArray'
- elseif type ~= nil then
- return 'kObjectType' .. type
+ elseif vim.startswith(type, 'ArrayOf') then
+ return 'kObjectTypeArray'
else
- return 'kObjectTypeNil'
+ return 'kObjectType' .. type
end
end
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 30a83330eb..a3bb76cb91 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -98,7 +98,7 @@ local function call_ui_event_method(output, ev)
end
events = vim.tbl_filter(function(ev)
- return ev[1] ~= 'empty'
+ return ev[1] ~= 'empty' and ev[1] ~= 'preproc'
end, events)
for i = 1, #events do
diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua
index 2ec0e9ab68..6e1ea92572 100644
--- a/src/nvim/generators/gen_declarations.lua
+++ b/src/nvim/generators/gen_declarations.lua
@@ -1,136 +1,105 @@
-local fname = arg[1]
-local static_fname = arg[2]
-local non_static_fname = arg[3]
-local preproc_fname = arg[4]
-local static_basename = arg[5]
-
-local lpeg = vim.lpeg
-
-local fold = function(func, ...)
- local result = nil
- for _, v in ipairs({ ... }) do
- if result == nil then
- result = v
- else
- result = func(result, v)
+local grammar = require('generators.c_grammar').grammar
+
+--- @param fname string
+--- @return string?
+local function read_file(fname)
+ local f = io.open(fname, 'r')
+ if not f then
+ return
+ end
+ local contents = f:read('*a')
+ f:close()
+ return contents
+end
+
+--- @param fname string
+--- @param contents string[]
+local function write_file(fname, contents)
+ local contents_s = table.concat(contents, '\n') .. '\n'
+ local fcontents = read_file(fname)
+ if fcontents == contents_s then
+ return
+ end
+ local f = assert(io.open(fname, 'w'))
+ f:write(contents_s)
+ f:close()
+end
+
+--- @param fname string
+--- @param non_static_fname string
+--- @return string? non_static
+local function add_iwyu_non_static(fname, non_static_fname)
+ if fname:find('.*/src/nvim/.*%.c$') then
+ -- Add an IWYU pragma comment if the corresponding .h file exists.
+ local header_fname = fname:sub(1, -3) .. '.h'
+ local header_f = io.open(header_fname, 'r')
+ if header_f then
+ header_f:close()
+ return (header_fname:gsub('.*/src/nvim/', 'nvim/'))
end
+ elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then
+ return 'nvim/api/private/dispatch.h'
+ elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then
+ return 'nvim/ui.h'
+ elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then
+ return 'nvim/ui_client.h'
+ elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then
+ return 'nvim/api/ui.h'
end
- return result
end
-local folder = function(func)
- return function(...)
- return fold(func, ...)
+--- @param d string
+local function process_decl(d)
+ -- Comments are really handled by preprocessor, so the following is not
+ -- needed
+ d = d:gsub('/%*.-%*/', '')
+ d = d:gsub('//.-\n', '\n')
+ d = d:gsub('# .-\n', '')
+ d = d:gsub('\n', ' ')
+ d = d:gsub('%s+', ' ')
+ d = d:gsub(' ?%( ?', '(')
+ d = d:gsub(' ?, ?', ', ')
+ d = d:gsub(' ?(%*+) ?', ' %1')
+ d = d:gsub(' ?(FUNC_ATTR_)', ' %1')
+ d = d:gsub(' $', '')
+ d = d:gsub('^ ', '')
+ return d .. ';'
+end
+
+--- @param fname string
+--- @param text string
+--- @return string[] static
+--- @return string[] non_static
+--- @return boolean any_static
+local function gen_declarations(fname, text)
+ local non_static = {} --- @type string[]
+ local static = {} --- @type string[]
+
+ local neededfile = fname:match('[^/]+$')
+ local curfile = nil
+ local any_static = false
+ for _, node in ipairs(grammar:match(text)) do
+ if node[1] == 'preproc' then
+ curfile = node.content:match('^%a* %d+ "[^"]-/?([^"/]+)"') or curfile
+ elseif node[1] == 'proto' and curfile == neededfile then
+ local node_text = text:sub(node.pos, node.endpos - 1)
+ local declaration = process_decl(node_text)
+
+ if node.static then
+ if not any_static and declaration:find('FUNC_ATTR_') then
+ any_static = true
+ end
+ static[#static + 1] = declaration
+ else
+ non_static[#non_static + 1] = 'DLLEXPORT ' .. declaration
+ end
+ end
end
-end
-local lit = lpeg.P
-local set = function(...)
- return lpeg.S(fold(function(a, b)
- return a .. b
- end, ...))
-end
-local any_character = lpeg.P(1)
-local rng = function(s, e)
- return lpeg.R(s .. e)
-end
-local concat = folder(function(a, b)
- return a * b
-end)
-local branch = folder(function(a, b)
- return a + b
-end)
-local one_or_more = function(v)
- return v ^ 1
-end
-local two_or_more = function(v)
- return v ^ 2
-end
-local any_amount = function(v)
- return v ^ 0
-end
-local one_or_no = function(v)
- return v ^ -1
-end
-local look_behind = lpeg.B
-local look_ahead = function(v)
- return #v
-end
-local neg_look_ahead = function(v)
- return -v
-end
-local neg_look_behind = function(v)
- return -look_behind(v)
+ return static, non_static, any_static
end
-local w = branch(rng('a', 'z'), rng('A', 'Z'), lit('_'))
-local aw = branch(w, rng('0', '9'))
-local s = set(' ', '\n', '\t')
-local raw_word = concat(w, any_amount(aw))
-local right_word = concat(raw_word, neg_look_ahead(aw))
-local word = branch(
- concat(
- branch(lit('ArrayOf('), lit('DictOf('), lit('Dict(')), -- typed container macro
- one_or_more(any_character - lit(')')),
- lit(')')
- ),
- concat(neg_look_behind(aw), right_word)
-)
-local inline_comment =
- concat(lit('/*'), any_amount(concat(neg_look_ahead(lit('*/')), any_character)), lit('*/'))
-local spaces = any_amount(branch(
- s,
- -- Comments are really handled by preprocessor, so the following is not needed
- inline_comment,
- concat(lit('//'), any_amount(concat(neg_look_ahead(lit('\n')), any_character)), lit('\n')),
- -- Linemarker inserted by preprocessor
- concat(lit('# '), any_amount(concat(neg_look_ahead(lit('\n')), any_character)), lit('\n'))
-))
-local typ_part = concat(word, any_amount(concat(spaces, lit('*'))), spaces)
-
-local typ_id = two_or_more(typ_part)
-local arg = typ_id -- argument name is swallowed by typ
-local pattern = concat(
- any_amount(branch(set(' ', '\t'), inline_comment)),
- typ_id, -- return type with function name
- spaces,
- lit('('),
- spaces,
- one_or_no(branch( -- function arguments
- concat(
- arg, -- first argument, does not require comma
- any_amount(concat( -- following arguments, start with a comma
- spaces,
- lit(','),
- spaces,
- arg,
- any_amount(concat(lit('['), spaces, any_amount(aw), spaces, lit(']')))
- )),
- one_or_no(concat(spaces, lit(','), spaces, lit('...')))
- ),
- lit('void') -- also accepts just void
- )),
- spaces,
- lit(')'),
- any_amount(concat( -- optional attributes
- spaces,
- lit('FUNC_'),
- any_amount(aw),
- one_or_no(concat( -- attribute argument
- spaces,
- lit('('),
- any_amount(concat(neg_look_ahead(lit(')')), any_character)),
- lit(')')
- ))
- )),
- look_ahead(concat( -- definition must be followed by "{"
- spaces,
- lit('{')
- ))
-)
-
-if fname == '--help' then
- print([[
+local usage = [[
Usage:
gen_declarations.lua definitions.c static.h non-static.h definitions.i
@@ -141,204 +110,77 @@ non-static.h. File `definitions.i' should contain an already preprocessed
version of definitions.c and it is the only one which is actually parsed,
definitions.c is needed only to determine functions from which file out of all
functions found in definitions.i are needed and to generate an IWYU comment.
-
-Additionally uses the following environment variables:
-
- NVIM_GEN_DECLARATIONS_LINE_NUMBERS:
- 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 with clang/etc).
-
- WARNING: setting this to 1 will cause extensive rebuilds: declarations
- generator script will not regenerate non-static.h file if its
- contents did not change, but including line numbers will make
- contents actually change.
-
- With contents changed timestamp of the file is regenerated even
- when no real changes were made (e.g. a few lines were added to
- a function which is not at the bottom of the file).
-
- With changed timestamp build system will assume that header
- changed, triggering rebuilds of all C files which depend on the
- "changed" header.
-]])
- os.exit()
-end
-
-local preproc_f = io.open(preproc_fname)
-local text = preproc_f:read('*all')
-preproc_f:close()
-
-local non_static = [[
-#define DEFINE_FUNC_ATTRIBUTES
-#include "nvim/func_attr.h"
-#undef DEFINE_FUNC_ATTRIBUTES
-#ifndef DLLEXPORT
-# ifdef MSWIN
-# define DLLEXPORT __declspec(dllexport)
-# else
-# define DLLEXPORT
-# endif
-#endif
-]]
-
-local static = [[
-#define DEFINE_FUNC_ATTRIBUTES
-#include "nvim/func_attr.h"
-#undef DEFINE_FUNC_ATTRIBUTES
]]
-local non_static_footer = [[
-#include "nvim/func_attr.h"
-]]
+local function main()
+ local fname = arg[1]
+ local static_fname = arg[2]
+ local non_static_fname = arg[3]
+ local preproc_fname = arg[4]
+ local static_basename = arg[5]
-local static_footer = [[
-#define DEFINE_EMPTY_ATTRIBUTES
-#include "nvim/func_attr.h" // IWYU pragma: export
-]]
-
-if fname:find('.*/src/nvim/.*%.c$') then
- -- Add an IWYU pragma comment if the corresponding .h file exists.
- local header_fname = fname:sub(1, -3) .. '.h'
- local header_f = io.open(header_fname, 'r')
- if header_f ~= nil then
- header_f:close()
- non_static = ([[
-// IWYU pragma: private, include "%s"
-]]):format(header_fname:gsub('.*/src/nvim/', 'nvim/')) .. non_static
+ if fname == '--help' or #arg < 5 then
+ print(usage)
+ os.exit()
end
-elseif fname:find('.*/src/nvim/.*%.h$') then
- static = ([[
-// IWYU pragma: private, include "%s"
-]]):format(fname:gsub('.*/src/nvim/', 'nvim/')) .. static
-elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/api/private/dispatch.h"
-]] .. non_static
-elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/ui.h"
-]] .. non_static
-elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/ui_client.h"
-]] .. non_static
-elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/api/ui.h"
-]] .. non_static
-end
-local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"'
+ local text = assert(read_file(preproc_fname))
-local init = 1
-local curfile = nil
-local neededfile = fname:match('[^/]+$')
-local declline = 0
-local declendpos = 0
-local curdir = nil
-local is_needed_file = false
-local init_is_nl = true
-local any_static = false
-while init ~= nil do
- if init_is_nl and text:sub(init, init) == '#' then
- local line, dir, file = text:match(filepattern, init)
- if file ~= nil then
- curfile = file
- is_needed_file = (curfile == neededfile)
- declline = tonumber(line) - 1
- curdir = dir:gsub('.*/src/nvim/', '')
- else
- declline = declline - 1
- end
- elseif init < declendpos then -- luacheck: ignore 542
- -- Skipping over declaration
- elseif is_needed_file then
- s = init
- local e = pattern:match(text, init)
- if e ~= nil then
- local declaration = text:sub(s, e - 1)
- -- Comments are really handled by preprocessor, so the following is not
- -- needed
- declaration = declaration:gsub('/%*.-%*/', '')
- declaration = declaration:gsub('//.-\n', '\n')
-
- declaration = declaration:gsub('# .-\n', '')
+ local static_decls, non_static_decls, any_static = gen_declarations(fname, text)
- declaration = declaration:gsub('\n', ' ')
- declaration = declaration:gsub('%s+', ' ')
- declaration = declaration:gsub(' ?%( ?', '(')
- -- declaration = declaration:gsub(' ?%) ?', ')')
- declaration = declaration:gsub(' ?, ?', ', ')
- declaration = declaration:gsub(' ?(%*+) ?', ' %1')
- declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1')
- declaration = declaration:gsub(' $', '')
- declaration = declaration:gsub('^ ', '')
- declaration = declaration .. ';'
-
- if os.getenv('NVIM_GEN_DECLARATIONS_LINE_NUMBERS') == '1' then
- declaration = declaration .. (' // %s/%s:%u'):format(curdir, curfile, declline)
- end
- declaration = declaration .. '\n'
- if declaration:sub(1, 6) == 'static' then
- if declaration:find('FUNC_ATTR_') then
- any_static = true
- end
- static = static .. declaration
- else
- declaration = 'DLLEXPORT ' .. declaration
- non_static = non_static .. declaration
- end
- declendpos = e
- end
- end
- init = text:find('[\n;}]', init)
- if init == nil then
- break
- end
- init_is_nl = text:sub(init, init) == '\n'
- init = init + 1
- if init_is_nl and is_needed_file then
- declline = declline + 1
+ local static = {} --- @type string[]
+ if fname:find('.*/src/nvim/.*%.h$') then
+ static[#static + 1] = ('// IWYU pragma: private, include "%s"'):format(
+ fname:gsub('.*/src/nvim/', 'nvim/')
+ )
end
-end
-
-non_static = non_static .. non_static_footer
-static = static .. static_footer
-
-local F
-F = io.open(static_fname, 'w')
-F:write(static)
-F:close()
-
-if any_static then
- F = io.open(fname, 'r')
- local orig_text = F:read('*a')
- local pat = '\n#%s?include%s+"' .. static_basename .. '"\n'
- local pat_comment = '\n#%s?include%s+"' .. static_basename .. '"%s*//'
- if not string.find(orig_text, pat) and not string.find(orig_text, pat_comment) then
- error('fail: missing include for ' .. static_basename .. ' in ' .. fname)
+ vim.list_extend(static, {
+ '#define DEFINE_FUNC_ATTRIBUTES',
+ '#include "nvim/func_attr.h"',
+ '#undef DEFINE_FUNC_ATTRIBUTES',
+ })
+ vim.list_extend(static, static_decls)
+ vim.list_extend(static, {
+ '#define DEFINE_EMPTY_ATTRIBUTES',
+ '#include "nvim/func_attr.h" // IWYU pragma: export',
+ '',
+ })
+
+ write_file(static_fname, static)
+
+ if any_static then
+ local orig_text = assert(read_file(fname))
+ local pat = '\n#%s?include%s+"' .. static_basename .. '"\n'
+ local pat_comment = '\n#%s?include%s+"' .. static_basename .. '"%s*//'
+ if not orig_text:find(pat) and not orig_text:find(pat_comment) then
+ error(('fail: missing include for %s in %s'):format(static_basename, fname))
+ end
end
- F:close()
-end
-if non_static_fname == 'SKIP' then
- return -- only want static declarations
-end
-
--- Before generating the non-static headers, check if the current file (if
--- exists) is different from the new one. If they are the same, we won't touch
--- the current version to avoid triggering an unnecessary rebuilds of modules
--- that depend on this one
-F = io.open(non_static_fname, 'r')
-if F ~= nil then
- if F:read('*a') == non_static then
- os.exit(0)
+ if non_static_fname ~= 'SKIP' then
+ local non_static = {} --- @type string[]
+ local iwyu_non_static = add_iwyu_non_static(fname, non_static_fname)
+ if iwyu_non_static then
+ non_static[#non_static + 1] = ('// IWYU pragma: private, include "%s"'):format(
+ iwyu_non_static
+ )
+ end
+ vim.list_extend(non_static, {
+ '#define DEFINE_FUNC_ATTRIBUTES',
+ '#include "nvim/func_attr.h"',
+ '#undef DEFINE_FUNC_ATTRIBUTES',
+ '#ifndef DLLEXPORT',
+ '# ifdef MSWIN',
+ '# define DLLEXPORT __declspec(dllexport)',
+ '# else',
+ '# define DLLEXPORT',
+ '# endif',
+ '#endif',
+ })
+ vim.list_extend(non_static, non_static_decls)
+ non_static[#non_static + 1] = '#include "nvim/func_attr.h"'
+ write_file(non_static_fname, non_static)
end
- F:close()
end
-F = io.open(non_static_fname, 'w')
-F:write(non_static)
-F:close()
+return main()
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index 443c68e008..0b6ee6cb24 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -19,6 +19,7 @@ hashpipe:write([[
#include "nvim/digraph.h"
#include "nvim/eval.h"
#include "nvim/eval/buffer.h"
+#include "nvim/eval/deprecated.h"
#include "nvim/eval/fs.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 02f3ac3257..e5dba90925 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -1,143 +1,30 @@
-local options_file = arg[1]
-local options_enum_file = arg[2]
-local options_map_file = arg[3]
-
-local opt_fd = assert(io.open(options_file, 'w'))
-local opt_enum_fd = assert(io.open(options_enum_file, 'w'))
-local opt_map_fd = assert(io.open(options_map_file, 'w'))
-
-local w = function(s)
- if s:match('^ %.') then
- opt_fd:write(s .. ',\n')
- else
- opt_fd:write(s .. '\n')
- end
-end
-
---- @param s string
-local function enum_w(s)
- opt_enum_fd:write(s .. '\n')
-end
-
---- @param s string
-local function map_w(s)
- opt_map_fd:write(s .. '\n')
-end
-
--- @module 'nvim.options'
local options = require('options')
local options_meta = options.options
-
local cstr = options.cstr
local valid_scopes = options.valid_scopes
---- Options for each scope.
---- @type table<string, vim.option_meta[]>
-local scope_options = {}
-for _, scope in ipairs(valid_scopes) do
- scope_options[scope] = {}
+--- @param o vim.option_meta
+--- @return string
+local function get_values_var(o)
+ return ('opt_%s_values'):format(o.abbreviation or o.full_name)
end
--- @param s string
--- @return string
-local lowercase_to_titlecase = function(s)
- return s:sub(1, 1):upper() .. s:sub(2)
+local function lowercase_to_titlecase(s)
+ return table.concat(vim.tbl_map(function(word) --- @param word string
+ return word:sub(1, 1):upper() .. word:sub(2)
+ end, vim.split(s, '[-_]')))
end
--- Generate options enum file
-enum_w('// IWYU pragma: private, include "nvim/option_defs.h"')
-enum_w('')
-
---- Map of option name to option index
---- @type table<string, string>
-local option_index = {}
-
--- Generate option index enum and populate the `option_index` and `scope_option` dicts.
-enum_w('typedef enum {')
-enum_w(' kOptInvalid = -1,')
-
-for i, o in ipairs(options_meta) do
- local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
- enum_w((' %s = %u,'):format(enum_val_name, i - 1))
-
- option_index[o.full_name] = enum_val_name
-
- if o.abbreviation then
- option_index[o.abbreviation] = enum_val_name
- end
-
- if o.alias then
- o.alias = type(o.alias) == 'string' and { o.alias } or o.alias
-
- for _, v in ipairs(o.alias) do
- option_index[v] = enum_val_name
- end
- end
-
- for _, scope in ipairs(o.scope) do
- table.insert(scope_options[scope], o)
- end
-end
-
-enum_w(' // Option count')
-enum_w('#define kOptCount ' .. tostring(#options_meta))
-enum_w('} OptIndex;')
-
--- @param scope string
--- @param option_name string
--- @return string
-local get_scope_option = function(scope, option_name)
+local function get_scope_option(scope, option_name)
return ('k%sOpt%s'):format(lowercase_to_titlecase(scope), lowercase_to_titlecase(option_name))
end
--- Generate option index enum for each scope
-for _, scope in ipairs(valid_scopes) do
- enum_w('')
-
- local scope_name = lowercase_to_titlecase(scope)
- enum_w('typedef enum {')
- enum_w((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
-
- for idx, option in ipairs(scope_options[scope]) do
- enum_w((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
- end
-
- enum_w((' // %s option count'):format(scope_name))
- enum_w(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
- enum_w(('} %sOptIndex;'):format(scope_name))
-end
-
--- Generate reverse lookup from option scope index to option index for each scope.
-for _, scope in ipairs(valid_scopes) do
- enum_w('')
- enum_w(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
- for _, option in ipairs(scope_options[scope]) do
- local idx = option_index[option.full_name]
- enum_w((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
- end
- enum_w('});')
-end
-
-opt_enum_fd:close()
-
--- Generate option index map.
-local hashy = require('generators.hashy')
-local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx)
- return ('option_hash_elems[%s].name'):format(idx)
-end)
-
-map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
-
-for _, name in ipairs(neworder) do
- assert(option_index[name] ~= nil)
- map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
-end
-
-map_w('};\n')
-map_w('static ' .. hashfun)
-
-opt_map_fd:close()
-
local redraw_flags = {
ui_option = 'kOptFlagUIOption',
tabline = 'kOptFlagRedrTabl',
@@ -161,28 +48,29 @@ local list_flags = {
--- @param o vim.option_meta
--- @return string
local function get_flags(o)
- --- @type string
- local flags = '0'
+ --- @type string[]
+ local flags = { '0' }
--- @param f string
- local add_flag = function(f)
- flags = flags .. '|' .. f
+ local function add_flag(f)
+ table.insert(flags, f)
end
if o.list then
add_flag(list_flags[o.list])
end
- if o.redraw then
- for _, r_flag in ipairs(o.redraw) do
- add_flag(redraw_flags[r_flag])
- end
+
+ for _, r_flag in ipairs(o.redraw or {}) do
+ add_flag(redraw_flags[r_flag])
end
+
if o.expand then
add_flag('kOptFlagExpand')
if o.expand == 'nodefault' then
add_flag('kOptFlagNoDefExp')
end
end
+
for _, flag_desc in ipairs({
{ 'nodefault', 'NoDefault' },
{ 'no_mkrc', 'NoMkrc' },
@@ -197,13 +85,14 @@ local function get_flags(o)
{ 'modelineexpr', 'MLE' },
{ 'func' },
}) do
- local key_name = flag_desc[1]
- local def_name = 'kOptFlag' .. (flag_desc[2] or lowercase_to_titlecase(key_name))
+ local key_name, flag_suffix = flag_desc[1], flag_desc[2]
if o[key_name] then
+ local def_name = 'kOptFlag' .. (flag_suffix or lowercase_to_titlecase(key_name))
add_flag(def_name)
end
end
- return flags
+
+ return table.concat(flags, '|')
end
--- @param opt_type vim.option_type
@@ -220,21 +109,6 @@ end
--- @param o vim.option_meta
--- @return string
-local function get_type_flags(o)
- local opt_types = (type(o.type) == 'table') and o.type or { o.type }
- local type_flags = '0'
- assert(type(opt_types) == 'table')
-
- for _, opt_type in ipairs(opt_types) do
- assert(type(opt_type) == 'string')
- type_flags = ('%s | (1 << %s)'):format(type_flags, opt_type_enum(opt_type))
- end
-
- return type_flags
-end
-
---- @param o vim.option_meta
---- @return string
local function get_scope_flags(o)
local scope_flags = '0'
@@ -262,35 +136,15 @@ local function get_scope_idx(o)
return ('{\n%s\n }'):format(table.concat(strs, ',\n'))
end
---- @param c string|string[]
---- @param base_string? string
---- @return string
-local function get_cond(c, base_string)
- local cond_string = base_string or '#if '
- if type(c) == 'table' then
- cond_string = cond_string .. get_cond(c[1], '')
- for i, subc in ipairs(c) do
- if i > 1 then
- cond_string = cond_string .. ' && ' .. get_cond(subc, '')
- end
- end
- elseif c:sub(1, 1) == '!' then
- cond_string = cond_string .. '!defined(' .. c:sub(2) .. ')'
- else
- cond_string = cond_string .. 'defined(' .. c .. ')'
- end
- return cond_string
-end
-
--- @param s string
--- @return string
-local static_cstr_as_string = function(s)
+local function static_cstr_as_string(s)
return ('{ .data = %s, .size = sizeof(%s) - 1 }'):format(s, s)
end
--- @param v vim.option_value|function
--- @return string
-local get_opt_val = function(v)
+local function get_opt_val(v)
--- @type vim.option_type
local v_type
@@ -308,6 +162,7 @@ local get_opt_val = function(v)
elseif v_type == 'number' then
v = ('%iL'):format(v)
elseif v_type == 'string' then
+ --- @cast v string
v = static_cstr_as_string(cstr(v))
end
end
@@ -318,7 +173,7 @@ end
--- @param d vim.option_value|function
--- @param n string
--- @return string
-local get_defaults = function(d, n)
+local function get_defaults(d, n)
if d == nil then
error("option '" .. n .. "' should have a default value")
end
@@ -327,84 +182,354 @@ end
--- @param i integer
--- @param o vim.option_meta
-local function dump_option(i, o)
- w(' [' .. ('%u'):format(i - 1) .. ']={')
- w(' .fullname=' .. cstr(o.full_name))
+--- @param write fun(...: string)
+local function dump_option(i, o, write)
+ write(' [', ('%u'):format(i - 1) .. ']={')
+ write(' .fullname=', cstr(o.full_name))
if o.abbreviation then
- w(' .shortname=' .. cstr(o.abbreviation))
+ write(' .shortname=', cstr(o.abbreviation))
end
- w(' .flags=' .. get_flags(o))
- w(' .type_flags=' .. get_type_flags(o))
- w(' .scope_flags=' .. get_scope_flags(o))
- w(' .scope_idx=' .. get_scope_idx(o))
+ write(' .type=', opt_type_enum(o.type))
+ write(' .flags=', get_flags(o))
+ write(' .scope_flags=', get_scope_flags(o))
+ write(' .scope_idx=', get_scope_idx(o))
+ write(' .values=', (o.values and get_values_var(o) or 'NULL'))
+ write(' .values_len=', (o.values and #o.values or '0'))
+ write(' .flags_var=', (o.flags_varname and ('&%s'):format(o.flags_varname) or 'NULL'))
if o.enable_if then
- w(get_cond(o.enable_if))
+ write(('#if defined(%s)'):format(o.enable_if))
end
local is_window_local = #o.scope == 1 and o.scope[1] == 'win'
- if not is_window_local then
- if o.varname then
- w(' .var=&' .. o.varname)
- elseif o.immutable then
- -- Immutable options can directly point to the default value.
- w((' .var=&options[%u].def_val.data'):format(i - 1))
- else
- -- Option must be immutable or have a variable.
- assert(false)
- end
+ if is_window_local then
+ write(' .var=NULL')
+ elseif o.varname then
+ write(' .var=&', o.varname)
+ elseif o.immutable then
+ -- Immutable options can directly point to the default value.
+ write((' .var=&options[%u].def_val.data'):format(i - 1))
else
- w(' .var=NULL')
- end
- w(' .immutable=' .. (o.immutable and 'true' or 'false'))
- if o.cb then
- w(' .opt_did_set_cb=' .. o.cb)
- end
- if o.expand_cb then
- w(' .opt_expand_cb=' .. o.expand_cb)
+ error('Option must be immutable or have a variable.')
end
+
+ write(' .immutable=', (o.immutable and 'true' or 'false'))
+ write(' .opt_did_set_cb=', o.cb or 'NULL')
+ write(' .opt_expand_cb=', o.expand_cb or 'NULL')
+
if o.enable_if then
- w('#else')
+ write('#else')
-- Hidden option directly points to default value.
- w((' .var=&options[%u].def_val.data'):format(i - 1))
+ write((' .var=&options[%u].def_val.data'):format(i - 1))
-- Option is always immutable on the false branch of `enable_if`.
- w(' .immutable=true')
- w('#endif')
+ write(' .immutable=true')
+ write('#endif')
end
- if o.defaults then
- if o.defaults.condition then
- w(get_cond(o.defaults.condition))
+
+ if not o.defaults then
+ write(' .def_val=NIL_OPTVAL')
+ elseif o.defaults.condition then
+ write(('#if defined(%s)'):format(o.defaults.condition))
+ write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name))
+ if o.defaults.if_false then
+ write('#else')
+ write(' .def_val=', get_defaults(o.defaults.if_false, o.full_name))
end
- w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name))
- if o.defaults.condition then
- if o.defaults.if_false then
- w('#else')
- w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name))
- end
- w('#endif')
+ write('#endif')
+ else
+ write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name))
+ end
+
+ write(' },')
+end
+
+--- @param prefix string
+--- @param values vim.option_valid_values
+local function preorder_traversal(prefix, values)
+ local out = {} --- @type string[]
+
+ local function add(s)
+ table.insert(out, s)
+ end
+
+ add('')
+ add(('EXTERN const char *(%s_values[%s]) INIT( = {'):format(prefix, #vim.tbl_keys(values) + 1))
+
+ --- @type [string,vim.option_valid_values][]
+ local children = {}
+
+ for _, value in ipairs(values) do
+ if type(value) == 'string' then
+ add((' "%s",'):format(value))
+ else
+ assert(type(value) == 'table' and type(value[1]) == 'string' and type(value[2]) == 'table')
+ add((' "%s",'):format(value[1]))
+ table.insert(children, value)
end
+ end
+
+ add(' NULL')
+ add('});')
+
+ for _, value in pairs(children) do
+ -- Remove trailing colon from the added prefix to prevent syntax errors.
+ add(preorder_traversal(prefix .. '_' .. value[1]:gsub(':$', ''), value[2]))
+ end
+
+ return table.concat(out, '\n')
+end
+
+--- @param o vim.option_meta
+--- @return string
+local function gen_opt_enum(o)
+ local out = {} --- @type string[]
+
+ local function add(s)
+ table.insert(out, s)
+ end
+
+ add('')
+ add('typedef enum {')
+
+ local opt_name = lowercase_to_titlecase(o.abbreviation or o.full_name)
+ --- @type table<string,integer>
+ local enum_values
+
+ if type(o.flags) == 'table' then
+ enum_values = o.flags --[[ @as table<string,integer> ]]
else
- w(' .def_val=NIL_OPTVAL')
+ enum_values = {}
+ for i, flag_name in ipairs(o.values) do
+ assert(type(flag_name) == 'string')
+ enum_values[flag_name] = math.pow(2, i - 1)
+ end
+ end
+
+ -- Sort the keys by the flag value so that the enum can be generated in order.
+ --- @type string[]
+ local flag_names = vim.tbl_keys(enum_values)
+ table.sort(flag_names, function(a, b)
+ return enum_values[a] < enum_values[b]
+ end)
+
+ for _, flag_name in pairs(flag_names) do
+ add(
+ (' kOpt%sFlag%s = 0x%02x,'):format(
+ opt_name,
+ lowercase_to_titlecase(flag_name:gsub(':$', '')),
+ enum_values[flag_name]
+ )
+ )
+ end
+
+ add(('} Opt%sFlags;'):format(opt_name))
+
+ return table.concat(out, '\n')
+end
+
+--- @param output_file string
+--- @return table<string,string> options_index Map of option name to option index
+local function gen_enums(output_file)
+ --- Options for each scope.
+ --- @type table<string, vim.option_meta[]>
+ local scope_options = {}
+ for _, scope in ipairs(valid_scopes) do
+ scope_options[scope] = {}
+ end
+
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param s string
+ local function write(s)
+ fd:write(s)
+ fd:write('\n')
+ end
+
+ -- Generate options enum file
+ write('// IWYU pragma: private, include "nvim/option_defs.h"')
+ write('')
+
+ --- Map of option name to option index
+ --- @type table<string, string>
+ local option_index = {}
+
+ -- Generate option index enum and populate the `option_index` and `scope_option` dicts.
+ write('typedef enum {')
+ write(' kOptInvalid = -1,')
+
+ for i, o in ipairs(options_meta) do
+ local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
+ write((' %s = %u,'):format(enum_val_name, i - 1))
+
+ option_index[o.full_name] = enum_val_name
+
+ if o.abbreviation then
+ option_index[o.abbreviation] = enum_val_name
+ end
+
+ local alias = o.alias or {} --[[@as string[] ]]
+ for _, v in ipairs(alias) do
+ option_index[v] = enum_val_name
+ end
+
+ for _, scope in ipairs(o.scope) do
+ table.insert(scope_options[scope], o)
+ end
+ end
+
+ write(' // Option count')
+ write('#define kOptCount ' .. tostring(#options_meta))
+ write('} OptIndex;')
+
+ -- Generate option index enum for each scope
+ for _, scope in ipairs(valid_scopes) do
+ write('')
+
+ local scope_name = lowercase_to_titlecase(scope)
+ write('typedef enum {')
+ write((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
+
+ for idx, option in ipairs(scope_options[scope]) do
+ write((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
+ end
+
+ write((' // %s option count'):format(scope_name))
+ write(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
+ write(('} %sOptIndex;'):format(scope_name))
end
- w(' },')
+
+ -- Generate reverse lookup from option scope index to option index for each scope.
+ for _, scope in ipairs(valid_scopes) do
+ write('')
+ write(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
+ for _, option in ipairs(scope_options[scope]) do
+ local idx = option_index[option.full_name]
+ write((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
+ end
+ write('});')
+ end
+
+ fd:close()
+
+ return option_index
end
--- Generate options[] array.
-w([[
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/insexpand.h"
-#include "nvim/mapping.h"
-#include "nvim/ops.h"
-#include "nvim/option.h"
-#include "nvim/optionstr.h"
-#include "nvim/quickfix.h"
-#include "nvim/runtime.h"
-#include "nvim/tag.h"
-#include "nvim/window.h"
-
-static vimoption_T options[] = {]])
-for i, o in ipairs(options.options) do
- dump_option(i, o)
+--- @param output_file string
+--- @param option_index table<string,string>
+local function gen_map(output_file, option_index)
+ -- Generate option index map.
+ local hashy = require('generators.hashy')
+
+ local neworder, hashfun = hashy.hashy_hash(
+ 'find_option',
+ vim.tbl_keys(option_index),
+ function(idx)
+ return ('option_hash_elems[%s].name'):format(idx)
+ end
+ )
+
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param s string
+ local function write(s)
+ fd:write(s)
+ fd:write('\n')
+ end
+
+ write('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
+
+ for _, name in ipairs(neworder) do
+ assert(option_index[name] ~= nil)
+ write((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
+ end
+
+ write('};')
+ write('')
+ write('static ' .. hashfun)
+
+ fd:close()
end
-w('};')
+
+--- @param output_file string
+local function gen_vars(output_file)
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param s string
+ local function write(s)
+ fd:write(s)
+ fd:write('\n')
+ end
+
+ write('// IWYU pragma: private, include "nvim/option_vars.h"')
+
+ -- Generate enums for option flags.
+ for _, o in ipairs(options_meta) do
+ if o.flags and (type(o.flags) == 'table' or o.values) then
+ write(gen_opt_enum(o))
+ end
+ end
+
+ -- Generate valid values for each option.
+ for _, option in ipairs(options_meta) do
+ -- Since option values can be nested, we need to do preorder traversal to generate the values.
+ if option.values then
+ local values_var = ('opt_%s'):format(option.abbreviation or option.full_name)
+ write(preorder_traversal(values_var, option.values))
+ end
+ end
+
+ fd:close()
+end
+
+--- @param output_file string
+local function gen_options(output_file)
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param ... string
+ local function write(...)
+ local s = table.concat({ ... }, '')
+ fd:write(s)
+ if s:match('^ %.') then
+ fd:write(',')
+ end
+ fd:write('\n')
+ end
+
+ -- Generate options[] array.
+ write([[
+ #include "nvim/ex_docmd.h"
+ #include "nvim/ex_getln.h"
+ #include "nvim/insexpand.h"
+ #include "nvim/mapping.h"
+ #include "nvim/ops.h"
+ #include "nvim/option.h"
+ #include "nvim/optionstr.h"
+ #include "nvim/quickfix.h"
+ #include "nvim/runtime.h"
+ #include "nvim/tag.h"
+ #include "nvim/window.h"
+
+ static vimoption_T options[] = {]])
+
+ for i, o in ipairs(options_meta) do
+ dump_option(i, o, write)
+ end
+
+ write('};')
+
+ fd:close()
+end
+
+local function main()
+ local options_file = arg[1]
+ local options_enum_file = arg[2]
+ local options_map_file = arg[3]
+ local option_vars_file = arg[4]
+
+ local option_index = gen_enums(options_enum_file)
+ gen_map(options_map_file, option_index)
+ gen_vars(option_vars_file)
+ gen_options(options_file)
+end
+
+main()
diff --git a/src/nvim/generators/gen_vimvim.lua b/src/nvim/generators/gen_vimvim.lua
index 0675f04b73..d8053822bf 100644
--- a/src/nvim/generators/gen_vimvim.lua
+++ b/src/nvim/generators/gen_vimvim.lua
@@ -52,11 +52,13 @@ local function is_special_cased_cmd(cmd)
end
local vimcmd_start = 'syn keyword vimCommand contained '
+local vimcmd_end = ' nextgroup=vimBang'
w(vimcmd_start)
+
local prev_cmd = nil
for _, cmd_desc in ipairs(ex_cmds.cmds) do
if lld.line_length > 850 then
- w('\n' .. vimcmd_start)
+ w(vimcmd_end .. '\n' .. vimcmd_start)
end
local cmd = cmd_desc.command
if cmd:match('%w') and cmd ~= 'z' and not is_special_cased_cmd(cmd) then
@@ -79,9 +81,11 @@ for _, cmd_desc in ipairs(ex_cmds.cmds) do
prev_cmd = cmd
end
+w(vimcmd_end .. '\n')
+
local vimopt_start = 'syn keyword vimOption contained '
local vimopt_end = ' skipwhite nextgroup=vimSetEqual,vimSetMod'
-w('\n\n' .. vimopt_start)
+w('\n' .. vimopt_start)
for _, opt_desc in ipairs(options.options) do
if not opt_desc.immutable then
diff --git a/src/nvim/generators/hashy.lua b/src/nvim/generators/hashy.lua
index ea35064962..74b7655324 100644
--- a/src/nvim/generators/hashy.lua
+++ b/src/nvim/generators/hashy.lua
@@ -55,7 +55,7 @@ function M.build_pos_hash(strings)
end
function M.switcher(put, tab, maxlen, worst_buck_size)
- local neworder = {}
+ local neworder = {} --- @type string[]
put ' switch (len) {\n'
local bucky = worst_buck_size > 1
for len = 1, maxlen do
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 82abd2888a..0817d40bb8 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2,7 +2,6 @@
// file, manipulations with redo buffer and stuff buffer.
#include <assert.h>
-#include <lauxlib.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
@@ -11,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
@@ -29,6 +29,7 @@
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
+#include "nvim/ex_getln_defs.h"
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
@@ -38,6 +39,7 @@
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mapping_defs.h"
@@ -45,6 +47,7 @@
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -53,6 +56,7 @@
#include "nvim/ops.h"
#include "nvim/option_vars.h"
#include "nvim/os/fileio.h"
+#include "nvim/os/fileio_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -471,7 +475,7 @@ void beep_flush(void)
{
if (emsg_silent == 0) {
flush_buffers(FLUSH_MINIMAL);
- vim_beep(BO_ERROR);
+ vim_beep(kOptBoFlagError);
}
}
@@ -1513,7 +1517,7 @@ int merge_modifiers(int c_arg, int *modifiers)
int c = c_arg;
if (*modifiers & MOD_MASK_CTRL) {
- if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) {
+ if (c >= '@' && c <= 0x7f) {
c &= 0x1f;
if (c == NUL) {
c = K_ZERO;
@@ -1862,29 +1866,73 @@ bool char_avail(void)
return retval != NUL;
}
+static int no_reduce_keys = 0; ///< Do not apply modifiers to the key.
+
/// "getchar()" and "getcharstr()" functions
-static void getchar_common(typval_T *argvars, typval_T *rettv)
+static void getchar_common(typval_T *argvars, typval_T *rettv, bool allow_number)
FUNC_ATTR_NONNULL_ALL
{
- varnumber_T n;
+ varnumber_T n = 0;
+ const int called_emsg_start = called_emsg;
bool error = false;
+ bool simplify = true;
+ char cursor_flag = NUL;
+
+ if (argvars[0].v_type != VAR_UNKNOWN
+ && tv_check_for_opt_dict_arg(argvars, 1) == FAIL) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type == VAR_DICT) {
+ dict_T *d = argvars[1].vval.v_dict;
+
+ if (allow_number) {
+ allow_number = tv_dict_get_bool(d, "number", true);
+ } else if (tv_dict_has_key(d, "number")) {
+ semsg(_(e_invarg2), "number");
+ }
+
+ simplify = tv_dict_get_bool(d, "simplify", true);
+
+ const char *cursor_str = tv_dict_get_string(d, "cursor", false);
+ if (cursor_str != NULL) {
+ if (strcmp(cursor_str, "hide") != 0
+ && strcmp(cursor_str, "keep") != 0
+ && strcmp(cursor_str, "msg") != 0) {
+ semsg(_(e_invargNval), "cursor", cursor_str);
+ } else {
+ cursor_flag = cursor_str[0];
+ }
+ }
+ }
+
+ if (called_emsg != called_emsg_start) {
+ return;
+ }
+
+ if (cursor_flag == 'h') {
+ ui_busy_start();
+ }
no_mapping++;
allow_keys++;
+ if (!simplify) {
+ no_reduce_keys++;
+ }
while (true) {
- if (msg_col > 0) {
- // Position the cursor. Needed after a message that ends in a space.
+ if (cursor_flag == 'm' || (cursor_flag == NUL && msg_col > 0)) {
ui_cursor_goto(msg_row, msg_col);
}
- if (argvars[0].v_type == VAR_UNKNOWN) {
+ if (argvars[0].v_type == VAR_UNKNOWN
+ || (argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number == -1)) {
// getchar(): blocking wait.
// TODO(bfredl): deduplicate shared logic with state_enter ?
if (!char_avail()) {
// Flush screen updates before blocking.
ui_flush();
input_get(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
- if (!multiqueue_empty(main_loop.events)) {
+ if (!input_available() && !multiqueue_empty(main_loop.events)) {
state_handle_k_event();
continue;
}
@@ -1912,14 +1960,20 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
}
no_mapping--;
allow_keys--;
+ if (!simplify) {
+ no_reduce_keys--;
+ }
+
+ if (cursor_flag == 'h') {
+ ui_busy_stop();
+ }
set_vim_var_nr(VV_MOUSE_WIN, 0);
set_vim_var_nr(VV_MOUSE_WINID, 0);
set_vim_var_nr(VV_MOUSE_LNUM, 0);
set_vim_var_nr(VV_MOUSE_COL, 0);
- rettv->vval.v_number = n;
- if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
+ if (n != 0 && (!allow_number || IS_SPECIAL(n) || mod_mask != 0)) {
char temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
int i = 0;
@@ -1966,35 +2020,23 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
set_vim_var_nr(VV_MOUSE_COL, col + 1);
}
}
+ } else if (!allow_number) {
+ rettv->v_type = VAR_STRING;
+ } else {
+ rettv->vval.v_number = n;
}
}
/// "getchar()" function
void f_getchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- getchar_common(argvars, rettv);
+ getchar_common(argvars, rettv, true);
}
/// "getcharstr()" function
void f_getcharstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- getchar_common(argvars, rettv);
-
- if (rettv->v_type != VAR_NUMBER) {
- return;
- }
-
- 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, temp);
- }
- assert(i < 7);
- temp[i] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xmemdupz(temp, (size_t)i);
+ getchar_common(argvars, rettv, false);
}
/// "getcharmod()" function
@@ -2052,6 +2094,12 @@ static bool at_ins_compl_key(void)
/// @return the length of the replaced bytes, 0 if nothing changed, -1 for error.
static int check_simplify_modifier(int max_offset)
{
+ // We want full modifiers in Terminal mode so that the key can be correctly
+ // encoded
+ if ((State & MODE_TERMINAL) || no_reduce_keys > 0) {
+ return 0;
+ }
+
for (int offset = 0; offset < max_offset; offset++) {
if (offset + 3 >= typebuf.tb_len) {
break;
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 4e962c9b03..766c5eb2a4 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -5,7 +5,6 @@
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/getchar_defs.h" // IWYU pragma: keep
-#include "nvim/os/fileio_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
/// Argument for flush_buffers().
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 472b77ccbe..a473180349 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -405,9 +405,6 @@ EXTERN buf_T *curbuf INIT( = NULL); // currently active buffer
#define FOR_ALL_BUFFERS_BACKWARDS(buf) \
for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev)
-#define FOR_ALL_BUF_WININFO(buf, wip) \
- for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next)
-
// List of files being edited (global argument list). curwin->w_alist points
// to this when the window is using the global argument list.
EXTERN alist_T global_alist; // global argument list
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index acb336c725..df93ad1655 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -383,7 +383,8 @@ void grid_line_start(ScreenGrid *grid, int row)
assert((size_t)grid_line_maxcol <= linebuf_size);
- if (rdb_flags & RDB_INVALID) {
+ if (full_screen && (rdb_flags & kOptRdbFlagInvalid)) {
+ assert(linebuf_char);
// Current batch must not depend on previous contents of linebuf_char.
// Set invalid values which will cause assertion failures later if they are used.
memset(linebuf_char, 0xFF, sizeof(schar_T) * linebuf_size);
@@ -602,7 +603,7 @@ void grid_line_flush(void)
void grid_line_flush_if_valid_row(void)
{
if (grid_line_row < 0 || grid_line_row >= grid_line_grid->rows) {
- if (rdb_flags & RDB_INVALID) {
+ if (rdb_flags & kOptRdbFlagInvalid) {
abort();
} else {
grid_line_grid = NULL;
@@ -639,7 +640,7 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int col, size_t off_to, int
|| (cols > 1 && linebuf_char[col + 1] == 0
&& linebuf_char[col + 1] != grid->chars[off_to + 1]))
|| exmode_active // TODO(bfredl): what in the actual fuck
- || rdb_flags & RDB_NODELTA));
+ || rdb_flags & kOptRdbFlagNodelta));
}
/// Move one buffered line to the window grid, but only the characters that
@@ -784,7 +785,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol
size_t off = off_to + (size_t)col;
if (grid->chars[off] != schar_from_ascii(' ')
|| grid->attrs[off] != bg_attr
- || rdb_flags & RDB_NODELTA) {
+ || rdb_flags & kOptRdbFlagNodelta) {
grid->chars[off] = schar_from_ascii(' ');
grid->attrs[off] = bg_attr;
if (clear_dirty_start == -1) {
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index 95df5e5383..4d09fc10db 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -26,6 +26,7 @@
#include "nvim/ascii_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/hashtab.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 5f0a2e0e4e..1a4b211f9c 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -1075,10 +1075,10 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
return hlattrs;
}
if (HAS_KEY_X(dict, global_link)) {
- *link_id = object_to_hl_id(dict->global_link, "link", err);
+ *link_id = (int)dict->global_link;
mask |= HL_GLOBAL;
} else {
- *link_id = object_to_hl_id(dict->link, "link", err);
+ *link_id = (int)dict->link;
}
if (ERROR_SET(err)) {
@@ -1100,6 +1100,9 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(cterm, cterm_mask, italic, , HL_ITALIC);
CHECK_FLAG(cterm, cterm_mask, underline, , HL_UNDERLINE);
CHECK_FLAG(cterm, cterm_mask, undercurl, , HL_UNDERCURL);
+ CHECK_FLAG(cterm, cterm_mask, underdouble, , HL_UNDERDOUBLE);
+ CHECK_FLAG(cterm, cterm_mask, underdotted, , HL_UNDERDOTTED);
+ CHECK_FLAG(cterm, cterm_mask, underdashed, , HL_UNDERDASHED);
CHECK_FLAG(cterm, cterm_mask, standout, , HL_STANDOUT);
CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH);
CHECK_FLAG(cterm, cterm_mask, altfont, , HL_ALTFONT);
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index 1be70100de..a89d778474 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -15,7 +15,6 @@ EXTERN const char *hlf_names[] INIT( = {
[HLF_8] = "SpecialKey",
[HLF_EOB] = "EndOfBuffer",
[HLF_TERM] = "TermCursor",
- [HLF_TERMNC] = "TermCursorNC",
[HLF_AT] = "NonText",
[HLF_D] = "Directory",
[HLF_E] = "ErrorMsg",
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index a3c4062714..cbbc28311f 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -63,7 +63,6 @@ typedef enum {
///< displayed different from what it is
HLF_EOB, ///< after the last line in the buffer
HLF_TERM, ///< terminal cursor focused
- HLF_TERMNC, ///< terminal cursor unfocused
HLF_AT, ///< @ characters at end of screen, characters that don't really exist in the text
HLF_D, ///< directories in CTRL-D listing
HLF_E, ///< error messages
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index b3c4aca1af..901d2c84bc 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -1,5 +1,6 @@
// highlight_group.c: code for managing highlight groups
+#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
@@ -31,6 +32,7 @@
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
@@ -148,7 +150,7 @@ static const char *highlight_init_both[] = {
"PmenuMatchSel gui=bold cterm=bold",
"PmenuSel gui=reverse cterm=reverse,underline blend=0",
"RedrawDebugNormal gui=reverse cterm=reverse",
- "TabLineSel gui=bold cterm=bold",
+ "TabLineSel gui=bold cterm=NONE",
"TermCursor gui=reverse cterm=reverse",
"Underlined gui=underline cterm=underline",
"lCursor guifg=bg guibg=fg",
@@ -173,12 +175,12 @@ static const char *highlight_init_both[] = {
"default link PmenuKind Pmenu",
"default link PmenuKindSel PmenuSel",
"default link PmenuSbar Pmenu",
+ "default link ComplMatchIns NONE",
"default link Substitute Search",
"default link StatusLineTerm StatusLine",
"default link StatusLineTermNC StatusLineNC",
"default link TabLine StatusLineNC",
"default link TabLineFill TabLine",
- "default link TermCursorNC NONE",
"default link VertSplit WinSeparator",
"default link VisualNOS Visual",
"default link Whitespace NonText",
@@ -230,6 +232,11 @@ static const char *highlight_init_both[] = {
"default link DiagnosticVirtualTextInfo DiagnosticInfo",
"default link DiagnosticVirtualTextHint DiagnosticHint",
"default link DiagnosticVirtualTextOk DiagnosticOk",
+ "default link DiagnosticVirtualLinesError DiagnosticError",
+ "default link DiagnosticVirtualLinesWarn DiagnosticWarn",
+ "default link DiagnosticVirtualLinesInfo DiagnosticInfo",
+ "default link DiagnosticVirtualLinesHint DiagnosticHint",
+ "default link DiagnosticVirtualLinesOk DiagnosticOk",
"default link DiagnosticSignError DiagnosticError",
"default link DiagnosticSignWarn DiagnosticWarn",
"default link DiagnosticSignInfo DiagnosticInfo",
@@ -303,7 +310,7 @@ static const char *highlight_init_both[] = {
"default link @tag.builtin Special",
// :help
- // Higlight "===" and "---" heading delimiters specially.
+ // Highlight "===" and "---" heading delimiters specially.
"default @markup.heading.1.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underdouble,nocombine ctermbg=NONE ctermfg=NONE cterm=underdouble,nocombine",
"default @markup.heading.2.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underline,nocombine ctermbg=NONE ctermfg=NONE cterm=underline,nocombine",
@@ -357,7 +364,7 @@ static const char *highlight_init_light[] = {
"ErrorMsg guifg=NvimDarkRed ctermfg=1",
"FloatShadow guibg=NvimLightGrey4 ctermbg=0 blend=80",
"FloatShadowThrough guibg=NvimLightGrey4 ctermbg=0 blend=100",
- "Folded guifg=NvimDarkGrey4 guibg=NvimLightGrey3",
+ "Folded guifg=NvimDarkGrey4 guibg=NvimLightGrey1",
"LineNr guifg=NvimLightGrey4",
"MatchParen guibg=NvimLightGrey4 gui=bold cterm=bold,underline",
"ModeMsg guifg=NvimDarkGreen ctermfg=2",
@@ -380,7 +387,7 @@ static const char *highlight_init_light[] = {
"SpellLocal guisp=NvimDarkGreen gui=undercurl cterm=undercurl",
"SpellRare guisp=NvimDarkCyan gui=undercurl cterm=undercurl",
"StatusLine guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=reverse",
- "StatusLineNC guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=bold,underline",
+ "StatusLineNC guifg=NvimDarkGrey2 guibg=NvimLightGrey4 cterm=bold,underline",
"Title guifg=NvimDarkGrey2 gui=bold cterm=bold",
"Visual guibg=NvimLightGrey4 ctermfg=15 ctermbg=0",
"WarningMsg guifg=NvimDarkYellow ctermfg=3",
@@ -441,7 +448,7 @@ static const char *highlight_init_dark[] = {
"ErrorMsg guifg=NvimLightRed ctermfg=9",
"FloatShadow guibg=NvimDarkGrey4 ctermbg=0 blend=80",
"FloatShadowThrough guibg=NvimDarkGrey4 ctermbg=0 blend=100",
- "Folded guifg=NvimLightGrey4 guibg=NvimDarkGrey3",
+ "Folded guifg=NvimLightGrey4 guibg=NvimDarkGrey1",
"LineNr guifg=NvimDarkGrey4",
"MatchParen guibg=NvimDarkGrey4 gui=bold cterm=bold,underline",
"ModeMsg guifg=NvimLightGreen ctermfg=10",
@@ -464,7 +471,7 @@ static const char *highlight_init_dark[] = {
"SpellLocal guisp=NvimLightGreen gui=undercurl cterm=undercurl",
"SpellRare guisp=NvimLightCyan gui=undercurl cterm=undercurl",
"StatusLine guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=reverse",
- "StatusLineNC guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=bold,underline",
+ "StatusLineNC guifg=NvimLightGrey2 guibg=NvimDarkGrey4 cterm=bold,underline",
"Title guifg=NvimLightGrey2 gui=bold cterm=bold",
"Visual guibg=NvimDarkGrey4 ctermfg=0 ctermbg=15",
"WarningMsg guifg=NvimLightYellow ctermfg=11",
@@ -1001,6 +1008,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
{
// If no argument, list current highlighting.
if (!init && ends_excmd((uint8_t)(*line))) {
+ msg_ext_set_kind("list_cmd");
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);
@@ -1038,6 +1046,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (id == 0) {
semsg(_(e_highlight_group_name_not_found_str), line);
} else {
+ msg_ext_set_kind("list_cmd");
highlight_list_one(id);
}
return;
@@ -1885,8 +1894,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool
if (got_int) {
return true;
}
- msg_outtrans(hl_table[id - 1].sg_name, 0, false);
- name_col = msg_col;
+ msg_col = name_col = msg_outtrans(hl_table[id - 1].sg_name, 0, false);
endcol = 15;
} else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
msg_putchar(' ');
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index e487728901..170346ec0d 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -34,7 +34,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
#include "nvim/pos_defs.h"
@@ -793,7 +792,7 @@ bool briopt_check(char *briopt, win_T *wp)
}
while (*p != NUL) {
- // Note: Keep this in sync with p_briopt_values
+ // Note: Keep this in sync with opt_briopt_values.
if (strncmp(p, "shift:", 6) == 0
&& ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
p += 6;
@@ -872,7 +871,7 @@ int get_breakindent_win(win_T *wp, char *line)
|| prev_tick != buf_get_changedtick(wp->w_buffer)
|| prev_listopt != wp->w_briopt_list
|| prev_no_ts != no_ts
- || prev_dy_uhex != (dy_flags & DY_UHEX)
+ || prev_dy_uhex != (dy_flags & kOptDyFlagUhex)
|| prev_flp == NULL
|| strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0
|| prev_line == NULL || strcmp(prev_line, line) != 0) {
@@ -893,7 +892,7 @@ int get_breakindent_win(win_T *wp, char *line)
prev_listopt = wp->w_briopt_list;
prev_list = 0;
prev_no_ts = no_ts;
- prev_dy_uhex = (dy_flags & DY_UHEX);
+ prev_dy_uhex = (dy_flags & kOptDyFlagUhex);
xfree(prev_flp);
prev_flp = xstrdup(get_flp_value(wp->w_buffer));
// add additional indent for numbered lists
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 98b0d6003a..c9b7a1ba3f 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -14,6 +14,7 @@
#include "nvim/indent_c.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
+#include "nvim/math.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/option.h"
@@ -1689,7 +1690,7 @@ void parse_cino(buf_T *buf)
p++;
}
char *digits_start = p; // remember where the digits start
- int n = getdigits_int(&p, true, 0);
+ int64_t n = getdigits_int(&p, true, 0);
divider = 0;
if (*p == '.') { // ".5s" means a fraction.
fraction = atoi(++p);
@@ -1708,7 +1709,7 @@ void parse_cino(buf_T *buf)
} else {
n *= sw;
if (divider) {
- n += (sw * fraction + divider / 2) / divider;
+ n += ((int64_t)sw * fraction + divider / 2) / divider;
}
}
p++;
@@ -1717,119 +1718,121 @@ void parse_cino(buf_T *buf)
n = -n;
}
+ n = trim_to_int(n);
+
// When adding an entry here, also update the default 'cinoptions' in
// doc/indent.txt, and add explanation for it!
switch (*l) {
case '>':
- buf->b_ind_level = n;
+ buf->b_ind_level = (int)n;
break;
case 'e':
- buf->b_ind_open_imag = n;
+ buf->b_ind_open_imag = (int)n;
break;
case 'n':
- buf->b_ind_no_brace = n;
+ buf->b_ind_no_brace = (int)n;
break;
case 'f':
- buf->b_ind_first_open = n;
+ buf->b_ind_first_open = (int)n;
break;
case '{':
- buf->b_ind_open_extra = n;
+ buf->b_ind_open_extra = (int)n;
break;
case '}':
- buf->b_ind_close_extra = n;
+ buf->b_ind_close_extra = (int)n;
break;
case '^':
- buf->b_ind_open_left_imag = n;
+ buf->b_ind_open_left_imag = (int)n;
break;
case 'L':
- buf->b_ind_jump_label = n;
+ buf->b_ind_jump_label = (int)n;
break;
case ':':
- buf->b_ind_case = n;
+ buf->b_ind_case = (int)n;
break;
case '=':
- buf->b_ind_case_code = n;
+ buf->b_ind_case_code = (int)n;
break;
case 'b':
- buf->b_ind_case_break = n;
+ buf->b_ind_case_break = (int)n;
break;
case 'p':
- buf->b_ind_param = n;
+ buf->b_ind_param = (int)n;
break;
case 't':
- buf->b_ind_func_type = n;
+ buf->b_ind_func_type = (int)n;
break;
case '/':
- buf->b_ind_comment = n;
+ buf->b_ind_comment = (int)n;
break;
case 'c':
- buf->b_ind_in_comment = n;
+ buf->b_ind_in_comment = (int)n;
break;
case 'C':
- buf->b_ind_in_comment2 = n;
+ buf->b_ind_in_comment2 = (int)n;
break;
case 'i':
- buf->b_ind_cpp_baseclass = n;
+ buf->b_ind_cpp_baseclass = (int)n;
break;
case '+':
- buf->b_ind_continuation = n;
+ buf->b_ind_continuation = (int)n;
break;
case '(':
- buf->b_ind_unclosed = n;
+ buf->b_ind_unclosed = (int)n;
break;
case 'u':
- buf->b_ind_unclosed2 = n;
+ buf->b_ind_unclosed2 = (int)n;
break;
case 'U':
- buf->b_ind_unclosed_noignore = n;
+ buf->b_ind_unclosed_noignore = (int)n;
break;
case 'W':
- buf->b_ind_unclosed_wrapped = n;
+ buf->b_ind_unclosed_wrapped = (int)n;
break;
case 'w':
- buf->b_ind_unclosed_whiteok = n;
+ buf->b_ind_unclosed_whiteok = (int)n;
break;
case 'm':
- buf->b_ind_matching_paren = n;
+ buf->b_ind_matching_paren = (int)n;
break;
case 'M':
- buf->b_ind_paren_prev = n;
+ buf->b_ind_paren_prev = (int)n;
break;
case ')':
- buf->b_ind_maxparen = n;
+ buf->b_ind_maxparen = (int)n;
break;
case '*':
- buf->b_ind_maxcomment = n;
+ buf->b_ind_maxcomment = (int)n;
break;
case 'g':
- buf->b_ind_scopedecl = n;
+ buf->b_ind_scopedecl = (int)n;
break;
case 'h':
- buf->b_ind_scopedecl_code = n;
+ buf->b_ind_scopedecl_code = (int)n;
break;
case 'j':
- buf->b_ind_java = n;
+ buf->b_ind_java = (int)n;
break;
case 'J':
- buf->b_ind_js = n;
+ buf->b_ind_js = (int)n;
break;
case 'l':
- buf->b_ind_keep_case_label = n;
+ buf->b_ind_keep_case_label = (int)n;
break;
case '#':
- buf->b_ind_hash_comment = n;
+ buf->b_ind_hash_comment = (int)n;
break;
case 'N':
- buf->b_ind_cpp_namespace = n;
+ buf->b_ind_cpp_namespace = (int)n;
break;
case 'k':
- buf->b_ind_if_for_while = n;
+ buf->b_ind_if_for_while = (int)n;
break;
case 'E':
- buf->b_ind_cpp_extern_c = n;
+ buf->b_ind_cpp_extern_c = (int)n;
break;
case 'P':
- buf->b_ind_pragma = n;
+ buf->b_ind_pragma = (int)n;
break;
}
if (*p == ',') {
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 3d3240c59f..fb89d86a75 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -1,16 +1,15 @@
// input.c: high level functions for prompting the user or input
// like yes/no or number prompts.
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "nvim/ascii_defs.h"
+#include "nvim/ex_getln.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
@@ -34,44 +33,36 @@
/// No other characters are accepted, the message is repeated until a valid
/// reply is entered or <C-c> is hit.
///
-/// @param[in] str Prompt: question to ask user. Is always followed by
-/// " (y/n)?".
-/// @param[in] direct Determines what function to use to get user input. If
-/// true then input_get() will be used, otherwise vgetc().
-/// I.e. when direct is true then characters are obtained
-/// directly from the user without buffers involved.
+/// @param[in] str Prompt: question to ask user. Is always followed by " (y/n)?".
///
/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
-int ask_yesno(const char *const str, const bool direct)
+int ask_yesno(const char *const str)
{
const int save_State = State;
no_wait_return++;
State = MODE_CONFIRM; // Mouse behaves like with :confirm.
setmouse(); // Disable mouse in xterm.
- no_mapping++;
- allow_keys++; // no mapping here, but recognize keys
+ snprintf(IObuff, IOSIZE, _("%s (y/n)?"), str);
+ char *prompt = xstrdup(IObuff);
int r = ' ';
while (r != 'y' && r != 'n') {
// same highlighting as for wait_return()
- smsg(HLF_R, "%s (y/n)?", str);
- if (direct) {
- r = get_keystroke(NULL);
- } else {
- r = plain_vgetc();
- }
+ r = prompt_for_input(prompt, HLF_R, true, NULL);
if (r == Ctrl_C || r == ESC) {
r = 'n';
+ if (!ui_has(kUIMessages)) {
+ msg_putchar(r);
+ }
}
- msg_putchar(r); // Show what you typed.
- ui_flush();
}
+
+ need_wait_return = msg_scrolled;
no_wait_return--;
State = save_State;
setmouse();
- no_mapping--;
- allow_keys--;
+ xfree(prompt);
return r;
}
@@ -157,104 +148,42 @@ int get_keystroke(MultiQueue *events)
return n;
}
-/// Get a number from the user.
-/// When "mouse_used" is not NULL allow using the mouse.
+/// Ask the user for input through a cmdline prompt.
///
-/// @param colon allow colon to abort
-int get_number(int colon, bool *mouse_used)
+/// @param one_key Return from cmdline after one key press.
+/// @param mouse_used When not NULL, allow using the mouse to press a number.
+int prompt_for_input(char *prompt, int hl_id, bool one_key, bool *mouse_used)
{
- int n = 0;
- int typed = 0;
+ int ret = one_key ? ESC : 0;
+ char *kmsg = keep_msg ? xstrdup(keep_msg) : NULL;
- if (mouse_used != NULL) {
- *mouse_used = false;
+ if (prompt == NULL) {
+ if (mouse_used != NULL) {
+ prompt = _("Type number and <Enter> or click with the mouse (q or empty cancels): ");
+ } else {
+ prompt = _("Type number and <Enter> (q or empty cancels): ");
+ }
}
- // When not printing messages, the user won't know what to type, return a
- // zero (as if CR was hit).
- if (msg_silent != 0) {
- return 0;
- }
+ cmdline_row = msg_row;
+ ui_flush();
- no_mapping++;
- allow_keys++; // no mapping here, but recognize keys
- while (true) {
- ui_cursor_goto(msg_row, msg_col);
- int c = safe_vgetc();
- if (ascii_isdigit(c)) {
- if (vim_append_digit_int(&n, c - '0') == FAIL) {
- return 0;
- }
- msg_putchar(c);
- typed++;
- } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
- if (typed > 0) {
- msg_puts("\b \b");
- typed--;
- }
- n /= 10;
- } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
- *mouse_used = true;
- n = mouse_row + 1;
- break;
- } else if (n == 0 && c == ':' && colon) {
- stuffcharReadbuff(':');
- if (!exmode_active) {
- cmdline_row = msg_row;
- }
- skip_redraw = true; // skip redraw once
- do_redraw = false;
- break;
- } else if (c == Ctrl_C || c == ESC || c == 'q') {
- n = 0;
- break;
- } else if (c == CAR || c == NL) {
- break;
- }
- }
- no_mapping--;
+ no_mapping++; // don't map prompt input
+ allow_keys++; // allow special keys
+ char *resp = getcmdline_prompt(-1, prompt, hl_id, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE, one_key, mouse_used);
allow_keys--;
- return n;
-}
+ no_mapping--;
-/// Ask the user to enter a number.
-///
-/// When "mouse_used" is not NULL allow using the mouse and in that case return
-/// the line number.
-int prompt_for_number(bool *mouse_used)
-{
- // When using ":silent" assume that <CR> was entered.
- if (mouse_used != NULL) {
- msg_puts(_("Type number and <Enter> or click with the mouse "
- "(q or empty cancels): "));
- } else {
- msg_puts(_("Type number and <Enter> (q or empty cancels): "));
+ if (resp) {
+ ret = one_key ? (int)(*resp) : atoi(resp);
+ xfree(resp);
}
- // Set the state such that text can be selected/copied/pasted and we still
- // get mouse events.
- int save_cmdline_row = cmdline_row;
- cmdline_row = 0;
- int save_State = State;
- State = MODE_ASKMORE; // prevents a screen update when using a timer
- // May show different mouse shape.
- setmouse();
-
- int i = get_number(true, mouse_used);
- if (KeyTyped) {
- // don't call wait_return() now
- if (msg_row > 0) {
- cmdline_row = msg_row - 1;
- }
- need_wait_return = false;
- msg_didany = false;
- msg_didout = false;
- } else {
- cmdline_row = save_cmdline_row;
+ if (kmsg != NULL) {
+ set_keep_msg(kmsg, keep_msg_hl_id);
+ xfree(kmsg);
}
- State = save_State;
- // May need to restore mouse shape.
- setmouse();
- return i;
+ return ret;
}
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index bd7ee55722..e21433f5ec 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -37,7 +38,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -156,7 +156,8 @@ typedef struct compl_S compl_T;
struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
- char *cp_str; ///< matched text
+ compl_T *cp_match_next; ///< matched next compl_T
+ String cp_str; ///< matched text
char *(cp_text[CPT_COUNT]); ///< text for the menu
typval_T cp_user_data;
char *cp_fname; ///< file containing the match, allocated when
@@ -164,6 +165,7 @@ struct compl_S {
int cp_flags; ///< CP_ values
int cp_number; ///< sequence number
int cp_score; ///< fuzzy match score
+ bool cp_in_match_array; ///< collected by compl_match_array
int cp_user_abbr_hlattr; ///< highlight attribute for abbr
int cp_user_kind_hlattr; ///< highlight attribute for kind
};
@@ -219,7 +221,7 @@ static bool compl_enter_selects = false;
/// When "compl_leader" is not NULL only matches that start with this string
/// are used.
-static char *compl_leader = NULL;
+static String compl_leader = STRING_INIT;
static bool compl_get_longest = false; ///< put longest common string in compl_leader
@@ -244,8 +246,7 @@ static bool compl_started = false;
static int ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; ///< number of completion matches
-static char *compl_pattern = NULL;
-static size_t compl_patternlen = 0;
+static String compl_pattern = STRING_INIT;
static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N
@@ -255,8 +256,9 @@ 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 *compl_orig_text = NULL; ///< text as it was before
- ///< completion started
+static colnr_T compl_ins_end_col = 0;
+static String compl_orig_text = STRING_INIT; ///< text as it was before
+ ///< completion started
/// Undo information to restore extmarks for original text.
static extmark_undo_vec_t compl_orig_extmarks;
static int compl_cont_mode = 0;
@@ -281,6 +283,11 @@ static size_t spell_bad_len = 0; // length of located bad word
static int compl_selected_item = -1;
+// "compl_match_array" points the currently displayed list of entries in the
+// popup menu. It is NULL when there is no popup menu.
+static pumitem_T *compl_match_array = NULL;
+static int compl_match_arraysize;
+
/// CTRL-X pressed in Insert mode.
void ins_ctrl_x(void)
{
@@ -475,7 +482,7 @@ bool check_compl_option(bool dict_opt)
msg((dict_opt ? _("'dictionary' option is empty") : _("'thesaurus' option is empty")),
HLF_E);
if (emsg_silent == 0 && !in_assert_fails) {
- vim_beep(BO_COMPL);
+ vim_beep(kOptBoFlagComplete);
setcursor();
ui_flush();
os_delay(2004, false);
@@ -560,11 +567,19 @@ static bool is_first_match(const compl_T *const match)
return match == compl_first_match;
}
-static void do_autocmd_completedone(int c)
+static void do_autocmd_completedone(int c, int mode, char *word)
{
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
+ mode = mode & ~CTRL_X_WANT_IDENT;
+ char *mode_str = NULL;
+ if (ctrl_x_mode_names[mode]) {
+ mode_str = ctrl_x_mode_names[mode];
+ }
+ tv_dict_add_str(v_event, S_LEN("complete_word"), word != NULL ? word : "");
+ tv_dict_add_str(v_event, S_LEN("complete_type"), mode_str != NULL ? mode_str : "");
+
tv_dict_add_str(v_event, S_LEN("reason"), (c == Ctrl_Y ? "accept" : "cancel"));
tv_dict_set_keys_readonly(v_event);
@@ -624,7 +639,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Rule 1: Were any chars converted to lower?
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -644,7 +659,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// upper case.
if (!has_lower) {
bool was_letter = false;
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
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])) {
@@ -660,7 +675,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Copy the original case of the part we typed.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -690,7 +705,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
ga_grow(&gap, IOSIZE);
*p = NUL;
STRCPY(gap.ga_data, IObuff);
- gap.ga_len = (int)strlen(IObuff);
+ gap.ga_len = (int)(p - IObuff);
} else {
p += utf_char2bytes(wca[i++], p);
}
@@ -737,7 +752,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// Find actual length of original text.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
@@ -747,7 +762,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// "char_len" may be smaller than "compl_char_len" when using
// thesaurus, only use the minimum when comparing.
- int min_len = char_len < compl_char_len ? char_len : compl_char_len;
+ int min_len = MIN(char_len, compl_char_len);
str = ins_compl_infercase_gettext(str, char_len, compl_char_len, min_len, &tofree);
}
@@ -758,7 +773,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
flags |= CP_ICASE;
}
- int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, -1, -1);
+ int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, NULL);
xfree(tofree);
return res;
}
@@ -789,6 +804,7 @@ static inline void free_cptext(char *const *const cptext)
/// @param[in] cdir match direction. If 0, use "compl_direction".
/// @param[in] flags_arg match flags (cp_flags)
/// @param[in] adup accept this match even if it is already present.
+/// @param[in] user_hl list of extra highlight attributes for abbr kind.
///
/// If "cdir" is FORWARD, then the match is added after the current match.
/// Otherwise, it is added before the current match.
@@ -798,7 +814,7 @@ static inline void free_cptext(char *const *const cptext)
/// returned in case of error.
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, int user_abbr_hlattr, int user_kind_hlattr)
+ int flags_arg, const bool adup, const int *user_hl)
FUNC_ATTR_NONNULL_ARG(1)
{
compl_T *match;
@@ -825,8 +841,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match = compl_first_match;
do {
if (!match_at_original_text(match)
- && strncmp(match->cp_str, str, (size_t)len) == 0
- && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) {
+ && strncmp(match->cp_str.data, str, (size_t)len) == 0
+ && ((int)match->cp_str.size <= len || match->cp_str.data[len] == NUL)) {
if (cptext_allocated) {
free_cptext(cptext);
}
@@ -846,7 +862,7 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
if (flags & CP_ORIGINAL_TEXT) {
match->cp_number = 0;
}
- match->cp_str = xstrnsave(str, (size_t)len);
+ match->cp_str = cbuf_to_string(str, (size_t)len);
// match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -864,8 +880,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match->cp_fname = NULL;
}
match->cp_flags = flags;
- match->cp_user_abbr_hlattr = user_abbr_hlattr;
- match->cp_user_kind_hlattr = user_kind_hlattr;
+ match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1;
+ match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1;
if (cptext != NULL) {
int i;
@@ -928,27 +944,55 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len)
return true;
}
if (match->cp_flags & CP_ICASE) {
- return STRNICMP(match->cp_str, str, len) == 0;
+ return STRNICMP(match->cp_str.data, str, len) == 0;
+ }
+ return strncmp(match->cp_str.data, str, len) == 0;
+}
+
+/// when len is -1 mean use whole length of p otherwise part of p
+static void ins_compl_insert_bytes(char *p, int len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (len == -1) {
+ len = (int)strlen(p);
}
- return strncmp(match->cp_str, str, len) == 0;
+ assert(len >= 0);
+ ins_bytes_len(p, (size_t)len);
+ compl_ins_end_col = curwin->w_cursor.col;
+}
+
+/// Checks if the column is within the currently inserted completion text
+/// column range. If it is, it returns a special highlight attribute.
+/// -1 mean normal item.
+int ins_compl_col_range_attr(int col)
+{
+ if (get_cot_flags() & kOptCotFlagFuzzy) {
+ return -1;
+ }
+
+ if (col >= (compl_col + (int)ins_compl_leader_len()) && col < compl_ins_end_col) {
+ return syn_name2attr("ComplMatchIns");
+ }
+
+ return -1;
}
/// Reduce the longest common string for match "match".
static void ins_compl_longest_match(compl_T *match)
{
- if (compl_leader == NULL) {
+ if (compl_leader.data == NULL) {
// First match, use it as a whole.
- compl_leader = xstrdup(match->cp_str);
+ compl_leader = copy_string(match->cp_str, NULL);
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(false);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
compl_used_match = false;
@@ -956,8 +1000,8 @@ static void ins_compl_longest_match(compl_T *match)
}
// Reduce the text if this match differs from compl_leader.
- char *p = compl_leader;
- char *s = match->cp_str;
+ char *p = compl_leader.data;
+ char *s = match->cp_str.data;
while (*p != NUL) {
int c1 = utf_ptr2char(p);
int c2 = utf_ptr2char(s);
@@ -974,15 +1018,17 @@ static void ins_compl_longest_match(compl_T *match)
if (*p != NUL) {
// Leader was shortened, need to change the inserted text.
*p = NUL;
+ compl_leader.size = (size_t)(p - compl_leader.data);
+
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(false);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
}
@@ -997,9 +1043,9 @@ 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(matches[i], -1, NULL, NULL, false, NULL, dir,
- CP_FAST | (icase ? CP_ICASE : 0),
- false, -1, -1)) == OK) {
+ add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir,
+ CP_FAST | (icase ? CP_ICASE : 0), false, NULL);
+ if (add_r == OK) {
// If dir was BACKWARD then honor it just once.
dir = FORWARD;
}
@@ -1038,8 +1084,8 @@ bool ins_compl_has_shown_match(void)
/// Return whether the shown match is long enough.
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;
+ return compl_shown_match != NULL && compl_shown_match->cp_str.data != NULL
+ && (colnr_T)compl_shown_match->cp_str.size > curwin->w_cursor.col - compl_col;
}
/// Get the local or global value of 'completeopt' flags.
@@ -1048,11 +1094,6 @@ unsigned get_cot_flags(void)
return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
}
-/// "compl_match_array" points the currently displayed list of entries in the
-/// popup menu. It is NULL when there is no popup menu.
-static pumitem_T *compl_match_array = NULL;
-static int compl_match_arraysize;
-
/// Remove any popup menu.
static void ins_compl_del_pum(void)
{
@@ -1069,7 +1110,7 @@ bool pum_wanted(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// "completeopt" must contain "menu" or "menuone"
- return (get_cot_flags() & COT_ANY_MENU) != 0;
+ return (get_cot_flags() & (kOptCotFlagMenu | kOptCotFlagMenuone)) != 0;
}
/// Check that there are two or more matches to be shown in the popup menu.
@@ -1088,7 +1129,7 @@ static bool pum_enough_matches(void)
comp = comp->cp_next;
} while (!is_first_match(comp));
- if (get_cot_flags() & COT_MENUONE) {
+ if (get_cot_flags() & kOptCotFlagMenuone) {
return i >= 1;
}
return i >= 2;
@@ -1099,7 +1140,7 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc_lock(VAR_FIXED);
- tv_dict_add_str(dict, S_LEN("word"), match->cp_str);
+ tv_dict_add_str(dict, S_LEN("word"), match->cp_str.data);
tv_dict_add_str(dict, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
tv_dict_add_str(dict, S_LEN("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]);
@@ -1168,54 +1209,47 @@ static int ins_compl_build_pum(void)
XFREE_CLEAR(compl_leader);
}
- const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
int max_fuzzy_score = 0;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
-
- do {
- // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
- // set the cp_score for later comparisons.
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
- comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader);
- }
-
- if (!match_at_original_text(comp)
- && (compl_leader == NULL
- || ins_compl_equal(comp, compl_leader, (size_t)lead_len)
- || (compl_fuzzy_match && comp->cp_score > 0))) {
- compl_match_arraysize++;
- }
- comp = comp->cp_next;
- } while (comp != NULL && !is_first_match(comp));
-
- if (compl_match_arraysize == 0) {
- return -1;
- }
-
- assert(compl_match_arraysize >= 0);
- compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
+ bool fuzzy_filter = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
+ bool fuzzy_sort = fuzzy_filter && !(cur_cot_flags & kOptCotFlagNosort);
+ compl_T *match_head = NULL, *match_tail = NULL;
// If the current match is the original text don't find the first
// match after it, don't highlight anything.
bool shown_match_ok = match_at_original_text(compl_shown_match);
- if (strequal(compl_leader, compl_orig_text) && !shown_match_ok) {
+ if (strequal(compl_leader.data, compl_orig_text.data) && !shown_match_ok) {
compl_shown_match = compl_no_select ? compl_first_match : compl_first_match->cp_next;
}
- compl_T *shown_compl = NULL;
bool did_find_shown_match = false;
- int cur = -1;
+ compl_T *shown_compl = NULL;
int i = 0;
- comp = compl_first_match;
+ int cur = -1;
+
do {
+ comp->cp_in_match_array = false;
+ // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
+ // set the cp_score for later comparisons.
+ if (fuzzy_filter && compl_leader.data != NULL && compl_leader.size > 0) {
+ comp->cp_score = fuzzy_match_str(comp->cp_str.data, compl_leader.data);
+ }
+
if (!match_at_original_text(comp)
- && (compl_leader == NULL
- || ins_compl_equal(comp, compl_leader, (size_t)lead_len)
- || (compl_fuzzy_match && comp->cp_score > 0))) {
- if (!shown_match_ok && !compl_fuzzy_match) {
+ && (compl_leader.data == NULL
+ || ins_compl_equal(comp, compl_leader.data, compl_leader.size)
+ || (fuzzy_filter && comp->cp_score > 0))) {
+ compl_match_arraysize++;
+ comp->cp_in_match_array = true;
+ if (match_head == NULL) {
+ match_head = comp;
+ } else {
+ match_tail->cp_match_next = comp;
+ }
+ match_tail = comp;
+ if (!shown_match_ok && !fuzzy_filter) {
if (comp == compl_shown_match || did_find_shown_match) {
// This item is the shown match or this is the
// first displayed item after the shown match.
@@ -1228,64 +1262,36 @@ static int ins_compl_build_pum(void)
shown_compl = comp;
}
cur = i;
- } else if (compl_fuzzy_match) {
+ } else if (fuzzy_filter) {
if (i == 0) {
shown_compl = comp;
}
// Update the maximum fuzzy score and the shown match
// if the current item's score is higher
- if (comp->cp_score > max_fuzzy_score) {
+ if (fuzzy_sort && comp->cp_score > max_fuzzy_score) {
did_find_shown_match = true;
max_fuzzy_score = comp->cp_score;
if (!compl_no_select) {
compl_shown_match = comp;
}
+ } else if (!fuzzy_sort && i == 0 && !compl_no_select) {
+ compl_shown_match = shown_compl;
}
-
if (!shown_match_ok && comp == compl_shown_match && !compl_no_select) {
cur = i;
shown_match_ok = true;
}
-
- // If there is no "no select" condition and the max fuzzy
- // score is positive, or there is no completion leader or the
- // leader length is zero, mark the shown match as valid and
- // reset the current index.
- if (!compl_no_select
- && (max_fuzzy_score > 0
- || (compl_leader == NULL || lead_len == 0))) {
- if (match_at_original_text(compl_shown_match)) {
- compl_shown_match = shown_compl;
- }
- }
- }
-
- if (comp->cp_text[CPT_ABBR] != NULL) {
- compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR];
- } else {
- compl_match_array[i].pum_text = comp->cp_str;
- }
- compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
- compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
- compl_match_array[i].pum_score = comp->cp_score;
- compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr;
- compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr;
- if (comp->cp_text[CPT_MENU] != NULL) {
- compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU];
- } else {
- compl_match_array[i++].pum_extra = comp->cp_fname;
}
+ i++;
}
- if (comp == compl_shown_match && !compl_fuzzy_match) {
+ if (comp == compl_shown_match && !fuzzy_filter) {
did_find_shown_match = true;
-
// When the original text is the shown match don't set
// compl_shown_match.
if (match_at_original_text(comp)) {
shown_match_ok = true;
}
-
if (!shown_match_ok && shown_compl != NULL) {
// The shown match isn't displayed, set it to the
// previously displayed match.
@@ -1296,7 +1302,31 @@ static int ins_compl_build_pum(void)
comp = comp->cp_next;
} while (comp != NULL && !is_first_match(comp));
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
+ if (compl_match_arraysize == 0) {
+ return -1;
+ }
+
+ assert(compl_match_arraysize >= 0);
+ compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
+
+ i = 0;
+ comp = match_head;
+ while (comp != NULL) {
+ compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR] != NULL
+ ? comp->cp_text[CPT_ABBR] : comp->cp_str.data;
+ compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
+ compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
+ compl_match_array[i].pum_score = comp->cp_score;
+ compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr;
+ compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr;
+ compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU] != NULL
+ ? comp->cp_text[CPT_MENU] : comp->cp_fname;
+ compl_T *match_next = comp->cp_match_next;
+ comp->cp_match_next = NULL;
+ comp = match_next;
+ }
+
+ if (fuzzy_sort && compl_leader.data != NULL && compl_leader.size > 0) {
for (i = 0; i < compl_match_arraysize; i++) {
compl_match_array[i].pum_idx = i;
}
@@ -1334,7 +1364,7 @@ void ins_compl_show_pum(void)
} else {
// popup menu already exists, only need to find the current item.
for (int i = 0; i < compl_match_arraysize; i++) {
- if (compl_match_array[i].pum_text == compl_shown_match->cp_str
+ if (compl_match_array[i].pum_text == compl_shown_match->cp_str.data
|| compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i;
break;
@@ -1403,7 +1433,13 @@ bool compl_match_curr_select(int selected)
/// Get current completion leader
char *ins_compl_leader(void)
{
- return compl_leader != NULL ? compl_leader : compl_orig_text;
+ return compl_leader.data != NULL ? compl_leader.data : compl_orig_text.data;
+}
+
+/// Get current completion leader length
+size_t ins_compl_leader_len(void)
+{
+ return compl_leader.data != NULL ? compl_leader.size : compl_orig_text.size;
}
/// Add any identifiers that match the given pattern "pat" in the list of
@@ -1562,6 +1598,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
FILE *fp = os_fopen(files[i], "r"); // open dictionary file
if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc()
+ msg_ext_set_kind("completion");
vim_snprintf(IObuff, IOSIZE,
_("Scanning dictionary: %s"), files[i]);
msg_trunc(IObuff, true, HLF_R);
@@ -1577,11 +1614,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
char *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);
- } else {
- ptr = find_word_end(ptr);
- }
+ ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr) : find_word_end(ptr);
int add_r = ins_compl_add_infercase(regmatch->startp[0],
(int)(ptr - regmatch->startp[0]),
p_ic, files[i], *dir, false);
@@ -1652,9 +1685,8 @@ static char *find_line_end(char *ptr)
/// Free the list of completions
static void ins_compl_free(void)
{
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
if (compl_first_match == NULL) {
return;
@@ -1667,7 +1699,7 @@ static void ins_compl_free(void)
do {
compl_T *match = compl_curr_match;
compl_curr_match = compl_curr_match->cp_next;
- xfree(match->cp_str);
+ API_CLEAR_STRING(match->cp_str);
// several entries may use the same fname, free it just once.
if (match->cp_flags & CP_FREE_FNAME) {
xfree(match->cp_fname);
@@ -1687,12 +1719,12 @@ void ins_compl_clear(void)
compl_cont_status = 0;
compl_started = false;
compl_matches = 0;
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ compl_ins_end_col = 0;
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
edit_submode_extra = NULL;
kv_destroy(compl_orig_extmarks);
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
@@ -1705,6 +1737,12 @@ bool ins_compl_active(void)
return compl_started;
}
+/// Return true when wp is the actual completion window
+bool ins_compl_win_active(win_T *wp)
+{
+ return ins_compl_active() && !(wp->w_p_pvw || wp->w_float_is_info);
+}
+
/// Selected one of the matches. When false the match was edited or using the
/// longest common string.
bool ins_compl_used_match(void)
@@ -1743,12 +1781,33 @@ int ins_compl_len(void)
return compl_length;
}
+/// Return true when preinsert is set otherwise FALSE.
+static bool ins_compl_has_preinsert(void)
+{
+ return (get_cot_flags() & (kOptCotFlagFuzzy|kOptCotFlagPreinsert)) == kOptCotFlagPreinsert;
+}
+
+/// Returns true if the pre-insert effect is valid and the cursor is within
+/// the `compl_ins_end_col` range.
+bool ins_compl_preinsert_effect(void)
+{
+ if (!ins_compl_has_preinsert()) {
+ return false;
+ }
+
+ return curwin->w_cursor.col < compl_ins_end_col;
+}
+
/// Delete one character before the cursor and show the subset of the matches
/// that match the word that is now before the cursor.
/// Returns the character to be used, NUL if the work is done and another char
/// to be got from the user.
int ins_compl_bs(void)
{
+ if (ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
char *line = get_cursor_line_ptr();
char *p = line + curwin->w_cursor.col;
MB_PTR_BACK(line, p);
@@ -1776,8 +1835,9 @@ int ins_compl_bs(void)
// TODO(bfredl): get rid of random update_screen() calls deep inside completion logic
line = get_cursor_line_ptr();
- xfree(compl_leader);
- compl_leader = xstrnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(line + compl_col,
+ (size_t)(p_off - (ptrdiff_t)compl_col));
ins_compl_new_leader();
if (compl_shown_match != NULL) {
@@ -1810,12 +1870,12 @@ static bool ins_compl_need_restart(void)
static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(true);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
compl_used_match = false;
if (compl_started) {
- ins_compl_set_original_text(compl_leader);
+ ins_compl_set_original_text(compl_leader.data, compl_leader.size);
} else {
spell_bad_len = 0; // need to redetect bad word
// Matches were cleared, need to search for them now.
@@ -1833,8 +1893,13 @@ static void ins_compl_new_leader(void)
ins_compl_show_pum();
// Don't let Enter select the original text when there is no popup menu.
+ if (compl_match_array == NULL) {
+ compl_enter_selects = false;
+ } else if (ins_compl_has_preinsert() && compl_leader.size > 0) {
+ ins_compl_insert(false, true);
+ }
// Don't let Enter select when use user function and refresh_always is set
- if (compl_match_array == NULL || ins_compl_refresh_always()) {
+ if (ins_compl_refresh_always()) {
compl_enter_selects = false;
}
}
@@ -1857,6 +1922,10 @@ void ins_compl_addleader(int c)
{
int cc;
+ if (ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
if (stop_arrow() == FAIL) {
return;
}
@@ -1875,9 +1944,9 @@ void ins_compl_addleader(int c)
ins_compl_restart();
}
- xfree(compl_leader);
- compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col,
- (size_t)(curwin->w_cursor.col - compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader();
}
@@ -1897,19 +1966,19 @@ static void ins_compl_restart(void)
}
/// Set the first match, the original text.
-static void ins_compl_set_original_text(char *str)
+static void ins_compl_set_original_text(char *str, size_t len)
FUNC_ATTR_NONNULL_ALL
{
// Replace the original text entry.
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
// be at the last item for backward completion
if (match_at_original_text(compl_first_match)) { // safety check
- xfree(compl_first_match->cp_str);
- compl_first_match->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_str);
+ compl_first_match->cp_str = cbuf_to_string(str, len);
} 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 = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
+ compl_first_match->cp_prev->cp_str = cbuf_to_string(str, len);
}
}
@@ -1919,8 +1988,8 @@ void ins_compl_addfrommatch(void)
{
int len = (int)curwin->w_cursor.col - (int)compl_col;
assert(compl_shown_match != NULL);
- char *p = compl_shown_match->cp_str;
- if ((int)strlen(p) <= len) { // the match is too short
+ char *p = compl_shown_match->cp_str.data;
+ if ((int)compl_shown_match->cp_str.size <= 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)) {
@@ -1928,15 +1997,17 @@ void ins_compl_addfrommatch(void)
}
p = NULL;
+ size_t plen = 0;
for (compl_T *cp = compl_shown_match->cp_next; cp != NULL
&& !is_first_match(cp); cp = cp->cp_next) {
- if (compl_leader == NULL
- || ins_compl_equal(cp, compl_leader, strlen(compl_leader))) {
- p = cp->cp_str;
+ if (compl_leader.data == NULL
+ || ins_compl_equal(cp, compl_leader.data, compl_leader.size)) {
+ p = cp->cp_str.data;
+ plen = cp->cp_str.size;
break;
}
}
- if (p == NULL || (int)strlen(p) <= len) {
+ if (p == NULL || (int)plen <= len) {
return;
}
}
@@ -2076,7 +2147,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// Get here when we have finished typing a sequence of ^N and
// ^P or other completion characters in CTRL-X mode. Free up
// memory that was used, and make sure we can redo the insert.
- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
+ if (compl_curr_match != NULL || compl_leader.data != NULL || c == Ctrl_E) {
// If any of the original typed text has been changed, eg when
// ignorecase is set, we must add back-spaces to the redo
// buffer. We add as few as necessary to delete just the part
@@ -2085,7 +2156,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// CTRL-E then don't use the current match.
char *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
+ ptr = compl_curr_match->cp_str.data;
} else {
ptr = NULL;
}
@@ -2121,36 +2192,46 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
}
+ char *word = NULL;
// If the popup menu is displayed pressing CTRL-Y means accepting
// the selection without inserting anything. When
// compl_enter_selects is set the Enter key does the same.
if ((c == Ctrl_Y || (compl_enter_selects
&& (c == CAR || c == K_KENTER || c == NL)))
&& pum_visible()) {
+ word = xstrdup(compl_shown_match->cp_str.data);
retval = true;
+ // May need to remove ComplMatchIns highlight.
+ redrawWinline(curwin, curwin->w_cursor.lnum);
}
// CTRL-E means completion is Ended, go back to the typed text.
// but only do this, if the Popup is still visible
if (c == Ctrl_E) {
- ins_compl_delete();
+ ins_compl_delete(false);
char *p = NULL;
- if (compl_leader != NULL) {
- p = compl_leader;
+ size_t plen = 0;
+ if (compl_leader.data != NULL) {
+ p = compl_leader.data;
+ plen = compl_leader.size;
} else if (compl_first_match != NULL) {
- p = compl_orig_text;
+ p = compl_orig_text.data;
+ plen = compl_orig_text.size;
}
if (p != NULL) {
const int compl_len = get_compl_len();
- const int len = (int)strlen(p);
- if (len > compl_len) {
- ins_bytes_len(p + compl_len, (size_t)(len - compl_len));
+ if ((int)plen > compl_len) {
+ ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
}
}
restore_orig_extmarks();
retval = true;
}
+ if ((c == Ctrl_W || c == Ctrl_U) && ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
auto_format(false, true);
// Trigger the CompleteDonePre event to give scripts a chance to
@@ -2184,7 +2265,8 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
// Trigger the CompleteDone event to give scripts a chance to act
// upon the end of completion.
- do_autocmd_completedone(c);
+ do_autocmd_completedone(c, prev_mode, word);
+ xfree(word);
return retval;
}
@@ -2236,7 +2318,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 = (get_cot_flags() & COT_LONGEST) != 0;
+ compl_get_longest = (get_cot_flags() & kOptCotFlagLongest) != 0;
compl_used_match = true;
}
@@ -2273,7 +2355,7 @@ bool ins_compl_prep(int c)
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act
// upon the (possibly failed) completion.
- do_autocmd_completedone(c);
+ do_autocmd_completedone(c, ctrl_x_mode, NULL);
}
may_trigger_modechanged();
@@ -2293,18 +2375,18 @@ bool ins_compl_prep(int c)
/// "ptr" is the known leader text or NUL.
static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
{
- int len;
+ int len = 0;
char *ptr = ptr_arg;
if (ptr == NULL) {
- if (compl_leader != NULL) {
- ptr = compl_leader;
+ if (compl_leader.data != NULL) {
+ ptr = compl_leader.data;
} else {
return; // nothing to do
}
}
- if (compl_orig_text != NULL) {
- char *p = compl_orig_text;
+ if (compl_orig_text.data != NULL) {
+ char *p = compl_orig_text.data;
for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {}
if (len > 0) {
len -= utf_head_off(p, p + len);
@@ -2312,8 +2394,6 @@ static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
for (p += len; *p != NUL; MB_PTR_ADV(p)) {
AppendCharToRedobuff(K_BS);
}
- } else {
- len = 0;
}
AppendToRedobuffLit(ptr + len, -1);
}
@@ -2573,9 +2653,8 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
int flags = fast ? CP_FAST : 0;
char *(cptext[CPT_COUNT]);
char *user_abbr_hlname = NULL;
- int user_abbr_hlattr = -1;
char *user_kind_hlname = NULL;
- int user_kind_hlattr = -1;
+ int user_hl[2] = { -1, -1 };
typval_T user_data;
user_data.v_type = VAR_UNKNOWN;
@@ -2587,10 +2666,10 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true);
user_abbr_hlname = tv_dict_get_string(tv->vval.v_dict, "abbr_hlgroup", false);
- user_abbr_hlattr = get_user_highlight_attr(user_abbr_hlname);
+ user_hl[0] = get_user_highlight_attr(user_abbr_hlname);
user_kind_hlname = tv_dict_get_string(tv->vval.v_dict, "kind_hlgroup", false);
- user_kind_hlattr = get_user_highlight_attr(user_kind_hlname);
+ user_hl[1] = get_user_highlight_attr(user_kind_hlname);
tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data);
@@ -2613,8 +2692,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
return FAIL;
}
int status = ins_compl_add((char *)word, -1, NULL, cptext, true,
- &user_data, dir, flags, dup,
- user_abbr_hlattr, user_kind_hlattr);
+ &user_data, dir, flags, dup, user_hl);
if (status != OK) {
tv_clear(&user_data);
}
@@ -2682,9 +2760,9 @@ static void set_completion(colnr_T startcol, list_T *list)
{
int flags = CP_ORIGINAL_TEXT;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_longest = (cur_cot_flags & COT_LONGEST) != 0;
- bool compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
+ bool compl_longest = (cur_cot_flags & kOptCotFlagLongest) != 0;
+ bool compl_no_insert = (cur_cot_flags & kOptCotFlagNoinsert) != 0;
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
// If already doing completions stop it.
if (ctrl_x_mode_not_default()) {
@@ -2701,13 +2779,15 @@ static void set_completion(colnr_T startcol, list_T *list)
compl_col = startcol;
compl_length = curwin->w_cursor.col - startcol;
// compl_pattern doesn't need to be set
- compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, (size_t)compl_length);
+ compl_orig_text = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)compl_length);
save_orig_extmarks();
if (p_ic) {
flags |= CP_ICASE;
}
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
- flags | CP_FAST, false, -1, -1) != OK) {
+ if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size,
+ NULL, NULL, false, NULL, 0,
+ flags | CP_FAST, false, NULL) != OK) {
return;
}
@@ -2841,6 +2921,25 @@ static void ins_compl_update_sequence_numbers(void)
}
}
+/// Fill the dict of complete_info
+static void fill_complete_info_dict(dict_T *di, compl_T *match, bool add_match)
+{
+ tv_dict_add_str(di, S_LEN("word"), match->cp_str.data);
+ tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
+ tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
+ tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]);
+ tv_dict_add_str(di, S_LEN("info"), match->cp_text[CPT_INFO]);
+ if (add_match) {
+ tv_dict_add_bool(di, S_LEN("match"), match->cp_in_match_array);
+ }
+ 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);
+ }
+}
+
/// Get complete information
static void get_complete_info(list_T *what_list, dict_T *retdict)
{
@@ -2848,12 +2947,13 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
#define CI_WHAT_PUM_VISIBLE 0x02
#define CI_WHAT_ITEMS 0x04
#define CI_WHAT_SELECTED 0x08
-#define CI_WHAT_INSERTED 0x10
+#define CI_WHAT_COMPLETED 0x10
+#define CI_WHAT_MATCHES 0x20
#define CI_WHAT_ALL 0xff
int what_flag;
if (what_list == NULL) {
- what_flag = CI_WHAT_ALL;
+ what_flag = CI_WHAT_ALL & ~(CI_WHAT_MATCHES|CI_WHAT_COMPLETED);
} else {
what_flag = 0;
for (listitem_T *item = tv_list_first(what_list)
@@ -2869,8 +2969,10 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
what_flag |= CI_WHAT_ITEMS;
} else if (strcmp(what, "selected") == 0) {
what_flag |= CI_WHAT_SELECTED;
- } else if (strcmp(what, "inserted") == 0) {
- what_flag |= CI_WHAT_INSERTED;
+ } else if (strcmp(what, "completed") == 0) {
+ what_flag |= CI_WHAT_COMPLETED;
+ } else if (strcmp(what, "matches") == 0) {
+ what_flag |= CI_WHAT_MATCHES;
}
}
}
@@ -2884,12 +2986,17 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible());
}
- if (ret == OK && (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_SELECTED)) {
+ if (ret == OK && (what_flag & (CI_WHAT_ITEMS|CI_WHAT_SELECTED
+ |CI_WHAT_MATCHES|CI_WHAT_COMPLETED))) {
list_T *li = NULL;
int selected_idx = -1;
- if (what_flag & CI_WHAT_ITEMS) {
+ bool has_items = what_flag & CI_WHAT_ITEMS;
+ bool has_matches = what_flag & CI_WHAT_MATCHES;
+ bool has_completed = what_flag & CI_WHAT_COMPLETED;
+ if (has_items || has_matches) {
li = tv_list_alloc(kListLenMayKnow);
- ret = tv_dict_add_list(retdict, S_LEN("items"), li);
+ const char *key = (has_matches && !has_items) ? "matches" : "items";
+ ret = tv_dict_add_list(retdict, key, strlen(key), li);
}
if (ret == OK && what_flag & CI_WHAT_SELECTED) {
if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) {
@@ -2901,20 +3008,10 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
compl_T *match = compl_first_match;
do {
if (!match_at_original_text(match)) {
- if (what_flag & CI_WHAT_ITEMS) {
+ if (has_items || (has_matches && match->cp_in_match_array)) {
dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di);
- tv_dict_add_str(di, S_LEN("word"), match->cp_str);
- tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
- tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
- tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]);
- tv_dict_add_str(di, S_LEN("info"), 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);
- }
+ fill_complete_info_dict(di, match, has_matches && has_items);
}
if (compl_curr_match != NULL
&& compl_curr_match->cp_number == match->cp_number) {
@@ -2933,11 +3030,14 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle);
}
}
+ if (ret == OK && selected_idx != -1 && has_completed) {
+ dict_T *di = tv_dict_alloc();
+ fill_complete_info_dict(di, compl_curr_match, false);
+ ret = tv_dict_add_dict(retdict, S_LEN("completed"), di);
+ }
}
(void)ret;
- // TODO(vim):
- // if (ret == OK && (what_flag & CI_WHAT_INSERTED))
}
/// "complete_info()" function
@@ -3040,6 +3140,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
}
if (!shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc()
+ msg_ext_set_kind("completion");
vim_snprintf(IObuff, IOSIZE, _("Scanning: %s"),
st->ins_buf->b_fname == NULL
? buf_spname(st->ins_buf)
@@ -3072,6 +3173,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
} else if (*st->e_cpt == ']' || *st->e_cpt == 't') {
compl_type = CTRL_X_TAGS;
if (!shortmess(SHM_COMPLETIONSCAN)) {
+ msg_ext_set_kind("completion");
msg_hist_off = true; // reset in msg_trunc()
vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags."));
msg_trunc(IObuff, true, HLF_R);
@@ -3096,8 +3198,8 @@ done:
/// included files.
static void get_next_include_file_completion(int compl_type)
{
- find_pattern_in_path(compl_pattern, compl_direction,
- compl_patternlen, false, false,
+ find_pattern_in_path(compl_pattern.data, compl_direction,
+ compl_pattern.size, false, false,
((compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY),
@@ -3109,14 +3211,14 @@ static void get_next_include_file_completion(int compl_type)
static void get_next_dict_tsr_completion(int compl_type, char *dict, int dict_f)
{
if (thesaurus_func_complete(compl_type)) {
- expand_by_function(compl_type, compl_pattern);
+ expand_by_function(compl_type, compl_pattern.data);
} else {
ins_compl_dictionaries(dict != NULL
? dict
: (compl_type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
: (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
- compl_pattern,
+ compl_pattern.data,
dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS);
}
@@ -3127,14 +3229,14 @@ static void get_next_tag_completion(void)
{
// set p_ic according to p_ic, p_scs and pat for find_tags().
const int save_p_ic = p_ic;
- p_ic = ignorecase(compl_pattern);
+ p_ic = ignorecase(compl_pattern.data);
// Find up to TAG_MANY matches. Avoids that an enormous number
// of matches is found when compl_pattern is empty
g_tag_at_cursor = true;
char **matches;
int num_matches;
- if (find_tags(compl_pattern, &num_matches, &matches,
+ if (find_tags(compl_pattern.data, &num_matches, &matches,
TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
| (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
@@ -3149,13 +3251,13 @@ static void get_next_filename_completion(void)
{
char **matches;
int num_matches;
- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+ if (expand_wildcards(1, &compl_pattern.data, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
return;
}
// May change home directory back to "~".
- tilde_replace(compl_pattern, num_matches, matches);
+ tilde_replace(compl_pattern.data, num_matches, matches);
#ifdef BACKSLASH_IN_FILENAME
if (curbuf->b_p_csl[0] != NUL) {
for (int i = 0; i < num_matches; i++) {
@@ -3179,8 +3281,8 @@ static void get_next_cmdline_completion(void)
{
char **matches;
int num_matches;
- if (expand_cmdline(&compl_xp, compl_pattern,
- (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) {
+ if (expand_cmdline(&compl_xp, compl_pattern.data,
+ (int)compl_pattern.size, &num_matches, &matches) == EXPAND_OK) {
ins_compl_add_matches(num_matches, matches, false);
}
}
@@ -3189,7 +3291,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, compl_pattern, &matches);
+ int num_matches = expand_spelling(lnum, compl_pattern.data, &matches);
if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic);
} else {
@@ -3208,22 +3310,24 @@ static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_po
{
*match_len = 0;
char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum) + cur_match_pos->col;
- int len;
+ int len = ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col;
if (ctrl_x_mode_line_or_eval()) {
if (compl_status_adding()) {
if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) {
return NULL;
}
ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1);
+ len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1);
if (!p_paste) {
- ptr = skipwhite(ptr);
+ char *tmp_ptr = ptr;
+ ptr = skipwhite(tmp_ptr);
+ len -= (int)(ptr - tmp_ptr);
}
}
- len = (int)strlen(ptr);
} else {
char *tmp_ptr = ptr;
- if (compl_status_adding() && compl_length <= (int)strlen(tmp_ptr)) {
+ if (compl_status_adding() && compl_length <= len) {
tmp_ptr += compl_length;
// Skip if already inside a word.
if (vim_iswordp(tmp_ptr)) {
@@ -3319,10 +3423,11 @@ 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, compl_pattern);
+ compl_direction, compl_pattern.data);
} else {
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
- NULL, compl_direction, compl_pattern, compl_patternlen,
+ NULL, compl_direction, compl_pattern.data,
+ compl_pattern.size,
1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
}
msg_silent--;
@@ -3368,7 +3473,8 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
int len;
char *ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
&len, &cont_s_ipos);
- if (ptr == NULL) {
+ if (ptr == NULL
+ || (ins_compl_has_preinsert() && strcmp(ptr, compl_pattern.data) == 0)) {
continue;
}
if (ins_compl_add_infercase(ptr, len, p_ic,
@@ -3419,7 +3525,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, compl_pattern);
+ expand_by_function(type, compl_pattern.data);
break;
case CTRL_X_SPELL:
@@ -3450,9 +3556,9 @@ static void get_next_bufname_token(void)
FOR_ALL_BUFFERS(b) {
if (b->b_p_bl && b->b_sfname != NULL) {
char *tail = path_tail(b->b_sfname);
- if (strncmp(tail, compl_orig_text, strlen(compl_orig_text)) == 0) {
+ if (strncmp(tail, compl_orig_text.data, compl_orig_text.size) == 0) {
ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0,
- p_ic ? CP_ICASE : 0, false, -1, -1);
+ p_ic ? CP_ICASE : 0, false, NULL);
}
}
}
@@ -3518,7 +3624,7 @@ static int ins_compl_get_exp(pos_T *ini)
// If complete() was called then compl_pattern has been reset.
// The following won't work then, bail out.
- if (compl_pattern == NULL) {
+ if (compl_pattern.data == NULL) {
break;
}
@@ -3586,7 +3692,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.data, compl_leader.size)
&& compl_shown_match->cp_next != NULL
&& !is_first_match(compl_shown_match->cp_next)) {
compl_shown_match = compl_shown_match->cp_next;
@@ -3595,10 +3701,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.data, compl_leader.size)
&& (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.data, compl_leader.size)
&& compl_shown_match->cp_prev != NULL
&& !is_first_match(compl_shown_match->cp_prev)) {
compl_shown_match = compl_shown_match->cp_prev;
@@ -3607,16 +3713,36 @@ static void ins_compl_update_shown_match(void)
}
/// Delete the old text being completed.
-void ins_compl_delete(void)
+void ins_compl_delete(bool new_leader)
{
+ // Avoid deleting text that will be reinserted when changing leader. This
+ // allows marks present on the original text to shrink/grow appropriately.
+ int orig_col = 0;
+ if (new_leader) {
+ char *orig = compl_orig_text.data;
+ char *leader = ins_compl_leader();
+ while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) {
+ leader += utf_ptr2len(leader);
+ orig += utf_ptr2len(orig);
+ }
+ orig_col = (int)(orig - compl_orig_text.data);
+ }
+
// In insert mode: Delete the typed part.
// In replace mode: Put the old characters back, if any.
- int col = compl_col + (compl_status_adding() ? compl_length : 0);
+ int col = compl_col + (compl_status_adding() ? compl_length : orig_col);
+ bool has_preinsert = ins_compl_preinsert_effect();
+ if (has_preinsert) {
+ col += (int)ins_compl_leader_len();
+ curwin->w_cursor.col = compl_ins_end_col;
+ }
+
if ((int)curwin->w_cursor.col > col) {
if (stop_arrow() == FAIL) {
return;
}
backspace_until_column(col);
+ compl_ins_end_col = curwin->w_cursor.col;
}
// TODO(vim): is this sufficient for redrawing? Redrawing everything
@@ -3628,15 +3754,25 @@ void ins_compl_delete(void)
/// Insert the new text being completed.
/// "in_compl_func" is true when called from complete_check().
-void ins_compl_insert(bool in_compl_func)
+/// "move_cursor" is used when 'completeopt' includes "preinsert" and when true
+/// cursor needs to move back from the inserted text to the compl_leader.
+void ins_compl_insert(bool in_compl_func, bool move_cursor)
{
int compl_len = get_compl_len();
+ bool preinsert = ins_compl_has_preinsert();
+ char *cp_str = compl_shown_match->cp_str.data;
+ size_t cp_str_len = compl_shown_match->cp_str.size;
+ size_t leader_len = ins_compl_leader_len();
+
// Make sure we don't go over the end of the string, this can happen with
// illegal bytes.
- if (compl_len < (int)strlen(compl_shown_match->cp_str)) {
- ins_bytes(compl_shown_match->cp_str + compl_len);
+ if (compl_len < (int)cp_str_len) {
+ ins_compl_insert_bytes(cp_str + compl_len, -1);
+ if (preinsert && move_cursor) {
+ curwin->w_cursor.col -= (colnr_T)(cp_str_len - leader_len);
+ }
}
- compl_used_match = !match_at_original_text(compl_shown_match);
+ compl_used_match = !(match_at_original_text(compl_shown_match) || preinsert);
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
@@ -3704,7 +3840,7 @@ static compl_T *find_comp_when_fuzzy(void)
comp = compl_first_match;
do {
if (comp->cp_score == score
- && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) {
+ && (str == comp->cp_str.data || str == comp->cp_text[CPT_ABBR])) {
return comp;
}
comp = comp->cp_next;
@@ -3731,8 +3867,8 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
bool found_end = false;
compl_T *found_compl = NULL;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
+ bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
while (--todo >= 0) {
if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) {
@@ -3787,9 +3923,9 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
found_end = false;
}
if (!match_at_original_text(compl_shown_match)
- && compl_leader != NULL
+ && compl_leader.data != NULL
&& !ins_compl_equal(compl_shown_match,
- compl_leader, strlen(compl_leader))
+ compl_leader.data, compl_leader.size)
&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) {
todo++;
} else {
@@ -3836,8 +3972,9 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
const bool started = compl_started;
buf_T *const orig_curbuf = curbuf;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
+ bool compl_no_insert = (cur_cot_flags & kOptCotFlagNoinsert) != 0;
+ bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
+ bool compl_preinsert = ins_compl_has_preinsert();
// When user complete function return -1 for findstart which is next
// time of 'always', compl_shown_match become NULL.
@@ -3845,7 +3982,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
return -1;
}
- if (compl_leader != NULL
+ if (compl_leader.data != NULL
&& !match_at_original_text(compl_shown_match)
&& !compl_fuzzy_match) {
// Update "compl_shown_match" to the actually shown match
@@ -3855,7 +3992,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
if (allow_get_expansion && insert_match
&& (!compl_get_longest || compl_used_match)) {
// Delete old text to be replaced
- ins_compl_delete();
+ ins_compl_delete(false);
}
// When finding the longest common text we stick at the original text,
@@ -3882,17 +4019,18 @@ 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(compl_orig_text + get_compl_len());
+ if (compl_no_insert && !started && !compl_preinsert) {
+ ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1);
compl_used_match = false;
restore_orig_extmarks();
} else if (insert_match) {
if (!compl_get_longest || compl_used_match) {
- ins_compl_insert(in_compl_func);
+ ins_compl_insert(in_compl_func, true);
} else {
- ins_bytes(compl_leader + get_compl_len());
+ assert(compl_leader.data != NULL);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
}
- if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) {
+ if (strequal(compl_curr_match->cp_str.data, compl_orig_text.data)) {
restore_orig_extmarks();
}
} else {
@@ -3908,7 +4046,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
// Delete old text to be replaced, since we're still searching and
// don't want to match ourselves!
- ins_compl_delete();
+ ins_compl_delete(false);
}
// Enter will select a match when the match wasn't inserted and the popup
@@ -3976,7 +4114,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func)
}
}
}
- if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT)) {
+ if (compl_pending != 0 && !got_int && !(cot_flags & kOptCotFlagNoinsert)) {
int todo = compl_pending > 0 ? compl_pending : -compl_pending;
compl_pending = 0;
@@ -4056,8 +4194,7 @@ static bool ins_compl_use_match(int c)
/// Get the pattern, column and length for normal completion (CTRL-N CTRL-P
/// completion)
-/// Sets the global variables: compl_col, compl_length, compl_pattern and
-/// compl_patternlen.
+/// 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 *line, int startcol, colnr_T curs_col)
{
@@ -4068,29 +4205,32 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
compl_length = curs_col - startcol;
}
if (p_ic) {
- compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = cstr_as_string(str_foldcase(line + compl_col,
+ compl_length, NULL, 0));
} else {
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
}
} else if (compl_status_adding()) {
char *prefix = "\\<";
size_t prefixlen = STRLEN_LITERAL("\\<");
- // we need up to 2 extra chars for the prefix
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col,
- compl_length) + prefixlen);
if (!vim_iswordp(line + compl_col)
|| (compl_col > 0
&& (vim_iswordp(mb_prevptr(line, line + compl_col))))) {
prefix = "";
prefixlen = 0;
}
- STRCPY(compl_pattern, prefix);
- quote_meta(compl_pattern + prefixlen, line + compl_col, compl_length);
+
+ // we need up to 2 extra chars for the prefix
+ size_t n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen;
+ compl_pattern.data = xmalloc(n);
+ STRCPY(compl_pattern.data, prefix);
+ quote_meta(compl_pattern.data + prefixlen, line + compl_col, compl_length);
+ compl_pattern.size = n - 1;
} else if (--startcol < 0
|| !vim_iswordp(mb_prevptr(line, line + startcol + 1))) {
// Match any word of at least two chars
- compl_pattern = xstrnsave(S_LEN("\\<\\k\\k"));
+ compl_pattern = cbuf_to_string(S_LEN("\\<\\k\\k"));
compl_col += curs_col;
compl_length = 0;
} else {
@@ -4111,19 +4251,20 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
// Only match word with at least two chars -- webb
// there's no need to call quote_meta,
// xmalloc(7) is enough -- Acevedo
- compl_pattern = xmalloc(7);
- STRCPY(compl_pattern, "\\<");
- quote_meta(compl_pattern + 2, line + compl_col, 1);
- strcat(compl_pattern, "\\k");
+ compl_pattern.data = xmalloc(7);
+ STRCPY(compl_pattern.data, "\\<");
+ quote_meta(compl_pattern.data + 2, line + compl_col, 1);
+ strcat(compl_pattern.data, "\\k");
+ compl_pattern.size = strlen(compl_pattern.data);
} else {
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2);
- STRCPY(compl_pattern, "\\<");
- quote_meta(compl_pattern + 2, line + compl_col, compl_length);
+ size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2;
+ compl_pattern.data = xmalloc(n);
+ STRCPY(compl_pattern.data, "\\<");
+ quote_meta(compl_pattern.data + 2, line + compl_col, compl_length);
+ compl_pattern.size = n - 1;
}
}
- compl_patternlen = strlen(compl_pattern);
-
return OK;
}
@@ -4138,13 +4279,12 @@ static int get_wholeline_compl_info(char *line, colnr_T curs_col)
compl_length = 0;
}
if (p_ic) {
- compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = cstr_as_string(str_foldcase(line + compl_col,
+ compl_length, NULL, 0));
} else {
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
}
- compl_patternlen = strlen(compl_pattern);
-
return OK;
}
@@ -4169,8 +4309,8 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
compl_col += startcol;
compl_length = (int)curs_col - startcol;
- compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES);
- compl_patternlen = strlen(compl_pattern);
+ compl_pattern = cstr_as_string(addstar(line + compl_col,
+ (size_t)compl_length, EXPAND_FILES));
return OK;
}
@@ -4179,9 +4319,9 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
/// Sets the global variables: compl_col, compl_length and compl_pattern.
static int get_cmdline_compl_info(char *line, colnr_T curs_col)
{
- compl_pattern = xstrnsave(line, (size_t)curs_col);
- compl_patternlen = (size_t)curs_col;
- set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false);
+ compl_pattern = cbuf_to_string(line, (size_t)curs_col);
+ set_cmd_context(&compl_xp, compl_pattern.data,
+ (int)compl_pattern.size, curs_col, false);
if (compl_xp.xp_context == EXPAND_LUA) {
nlua_expand_pat(&compl_xp);
}
@@ -4191,7 +4331,7 @@ static int get_cmdline_compl_info(char *line, colnr_T curs_col)
// "pattern not found" message.
compl_col = curs_col;
} else {
- compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
+ compl_col = (int)(compl_xp.xp_pattern - compl_pattern.data);
}
compl_length = curs_col - compl_col;
@@ -4269,8 +4409,7 @@ static int get_userdefined_compl_info(colnr_T curs_col)
// it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum);
compl_length = curs_col - compl_col;
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
- compl_patternlen = (size_t)compl_length;
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -4295,8 +4434,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
}
// Need to obtain "line" again, it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum);
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
- compl_patternlen = (size_t)compl_length;
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -4481,19 +4619,19 @@ static int ins_compl_start(void)
ins_compl_fixRedoBufForLeader(NULL);
// Always add completion for the original text.
- xfree(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
- compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_orig_text = cbuf_to_string(line + compl_col, (size_t)compl_length);
save_orig_extmarks();
int flags = CP_ORIGINAL_TEXT;
if (p_ic) {
flags |= CP_ICASE;
}
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
- flags, false, -1, -1) != OK) {
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_orig_text);
+ if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size,
+ NULL, NULL, false, NULL, 0,
+ flags, false, NULL) != OK) {
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
return FAIL;
}
@@ -4567,6 +4705,7 @@ static void ins_compl_show_statusmsg(void)
if (edit_submode_extra != NULL) {
if (!p_smd) {
msg_hist_off = true;
+ msg_ext_set_kind("completion");
msg(edit_submode_extra, (edit_submode_highl < HLF_COUNT
? (int)edit_submode_highl + 1 : 0));
msg_hist_off = false;
@@ -4726,7 +4865,7 @@ static unsigned quote_meta(char *dest, char *src, int len)
#if defined(EXITFREE)
void free_insexpand_stuff(void)
{
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
callback_free(&cfu_cb);
callback_free(&ofu_cb);
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index f7215d3d12..a9f8c9222a 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -917,7 +917,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
result[dlen++] = (char)K_SPECIAL;
result[dlen++] = (char)KS_EXTRA;
result[dlen++] = KE_SNR;
- snprintf(result + dlen, buf_len - dlen, "%" PRId64, (int64_t)sid);
+ snprintf(result + dlen, buf_len - dlen, "%" PRIdSCID, sid);
dlen += strlen(result + dlen);
result[dlen++] = '_';
continue;
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
index 39dfb8eeb9..79f4c0d692 100644
--- a/src/nvim/linematch.c
+++ b/src/nvim/linematch.c
@@ -3,9 +3,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-#include <string.h>
-#include "nvim/ascii_defs.h"
#include "nvim/linematch.h"
#include "nvim/macros_defs.h"
#include "nvim/memory.h"
@@ -34,12 +32,8 @@ struct diffcmppath_S {
static size_t line_len(const mmfile_t *m)
{
char *s = m->ptr;
- size_t n = (size_t)m->size;
- char *end = strnchr(s, &n, '\n');
- if (end) {
- return (size_t)(end - s);
- }
- return (size_t)m->size;
+ char *end = memchr(s, '\n', (size_t)m->size);
+ return end ? (size_t)(end - s) : (size_t)m->size;
}
#define MATCH_CHAR_MAX_LEN 800
@@ -150,9 +144,9 @@ static int count_n_matched_chars(mmfile_t **sp, const size_t n, bool iwhite)
mmfile_t fastforward_buf_to_lnum(mmfile_t s, linenr_T lnum)
{
for (int i = 0; i < lnum - 1; i++) {
- size_t n = (size_t)s.size;
- s.ptr = strnchr(s.ptr, &n, '\n');
- s.size = (int)n;
+ char *line_end = memchr(s.ptr, '\n', (size_t)s.size);
+ s.size = line_end ? (int)(s.size - (line_end - s.ptr)) : 0;
+ s.ptr = line_end;
if (!s.ptr) {
break;
}
diff --git a/src/nvim/linematch.h b/src/nvim/linematch.h
index 5f6667a7df..08daf0e16c 100644
--- a/src/nvim/linematch.h
+++ b/src/nvim/linematch.h
@@ -3,7 +3,7 @@
#include <stddef.h> // IWYU pragma: keep
#include "nvim/pos_defs.h" // IWYU pragma: keep
-#include "xdiff/xdiff.h"
+#include "xdiff/xdiff.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "linematch.h.generated.h"
diff --git a/src/nvim/lua/api_wrappers.c b/src/nvim/lua/api_wrappers.c
index 36847d1fc9..447ba846b4 100644
--- a/src/nvim/lua/api_wrappers.c
+++ b/src/nvim/lua/api_wrappers.c
@@ -1,19 +1,19 @@
-#include <lauxlib.h>
-#include <lua.h>
-#include <lualib.h>
+#include <lauxlib.h> // IWYU pragma: keep
+#include <lua.h> // IWYU pragma: keep
+#include <lualib.h> // IWYU pragma: keep
-#include "nvim/api/private/defs.h"
-#include "nvim/api/private/dispatch.h"
-#include "nvim/api/private/helpers.h"
-#include "nvim/errors.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/func_attr.h"
-#include "nvim/globals.h"
-#include "nvim/lua/converter.h"
-#include "nvim/lua/executor.h"
-#include "nvim/memory.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
+#include "nvim/api/private/dispatch.h" // IWYU pragma: keep
+#include "nvim/api/private/helpers.h" // IWYU pragma: keep
+#include "nvim/errors.h" // IWYU pragma: keep
+#include "nvim/ex_docmd.h" // IWYU pragma: keep
+#include "nvim/ex_getln.h" // IWYU pragma: keep
+#include "nvim/func_attr.h" // IWYU pragma: keep
+#include "nvim/globals.h" // IWYU pragma: keep
+#include "nvim/lua/converter.h" // IWYU pragma: keep
+#include "nvim/lua/executor.h" // IWYU pragma: keep
+#include "nvim/memory.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "lua_api_c_bindings.generated.h"
+# include "lua_api_c_bindings.generated.h" // IWYU pragma: keep
#endif
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 45ead154bc..292588ee64 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -22,6 +22,7 @@
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index c4fa8b0fff..71c5cd4585 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -23,7 +23,6 @@
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
-#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
@@ -47,10 +46,12 @@
#include "nvim/lua/treesitter.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
#include "nvim/message.h"
+#include "nvim/message_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/option_vars.h"
#include "nvim/os/fileio.h"
@@ -275,10 +276,9 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres
#endif
}
const char *error = lua_tostring(lstate, -1);
-
loop_schedule_deferred(&main_loop,
event_create(nlua_luv_error_event,
- xstrdup(error),
+ error != NULL ? xstrdup(error) : NULL,
(void *)(intptr_t)(is_callback
? kThreadCallback
: kThread)));
@@ -957,7 +957,7 @@ static void nlua_print_event(void **argv)
HlMessage msg = KV_INITIAL_VALUE;
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
kv_push(msg, chunk);
- msg_multihl(msg, "lua_print", true);
+ msg_multihl(msg, "lua_print", true, false);
}
/// Print as a Vim message
@@ -1587,8 +1587,8 @@ Object nlua_call_ref_ctx(bool fast, LuaRef ref, const char *name, Array args, Lu
if (nlua_fast_cfpcall(lstate, nargs, 1, -1) < 0) {
// error is already scheduled, set anyways to convey failure.
api_set_error(err, kErrorTypeException, "fast context failure");
+ return NIL;
}
- return NIL;
} else if (nlua_pcall(lstate, nargs, 1)) {
// if err is passed, the caller will deal with the error.
if (err) {
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 32fde3853b..3a0c03412f 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -4,10 +4,10 @@
#include <lua.h>
#include <stdbool.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
-#include "nvim/func_attr.h"
#include "nvim/macros_defs.h"
#include "nvim/types_defs.h"
#include "nvim/usercmd.h" // IWYU pragma: keep
diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c
index 61277949c4..1560881b55 100644
--- a/src/nvim/lua/secure.c
+++ b/src/nvim/lua/secure.c
@@ -2,11 +2,11 @@
#include <stdbool.h>
#include <string.h>
+#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
#include "nvim/errors.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/lua/secure.h"
#include "nvim/memory.h"
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
index f4dacd7a55..eec5892307 100644
--- a/src/nvim/lua/spell.c
+++ b/src/nvim/lua/spell.c
@@ -16,7 +16,7 @@
#include "nvim/spell.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "lua/spell.c.generated.h"
+# include "lua/spell.c.generated.h" // IWYU pragma: keep
#endif
int nlua_spell_check(lua_State *lstate)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index e719d99640..2fc9367ead 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -18,11 +18,13 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer_defs.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_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fold.h"
@@ -619,41 +621,36 @@ static int nlua_with(lua_State *L)
int rets = 0;
cmdmod_T save_cmdmod = cmdmod;
+ CLEAR_FIELD(cmdmod);
cmdmod.cmod_flags = flags;
apply_cmdmod(&cmdmod);
- if (buf || win) {
- try_start();
- }
-
- aco_save_T aco;
- win_execute_T win_execute_args;
Error err = ERROR_INIT;
+ TRY_WRAP(&err, {
+ aco_save_T aco;
+ win_execute_T win_execute_args;
- if (win) {
- tabpage_T *tabpage = win_find_tabpage(win);
- if (!win_execute_before(&win_execute_args, win, tabpage)) {
- goto end;
+ if (win) {
+ tabpage_T *tabpage = win_find_tabpage(win);
+ if (!win_execute_before(&win_execute_args, win, tabpage)) {
+ goto end;
+ }
+ } else if (buf) {
+ aucmd_prepbuf(&aco, buf);
}
- } else if (buf) {
- aucmd_prepbuf(&aco, buf);
- }
- int s = lua_gettop(L);
- lua_pushvalue(L, 2);
- status = lua_pcall(L, 0, LUA_MULTRET, 0);
- rets = lua_gettop(L) - s;
+ int s = lua_gettop(L);
+ lua_pushvalue(L, 2);
+ status = lua_pcall(L, 0, LUA_MULTRET, 0);
+ rets = lua_gettop(L) - s;
- if (win) {
- win_execute_after(&win_execute_args);
- } else if (buf) {
- aucmd_restbuf(&aco);
- }
-
-end:
- if (buf || win) {
- try_end(&err);
- }
+ if (win) {
+ win_execute_after(&win_execute_args);
+ } else if (buf) {
+ aucmd_restbuf(&aco);
+ }
+ end:;
+ });
undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod;
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index c80e7b7672..3e33fcd142 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -17,10 +17,12 @@
#ifdef HAVE_WASMTIME
# include <wasm.h>
+
+# include "nvim/os/fs.h"
#endif
-#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
#include "nvim/lua/treesitter.h"
@@ -28,7 +30,6 @@
#include "nvim/map_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/os/fs.h"
#include "nvim/pos_defs.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
@@ -127,9 +128,9 @@ static const TSLanguage *load_language_from_object(lua_State *L, const char *pat
{
uv_lib_t lib;
if (uv_dlopen(path, &lib)) {
+ xstrlcpy(IObuff, uv_dlerror(&lib), sizeof(IObuff));
uv_dlclose(&lib);
- luaL_error(L, "Failed to load parser for language '%s': uv_dlopen: %s",
- lang_name, uv_dlerror(&lib));
+ luaL_error(L, "Failed to load parser for language '%s': uv_dlopen: %s", lang_name, IObuff);
}
char symbol_buf[128];
@@ -137,8 +138,9 @@ static const TSLanguage *load_language_from_object(lua_State *L, const char *pat
TSLanguage *(*lang_parser)(void);
if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) {
+ xstrlcpy(IObuff, uv_dlerror(&lib), sizeof(IObuff));
uv_dlclose(&lib);
- luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib));
+ luaL_error(L, "Failed to load parser: uv_dlsym: %s", IObuff);
}
TSLanguage *lang = lang_parser();
@@ -216,7 +218,7 @@ static int add_language(lua_State *L, bool is_wasm)
? load_language_from_wasm(L, path, lang_name)
: load_language_from_object(L, path, lang_name, symbol_name);
- uint32_t lang_version = ts_language_version(lang);
+ uint32_t lang_version = ts_language_abi_version(lang);
if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION
|| lang_version > TREE_SITTER_LANGUAGE_VERSION) {
return luaL_error(L,
@@ -298,7 +300,7 @@ int tslua_inspect_lang(lua_State *L)
lua_pushboolean(L, ts_language_is_wasm(lang));
lua_setfield(L, -2, "_wasm");
- lua_pushinteger(L, ts_language_version(lang)); // [retval, version]
+ lua_pushinteger(L, ts_language_abi_version(lang)); // [retval, version]
lua_setfield(L, -2, "_abi_version");
return 1;
@@ -474,7 +476,7 @@ static int parser_parse(lua_State *L)
#undef BUFSIZE
}
- input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 };
+ input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8, NULL };
new_tree = ts_parser_parse(p, old_tree, input);
break;
@@ -488,13 +490,18 @@ static int parser_parse(lua_State *L)
// Sometimes parsing fails (timeout, or wrong parser ABI)
// In those case, just return an error.
if (!new_tree) {
- return luaL_error(L, "An error occurred when parsing.");
+ if (ts_parser_timeout_micros(p) == 0) {
+ // No timeout set, must have had an error
+ return luaL_error(L, "An error occurred when parsing.");
+ }
+ return 0;
}
// The new tree will be pushed to the stack, without copy, ownership is now to the lua GC.
// Old tree is owned by lua GC since before
uint32_t n_ranges = 0;
- TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL;
+ TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges)
+ : ts_tree_included_ranges(new_tree, &n_ranges);
push_tree(L, new_tree); // [tree]
@@ -831,7 +838,6 @@ static struct luaL_Reg node_meta[] = {
{ "named_descendant_for_range", node_named_descendant_for_range },
{ "parent", node_parent },
{ "__has_ancestor", __has_ancestor },
- { "child_containing_descendant", node_child_containing_descendant },
{ "child_with_descendant", node_child_with_descendant },
{ "iter_children", node_iter_children },
{ "next_sibling", node_next_sibling },
@@ -1175,15 +1181,6 @@ static int __has_ancestor(lua_State *L)
return 1;
}
-static int node_child_containing_descendant(lua_State *L)
-{
- TSNode node = node_check(L, 1);
- TSNode descendant = node_check(L, 2);
- TSNode child = ts_node_child_containing_descendant(node, descendant);
- push_node(L, child, 1);
- return 1;
-}
-
static int node_child_with_descendant(lua_State *L)
{
TSNode node = node_check(L, 1);
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index b9f96abf73..f51c1a05cd 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -1,4 +1,5 @@
#include <lauxlib.h>
+#include <limits.h>
#include <lua.h>
#include <stdbool.h>
#include <stdint.h>
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 695bd4c95a..0bd4277d19 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -7,6 +7,7 @@
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -19,6 +20,7 @@
#endif
#include "auto/config.h" // IWYU pragma: keep
+#include "klib/kvec.h"
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
@@ -79,9 +81,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
-#include "nvim/os/fileio.h"
-#include "nvim/os/fileio_defs.h"
#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
@@ -111,6 +110,9 @@
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
+# ifndef _UCRT
+# error UCRT is the only supported C runtime on windows
+# endif
#endif
#if defined(MSWIN) && !defined(MAKE_LIB)
@@ -241,22 +243,10 @@ 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(MSWIN)
-int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
#else
int main(int argc, char **argv)
#endif
{
-#if defined(MSWIN) && !defined(MAKE_LIB)
- char **argv = xmalloc((size_t)argc * sizeof(char *));
- for (int i = 0; i < argc; i++) {
- char *buf = NULL;
- utf16_to_utf8(argv_w[i], -1, &buf);
- assert(buf);
- argv[i] = buf;
- }
-#endif
-
argv0 = argv[0];
if (!appname_is_valid()) {
@@ -370,7 +360,7 @@ int main(int argc, char **argv)
setbuf(stdout, NULL); // NOLINT(bugprone-unsafe-functions)
- full_screen = !silent_mode || exmode_active;
+ full_screen = !silent_mode;
// Set the default values for the options that use Rows and Columns.
win_init_size();
@@ -637,13 +627,14 @@ int main(int argc, char **argv)
}
// WORKAROUND(mhi): #3023
- if (cb_flags & CB_UNNAMEDMASK) {
+ if (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus)) {
eval_has_provider("clipboard", false);
}
if (params.luaf != NULL) {
// Like "--cmd", "+", "-c" and "-S", don't truncate messages.
msg_scroll = true;
+ DLOG("executing Lua -l script");
bool lua_ok = nlua_exec_file(params.luaf);
TIME_MSG("executing Lua -l script");
if (msg_didout) {
@@ -1237,6 +1228,9 @@ static void command_line_scan(mparm_T *parmp)
if (exmode_active) { // "-es" silent (batch) Ex-mode
silent_mode = true;
parmp->no_swap_file = true;
+ if (p_shadafile == NULL || *p_shadafile == NUL) {
+ set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0);
+ }
} else { // "-s {scriptin}" read from script file
want_argument = true;
}
@@ -2094,8 +2088,7 @@ static void source_startup_scripts(const mparm_T *const parmp)
{
// If -u given, use only the initializations from that file and nothing else.
if (parmp->use_vimrc != NULL) {
- if (strequal(parmp->use_vimrc, "NONE")
- || strequal(parmp->use_vimrc, "NORC")) {
+ if (strequal(parmp->use_vimrc, "NONE") || strequal(parmp->use_vimrc, "NORC")) {
// Do nothing.
} else {
if (do_source(parmp->use_vimrc, false, DOSO_NONE, NULL) != OK) {
diff --git a/src/nvim/main.h b/src/nvim/main.h
index dedfadf270..5ae5d98de4 100644
--- a/src/nvim/main.h
+++ b/src/nvim/main.h
@@ -2,7 +2,6 @@
#include <stdbool.h>
-#include "nvim/event/loop.h"
#include "nvim/types_defs.h"
// Maximum number of commands from + or -c arguments.
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 1a6b2c3581..ca0349d1f6 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
@@ -32,7 +33,6 @@
#include "nvim/getchar_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
@@ -42,6 +42,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
@@ -581,6 +582,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
const bool has_lhs = (args->lhs[0] != NUL);
const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs);
+ if (do_print) {
+ msg_ext_set_kind("list_cmd");
+ }
// check for :unmap without argument
if (maptype == MAPTYPE_UNMAP && !has_lhs) {
@@ -1897,7 +1901,7 @@ int makemap(FILE *fd, buf_T *buf)
// "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
//
// return FAIL for failure, OK otherwise
-int put_escstr(FILE *fd, char *strstart, int what)
+int put_escstr(FILE *fd, const char *strstart, int what)
{
uint8_t *str = (uint8_t *)strstart;
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 3dbcbbd47b..d1ad920537 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -23,7 +23,6 @@
#include "nvim/fold.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -234,7 +233,7 @@ void setpcmark(void)
curwin->w_pcmark.lnum = 1;
}
- if (jop_flags & JOP_STACK) {
+ if (jop_flags & kOptJopFlagStack) {
// jumpoptions=stack: if we're somewhere in the middle of the jumplist
// discard everything after the current index.
if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1) {
@@ -1473,7 +1472,7 @@ void cleanup_jumplist(win_T *wp, bool loadfiles)
mustfree = false;
} else if (i > from + 1) { // non-adjacent duplicate
// jumpoptions=stack: remove duplicates only when adjacent.
- mustfree = !(jop_flags & JOP_STACK);
+ mustfree = !(jop_flags & kOptJopFlagStack);
} else { // adjacent duplicate
mustfree = true;
}
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 555fef5bbd..5ccd4fd45d 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -2330,7 +2330,6 @@ void marktree_check(MarkTree *b)
#endif
}
-#ifndef NDEBUG
size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right,
const uint32_t *meta_node_ref)
{
@@ -2485,8 +2484,6 @@ bool mt_recurse_nodes_compare(MTNode *x, PMap(ptr_t) *checked)
return true;
}
-#endif
-
// TODO(bfredl): kv_print
#define GA_PUT(x) ga_concat(ga, (char *)(x))
#define GA_PRINT(fmt, ...) snprintf(buf, sizeof(buf), fmt, __VA_ARGS__); \
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index 15df57ef63..1fb1a74487 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -4,7 +4,6 @@
#include <stddef.h> // IWYU pragma: keep
#include <stdint.h>
-#include "nvim/buffer_defs.h"
#include "nvim/decoration_defs.h"
#include "nvim/marktree_defs.h" // IWYU pragma: keep
#include "nvim/pos_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/math.c b/src/nvim/math.c
index 4ca212413b..0b5886d36c 100644
--- a/src/nvim/math.c
+++ b/src/nvim/math.c
@@ -106,3 +106,9 @@ int vim_append_digit_int(int *value, int digit)
*value = x * 10 + digit;
return OK;
}
+
+/// Return something that fits into an int.
+int trim_to_int(int64_t x)
+{
+ return x > INT_MAX ? INT_MAX : x < INT_MIN ? INT_MIN : (int)x;
+}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 65f718f925..ffd4472b8a 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -26,6 +26,7 @@
#include <ctype.h>
#include <errno.h>
#include <iconv.h>
+#include <limits.h>
#include <locale.h>
#include <stdbool.h>
#include <stddef.h>
@@ -51,7 +52,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
#include "nvim/iconv_defs.h"
#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
@@ -1119,7 +1119,7 @@ int utf_char2bytes(const int c, char *const buf)
/// stateful algorithm to determine grapheme clusters. Still available
/// to support some legacy code which hasn't been refactored yet.
///
-/// To check if a char would combine with a preceeding space, use
+/// To check if a char would combine with a preceding space, use
/// utf_iscomposing_first() instead.
///
/// Based on code from Markus Kuhn.
@@ -1400,11 +1400,11 @@ int utf_fold(int a)
int mb_toupper(int a)
{
// If 'casemap' contains "keepascii" use ASCII style toupper().
- if (a < 128 && (cmp_flags & CMP_KEEPASCII)) {
+ if (a < 128 && (cmp_flags & kOptCmpFlagKeepascii)) {
return TOUPPER_ASC(a);
}
- if (!(cmp_flags & CMP_INTERNAL)) {
+ if (!(cmp_flags & kOptCmpFlagInternal)) {
return (int)towupper((wint_t)a);
}
@@ -1426,11 +1426,11 @@ bool mb_islower(int a)
int mb_tolower(int a)
{
// If 'casemap' contains "keepascii" use ASCII style tolower().
- if (a < 128 && (cmp_flags & CMP_KEEPASCII)) {
+ if (a < 128 && (cmp_flags & kOptCmpFlagKeepascii)) {
return TOLOWER_ASC(a);
}
- if (!(cmp_flags & CMP_INTERNAL)) {
+ if (!(cmp_flags & kOptCmpFlagInternal)) {
return (int)towlower((wint_t)a);
}
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index 2da051fca2..674e0a2638 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -12,7 +12,6 @@
#include "nvim/mbyte_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
-typedef utf8proc_int32_t GraphemeState;
#define GRAPHEME_STATE_INIT 0
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h
index 7f2d1ba6ce..8d5ff2a8c1 100644
--- a/src/nvim/mbyte_defs.h
+++ b/src/nvim/mbyte_defs.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include <utf8proc.h>
#include "nvim/iconv_defs.h"
@@ -71,3 +72,7 @@ typedef struct {
int8_t begin_off; ///< Offset to the first byte of the codepoint.
int8_t end_off; ///< Offset to one past the end byte of the codepoint.
} CharBoundsOff;
+
+typedef utf8proc_int32_t GraphemeState;
+
+enum { UNICODE_INVALID = 0xFFFD, };
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index bfe90bb680..fb7fdfb8b2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -63,7 +63,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/macros_defs.h"
@@ -805,8 +804,7 @@ void ml_recover(bool checkext)
// list the names of the swapfiles
recover_names(fname, true, NULL, 0, NULL);
msg_putchar('\n');
- msg_puts(_("Enter number of swap file to use (0 to quit): "));
- i = get_number(false, NULL);
+ i = prompt_for_input(_("Enter number of swap file to use (0 to quit): "), 0, false, NULL);
if (i < 1 || i > len) {
goto theend;
}
@@ -1862,7 +1860,7 @@ int gchar_pos(pos_T *pos)
FUNC_ATTR_NONNULL_ARG(1)
{
// When searching columns is sometimes put at the end of a line.
- if (pos->col == MAXCOL) {
+ if (pos->col == MAXCOL || pos->col > ml_get_len(pos->lnum)) {
return NUL;
}
return utf_ptr2char(ml_get_pos(pos));
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 3aa37c047c..fa102f4e12 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -34,7 +34,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
-#include "nvim/os/input.h"
#include "nvim/sign.h"
#include "nvim/state_defs.h"
#include "nvim/statusline.h"
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index a6ff82e39d..0955c5306c 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdint.h> // IWYU pragma: keep
+#include <string.h>
#include <time.h> // IWYU pragma: keep
#include "auto/config.h"
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 4d3058ee44..5f14ea1eed 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -25,7 +25,6 @@
#include "nvim/getchar_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 47f33c8967..6fc102e4ff 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2,6 +2,7 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
@@ -26,6 +27,7 @@
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
@@ -45,6 +47,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/ops.h"
@@ -89,11 +92,21 @@ static int confirm_msg_used = false; // displaying confirm_msg
# include "message.c.generated.h"
#endif
static char *confirm_msg = NULL; // ":confirm" message
-static char *confirm_msg_tail; // tail of confirm_msg
+static char *confirm_buttons; // ":confirm" buttons sent to cmdline as prompt
MessageHistoryEntry *first_msg_hist = NULL;
MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
+static int msg_hist_max = 500; // The default max value is 500
+
+// args in 'messagesopt' option
+#define MESSAGES_OPT_HIT_ENTER "hit-enter"
+#define MESSAGES_OPT_WAIT "wait:"
+#define MESSAGES_OPT_HISTORY "history:"
+
+// The default is "hit-enter,history:500"
+static int msg_flags = kOptMoptFlagHitEnter | kOptMoptFlagHistory;
+static int msg_wait = 0;
static FILE *verbose_fd = NULL;
static bool verbose_did_open = false;
@@ -119,7 +132,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
// 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_hl_id()
+// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_hl()
// 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.
@@ -140,8 +153,8 @@ static Array *msg_ext_chunks = NULL;
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
static sattr_T msg_ext_last_attr = -1;
static int msg_ext_last_hl_id;
-static size_t msg_ext_cur_len = 0;
+static bool msg_ext_history = false; ///< message was added to history
static bool msg_ext_overwrite = false; ///< will overwrite last message
static int msg_ext_visible = 0; ///< number of messages currently visible
@@ -233,7 +246,7 @@ void msg_grid_validate(void)
int verb_msg(const char *s)
{
verbose_enter();
- int n = msg_hl_keep(s, 0, false, false);
+ int n = msg_keep(s, 0, false, false);
verbose_leave();
return n;
@@ -246,7 +259,7 @@ int verb_msg(const char *s)
bool msg(const char *s, const int hl_id)
FUNC_ATTR_NONNULL_ARG(1)
{
- return msg_hl_keep(s, hl_id, false, false);
+ return msg_keep(s, hl_id, false, false);
}
/// Similar to msg_outtrans_len, but support newlines and tabs.
@@ -279,26 +292,45 @@ void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_
}
}
-void msg_multihl(HlMessage hl_msg, const char *kind, bool history)
+// Avoid starting a new message for each chunk and adding message to history in msg_keep().
+static bool is_multihl = false;
+
+/// Print message chunks, each with their own highlight ID.
+///
+/// @param hl_msg Message chunks
+/// @param kind Message kind (can be NULL to avoid setting kind)
+/// @param history Whether to add message to history
+/// @param err Whether to print message as an error
+void msg_multihl(HlMessage hl_msg, const char *kind, bool history, bool err)
{
no_wait_return++;
msg_start();
msg_clr_eos();
bool need_clear = false;
- msg_ext_set_kind(kind);
+ msg_ext_history = history;
+ if (kind != NULL) {
+ msg_ext_set_kind(kind);
+ }
+ is_multihl = true;
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
HlMessageChunk chunk = kv_A(hl_msg, i);
- msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear);
+ if (err) {
+ emsg_multiline(chunk.text.data, kind, chunk.hl_id, true);
+ } else {
+ msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear);
+ }
+ assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
}
if (history && kv_size(hl_msg)) {
add_msg_hist_multihl(NULL, 0, 0, true, hl_msg);
}
+ is_multihl = false;
no_wait_return--;
msg_end();
}
/// @param keep set keep_msg if it doesn't scroll
-bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
+bool msg_keep(const char *s, int hl_id, bool keep, bool multiline)
FUNC_ATTR_NONNULL_ALL
{
static int entered = 0;
@@ -328,18 +360,19 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
}
entered++;
- // Add message to history (unless it's a repeated kept message or a
- // truncated message)
- if (s != keep_msg
- || (*s != '<'
- && last_msg_hist != NULL
- && last_msg_hist->msg != NULL
- && strcmp(s, last_msg_hist->msg) != 0)) {
+ // Add message to history (unless it's a truncated, repeated kept or multihl message).
+ if ((s != keep_msg
+ || (*s != '<'
+ && last_msg_hist != NULL
+ && last_msg_hist->msg != NULL
+ && strcmp(s, last_msg_hist->msg) != 0)) && !is_multihl) {
add_msg_hist(s, -1, hl_id, multiline);
}
+ if (!is_multihl) {
+ msg_start();
+ }
// Truncate the message if needed.
- msg_start();
char *buf = msg_strtrunc(s, false);
if (buf != NULL) {
s = buf;
@@ -354,7 +387,10 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
if (need_clear) {
msg_clr_eos();
}
- bool retval = msg_end();
+ bool retval = true;
+ if (!is_multihl) {
+ retval = msg_end();
+ }
if (keep && retval && vim_strsize(s) < (Rows - cmdline_row - 1) * Columns + sc_col) {
set_keep_msg(s, 0);
@@ -503,7 +539,7 @@ int smsg(int hl_id, const char *s, ...)
return msg(IObuff, hl_id);
}
-int smsg_hl_keep(int hl_id, const char *s, ...)
+int smsg_keep(int hl_id, const char *s, ...)
FUNC_ATTR_PRINTF(2, 3)
{
va_list arglist;
@@ -511,7 +547,7 @@ int smsg_hl_keep(int hl_id, const char *s, ...)
va_start(arglist, s);
vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
- return msg_hl_keep(IObuff, hl_id, true, false);
+ return msg_keep(IObuff, hl_id, true, false);
}
// Remember the last sourcing name/lnum used in an error message, so that it
@@ -604,6 +640,9 @@ void msg_source(int hl_id)
msg_scroll = true; // this will take more than one line
msg(p, hl_id);
xfree(p);
+ if (is_multihl) {
+ msg_start(); // avoided in msg_keep() but need the "msg_didout" newline here
+ }
}
p = get_emsg_lnum();
if (p != NULL) {
@@ -638,7 +677,7 @@ int emsg_not_now(void)
return false;
}
-bool emsg_multiline(const char *s, bool multiline)
+bool emsg_multiline(const char *s, const char *kind, int hl_id, bool multiline)
{
bool ignore = false;
@@ -736,23 +775,26 @@ bool emsg_multiline(const char *s, bool multiline)
}
emsg_on_display = true; // remember there is an error message
- int hl_id = HLF_E; // set highlight mode for error messages
if (msg_scrolled != 0) {
need_wait_return = true; // needed in case emsg() is called after
} // wait_return() has reset need_wait_return
// and a redraw is expected because
// msg_scrolled is non-zero
if (msg_ext_kind == NULL) {
- msg_ext_set_kind("emsg");
+ msg_ext_set_kind(kind);
}
// Display name and line number for the source of the error.
msg_scroll = true;
msg_source(hl_id);
+ if (msg_ext_kind == NULL) {
+ msg_ext_set_kind(kind);
+ }
+
// Display the error message itself.
msg_nowait = false; // Wait for this msg.
- return msg_hl_keep(s, hl_id, false, multiline);
+ return msg_keep(s, hl_id, false, multiline);
}
/// emsg() - display an error message
@@ -763,7 +805,7 @@ bool emsg_multiline(const char *s, bool multiline)
/// @return true if wait_return() not called
bool emsg(const char *s)
{
- return emsg_multiline(s, false);
+ return emsg_multiline(s, "emsg", HLF_E, false);
}
void emsg_invreg(int name)
@@ -803,7 +845,7 @@ bool semsg_multiline(const char *const fmt, ...)
vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
va_end(ap);
- ret = emsg_multiline(errbuf, true);
+ ret = emsg_multiline(errbuf, "emsg", HLF_E, true);
return ret;
}
@@ -887,7 +929,7 @@ void msg_schedule_semsg(const char *const fmt, ...)
static void msg_semsg_multiline_event(void **argv)
{
char *s = argv[0];
- emsg_multiline(s, true);
+ emsg_multiline(s, "emsg", HLF_E, true);
xfree(s);
}
@@ -973,28 +1015,24 @@ static void add_msg_hist(const char *s, int len, int hl_id, bool multiline)
static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multiline,
HlMessage multihl)
{
- if (msg_hist_off || msg_silent != 0) {
+ if (msg_hist_off || msg_silent != 0 || (s != NULL && *s == NUL)) {
hl_msg_free(multihl);
return;
}
- // Don't let the message history get too big
- while (msg_hist_len > p_mhi) {
- delete_first_msg();
- }
-
// allocate an entry and add the message at the end of the history
struct msg_hist *p = xmalloc(sizeof(struct msg_hist));
if (s) {
if (len < 0) {
len = (int)strlen(s);
}
+ assert(len > 0);
// remove leading and trailing newlines
- while (len > 0 && *s == '\n') {
+ while (*s == '\n') {
s++;
len--;
}
- while (len > 0 && s[len - 1] == '\n') {
+ while (s[len - 1] == '\n') {
len--;
}
p->msg = xmemdupz(s, (size_t)len);
@@ -1014,6 +1052,9 @@ static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multili
first_msg_hist = last_msg_hist;
}
msg_hist_len++;
+ msg_ext_history = true;
+
+ check_msg_hist();
}
/// Delete the first (oldest) message from the history.
@@ -1037,6 +1078,76 @@ int delete_first_msg(void)
return OK;
}
+static void check_msg_hist(void)
+{
+ // Don't let the message history get too big
+ while (msg_hist_len > 0 && msg_hist_len > msg_hist_max) {
+ (void)delete_first_msg();
+ }
+}
+
+int messagesopt_changed(void)
+{
+ int messages_flags_new = 0;
+ int messages_wait_new = 0;
+ int messages_history_new = 0;
+
+ char *p = p_mopt;
+ while (*p != NUL) {
+ if (strnequal(p, S_LEN(MESSAGES_OPT_HIT_ENTER))) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER);
+ messages_flags_new |= kOptMoptFlagHitEnter;
+ } else if (strnequal(p, S_LEN(MESSAGES_OPT_WAIT))
+ && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_WAIT)])) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_WAIT);
+ messages_wait_new = getdigits_int(&p, false, INT_MAX);
+ messages_flags_new |= kOptMoptFlagWait;
+ } else if (strnequal(p, S_LEN(MESSAGES_OPT_HISTORY))
+ && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_HISTORY)])) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_HISTORY);
+ messages_history_new = getdigits_int(&p, false, INT_MAX);
+ messages_flags_new |= kOptMoptFlagHistory;
+ }
+
+ if (*p != ',' && *p != NUL) {
+ return FAIL;
+ }
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ // Either "wait" or "hit-enter" is required
+ if (!(messages_flags_new & (kOptMoptFlagHitEnter | kOptMoptFlagWait))) {
+ return FAIL;
+ }
+
+ // "history" must be set
+ if (!(messages_flags_new & kOptMoptFlagHistory)) {
+ return FAIL;
+ }
+
+ assert(messages_history_new >= 0);
+ // "history" must be <= 10000
+ if (messages_history_new > 10000) {
+ return FAIL;
+ }
+
+ assert(messages_wait_new >= 0);
+ // "wait" must be <= 10000
+ if (messages_wait_new > 10000) {
+ return FAIL;
+ }
+
+ msg_flags = messages_flags_new;
+ msg_wait = messages_wait_new;
+
+ msg_hist_max = messages_history_new;
+ check_msg_hist();
+
+ return OK;
+}
+
/// :messages command implementation
void ex_messages(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
@@ -1109,9 +1220,9 @@ void ex_messages(exarg_T *eap)
msg_hist_off = true;
for (; p != NULL && !got_int; p = p->next) {
if (kv_size(p->multihl)) {
- msg_multihl(p->multihl, p->kind, false);
+ msg_multihl(p->multihl, p->kind, false, false);
} else if (p->msg != NULL) {
- msg_hl_keep(p->msg, p->hl_id, false, p->multiline);
+ msg_keep(p->msg, p->hl_id, false, p->multiline);
}
}
msg_hist_off = false;
@@ -1200,83 +1311,88 @@ void wait_return(int redraw)
cmdline_row = Rows - 1;
}
- hit_return_msg(true);
-
- do {
- // Remember "got_int", if it is set vgetc() probably returns a
- // CTRL-C, but we need to loop then.
- had_got_int = got_int;
-
- // Don't do mappings here, we put the character back in the
- // typeahead buffer.
- no_mapping++;
- allow_keys++;
-
- // Temporarily disable Recording. If Recording is active, the
- // character will be recorded later, since it will be added to the
- // typebuf after the loop
- const int save_reg_recording = reg_recording;
- save_scriptout = scriptout;
- reg_recording = 0;
- scriptout = NULL;
- c = safe_vgetc();
- if (had_got_int && !global_busy) {
- got_int = false;
- }
- no_mapping--;
- allow_keys--;
- reg_recording = save_reg_recording;
- scriptout = save_scriptout;
-
- // Allow scrolling back in the messages.
- // Also accept scroll-down commands when messages fill the screen,
- // 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) {
- if (msg_scrolled > Rows) {
- // scroll back to show older messages
- do_more_prompt(c);
- } else {
- msg_didout = false;
- c = K_IGNORE;
- msg_col = 0;
- }
- if (quit_more) {
- c = CAR; // just pretend CR was hit
- quit_more = false;
- got_int = false;
- } else if (c != K_IGNORE) {
+ if (msg_flags & kOptMoptFlagHitEnter) {
+ hit_return_msg(true);
+
+ do {
+ // Remember "got_int", if it is set vgetc() probably returns a
+ // CTRL-C, but we need to loop then.
+ had_got_int = got_int;
+
+ // Don't do mappings here, we put the character back in the
+ // typeahead buffer.
+ no_mapping++;
+ allow_keys++;
+
+ // Temporarily disable Recording. If Recording is active, the
+ // character will be recorded later, since it will be added to the
+ // typebuf after the loop
+ const int save_reg_recording = reg_recording;
+ save_scriptout = scriptout;
+ reg_recording = 0;
+ scriptout = NULL;
+ c = safe_vgetc();
+ if (had_got_int && !global_busy) {
+ got_int = false;
+ }
+ no_mapping--;
+ allow_keys--;
+ reg_recording = save_reg_recording;
+ scriptout = save_scriptout;
+
+ // Allow scrolling back in the messages.
+ // Also accept scroll-down commands when messages fill the screen,
+ // 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) {
+ if (msg_scrolled > Rows) {
+ // scroll back to show older messages
+ do_more_prompt(c);
+ } else {
+ msg_didout = false;
+ c = K_IGNORE;
+ msg_col = 0;
+ }
+ if (quit_more) {
+ c = CAR; // just pretend CR was hit
+ quit_more = false;
+ got_int = false;
+ } else if (c != K_IGNORE) {
+ c = K_IGNORE;
+ hit_return_msg(false);
+ }
+ } else if (msg_scrolled > Rows - 2
+ && (c == 'j' || c == 'd' || c == 'f'
+ || c == K_DOWN || c == K_PAGEDOWN)) {
c = K_IGNORE;
- hit_return_msg(false);
}
- } else if (msg_scrolled > Rows - 2
- && (c == 'j' || c == 'd' || c == 'f'
- || c == K_DOWN || c == K_PAGEDOWN)) {
- c = K_IGNORE;
}
+ } while ((had_got_int && c == Ctrl_C)
+ || c == K_IGNORE
+ || c == K_LEFTDRAG || c == K_LEFTRELEASE
+ || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
+ || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
+ || c == K_MOUSELEFT || c == K_MOUSERIGHT
+ || c == K_MOUSEDOWN || c == K_MOUSEUP
+ || c == K_MOUSEMOVE);
+ os_breakcheck();
+
+ // 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) {
+ jump_to_mouse(MOUSE_SETPOS, NULL, 0);
+ } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
+ // Put the character back in the typeahead buffer. Don't use the
+ // stuff buffer, because lmaps wouldn't work.
+ ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
+ do_redraw = true; // need a redraw even though there is typeahead
}
- } while ((had_got_int && c == Ctrl_C)
- || c == K_IGNORE
- || c == K_LEFTDRAG || c == K_LEFTRELEASE
- || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
- || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
- || c == K_MOUSELEFT || c == K_MOUSERIGHT
- || c == K_MOUSEDOWN || c == K_MOUSEUP
- || c == K_MOUSEMOVE);
- os_breakcheck();
-
- // 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) {
- jump_to_mouse(MOUSE_SETPOS, NULL, 0);
- } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
- // Put the character back in the typeahead buffer. Don't use the
- // stuff buffer, because lmaps wouldn't work.
- ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
- do_redraw = true; // need a redraw even though there is
- // typeahead
+ } else {
+ c = CAR;
+ // Wait to allow the user to verify the output.
+ do_sleep(msg_wait, true);
}
}
redir_off = false;
@@ -2016,12 +2132,12 @@ void msg_outtrans_long(const char *longstr, int hl_id)
int len = (int)strlen(longstr);
int slen = len;
int room = Columns - msg_col;
- if (len > room && room >= 20) {
+ if (!ui_has(kUIMessages) && len > room && room >= 20) {
slen = (room - 3) / 2;
msg_outtrans_len(longstr, slen, hl_id, false);
msg_puts_hl("...", HLF_8, false);
}
- msg_outtrans_len(longstr + len - slen, slen, hl_id, len);
+ msg_outtrans_len(longstr + len - slen, slen, hl_id, false);
}
/// Basic function for writing a message with highlight id.
@@ -2042,8 +2158,8 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
// If redirection is on, also write to the redirection file.
redir_write(str, len);
- // Don't print anything when using ":silent cmd".
- if (msg_silent != 0) {
+ // Don't print anything when using ":silent cmd" or empty message.
+ if (msg_silent != 0 || *str == NUL) {
return;
}
@@ -2091,27 +2207,6 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
need_fileinfo = false;
}
-/// Print a formatted message
-///
-/// Message printed is limited by #IOSIZE. Must not be used from inside
-/// msg_puts_hl_id().
-///
-/// @param[in] hl_id Highlight id.
-/// @param[in] fmt Format string.
-void msg_printf_hl(const int hl_id, const char *const fmt, ...)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PRINTF(2, 3)
-{
- static char msgbuf[IOSIZE];
-
- va_list ap;
- va_start(ap, fmt);
- const size_t len = (size_t)vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
- va_end(ap);
-
- msg_scroll = true;
- msg_puts_len(msgbuf, (ptrdiff_t)len, hl_id, true);
-}
-
static void msg_ext_emit_chunk(void)
{
if (msg_ext_chunks == NULL) {
@@ -2150,7 +2245,13 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
// Concat pieces with the same highlight
size_t len = maxlen < 0 ? strlen(str) : strnlen(str, (size_t)maxlen);
ga_concat_len(&msg_ext_last_chunk, str, len);
- msg_ext_cur_len += len;
+
+ // Find last newline in the message and calculate the current message column
+ const char *lastline = strrchr(str, '\n');
+ maxlen -= (int)(lastline ? (lastline - str) : 0);
+ const char *p = lastline ? lastline + 1 : str;
+ int col = (int)(maxlen < 0 ? mb_string2cells(p) : mb_string2cells_len(p, (size_t)(maxlen)));
+ msg_col = (lastline ? 0 : msg_col) + col;
return;
}
@@ -2209,7 +2310,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
if (p_more && lines_left == 0 && State != MODE_HITRETURN
&& !msg_no_more && !exmode_active) {
if (do_more_prompt(NUL)) {
- s = confirm_msg_tail;
+ s = confirm_buttons;
}
if (quit_more) {
return;
@@ -2273,7 +2374,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
}
} while (msg_col & 7);
} else if (c == BELL) { // beep (from ":sh")
- vim_beep(BO_SH);
+ vim_beep(kOptBoFlagShell);
}
}
}
@@ -2328,7 +2429,7 @@ int msg_scrollsize(void)
bool msg_do_throttle(void)
{
- return msg_use_grid() && !(rdb_flags & RDB_NOTHROTTLE);
+ return msg_use_grid() && !(rdb_flags & kOptRdbFlagNothrottle);
}
/// Scroll the screen up one line for displaying the next message line.
@@ -2593,7 +2694,7 @@ void show_sb_text(void)
// weird, typing a command without output results in one line.
msgchunk_T *mp = msg_sb_start(last_msgchunk);
if (mp == NULL || mp->sb_prev == NULL) {
- vim_beep(BO_MESS);
+ vim_beep(kOptBoFlagMess);
} else {
do_more_prompt('G');
wait_return(false);
@@ -2641,7 +2742,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
}
/// @return true when messages should be printed to stdout/stderr:
-/// - "batch mode" ("silent mode", -es/-Es)
+/// - "batch mode" ("silent mode", -es/-Es/-l)
/// - no UI and not embedded
int msg_use_printf(void)
{
@@ -2701,7 +2802,7 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
/// When at hit-enter prompt "typed_char" is the already typed character,
/// otherwise it's NUL.
///
-/// @return true when jumping ahead to "confirm_msg_tail".
+/// @return true when jumping ahead to "confirm_buttons".
static bool do_more_prompt(int typed_char)
{
static bool entered = false;
@@ -3052,7 +3153,7 @@ static Array *msg_ext_init_chunks(void)
{
Array *tofree = msg_ext_chunks;
msg_ext_chunks = xcalloc(1, sizeof(*msg_ext_chunks));
- msg_ext_cur_len = 0;
+ msg_col = 0;
return tofree;
}
@@ -3066,13 +3167,14 @@ void msg_ext_ui_flush(void)
msg_ext_emit_chunk();
if (msg_ext_chunks->size > 0) {
Array *tofree = msg_ext_init_chunks();
- ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite);
+ ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history);
api_free_array(*tofree);
xfree(tofree);
if (!msg_ext_overwrite) {
msg_ext_visible++;
}
msg_ext_overwrite = false;
+ msg_ext_history = false;
msg_ext_kind = NULL;
}
}
@@ -3081,7 +3183,11 @@ void msg_ext_flush_showmode(void)
{
// Showmode messages doesn't interrupt normal message flow, so we use
// separate event. Still reuse the same chunking logic, for simplicity.
- if (ui_has(kUIMessages)) {
+ // This is called unconditionally; check if we are emitting, or have
+ // emitted non-empty "content".
+ static bool clear = false;
+ if (ui_has(kUIMessages) && (msg_ext_last_attr != -1 || clear)) {
+ clear = msg_ext_last_attr != -1;
msg_ext_emit_chunk();
Array *tofree = msg_ext_init_chunks();
ui_call_msg_showmode(*tofree);
@@ -3228,6 +3334,10 @@ int redirecting(void)
|| redir_reg || redir_vname || capture_ga != NULL;
}
+// Save and restore message kind when emitting a verbose message.
+static const char *pre_verbose_kind = NULL;
+static const char *verbose_kind = "verbose";
+
/// Before giving verbose message.
/// Must always be called paired with verbose_leave()!
void verbose_enter(void)
@@ -3235,6 +3345,10 @@ void verbose_enter(void)
if (*p_vfile != NUL) {
msg_silent++;
}
+ if (msg_ext_kind != verbose_kind) {
+ pre_verbose_kind = msg_ext_kind;
+ msg_ext_set_kind("verbose");
+ }
}
/// After giving verbose message.
@@ -3246,14 +3360,17 @@ void verbose_leave(void)
msg_silent = 0;
}
}
+ if (pre_verbose_kind != NULL) {
+ msg_ext_set_kind(pre_verbose_kind);
+ pre_verbose_kind = NULL;
+ }
}
/// Like verbose_enter() and set msg_scroll when displaying the message.
void verbose_enter_scroll(void)
{
- if (*p_vfile != NUL) {
- msg_silent++;
- } else {
+ verbose_enter();
+ if (*p_vfile == NUL) {
// always scroll up, don't overwrite
msg_scroll = true;
}
@@ -3262,11 +3379,8 @@ void verbose_enter_scroll(void)
/// Like verbose_leave() and set cmdline_row when displaying the message.
void verbose_leave_scroll(void)
{
- if (*p_vfile != NUL) {
- if (--msg_silent < 0) {
- msg_silent = 0;
- }
- } else {
+ verbose_leave();
+ if (*p_vfile == NUL) {
cmdline_row = msg_row;
}
}
@@ -3360,14 +3474,6 @@ void msg_advance(int col)
msg_col = col; // for redirection, may fill it up later
return;
}
- if (ui_has(kUIMessages)) {
- // TODO(bfredl): use byte count as a basic proxy.
- // later on we might add proper support for formatted messages.
- while (msg_ext_cur_len < (size_t)col) {
- msg_putchar(' ');
- }
- return;
- }
col = MIN(col, Columns - 1); // not enough room
while (msg_col < col) {
msg_putchar(' ');
@@ -3424,10 +3530,10 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
}
// Get a typed character directly from the user.
- int c = get_keystroke(NULL);
+ int c = prompt_for_input(confirm_buttons, HLF_M, true, NULL);
switch (c) {
case CAR: // User accepts default option
- case NL:
+ case NUL:
retval = dfltbutton;
break;
case Ctrl_C: // User aborts/cancels
@@ -3436,6 +3542,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
break;
default: // Could be a hotkey?
if (c < 0) { // special keys are ignored here
+ msg_didout = msg_didany = false;
continue;
}
if (c == ':' && ex_cmd) {
@@ -3458,6 +3565,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
break;
}
// No hotkey match, so keep waiting
+ msg_didout = msg_didany = false;
continue;
}
break;
@@ -3511,19 +3619,20 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
has_hotkey[0] = false;
// Compute the size of memory to allocate.
- int len = 0;
+ int msg_len = 0;
+ int button_len = 0;
int idx = 0;
const char *r = buttons;
while (*r) {
if (*r == DLG_BUTTON_SEP) {
- len += 3; // '\n' -> ', '; 'x' -> '(x)'
+ button_len += 3; // '\n' -> ', '; 'x' -> '(x)'
lenhotkey += HOTK_LEN; // each button needs a hotkey
if (idx < HAS_HOTKEY_LEN - 1) {
has_hotkey[++idx] = false;
}
} else if (*r == DLG_HOTKEY_CHAR) {
r++;
- len++; // '&a' -> '[a]'
+ button_len++; // '&a' -> '[a]'
if (idx < HAS_HOTKEY_LEN - 1) {
has_hotkey[idx] = true;
}
@@ -3533,21 +3642,22 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
MB_PTR_ADV(r);
}
- len += (int)(strlen(message)
- + 2 // for the NL's
- + strlen(buttons)
- + 3); // for the ": " and NUL
- lenhotkey++; // for the NUL
+ msg_len += (int)strlen(message) + 3; // for the NL's and NUL
+ button_len += (int)strlen(buttons) + 3; // for the ": " and NUL
+ lenhotkey++; // for the NUL
// If no hotkey is specified, first char is used.
if (!has_hotkey[0]) {
- len += 2; // "x" -> "[x]"
+ button_len += 2; // "x" -> "[x]"
}
// Now allocate space for the strings
xfree(confirm_msg);
- confirm_msg = xmalloc((size_t)len);
- *confirm_msg = NUL;
+ confirm_msg = xmalloc((size_t)msg_len);
+ snprintf(confirm_msg, (size_t)msg_len, "\n%s\n", message);
+
+ xfree(confirm_buttons);
+ confirm_buttons = xmalloc((size_t)button_len);
return xmalloc((size_t)lenhotkey);
}
@@ -3565,42 +3675,34 @@ static char *msg_show_console_dialog(const char *message, const char *buttons, i
bool has_hotkey[HAS_HOTKEY_LEN] = { false };
char *hotk = console_dialog_alloc(message, buttons, has_hotkey);
- copy_hotkeys_and_msg(message, buttons, dfltbutton, has_hotkey, hotk);
+ copy_confirm_hotkeys(buttons, dfltbutton, has_hotkey, hotk);
display_confirm_msg();
return hotk;
}
-/// Copies hotkeys & dialog message into the memory allocated for it
+/// Copies hotkeys into the memory allocated for it
///
-/// @param message Message which will be part of the confirm_msg
/// @param buttons String containing button names
/// @param default_button_idx Number of default button
/// @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 *message, const char *buttons, int default_button_idx,
+static void copy_confirm_hotkeys(const char *buttons, int default_button_idx,
const bool has_hotkey[], char *hotkeys_ptr)
{
- *confirm_msg = '\n';
- STRCPY(confirm_msg + 1, message);
-
- char *msgp = confirm_msg + 1 + strlen(message);
-
// Define first default hotkey. Keep the hotkey string NUL
// terminated to avoid reading past the end.
hotkeys_ptr[copy_char(buttons, hotkeys_ptr, true)] = NUL;
- // Remember where the choices start, displaying starts here when
- // "hotkeys_ptr" typed at the more prompt.
- confirm_msg_tail = msgp;
- *msgp++ = '\n';
-
bool first_hotkey = false; // Is the first char of button a hotkey
if (!has_hotkey[0]) {
first_hotkey = true; // If no hotkey is specified, first char is used
}
+ // Remember where the choices start, sent as prompt to cmdline.
+ char *msgp = confirm_buttons;
+
int idx = 0;
const char *r = buttons;
while (*r) {
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 1289adfabb..b0ab235f6b 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -280,6 +280,54 @@ static int get_fpos_of_mouse(pos_T *mpos)
return IN_BUFFER;
}
+static int do_popup(int which_button, int m_pos_flag, pos_T m_pos)
+{
+ int jump_flags = 0;
+ if (strcmp(p_mousem, "popup_setpos") == 0) {
+ // First set the cursor position before showing the popup menu.
+ if (VIsual_active) {
+ // set MOUSE_MAY_STOP_VIS if we are outside the selection
+ // or the current window (might have false negative here)
+ if (m_pos_flag != 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) {
+ colnr_T leftcol, rightcol;
+ 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;
+}
+
/// Do the appropriate action for the current mouse click in the current mode.
/// Not used for Command-line mode.
///
@@ -324,24 +372,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
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;
- win_T *old_curwin = curwin;
static pos_T orig_cursor;
- colnr_T leftcol, rightcol;
- pos_T end_visual;
- int old_active = VIsual_active;
- int old_mode = VIsual_mode;
- int regname;
-
- pos_T save_cursor = curwin->w_cursor;
while (true) {
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
@@ -434,12 +466,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
return false;
}
- if (oap != NULL) {
- regname = oap->regname;
- } else {
- regname = 0;
- }
-
+ int regname = oap != NULL ? oap->regname : 0;
// Middle mouse button does a 'put' of the selected text
if (which_button == MOUSE_MIDDLE) {
if (State == MODE_NORMAL) {
@@ -496,12 +523,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
}
}
+ // flags for jump_to_mouse()
// When dragging or button-up stay in the same window.
- if (!is_click) {
- jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
- }
-
- start_visual.lnum = 0;
+ int jump_flags = is_click ? 0 : (MOUSE_FOCUS|MOUSE_DID_MOVE);
+ win_T *old_curwin = curwin;
if (tab_page_click_defs != NULL) { // only when initialized
// Check for clicking in the tab page line.
@@ -515,8 +540,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
// click in a tab selects that tab page
if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
+ int tabnr = tab_page_click_defs[mouse_col].tabnr;
in_tab_line = true;
- c1 = tab_page_click_defs[mouse_col].tabnr;
switch (tab_page_click_defs[mouse_col].type) {
case kStlClickDisabled:
@@ -527,11 +552,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
// double click opens new page
end_visual_mode();
tabpage_new();
- tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ tabpage_move(tabnr == 0 ? 9999 : tabnr - 1);
} else {
// Go to specified tab page, or next one if not clicking
// on a label.
- goto_tabpage(c1);
+ goto_tabpage(tabnr);
// It's like clicking on the status line of a window.
if (curwin != old_curwin) {
@@ -542,7 +567,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
}
FALLTHROUGH;
case kStlClickTabClose:
- mouse_tab_close(c1);
+ mouse_tab_close(tabnr);
break;
case kStlClickFuncRun:
call_click_def_func(tab_page_click_defs, mouse_col, which_button);
@@ -556,75 +581,33 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
}
}
+ int m_pos_flag = 0;
+ pos_T m_pos = { 0 };
// 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()) {
- pos_T m_pos;
- int m_pos_flag = get_fpos_of_mouse(&m_pos);
- if (m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL)) {
- goto popupexit;
- }
- if (which_button == MOUSE_RIGHT
- && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+ m_pos_flag = get_fpos_of_mouse(&m_pos);
+ if (!(m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL))
+ && 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) {
- // set MOUSE_MAY_STOP_VIS if we are outside the selection
- // or the current window (might have false negative here)
- if (m_pos_flag != 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;
+ return (do_popup(which_button, m_pos_flag, m_pos) & CURSOR_MOVED);
}
- if (which_button == MOUSE_LEFT
- && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
+ // Only do this translation when mouse is over the buffer text
+ if (!(m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL))
+ && (which_button == MOUSE_LEFT && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))) {
which_button = MOUSE_RIGHT;
mod_mask &= ~MOD_MASK_SHIFT;
}
}
-popupexit:
+ pos_T end_visual = { 0 };
+ pos_T start_visual = { 0 };
if ((State & (MODE_NORMAL | MODE_INSERT))
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
if (which_button == MOUSE_LEFT) {
@@ -664,15 +647,15 @@ popupexit:
}
// JUMP!
- jump_flags = jump_to_mouse(jump_flags,
- oap == NULL ? NULL : &(oap->inclusive),
- which_button);
+ int old_active = VIsual_active;
+ pos_T save_cursor = curwin->w_cursor;
+ 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);
+ bool moved = (jump_flags & CURSOR_MOVED);
+ bool in_winbar = (jump_flags & MOUSE_WINBAR);
+ bool in_statuscol = (jump_flags & MOUSE_STATUSCOL);
+ bool in_status_line = (jump_flags & IN_STATUS_LINE);
+ bool in_sep_line = (jump_flags & IN_SEP_LINE);
if ((in_winbar || in_status_line || in_statuscol) && is_click) {
// Handle click event on window bar, status line or status column
@@ -705,6 +688,12 @@ popupexit:
if (click_defs != NULL) {
switch (click_defs[click_col].type) {
case kStlClickDisabled:
+ // If there is no click definition, still open the popupmenu for a
+ // statuscolumn click like a click in the sign/number column does.
+ if (in_statuscol && mouse_model_popup()
+ && which_button == MOUSE_RIGHT && !(mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) {
+ do_popup(which_button, m_pos_flag, m_pos);
+ }
break;
case kStlClickFuncRun:
call_click_def_func(click_defs, click_col, which_button);
@@ -715,7 +704,9 @@ popupexit:
}
}
- return false;
+ if (!(in_statuscol && (jump_flags & (MOUSE_FOLD_CLOSE|MOUSE_FOLD_OPEN)))) {
+ 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;
@@ -760,6 +751,7 @@ popupexit:
mouse_row = 0;
}
+ int old_mode = VIsual_mode;
if (start_visual.lnum) { // right click in visual mode
linenr_T diff;
// When ALT is pressed make Visual mode blockwise.
@@ -770,6 +762,7 @@ popupexit:
// 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) {
+ colnr_T leftcol, rightcol;
getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
if (curwin->w_curswant > (leftcol + rightcol) / 2) {
end_visual.col = leftcol;
@@ -831,7 +824,6 @@ popupexit:
// Middle mouse click: Put text before cursor.
if (which_button == MOUSE_MIDDLE) {
- int c2;
if (regname == 0 && eval_has_provider("clipboard", false)) {
regname = '*';
}
@@ -843,6 +835,7 @@ popupexit:
dir = FORWARD;
}
+ int c1, c2;
if (fixindent) {
c1 = (dir == BACKWARD) ? '[' : ']';
c2 = 'p';
@@ -1258,8 +1251,6 @@ retnomove:
if (flags & MOUSE_SETPOS) {
goto retnomove; // ugly goto...
}
- win_T *old_curwin = curwin;
- pos_T old_cursor = curwin->w_cursor;
if (row < 0 || col < 0) { // check if it makes sense
return IN_UNKNOWN;
@@ -1299,13 +1290,15 @@ retnomove:
grid = mouse_grid;
}
+ win_T *old_curwin = curwin;
+ pos_T old_cursor = curwin->w_cursor;
if (!keep_focus) {
if (on_winbar) {
return IN_OTHER_WIN | MOUSE_WINBAR;
}
if (on_statuscol) {
- return IN_OTHER_WIN | MOUSE_STATUSCOL;
+ goto foldclick;
}
fdc = win_fdccol_count(wp);
@@ -1497,6 +1490,7 @@ retnomove:
}
}
+foldclick:;
colnr_T col_from_screen = -1;
int mouse_fold_flags = 0;
mouse_check_grid(&col_from_screen, &mouse_fold_flags);
@@ -1535,7 +1529,7 @@ retnomove:
*inclusive = false;
}
- count = IN_BUFFER;
+ count = on_statuscol ? (IN_OTHER_WIN|MOUSE_STATUSCOL) : IN_BUFFER;
if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
|| curwin->w_cursor.col != old_cursor.col) {
count |= CURSOR_MOVED; // Cursor has moved
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 6324466dcc..afd569ba7d 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -10,8 +10,8 @@
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
-#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
@@ -28,8 +28,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/highlight.h"
-#include "nvim/highlight_defs.h"
+#include "nvim/grid_defs.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
@@ -38,12 +37,12 @@
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
+#include "nvim/normal_defs.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
-#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -150,32 +149,27 @@ static void redraw_for_cursorline(win_T *wp)
}
}
-/// Redraw when w_virtcol changes and
+/// Redraw when 'concealcursor' is active, or when w_virtcol changes and:
/// - 'cursorcolumn' is set, or
/// - 'cursorlineopt' contains "screenline", or
-/// - 'concealcursor' is active, or
/// - Visual mode is active.
static void redraw_for_cursorcolumn(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- if (wp->w_valid & VALID_VIRTCOL) {
- return;
- }
-
// If the cursor moves horizontally when 'concealcursor' is active, then the
// current line needs to be redrawn to calculate the correct cursor position.
if (wp->w_p_cole > 0 && conceal_cursor_line(wp)) {
redrawWinline(wp, wp->w_cursor.lnum);
}
- if (pum_visible()) {
+ if ((wp->w_valid & VALID_VIRTCOL) || pum_visible()) {
return;
}
if (wp->w_p_cuc) {
// When 'cursorcolumn' is set need to redraw with UPD_SOME_VALID.
redraw_later(wp, UPD_SOME_VALID);
- } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) {
+ } else if (wp->w_p_cul && (wp->w_p_culopt_flags & kOptCuloptFlagScreenline)) {
// When 'cursorlineopt' contains "screenline" need to redraw with UPD_VALID.
redraw_later(wp, UPD_VALID);
}
@@ -2496,7 +2490,10 @@ int pagescroll(Direction dir, int count, bool half)
if (!nochange) {
// Place cursor at top or bottom of window.
validate_botline(curwin);
- curwin->w_cursor.lnum = (dir == FORWARD ? curwin->w_topline : curwin->w_botline - 1);
+ linenr_T lnum = (dir == FORWARD ? curwin->w_topline : curwin->w_botline - 1);
+ // In silent Ex mode the value of w_botline - 1 may be 0,
+ // but cursor lnum needs to be at least 1.
+ curwin->w_cursor.lnum = MAX(lnum, 1);
}
}
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 626312b666..e38bc1896d 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -26,6 +26,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/os/input.h"
#include "nvim/types_defs.h"
@@ -43,17 +44,20 @@
static void log_request(char *dir, uint64_t channel_id, uint32_t req_id, const char *name)
{
- DLOGN("RPC %s %" PRIu64 ": %s id=%u: %s\n", dir, channel_id, REQ, req_id, name);
+ logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, false, "%s %" PRIu64 ": %s id=%u: %s\n", dir, channel_id,
+ REQ, req_id, name);
}
static void log_response(char *dir, uint64_t channel_id, char *kind, uint32_t req_id)
{
- DLOGN("RPC %s %" PRIu64 ": %s id=%u\n", dir, channel_id, kind, req_id);
+ logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, false, "%s %" PRIu64 ": %s id=%u\n", dir, channel_id, kind,
+ req_id);
}
static void log_notify(char *dir, uint64_t channel_id, const char *name)
{
- DLOGN("RPC %s %" PRIu64 ": %s %s\n", dir, channel_id, NOT, name);
+ logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, false, "%s %" PRIu64 ": %s %s\n", dir, channel_id, NOT,
+ name);
}
#else
diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c
index b739f7ba28..e5eab91b34 100644
--- a/src/nvim/msgpack_rpc/packer.c
+++ b/src/nvim/msgpack_rpc/packer.c
@@ -1,8 +1,17 @@
#include <assert.h>
+#include <lauxlib.h>
+#include <stddef.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/lua/executor.h"
+#include "nvim/macros_defs.h"
+#include "nvim/memory.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "msgpack_rpc/packer.c.generated.h"
diff --git a/src/nvim/msgpack_rpc/packer.h b/src/nvim/msgpack_rpc/packer.h
index 299962bab4..da86f8e969 100644
--- a/src/nvim/msgpack_rpc/packer.h
+++ b/src/nvim/msgpack_rpc/packer.h
@@ -1,11 +1,11 @@
#pragma once
#include <stdbool.h>
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include <stdint.h>
-#include <string.h>
+#include <string.h> // IWYU pragma: keep
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/msgpack_rpc/packer_defs.h"
#define mpack_w(b, byte) *(*(b))++ = (char)(byte);
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 462f8397f4..b2c6d0d007 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -17,6 +17,7 @@
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/stdpaths_defs.h"
#include "nvim/types_defs.h"
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
index 4ddc41e596..185a1abccb 100644
--- a/src/nvim/msgpack_rpc/unpacker.c
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -1,6 +1,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <uv.h>
#include "klib/kvec.h"
#include "mpack/conv.h"
diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h
index c29462292f..a9cd7e4652 100644
--- a/src/nvim/msgpack_rpc/unpacker.h
+++ b/src/nvim/msgpack_rpc/unpacker.h
@@ -1,8 +1,10 @@
#pragma once
#include <inttypes.h>
+#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.h"
#include "mpack/mpack_core.h"
#include "mpack/object.h"
#include "nvim/api/private/defs.h"
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 55aa385b33..7d0080622d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -52,6 +52,7 @@
#include "nvim/mark_defs.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memline_defs.h"
#include "nvim/memory.h"
@@ -96,7 +97,7 @@ typedef struct {
bool previous_got_int; // `got_int` was true
bool cmdwin; // command-line window normal mode
bool noexmode; // true if the normal mode was pushed from
- // ex mode(:global or :visual for example)
+ // ex mode (:global or :visual for example)
bool toplevel; // top-level normal mode
oparg_T oa; // operator arguments
cmdarg_T ca; // command arguments
@@ -503,9 +504,9 @@ bool op_pending(void)
/// Normal state entry point. This is called on:
///
/// - Startup, In this case the function never returns.
-/// - The command-line window is opened(`q:`). Returns when `cmdwin_result` != 0.
+/// - The command-line window is opened (`q:`). Returns when `cmdwin_result` != 0.
/// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
-/// for example. Returns when re-entering ex mode(because ex mode recursion is
+/// for example. Returns when re-entering ex mode (because ex mode recursion is
/// not allowed)
///
/// This used to be called main_loop() on main.c
@@ -641,8 +642,7 @@ static bool normal_need_redraw_mode_message(NormalState *s)
return (
// 'showmode' is set and messages can be printed
((p_smd && msg_silent == 0
- // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual
- // mode
+ // must restart insert mode (ctrl+o or ctrl+l) or just entered visual mode
&& (restart_edit != 0 || (VIsual_active
&& s->old_pos.lnum == curwin->w_cursor.lnum
&& s->old_pos.col == curwin->w_cursor.col))
@@ -812,7 +812,7 @@ static void normal_get_additional_char(NormalState *s)
// There is a busy wait here when typing "f<C-\>" and then
// something different from CTRL-N. Can't be avoided.
while ((s->c = vpeekc()) <= 0 && towait > 0) {
- do_sleep(towait > 50 ? 50 : towait);
+ do_sleep(towait > 50 ? 50 : towait, false);
towait -= 50;
}
if (s->c > 0) {
@@ -1348,7 +1348,7 @@ static void normal_check_folds(NormalState *s)
if (hasAnyFolding(curwin) && !char_avail()) {
foldCheckClose();
- if (fdo_flags & FDO_ALL) {
+ if (fdo_flags & kOptFdoFlagAll) {
foldOpenCursor();
}
}
@@ -1372,10 +1372,6 @@ static void normal_redraw(NormalState *s)
}
}
- if (need_maketitle) {
- maketitle();
- }
-
curbuf->b_last_used = time(NULL);
// Display message after redraw. If an external message is still visible,
@@ -2310,7 +2306,7 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock)
return;
}
- if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagSearch) && KeyTyped && oap->op_type == OP_NOP) {
foldOpenCursor();
}
// clear any search statistics
@@ -3752,7 +3748,7 @@ static void nv_right(cmdarg_T *cap)
}
}
}
- if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
+ if (n != cap->count1 && (fdo_flags & kOptFdoFlagHor) && KeyTyped
&& cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
@@ -3811,7 +3807,7 @@ static void nv_left(cmdarg_T *cap)
break;
}
}
- if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
+ if (n != cap->count1 && (fdo_flags & kOptFdoFlagHor) && KeyTyped
&& cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
@@ -3929,7 +3925,7 @@ static void nv_dollar(cmdarg_T *cap)
if (cursor_down(cap->count1 - 1,
cap->oap->op_type == OP_NOP) == false) {
clearopbeep(cap->oap);
- } else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ } else if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4016,7 +4012,7 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int o
cap->oap->motion_type = kMTLineWise;
}
curwin->w_cursor.coladd = 0;
- if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
+ if (cap->oap->op_type == OP_NOP && (fdo_flags & kOptFdoFlagSearch) && KeyTyped) {
foldOpenCursor();
}
}
@@ -4065,7 +4061,7 @@ static void nv_csearch(cmdarg_T *cap)
curwin->w_cursor.coladd = 0;
}
adjust_for_sel(cap);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4181,7 +4177,7 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos)
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = true;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped
&& cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
@@ -4261,7 +4257,7 @@ static void nv_brackets(cmdarg_T *cap)
if (cap->oap->op_type == OP_NOP) {
beginline(BL_WHITE | BL_FIX);
}
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4319,7 +4315,7 @@ static void nv_brackets(cmdarg_T *cap)
}
curwin->w_set_curswant = true;
}
- if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
+ if (cap->oap->op_type == OP_NOP && (fdo_flags & kOptFdoFlagSearch) && KeyTyped) {
foldOpenCursor();
}
} else {
@@ -4371,7 +4367,7 @@ static void nv_percent(cmdarg_T *cap)
}
if (cap->oap->op_type == OP_NOP
&& lnum != curwin->w_cursor.lnum
- && (fdo_flags & FDO_PERCENT)
+ && (fdo_flags & kOptFdoFlagPercent)
&& KeyTyped) {
foldOpenCursor();
}
@@ -4395,7 +4391,7 @@ static void nv_brace(cmdarg_T *cap)
// Don't leave the cursor on the NUL past end of line.
adjust_cursor(cap->oap);
curwin->w_cursor.coladd = 0;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4426,7 +4422,7 @@ static void nv_findpar(cmdarg_T *cap)
}
curwin->w_cursor.coladd = 0;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4864,7 +4860,7 @@ static void nv_optrans(cmdarg_T *cap)
static void nv_gomark(cmdarg_T *cap)
{
int name;
- MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMove flags = jop_flags & kOptJopFlagView ? 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;
@@ -4893,7 +4889,7 @@ static void nv_gomark(cmdarg_T *cap)
if (cap->oap->op_type == OP_NOP
&& move_res & kMarkMoveSuccess
&& (move_res & kMarkSwitchedBuf || move_res & kMarkChangedCursor)
- && (fdo_flags & FDO_MARK)
+ && (fdo_flags & kOptFdoFlagMark)
&& old_KeyTyped) {
foldOpenCursor();
}
@@ -4904,7 +4900,7 @@ static void nv_gomark(cmdarg_T *cap)
static void nv_pcmark(cmdarg_T *cap)
{
fmark_T *fm = NULL;
- MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMove flags = jop_flags & kOptJopFlagView ? kMarkSetView : 0; // flags for moving to the mark
MarkMoveRes move_res = 0; // Result from moving to the mark
const bool old_KeyTyped = KeyTyped; // getting file may reset it.
@@ -4943,7 +4939,7 @@ static void nv_pcmark(cmdarg_T *cap)
}
if (cap->oap->op_type == OP_NOP
&& (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
- && (fdo_flags & FDO_MARK)
+ && (fdo_flags & kOptFdoFlagMark)
&& old_KeyTyped) {
foldOpenCursor();
}
@@ -5042,6 +5038,9 @@ static void nv_visual(cmdarg_T *cap)
assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1;
curwin->w_cursor.lnum = lnum;
+ if (*p_sel == 'e') {
+ curwin->w_curswant++;
+ }
coladvance(curwin, curwin->w_curswant);
} else {
curwin->w_set_curswant = true;
@@ -5094,7 +5093,7 @@ static void n_start_visual_mode(int c)
// Corner case: the 0 position in a tab may change when going into
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
//
- if (c == Ctrl_V && (get_ve_flags(curwin) & VE_BLOCK) && gchar_cursor() == TAB) {
+ if (c == Ctrl_V && (get_ve_flags(curwin) & kOptVeFlagBlock) && gchar_cursor() == TAB) {
validate_virtcol(curwin);
coladvance(curwin, curwin->w_virtcol);
}
@@ -5561,7 +5560,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gs": Goto sleep.
case 's':
- do_sleep(cap->count1 * 1000);
+ do_sleep(cap->count1 * 1000, false);
break;
// "ga": Display the ascii value of the character under the
@@ -5915,7 +5914,7 @@ static void nv_bck_word(cmdarg_T *cap)
curwin->w_set_curswant = true;
if (bck_word(cap->count1, cap->arg, false) == false) {
clearopbeep(cap->oap);
- } else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ } else if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -5975,7 +5974,7 @@ static void nv_wordcmd(cmdarg_T *cap)
clearopbeep(cap->oap);
} else {
adjust_for_sel(cap);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -5993,7 +5992,7 @@ static void adjust_cursor(oparg_T *oap)
if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
&& (!VIsual_active || *p_sel == 'o')
&& !virtual_active(curwin)
- && (get_ve_flags(curwin) & VE_ONEMORE) == 0) {
+ && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
curwin->w_cursor.col--;
// prevent cursor from moving on the trail byte
mb_adjust_cursor();
@@ -6008,7 +6007,7 @@ static void nv_beginline(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
beginline(cap->arg);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
ins_at_eol = false; // Don't move cursor past eol (only necessary in a
@@ -6097,7 +6096,7 @@ static void nv_goto(cmdarg_T *cap)
lnum = MIN(MAX(lnum, 1), curbuf->b_ml.ml_line_count);
curwin->w_cursor.lnum = lnum;
beginline(BL_SOL | BL_FIX);
- if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagJump) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -6167,7 +6166,7 @@ static void nv_esc(cmdarg_T *cap)
curwin->w_set_curswant = true;
redraw_curbuf_later(UPD_INVERTED);
} else if (no_reason) {
- vim_beep(BO_ESC);
+ vim_beep(kOptBoFlagEsc);
}
clearop(cap->oap);
}
@@ -6176,7 +6175,7 @@ static void nv_esc(cmdarg_T *cap)
void set_cursor_for_append_to_line(void)
{
curwin->w_set_curswant = true;
- if (get_ve_flags(curwin) == VE_ALL) {
+ if (get_ve_flags(curwin) == kOptVeFlagAll) {
const int save_State = State;
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
@@ -6501,7 +6500,8 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
int regname = cap->oap->regname;
bool keep_registers = cap->cmdchar == 'P';
// '+' and '*' could be the same selection
- bool clipoverwrite = (regname == '+' || regname == '*') && (cb_flags & CB_UNNAMEDMASK);
+ bool clipoverwrite = (regname == '+' || regname == '*')
+ && (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus));
if (regname == 0 || regname == '"' || clipoverwrite
|| ascii_isdigit(regname) || regname == '-') {
// The delete might overwrite the register we want to put, save it first
@@ -6624,8 +6624,8 @@ static void nv_event(cmdarg_T *cap)
// `input_get` branch was not executed (!multiqueue_empty(loop.events), which
// could have `may_garbage_collect` set to true in `normal_check`).
//
- // That is because here we may run code that calls `input_get`
- // later(`f_confirm` or `get_keystroke` for example), but in these cases it is
+ // That is because here we may run code that calls `input_get` later
+ // (`f_confirm` or `get_keystroke` for example), but in these cases it is
// not safe to perform garbage collection because there could be unreferenced
// lists or dicts being used.
may_garbage_collect = false;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 7dd3f665ba..2f45d862c3 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -29,6 +29,7 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
@@ -41,7 +42,6 @@
#include "nvim/getchar_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -49,6 +49,7 @@
#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
+#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
@@ -78,6 +79,7 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
#include "nvim/yankmap.h"
+#include "nvim/window.h"
struct yank_registers {
yankmap_T inner;
@@ -274,7 +276,7 @@ void op_shift(oparg_T *oap, bool curs_top, int amount)
vim_snprintf(IObuff, IOSIZE,
NGETTEXT(msg_line_single, msg_line_plural, oap->line_count),
(int64_t)oap->line_count, op, amount);
- msg_hl_keep(IObuff, 0, true, false);
+ msg_keep(IObuff, 0, true, false);
}
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
@@ -290,21 +292,53 @@ void op_shift(oparg_T *oap, bool curs_top, int amount)
changed_lines(curbuf, oap->start.lnum, 0, oap->end.lnum + 1, 0, true);
}
-/// Shift the current line one shiftwidth left (if left != 0) or right
-/// leaves cursor on first blank in the line.
-///
-/// @param call_changed_bytes call changed_bytes()
-void shift_line(bool left, bool round, int amount, int call_changed_bytes)
+/// Return the tabstop width at the index of the variable tabstop array. If an
+/// index greater than the length of the array is given, the last tabstop width
+/// in the array is returned.
+static int get_vts(const int *vts_array, int index)
{
- int sw_val = get_sw_value_indent(curbuf, left);
- if (sw_val == 0) {
- sw_val = 1; // shouldn't happen, just in case
+ int ts;
+
+ if (index < 1) {
+ ts = 0;
+ } else if (index <= vts_array[0]) {
+ ts = vts_array[index];
+ } else {
+ ts = vts_array[vts_array[0]];
+ }
+
+ return ts;
+}
+
+/// Return the sum of all the tabstops through the index-th.
+static int get_vts_sum(const int *vts_array, int index)
+{
+ int sum = 0;
+ int i;
+
+ // Perform the summation for indeces within the actual array.
+ for (i = 1; i <= index && i <= vts_array[0]; i++) {
+ sum += vts_array[i];
}
- int count = get_indent(); // get current indent
+
+ // Add topstops whose indeces exceed the actual array.
+ if (i <= index) {
+ sum += vts_array[vts_array[0]] * (index - vts_array[0]);
+ }
+
+ return sum;
+}
+
+/// @param left true if shift is to the left
+/// @param count true if new indent is to be to a tabstop
+/// @param amount number of shifts
+static int64_t get_new_sw_indent(bool left, bool round, int64_t amount, int64_t sw_val)
+{
+ int64_t count = get_indent(); // get current indent
if (round) { // round off indent
- int i = count / sw_val; // number of 'shiftwidth' rounded down
- int j = count % sw_val; // extra spaces
+ int64_t i = trim_to_int(count / sw_val); // number of 'shiftwidth' rounded down
+ int64_t j = trim_to_int(count % sw_val); // extra spaces
if (j && left) { // first remove extra spaces
amount--;
}
@@ -322,11 +356,94 @@ void shift_line(bool left, bool round, int amount, int call_changed_bytes)
}
}
+ return count;
+}
+
+/// @param left true if shift is to the left
+/// @param count true if new indent is to be to a tabstop
+/// @param amount number of shifts
+static int64_t get_new_vts_indent(bool left, bool round, int amount, int *vts_array)
+{
+ int64_t indent = get_indent();
+ int vtsi = 0;
+ int vts_indent = 0;
+ int ts = 0; // Silence uninitialized variable warning.
+
+ // Find the tabstop at or to the left of the current indent.
+ while (vts_indent <= indent) {
+ vtsi++;
+ ts = get_vts(vts_array, vtsi);
+ vts_indent += ts;
+ }
+ vts_indent -= ts;
+ vtsi--;
+
+ // Extra indent spaces to the right of the tabstop
+ int64_t offset = indent - vts_indent;
+
+ if (round) {
+ if (left) {
+ if (offset == 0) {
+ indent = get_vts_sum(vts_array, vtsi - amount);
+ } else {
+ indent = get_vts_sum(vts_array, vtsi - (amount - 1));
+ }
+ } else {
+ indent = get_vts_sum(vts_array, vtsi + amount);
+ }
+ } else {
+ if (left) {
+ if (amount > vtsi) {
+ indent = 0;
+ } else {
+ indent = get_vts_sum(vts_array, vtsi - amount) + offset;
+ }
+ } else {
+ indent = get_vts_sum(vts_array, vtsi + amount) + offset;
+ }
+ }
+
+ return indent;
+}
+
+/// Shift the current line 'amount' shiftwidth(s) left (if 'left' is true) or
+/// right.
+///
+/// The rules for choosing a shiftwidth are: If 'shiftwidth' is non-zero, use
+/// 'shiftwidth'; else if 'vartabstop' is not empty, use 'vartabstop'; else use
+/// 'tabstop'. The Vim documentation says nothing about 'softtabstop' or
+/// 'varsofttabstop' affecting the shiftwidth, and neither affects the
+/// shiftwidth in current versions of Vim, so they are not considered here.
+///
+/// @param left true if shift is to the left
+/// @param count true if new indent is to be to a tabstop
+/// @param amount number of shifts
+/// @param call_changed_bytes call changed_bytes()
+void shift_line(bool left, bool round, int amount, int call_changed_bytes)
+{
+ int64_t count;
+ int64_t sw_val = curbuf->b_p_sw;
+ int64_t ts_val = curbuf->b_p_ts;
+ int *vts_array = curbuf->b_p_vts_array;
+
+ if (sw_val != 0) {
+ // 'shiftwidth' is not zero; use it as the shift size.
+ count = get_new_sw_indent(left, round, amount, sw_val);
+ } else if ((vts_array == NULL) || (vts_array[0] == 0)) {
+ // 'shiftwidth' is zero and 'vartabstop' is empty; use 'tabstop' as the
+ // shift size.
+ count = get_new_sw_indent(left, round, amount, ts_val);
+ } else {
+ // 'shiftwidth' is zero and 'vartabstop' is defined; use 'vartabstop'
+ // to determine the new indent.
+ count = get_new_vts_indent(left, round, amount, vts_array);
+ }
+
// Set new indent
if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, count, false, call_changed_bytes);
+ change_indent(INDENT_SET, trim_to_int(count), false, call_changed_bytes);
} else {
- set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
+ set_indent(trim_to_int(count), call_changed_bytes ? SIN_CHANGED : 0);
}
}
@@ -2468,7 +2585,7 @@ void op_insert(oparg_T *oap, int count1)
if (u_save_cursor() == FAIL) {
return;
}
- curwin->w_ve_flags = VE_ALL;
+ curwin->w_ve_flags = kOptVeFlagAll;
coladvance_force(oap->op_type == OP_APPEND
? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
@@ -3231,7 +3348,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
}
- bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE);
+ bool ve_allows = (cur_ve_flags == kOptVeFlagAll || cur_ve_flags == kOptVeFlagOnemore);
bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum
&& one_past_line;
if (ve_allows || !(eol || eof)) {
@@ -3419,7 +3536,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
goto end;
}
- if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
+ if (cur_ve_flags == kOptVeFlagAll && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
int viscol = getviscol();
OptInt ts = curbuf->b_p_ts;
@@ -3448,7 +3565,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
colnr_T endcol2 = 0;
if (dir == FORWARD && c != NUL) {
- if (cur_ve_flags == VE_ALL) {
+ if (cur_ve_flags == kOptVeFlagAll) {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
@@ -3462,7 +3579,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
}
col += curwin->w_cursor.coladd;
- if (cur_ve_flags == VE_ALL
+ if (cur_ve_flags == kOptVeFlagAll
&& (curwin->w_cursor.coladd > 0 || endcol2 == curwin->w_cursor.col)) {
if (dir == FORWARD && c == NUL) {
col++;
@@ -3922,7 +4039,7 @@ error:
// Make sure the cursor is not after the NUL.
int len = get_cursor_line_len();
if (curwin->w_cursor.col > len) {
- if (cur_ve_flags == VE_ALL) {
+ if (cur_ve_flags == kOptVeFlagAll) {
curwin->w_cursor.coladd = curwin->w_cursor.col - len;
}
curwin->w_cursor.col = len;
@@ -3958,7 +4075,7 @@ void adjust_cursor_eol(void)
const bool adj_cursor = (curwin->w_cursor.col > 0
&& gchar_cursor() == NUL
- && (cur_ve_flags & VE_ONEMORE) == 0
+ && (cur_ve_flags & kOptVeFlagOnemore) == 0
&& !(restart_edit || (State & MODE_INSERT)));
if (!adj_cursor) {
return;
@@ -3967,7 +4084,7 @@ void adjust_cursor_eol(void)
// Put the cursor on the last character in the line.
dec_cursor();
- if (cur_ve_flags == VE_ALL) {
+ if (cur_ve_flags == kOptVeFlagAll) {
colnr_T scol, ecol;
// Coladd is set to the width of the last character.
@@ -4596,6 +4713,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
colnr_T endcol = MAXCOL;
colnr_T cs, ce;
char *p = ml_get(lnum);
+ int plen = ml_get_len(lnum);
bdp->startspaces = 0;
bdp->endspaces = 0;
@@ -4645,7 +4763,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
bdp->textlen = endcol - startcol + inclusive;
}
bdp->textcol = startcol;
- bdp->textstart = p + startcol;
+ bdp->textstart = startcol <= plen ? p + startcol : p;
}
/// Handle the add/subtract operator.
@@ -6446,7 +6564,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_DELETE:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
op_delete(oap);
@@ -6462,7 +6580,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_YANK:
if (empty_region_error) {
if (!gui_yank) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
}
} else {
@@ -6476,7 +6594,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_CHANGE:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
// This is a new edit command, not a restart. Need to
@@ -6539,7 +6657,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_LOWER:
case OP_ROT13:
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
op_tilde(oap);
@@ -6581,7 +6699,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_APPEND:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
// This is a new edit command, not a restart. Need to
@@ -6616,7 +6734,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_REPLACE:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
// Restore linebreak, so that when the user edits it looks as before.
@@ -6654,7 +6772,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_NR_ADD:
case OP_NR_SUB:
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
VIsual_active = true;
@@ -6714,7 +6832,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
yankreg_T *target = NULL;
bool explicit_cb_reg = (*name == '*' || *name == '+');
- bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK);
+ bool implicit_cb_reg = (*name == NUL) && (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus));
if (!explicit_cb_reg && !implicit_cb_reg) {
goto end;
}
@@ -6733,7 +6851,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
if (explicit_cb_reg) {
target = get_global_reg(*name == '*' ? STAR_REGISTER : PLUS_REGISTER);
- if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) {
+ if (writing && (cb_flags & (*name == '*' ? kOptCbFlagUnnamed : kOptCbFlagUnnamedplus))) {
clipboard_needs_update = false;
}
goto end;
@@ -6747,8 +6865,8 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
goto end;
}
- if (cb_flags & CB_UNNAMEDPLUS) {
- *name = (cb_flags & CB_UNNAMED && writing) ? '"' : '+';
+ if (cb_flags & kOptCbFlagUnnamedplus) {
+ *name = (cb_flags & kOptCbFlagUnnamed && writing) ? '"' : '+';
target = get_global_reg(PLUS_REGISTER);
} else {
*name = '*';
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 35ee1e76dd..05dd0454dc 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -3,8 +3,8 @@
#include <stdbool.h>
#include <stddef.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/extmark_defs.h" // IWYU pragma: keep
#include "nvim/macros_defs.h"
diff --git a/src/nvim/option.c b/src/nvim/option.c
index bb86d10425..3d805dc319 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <uv.h>
#include "auto/config.h"
#include "klib/kvec.h"
@@ -47,6 +48,7 @@
#include "nvim/errors.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_defs.h"
@@ -58,6 +60,7 @@
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -74,6 +77,7 @@
#include "nvim/memfile.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -88,7 +92,6 @@
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/path.h"
-#include "nvim/plines.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
#include "nvim/regexp.h"
@@ -104,7 +107,6 @@
#include "nvim/terminal.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/ui_defs.h"
#include "nvim/undo.h"
#include "nvim/undo_defs.h"
#include "nvim/vim_defs.h"
@@ -228,7 +230,7 @@ static void set_init_default_backupskip(void)
#endif
{
p = vim_getenv(names[i]);
- plen = 0; // will be calcuated below
+ plen = 0; // will be calculated below
}
if (p != NULL && *p != NUL) {
bool has_trailing_path_sep = false;
@@ -429,7 +431,7 @@ void set_init_1(bool clean_arg)
/// Get default value for option, based on the option's type and scope.
///
/// @param opt_idx Option index in options[] table.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// @return Default value of option for the scope specified in opt_flags.
static OptVal get_option_default(const OptIndex opt_idx, int opt_flags)
@@ -480,7 +482,7 @@ static void change_option_default(const OptIndex opt_idx, OptVal value)
/// This does not take care of side effects!
///
/// @param opt_idx Option index in options[] table.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void set_option_default(const OptIndex opt_idx, int opt_flags)
{
OptVal def_val = get_option_default(opt_idx, opt_flags);
@@ -497,7 +499,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags)
/// Set all options (except terminal options) to their default value.
///
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void set_options_default(int opt_flags)
{
for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
@@ -531,8 +533,8 @@ static void set_string_default(OptIndex opt_idx, char *val, bool allocated)
/// 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, const size_t newvallen,
- uint32_t flags)
+static const char *find_dup_item(const char *origval, const char *newval, const size_t newvallen,
+ uint32_t flags)
FUNC_ATTR_NONNULL_ARG(2)
{
if (origval == NULL) {
@@ -541,7 +543,7 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
int bs = 0;
- for (char *s = origval; *s != NUL; s++) {
+ for (const char *s = origval; *s != NUL; s++) {
if ((!(flags & kOptFlagComma) || s == origval || (s[-1] == ',' && !(bs & 1)))
&& strncmp(s, newval, newvallen) == 0
&& (!(flags & kOptFlagComma) || s[newvallen] == ',' || s[newvallen] == NUL)) {
@@ -725,7 +727,7 @@ void ex_set(exarg_T *eap)
/// Copy the new string value into allocated memory for the option.
/// Can't use set_option_direct(), because we need to remove the backslashes.
-static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
+static char *stropt_copy_value(const char *origval, char **argp, set_op_T op,
uint32_t flags FUNC_ATTR_UNUSED)
{
char *arg = *argp;
@@ -772,7 +774,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
}
/// Expand environment variables and ~ in string option value 'newval'.
-static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval, set_op_T op)
+static char *stropt_expand_envvar(OptIndex opt_idx, const char *origval, char *newval, set_op_T op)
{
char *s = option_expand(opt_idx, newval);
if (s == NULL) {
@@ -792,7 +794,7 @@ static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval,
/// Concatenate the original and new values of a string option, adding a "," if
/// needed.
-static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, uint32_t flags)
+static void stropt_concat_with_comma(const char *origval, char *newval, set_op_T op, uint32_t flags)
{
int len = 0;
int comma = ((flags & kOptFlagComma) && *origval != NUL && *newval != NUL);
@@ -818,7 +820,8 @@ static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, u
/// Remove a value from a string option. Copy string option value in "origval"
/// to "newval" and then remove the string "strval" of length "len".
-static void stropt_remove_val(char *origval, char *newval, uint32_t flags, char *strval, int len)
+static void stropt_remove_val(const char *origval, char *newval, uint32_t flags, const char *strval,
+ int len)
{
// Remove newval[] from origval[]. (Note: "len" has been set above
// and is used here).
@@ -870,13 +873,13 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
/// set {opt}={val}
/// set {opt}:{val}
static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp,
- char *origval, set_op_T *op_arg, uint32_t flags)
+ const char *origval, set_op_T *op_arg, uint32_t flags)
{
char *arg = *argp;
set_op_T op = *op_arg;
char *save_arg = NULL;
char *newval;
- char *s = NULL;
+ const char *s = NULL;
arg++; // jump to after the '=' or ':'
@@ -977,12 +980,12 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_
// Skip all options that are not window-local (used when showing
// an already loaded buffer in a window).
- if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || !option_is_window_local(opt_idx))) {
+ if ((opt_flags & OPT_WINONLY) && !option_is_window_local(opt_idx)) {
return FAIL;
}
// Skip all options that are window-local (used for :vimgrep).
- if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && option_is_window_local(opt_idx)) {
+ if ((opt_flags & OPT_NOWIN) && option_is_window_local(opt_idx)) {
return FAIL;
}
@@ -1192,9 +1195,10 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
break;
}
case kOptValTypeString: {
- char *oldval_str = oldval.data.string.data;
+ const char *oldval_str = oldval.data.string.data;
// Get the new value for the option
- char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op, flags);
+ const char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op,
+ flags);
newval = CSTR_AS_OPTVAL(newval_str);
break;
}
@@ -1276,6 +1280,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
gotocmdline(true); // cursor at status line
*did_show = true; // remember that we did a line
}
+ msg_ext_set_kind("list_cmd");
showoneopt(&options[opt_idx], opt_flags);
if (p_verbose > 0) {
@@ -1474,7 +1479,7 @@ void did_set_title(void)
/// set_options_bin - called when 'bin' changes value.
///
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_options_bin(int oldval, int newval, int opt_flags)
{
// The option values that are changed when 'bin' changes are
@@ -1565,7 +1570,7 @@ char *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 *option_expand(OptIndex opt_idx, char *val)
+static char *option_expand(OptIndex opt_idx, const char *val)
{
// if option doesn't need expansion nothing to do
if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) {
@@ -1651,7 +1656,7 @@ void check_options(void)
///
/// @param wp Window.
/// @param opt_idx Option index in options[] table.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// @return True if option was set from a modeline or in secure mode, false if it wasn't.
int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags)
@@ -1735,8 +1740,14 @@ bool parse_winhl_opt(const char *winhl, win_T *wp)
p = wp->w_p_winhl;
}
+ if (wp != NULL && wp->w_ns_hl_winhl < 0) {
+ // 'winhighlight' shouldn't be used for this window.
+ // Only check that the value is valid.
+ wp = NULL;
+ }
+
if (!*p) {
- if (wp != NULL && wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) {
+ if (wp != NULL && wp->w_ns_hl_winhl > 0 && wp->w_ns_hl == wp->w_ns_hl_winhl) {
wp->w_ns_hl = 0;
wp->w_hl_needs_update = true;
}
@@ -1970,8 +1981,8 @@ static const char *did_set_cmdheight(optset_T *args)
{
OptInt old_value = args->os_oldval.number;
- if (p_ch > Rows - min_rows() + 1) {
- p_ch = Rows - min_rows() + 1;
+ if (p_ch > Rows - min_rows(curtab) + 1) {
+ p_ch = Rows - min_rows(curtab) + 1;
}
// if p_ch changed value, change the command line height
@@ -2109,14 +2120,14 @@ static const char *did_set_laststatus(optset_T *args)
// When switching to global statusline, decrease topframe height
// Also clear the cmdline to remove the ruler if there is one
if (value == 3 && old_value != 3) {
- frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false);
+ frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false, false);
win_comp_pos();
clear_cmdline = true;
}
// When switching from global statusline, increase height of topframe by STATUS_HEIGHT
// in order to to re-add the space that was previously taken by the global statusline
if (old_value == 3 && value != 3) {
- frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false);
+ frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false, false);
win_comp_pos();
}
@@ -2750,10 +2761,11 @@ static const char *check_num_option_bounds(OptIndex opt_idx, OptInt *newval, cha
switch (opt_idx) {
case kOptLines:
- if (*newval < min_rows() && full_screen) {
- vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"), min_rows());
+ if (*newval < min_rows_for_all_tabpages() && full_screen) {
+ vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"),
+ min_rows_for_all_tabpages());
errmsg = errbuf;
- *newval = min_rows();
+ *newval = min_rows_for_all_tabpages();
}
// True max size is defined by check_screensize().
*newval = MIN(*newval, INT_MAX);
@@ -2860,8 +2872,6 @@ static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *e
case kOptCmdheight:
if (value < 0) {
return e_positive;
- } else {
- p_ch_was_zero = value == 0;
}
break;
case kOptHistory:
@@ -2871,13 +2881,6 @@ static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *e
return e_invarg;
}
break;
- case kOptMsghistory:
- if (value < 0) {
- return e_positive;
- } else if (value > 10000) {
- return e_invarg;
- }
- break;
case kOptPyxversion:
if (value == 0) {
*newval = 3;
@@ -3118,17 +3121,10 @@ bool optval_equal(OptVal o1, OptVal o2)
UNREACHABLE;
}
-/// Get type of option. Does not support multitype options.
+/// Get type of option.
static OptValType option_get_type(const OptIndex opt_idx)
{
- assert(!option_is_multitype(opt_idx));
-
- // If the option only supports a single type, it means that the index of the option's type flag
- // corresponds to the value of the type enum. So get the index of the type flag using xctz() and
- // use that as the option's type.
- OptValType type = xctz(options[opt_idx].type_flags);
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
- return type;
+ return options[opt_idx].type;
}
/// Create OptVal from var pointer.
@@ -3146,11 +3142,6 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
return BOOLEAN_OPTVAL(curbufIsChanged());
}
- if (option_is_multitype(opt_idx)) {
- // Multitype options are stored as OptVal.
- return *(OptVal *)varp;
- }
-
OptValType type = option_get_type(opt_idx);
switch (type) {
@@ -3261,33 +3252,6 @@ OptVal object_as_optval(Object o, bool *error)
UNREACHABLE;
}
-/// Get an allocated string containing a list of valid types for an option.
-/// For options with a singular type, it returns the name of the type. For options with multiple
-/// possible types, it returns a slash separated list of types. For example, if an option can be a
-/// number, boolean or string, the function returns "number/boolean/string"
-static char *option_get_valid_types(OptIndex opt_idx)
-{
- StringBuilder str = KV_INITIAL_VALUE;
- kv_resize(str, 32);
-
- // Iterate through every valid option value type and check if the option supports that type
- for (OptValType type = 0; type < kOptValTypeSize; type++) {
- if (option_has_type(opt_idx, type)) {
- const char *typename = optval_type_get_name(type);
-
- if (str.size == 0) {
- kv_concat(str, typename);
- } else {
- kv_printf(str, "/%s", typename);
- }
- }
- }
-
- // Ensure that the string is NUL-terminated.
- kv_push(str, NUL);
- return str.items;
-}
-
/// Check if option is hidden.
///
/// @param opt_idx Option index in options[] table.
@@ -3300,25 +3264,10 @@ bool is_option_hidden(OptIndex opt_idx)
&& options[opt_idx].var == &options[opt_idx].def_val.data;
}
-/// Check if option is multitype (supports multiple types).
-static bool option_is_multitype(OptIndex opt_idx)
-{
- const OptTypeFlags type_flags = get_option(opt_idx)->type_flags;
- assert(type_flags != 0);
- return !is_power_of_two(type_flags);
-}
-
/// Check if option supports a specific type.
bool option_has_type(OptIndex opt_idx, OptValType type)
{
- // Ensure that type flags variable can hold all types.
- STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8,
- "Option type_flags cannot fit all option types");
- // Ensure that the type is valid before accessing type_flags.
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
- // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in
- // the type_flags bit field.
- return get_option(opt_idx)->type_flags & (1 << type);
+ return opt_idx != kOptInvalid && options[opt_idx].type == type;
}
/// Check if option supports a specific scope.
@@ -3377,18 +3326,18 @@ uint32_t get_option_flags(OptIndex opt_idx)
/// Gets the value for an option.
///
-/// @param opt_idx Option index in options[] table.
-/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
+/// @param opt_idx Option index in options[] table.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// @return [allocated] Option value. Returns NIL_OPTVAL for invalid option index.
-OptVal get_option_value(OptIndex opt_idx, int scope)
+OptVal get_option_value(OptIndex opt_idx, int opt_flags)
{
if (opt_idx == kOptInvalid) { // option not in the options[] table.
return NIL_OPTVAL;
}
vimoption_T *opt = &options[opt_idx];
- void *varp = get_varp_scope(opt, scope);
+ void *varp = get_varp_scope(opt, opt_flags);
return optval_copy(optval_from_varp(opt_idx, varp));
}
@@ -3461,7 +3410,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx)
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
/// @param[in] varp Option variable pointer, cannot be NULL.
/// @param old_value Old option value.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
@@ -3473,8 +3422,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx)
/// @return NULL on success, an untranslated error message on error.
static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value,
int opt_flags, scid_T set_sid, const bool direct,
- const bool value_replaced, char *errbuf, // NOLINT(readability-non-const-parameter)
- size_t errbuflen)
+ const bool value_replaced, char *errbuf, size_t errbuflen)
{
vimoption_T *opt = &options[opt_idx];
const char *errmsg = NULL;
@@ -3656,11 +3604,10 @@ static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval,
}
} else if (!option_has_type(opt_idx, newval->type)) {
char *rep = optval_to_cstr(*newval);
- char *valid_types = option_get_valid_types(opt_idx);
+ const char *type_str = optval_type_get_name(opt->type);
snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"),
- opt->fullname, valid_types, optval_type_get_name(newval->type), rep);
+ opt->fullname, type_str, optval_type_get_name(newval->type), rep);
xfree(rep);
- xfree(valid_types);
errmsg = errbuf;
} else if (newval->type == kOptValTypeNumber) {
// Validate and bound check num option values.
@@ -3674,7 +3621,7 @@ static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval,
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
/// @param value New option value. Might get freed.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
@@ -3778,7 +3725,7 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag
///
/// @param opt_idx Option index in options[] table.
/// @param value Option value.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
@@ -3800,14 +3747,14 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
///
/// @param opt_idx Option index in options[] table.
/// @param value Option value.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
- OptScope req_scope, void *const from)
+ OptScope scope, void *const from)
{
buf_T *save_curbuf = curbuf;
win_T *save_curwin = curwin;
@@ -3815,7 +3762,7 @@ void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T
// Don't use switch_option_context(), as that calls aucmd_prepbuf(), which may have unintended
// side-effects when setting an option directly. Just change the values of curbuf and curwin if
// needed, no need to properly switch the window / buffer.
- switch (req_scope) {
+ switch (scope) {
case kOptScopeGlobal:
break;
case kOptScopeWin:
@@ -3898,7 +3845,7 @@ const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, cons
///
/// @param opt_idx Option index in options[] table.
/// @param value Option value. If NIL_OPTVAL, the option value is cleared.
-/// @param opt_flags OPT_LOCAL or 0 (both)
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_flags)
{
const char *errmsg = set_option_value(opt_idx, value, opt_flags);
@@ -3911,14 +3858,14 @@ void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_fla
/// Switch current context to get/set option value for window/buffer.
///
/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
///
/// @return true if context was switched, false otherwise.
-static bool switch_option_context(void *const ctx, OptScope req_scope, void *const from, Error *err)
+static bool switch_option_context(void *const ctx, OptScope scope, void *const from, Error *err)
{
- switch (req_scope) {
+ switch (scope) {
case kOptScopeGlobal:
return false;
case kOptScopeWin: {
@@ -3933,7 +3880,7 @@ static bool switch_option_context(void *const ctx, OptScope req_scope, void *con
== FAIL) {
restore_win_noblock(switchwin, true);
- if (try_end(err)) {
+ if (ERROR_SET(err)) {
return false;
}
api_set_error(err, kErrorTypeException, "Problem while switching windows");
@@ -3957,9 +3904,9 @@ static bool switch_option_context(void *const ctx, OptScope req_scope, void *con
/// Restore context after getting/setting option for window/buffer. See switch_option_context() for
/// params.
-static void restore_option_context(void *const ctx, OptScope req_scope)
+static void restore_option_context(void *const ctx, OptScope scope)
{
- switch (req_scope) {
+ switch (scope) {
case kOptScopeGlobal:
break;
case kOptScopeWin:
@@ -3977,28 +3924,28 @@ static void restore_option_context(void *const ctx, OptScope req_scope)
/// @param[out] flagsp Set to the option flags (see OptFlags) (if not NULL).
/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param[out] hidden Whether option is hidden.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
///
/// @return Option value. Must be freed by caller.
-OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scope, void *const from,
+OptVal get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scope, void *const from,
Error *err)
{
switchwin_T switchwin;
aco_save_T aco;
- void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin
- : (req_scope == kOptScopeBuf ? (void *)&aco : NULL);
+ void *ctx = scope == kOptScopeWin ? (void *)&switchwin
+ : (scope == kOptScopeBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
+ bool switched = switch_option_context(ctx, scope, from, err);
if (ERROR_SET(err)) {
return NIL_OPTVAL;
}
- OptVal retv = get_option_value(opt_idx, scope);
+ OptVal retv = get_option_value(opt_idx, opt_flags);
if (switched) {
- restore_option_context(ctx, req_scope);
+ restore_option_context(ctx, scope);
}
return retv;
@@ -4010,19 +3957,19 @@ OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scop
/// @param opt_idx Option index in options[] table.
/// @param[in] value Option value.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags,
- const OptScope req_scope, void *const from, Error *err)
+ const OptScope scope, void *const from, Error *err)
FUNC_ATTR_NONNULL_ARG(1)
{
switchwin_T switchwin;
aco_save_T aco;
- void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin
- : (req_scope == kOptScopeBuf ? (void *)&aco : NULL);
+ void *ctx = scope == kOptScopeWin ? (void *)&switchwin
+ : (scope == kOptScopeBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
+ bool switched = switch_option_context(ctx, scope, from, err);
if (ERROR_SET(err)) {
return;
}
@@ -4033,14 +3980,14 @@ void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, cons
}
if (switched) {
- restore_option_context(ctx, req_scope);
+ restore_option_context(ctx, scope);
}
}
/// if 'all' == false: show changed options
/// if 'all' == true: show all normal options
///
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void showoptions(bool all, int opt_flags)
{
#define INC 20
@@ -4048,6 +3995,7 @@ static void showoptions(bool all, int opt_flags)
vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT);
+ msg_ext_set_kind("list_cmd");
// Highlight title
if (opt_flags & OPT_GLOBAL) {
msg_puts_title(_("\n--- Global option values ---"));
@@ -4166,7 +4114,7 @@ void ui_refresh_options(void)
/// showoneopt: show the value of one option
/// must not be called with a hidden option!
///
-/// @param opt_flags OPT_LOCAL or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void showoneopt(vimoption_T *opt, int opt_flags)
{
int save_silent = silent_mode;
@@ -4389,7 +4337,7 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
return FAIL;
}
- char *value_str = value.data.string.data;
+ const char *value_str = value.data.string.data;
char *buf = NULL;
char *part = NULL;
@@ -4451,18 +4399,18 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
return OK;
}
-void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
+void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win)
{
OptIndex opt_idx = get_opt_idx(p);
- if ((scope & OPT_GLOBAL) && !option_is_global_only(opt_idx)) {
+ if ((opt_flags & OPT_GLOBAL) && !option_is_global_only(opt_idx)) {
if (option_is_window_local(opt_idx)) {
return GLOBAL_WO(get_varp_from(p, buf, win));
}
return p->var;
}
- if ((scope & OPT_LOCAL) && option_is_global_local(opt_idx)) {
+ if ((opt_flags & OPT_LOCAL) && option_is_global_local(opt_idx)) {
switch (opt_idx) {
case kOptFormatprg:
return &(buf->b_p_fp);
@@ -4533,17 +4481,17 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
/// Get pointer to option variable, depending on local or global scope.
///
-/// @param scope can be OPT_LOCAL, OPT_GLOBAL or a combination.
-void *get_varp_scope(vimoption_T *p, int scope)
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
+void *get_varp_scope(vimoption_T *p, int opt_flags)
{
- return get_varp_scope_from(p, scope, curbuf, curwin);
+ return get_varp_scope_from(p, opt_flags, curbuf, curwin);
}
/// Get pointer to option variable at 'opt_idx', depending on local or global
/// scope.
-void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T *win)
+void *get_option_varp_scope_from(OptIndex opt_idx, int opt_flags, buf_T *buf, win_T *win)
{
- return get_varp_scope_from(&(options[opt_idx]), scope, buf, win);
+ return get_varp_scope_from(&(options[opt_idx]), opt_flags, buf, win);
}
void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
@@ -5282,7 +5230,7 @@ void buf_copy_options(buf_T *buf, int flags)
// or to a help buffer.
if (dont_do_help) {
buf->b_p_isk = save_p_isk;
- if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) {
+ if (p_vts && *p_vts != NUL && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
@@ -5295,7 +5243,7 @@ void buf_copy_options(buf_T *buf, int flags)
COPY_OPT_SCTX(buf, kBufOptTabstop);
buf->b_p_vts = xstrdup(p_vts);
COPY_OPT_SCTX(buf, kBufOptVartabstop);
- if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) {
+ if (p_vts && *p_vts != NUL && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
@@ -5348,7 +5296,7 @@ static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
static int expand_option_flags = 0;
static bool expand_option_append = false;
-/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
{
expand_option_flags = opt_flags;
@@ -5771,6 +5719,7 @@ int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, cha
optexpand_T args = {
.oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags),
+ .oe_idx = expand_option_idx,
.oe_append = expand_option_append,
.oe_regmatch = regmatch,
.oe_xp = xp,
@@ -5900,12 +5849,12 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
/// Get the value for the numeric or string option///opp in a nice format into
/// NameBuff[]. Must not be called with a hidden option!
///
-/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// TODO(famiu): Replace this with optval_to_cstr() if possible.
-static void option_value2string(vimoption_T *opt, int scope)
+static void option_value2string(vimoption_T *opt, int opt_flags)
{
- void *varp = get_varp_scope(opt, scope);
+ void *varp = get_varp_scope(opt, opt_flags);
assert(varp != NULL);
if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) {
@@ -6009,19 +5958,19 @@ int fill_culopt_flags(char *val, win_T *wp)
p = val;
}
while (*p != NUL) {
- // Note: Keep this in sync with p_culopt_values.
+ // Note: Keep this in sync with opt_culopt_values.
if (strncmp(p, "line", 4) == 0) {
p += 4;
- culopt_flags_new |= CULOPT_LINE;
+ culopt_flags_new |= kOptCuloptFlagLine;
} else if (strncmp(p, "both", 4) == 0) {
p += 4;
- culopt_flags_new |= CULOPT_LINE | CULOPT_NBR;
+ culopt_flags_new |= kOptCuloptFlagLine | kOptCuloptFlagNumber;
} else if (strncmp(p, "number", 6) == 0) {
p += 6;
- culopt_flags_new |= CULOPT_NBR;
+ culopt_flags_new |= kOptCuloptFlagNumber;
} else if (strncmp(p, "screenline", 10) == 0) {
p += 10;
- culopt_flags_new |= CULOPT_SCRLINE;
+ culopt_flags_new |= kOptCuloptFlagScreenline;
}
if (*p != ',' && *p != NUL) {
@@ -6033,7 +5982,7 @@ int fill_culopt_flags(char *val, win_T *wp)
}
// Can't have both "line" and "screenline".
- if ((culopt_flags_new & CULOPT_LINE) && (culopt_flags_new & CULOPT_SCRLINE)) {
+ if ((culopt_flags_new & kOptCuloptFlagLine) && (culopt_flags_new & kOptCuloptFlagScreenline)) {
return FAIL;
}
wp->w_p_culopt_flags = culopt_flags_new;
@@ -6143,7 +6092,8 @@ char *get_flp_value(buf_T *buf)
/// Get the local or global value of 'virtualedit' flags.
unsigned get_ve_flags(win_T *wp)
{
- return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU);
+ return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags)
+ & ~(unsigned)(kOptVeFlagNone | kOptVeFlagNoneU);
}
/// Get the local or global value of 'showbreak'.
@@ -6221,7 +6171,7 @@ int default_fileformat(void)
/// Sets 'fileformat'.
///
/// @param eol_style End-of-line style.
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_fileformat(int eol_style, int opt_flags)
{
char *p = NULL;
@@ -6352,14 +6302,14 @@ int get_sidescrolloff_value(win_T *wp)
return (int)(wp->w_p_siso < 0 ? p_siso : wp->w_p_siso);
}
-Dict get_vimoption(String name, int scope, buf_T *buf, win_T *win, Arena *arena, Error *err)
+Dict get_vimoption(String name, int opt_flags, buf_T *buf, win_T *win, Arena *arena, Error *err)
{
OptIndex opt_idx = find_option_len(name.data, name.size);
VALIDATE_S(opt_idx != kOptInvalid, "option (not found)", name.data, {
return (Dict)ARRAY_DICT_INIT;
});
- return vimoption2dict(&options[opt_idx], scope, buf, win, arena);
+ return vimoption2dict(&options[opt_idx], opt_flags, buf, win, arena);
}
Dict get_all_vimoptions(Arena *arena)
@@ -6372,7 +6322,7 @@ Dict get_all_vimoptions(Arena *arena)
return retval;
}
-static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win, Arena *arena)
+static Dict vimoption2dict(vimoption_T *opt, int opt_flags, buf_T *buf, win_T *win, Arena *arena)
{
OptIndex opt_idx = get_opt_idx(opt);
Dict dict = arena_dict(arena, 13);
@@ -6399,7 +6349,7 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & kOptFlagWasSet));
LastSet last_set = { .channel_id = 0 };
- if (req_scope == OPT_GLOBAL) {
+ if (opt_flags == OPT_GLOBAL) {
last_set = opt->last_set;
} else {
// Scope is either OPT_LOCAL or a fallback mode was requested.
@@ -6409,7 +6359,7 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
if (option_has_scope(opt_idx, kOptScopeWin)) {
last_set = win->w_p_script_ctx[opt->scope_idx[kOptScopeWin]];
}
- if (req_scope != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) {
+ if (opt_flags != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) {
last_set = opt->last_set;
}
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index cba5b00d95..2f71990150 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -1,13 +1,11 @@
#pragma once
-#include <stdbool.h>
-#include <stdint.h>
#include <stdio.h> // IWYU pragma: keep
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/api/private/helpers.h"
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/macros_defs.h"
#include "nvim/option_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 832e03148a..7dd4bd2bc7 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -54,9 +54,6 @@ typedef enum {
kOptValTypeNumber,
kOptValTypeString,
} OptValType;
-/// Always update this whenever a new option type is added.
-#define kOptValTypeSize (kOptValTypeString + 1)
-typedef uint32_t OptTypeFlags;
/// Scopes that an option can support.
typedef enum {
@@ -99,7 +96,6 @@ typedef struct {
int os_flags;
/// Old value of the option.
- /// TODO(famiu): Convert `os_oldval` and `os_newval` to `OptVal` to accommodate multitype options.
OptValData os_oldval;
/// New value of the option.
OptValData os_newval;
@@ -138,6 +134,7 @@ typedef const char *(*opt_did_set_cb_T)(optset_T *args);
typedef struct {
/// Pointer to the option variable. It's always a string.
char *oe_varp;
+ OptIndex oe_idx;
/// The original option value, escaped.
char *oe_opt_value;
@@ -173,14 +170,18 @@ typedef struct {
char *fullname; ///< full option name
char *shortname; ///< permissible abbreviation
uint32_t flags; ///< see above
- OptTypeFlags type_flags; ///< option type flags, see OptValType
+ OptValType type; ///< option type
OptScopeFlags scope_flags; ///< option scope flags, see OptScope
void *var; ///< global option: pointer to variable;
///< window-local option: NULL;
///< buffer-local option: global value
+ unsigned *flags_var;
ssize_t scope_idx[kOptScopeSize]; ///< index of option at every scope.
bool immutable; ///< option is immutable, trying to set it will give an error.
+ const char **values; ///< possible values for string options
+ const size_t values_len; ///< length of values array
+
/// callback function to invoke after an option is modified to validate and
/// apply the new value.
opt_did_set_cb_T opt_did_set_cb;
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index 9ed24c4c9c..d31e76aff8 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -3,12 +3,17 @@
#include "nvim/macros_defs.h"
#include "nvim/os/os_defs.h"
#include "nvim/sign_defs.h"
+#include "nvim/statusline_defs.h"
#include "nvim/types_defs.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "option_vars.generated.h" // NOLINT(build/include_defs)
+#endif
+
// option_vars.h: definition of global variables for settable options
#define HIGHLIGHT_INIT \
- "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
+ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,@:NonText,d:Directory,e:ErrorMsg," \
"i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \
"N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC," \
"c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn," \
@@ -16,7 +21,7 @@
"R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind," \
"]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel," \
"_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm," \
- "Z:StatusLineTermNC,g:MsgArea,0:Whitespace,I:NormalNC"
+ "Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,0:Whitespace,I:NormalNC"
// Default values for 'errorformat'.
// The "%f|%l| %m" one is used for when the contents of the quickfix window is
@@ -208,49 +213,6 @@ enum {
#define COM_ALL "nbsmexflrO" // all flags for 'comments' option
#define COM_MAX_LEN 50 // maximum length of a part
-/// 'statusline' option flags
-enum {
- STL_FILEPATH = 'f', ///< Path of file in buffer.
- STL_FULLPATH = 'F', ///< Full path of file in buffer.
- STL_FILENAME = 't', ///< Last part (tail) of file path.
- STL_COLUMN = 'c', ///< Column og cursor.
- STL_VIRTCOL = 'v', ///< Virtual column.
- STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display.
- STL_LINE = 'l', ///< Line number of cursor.
- STL_NUMLINES = 'L', ///< Number of lines in buffer.
- STL_BUFNO = 'n', ///< Current buffer number.
- STL_KEYMAP = 'k', ///< 'keymap' when active.
- STL_OFFSET = 'o', ///< Offset of character under cursor.
- STL_OFFSET_X = 'O', ///< - in hexadecimal.
- STL_BYTEVAL = 'b', ///< Byte value of character.
- STL_BYTEVAL_X = 'B', ///< - in hexadecimal.
- STL_ROFLAG = 'r', ///< Readonly flag.
- STL_ROFLAG_ALT = 'R', ///< - other display.
- STL_HELPFLAG = 'h', ///< Window is showing a help file.
- STL_HELPFLAG_ALT = 'H', ///< - other display.
- STL_FILETYPE = 'y', ///< 'filetype'.
- STL_FILETYPE_ALT = 'Y', ///< - other display.
- STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf.
- STL_PREVIEWFLAG_ALT = 'W', ///< - other display.
- STL_MODIFIED = 'm', ///< Modified flag.
- STL_MODIFIED_ALT = 'M', ///< - other display.
- STL_QUICKFIX = 'q', ///< Quickfix window description.
- STL_PERCENTAGE = 'p', ///< Percentage through file.
- 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.
- STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0.
- STL_HIGHLIGHT = '#', ///< Highlight name.
- STL_TABPAGENR = 'T', ///< Tab page label nr.
- STL_TABCLOSENR = 'X', ///< Tab page close nr.
- STL_CLICK_FUNC = '@', ///< Click region start.
-};
/// C string containing all 'statusline' option flags
#define STL_ALL ((char[]) { \
STL_FILEPATH, STL_FULLPATH, STL_FILENAME, STL_COLUMN, STL_VIRTCOL, \
@@ -264,12 +226,6 @@ enum {
STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
0, })
-// flags used for parsed 'wildmode'
-#define WIM_FULL 0x01
-#define WIM_LONGEST 0x02
-#define WIM_LIST 0x04
-#define WIM_BUFLASTUSED 0x08
-
// arguments for can_bs()
// each defined char should be unique over all values
// except for BS_START, that intentionally also matches BS_NOSTOP
@@ -280,11 +236,6 @@ enum {
#define BS_START 's' // "Start"
#define BS_NOSTOP 'p' // "nostoP
-// flags for the 'culopt' option
-#define CULOPT_LINE 0x01 // Highlight complete line
-#define CULOPT_SCRLINE 0x02 // Highlight screen line
-#define CULOPT_NBR 0x04 // Highlight Number column
-
#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"
@@ -319,47 +270,17 @@ EXTERN char *p_bg; ///< 'background'
EXTERN int p_bk; ///< 'backup'
EXTERN char *p_bkc; ///< 'backupcopy'
EXTERN unsigned bkc_flags; ///< flags from 'backupcopy'
-#define BKC_YES 0x001
-#define BKC_AUTO 0x002
-#define BKC_NO 0x004
-#define BKC_BREAKSYMLINK 0x008
-#define BKC_BREAKHARDLINK 0x010
EXTERN char *p_bdir; ///< 'backupdir'
EXTERN char *p_bex; ///< 'backupext'
EXTERN char *p_bo; ///< 'belloff'
EXTERN char breakat_flags[256]; ///< which characters are in 'breakat'
EXTERN unsigned bo_flags;
-
-// values for the 'belloff' option
-#define BO_ALL 0x0001
-#define BO_BS 0x0002
-#define BO_CRSR 0x0004
-#define BO_COMPL 0x0008
-#define BO_COPY 0x0010
-#define BO_CTRLG 0x0020
-#define BO_ERROR 0x0040
-#define BO_ESC 0x0080
-#define BO_EX 0x0100
-#define BO_HANGUL 0x0200
-#define BO_IM 0x0400
-#define BO_LANG 0x0800
-#define BO_MESS 0x1000
-#define BO_MATCH 0x2000
-#define BO_OPER 0x4000
-#define BO_REG 0x8000
-#define BO_SH 0x10000
-#define BO_SPELL 0x20000
-#define BO_TERM 0x40000
-#define BO_WILD 0x80000
-
EXTERN char *p_bsk; ///< 'backupskip'
EXTERN char *p_breakat; ///< 'breakat'
EXTERN char *p_bh; ///< 'bufhidden'
EXTERN char *p_bt; ///< 'buftype'
EXTERN char *p_cmp; ///< 'casemap'
EXTERN unsigned cmp_flags;
-#define CMP_INTERNAL 0x001
-#define CMP_KEEPASCII 0x002
EXTERN char *p_enc; ///< 'encoding'
EXTERN int p_deco; ///< 'delcombine'
EXTERN char *p_ccv; ///< 'charconvert'
@@ -367,9 +288,6 @@ EXTERN char *p_cino; ///< 'cinoptions'
EXTERN char *p_cedit; ///< 'cedit'
EXTERN char *p_cb; ///< 'clipboard'
EXTERN unsigned cb_flags;
-#define CB_UNNAMED 0x001
-#define CB_UNNAMEDPLUS 0x002
-#define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS)
EXTERN OptInt p_cwh; ///< 'cmdwinheight'
EXTERN OptInt p_ch; ///< 'cmdheight'
EXTERN char *p_cms; ///< 'commentstring'
@@ -380,18 +298,6 @@ EXTERN char *p_cia; ///< 'completeitemalign'
EXTERN unsigned cia_flags; ///< order flags of 'completeitemalign'
EXTERN char *p_cot; ///< 'completeopt'
EXTERN unsigned cot_flags; ///< flags from 'completeopt'
-// Keep in sync with p_cot_values in optionstr.c
-#define COT_MENU 0x001
-#define COT_MENUONE 0x002
-#define COT_ANY_MENU 0x003 // combination of menu flags
-#define COT_LONGEST 0x004 // false: insert full match,
- // true: insert longest prefix
-#define COT_PREVIEW 0x008
-#define COT_POPUP 0x010
-#define COT_ANY_PREVIEW 0x018 // combination of preview flags
-#define COT_NOINSERT 0x020 // false: select & insert, true: noinsert
-#define COT_NOSELECT 0x040 // false: select & insert, true: noselect
-#define COT_FUZZY 0x080 // true: fuzzy match enabled
#ifdef BACKSLASH_IN_FILENAME
EXTERN char *p_csl; ///< 'completeslash'
#endif
@@ -410,11 +316,6 @@ EXTERN int p_dg; ///< 'digraph'
EXTERN char *p_dir; ///< 'directory'
EXTERN char *p_dy; ///< 'display'
EXTERN unsigned dy_flags;
-#define DY_LASTLINE 0x001
-#define DY_TRUNCATE 0x002
-#define DY_UHEX 0x004
-// legacy flag, not used
-#define DY_MSGSEP 0x008
EXTERN char *p_ead; ///< 'eadirection'
EXTERN int p_emoji; ///< 'emoji'
EXTERN int p_ea; ///< 'equalalways'
@@ -442,17 +343,6 @@ EXTERN char *p_fcl; ///< 'foldclose'
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
EXTERN char *p_fdo; ///< 'foldopen'
EXTERN unsigned fdo_flags;
-#define FDO_ALL 0x001
-#define FDO_BLOCK 0x002
-#define FDO_HOR 0x004
-#define FDO_MARK 0x008
-#define FDO_PERCENT 0x010
-#define FDO_QUICKFIX 0x020
-#define FDO_SEARCH 0x040
-#define FDO_TAG 0x080
-#define FDO_INSERT 0x100
-#define FDO_UNDO 0x200
-#define FDO_JUMP 0x400
EXTERN char *p_fex; ///< 'formatexpr'
EXTERN char *p_flp; ///< 'formatlistpat'
EXTERN char *p_fo; ///< 'formatoptions'
@@ -488,9 +378,6 @@ EXTERN char *p_isp; ///< 'isprint'
EXTERN int p_js; ///< 'joinspaces'
EXTERN char *p_jop; ///< 'jumpooptions'
EXTERN unsigned jop_flags;
-#define JOP_STACK 0x01
-#define JOP_VIEW 0x02
-#define JOP_CLEAN 0x04
EXTERN char *p_keymap; ///< 'keymap'
EXTERN char *p_kp; ///< 'keywordprg'
EXTERN char *p_km; ///< 'keymodel'
@@ -506,7 +393,6 @@ EXTERN char *p_lispwords; ///< 'lispwords'
EXTERN OptInt p_ls; ///< 'laststatus'
EXTERN OptInt p_stal; ///< 'showtabline'
EXTERN char *p_lcs; ///< 'listchars'
-
EXTERN int p_lz; ///< 'lazyredraw'
EXTERN int p_lpl; ///< 'loadplugins'
EXTERN int p_magic; ///< 'magic'
@@ -520,6 +406,7 @@ EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern'
EXTERN OptInt p_mis; ///< 'menuitems'
+EXTERN char *p_mopt; ///< 'messagesopt'
EXTERN char *p_msm; ///< 'mkspellmem'
EXTERN int p_ml; ///< 'modeline'
EXTERN int p_mle; ///< 'modelineexpr'
@@ -536,7 +423,6 @@ EXTERN OptInt p_mousescroll_vert INIT( = MOUSESCROLL_VERT_DFLT);
EXTERN OptInt p_mousescroll_hor INIT( = MOUSESCROLL_HOR_DFLT);
EXTERN OptInt p_mouset; ///< 'mousetime'
EXTERN int p_more; ///< 'more'
-EXTERN OptInt p_mhi; ///< 'msghistory'
EXTERN char *p_nf; ///< 'nrformats'
EXTERN char *p_opfunc; ///< 'operatorfunc'
EXTERN char *p_para; ///< 'paragraphs'
@@ -551,14 +437,6 @@ EXTERN char *p_qe; ///< 'quoteescape'
EXTERN int p_ro; ///< 'readonly'
EXTERN char *p_rdb; ///< 'redrawdebug'
EXTERN unsigned rdb_flags;
-#define RDB_COMPOSITOR 0x001
-#define RDB_NOTHROTTLE 0x002
-#define RDB_INVALID 0x004
-#define RDB_NODELTA 0x008
-#define RDB_LINE 0x010
-#define RDB_FLUSH 0x020
-#define RDB_INTERSECT 0x040
-
EXTERN OptInt p_rdt; ///< 'redrawtime'
EXTERN OptInt p_re; ///< 'regexpengine'
EXTERN OptInt p_report; ///< 'report'
@@ -580,26 +458,6 @@ EXTERN char *p_sel; ///< 'selection'
EXTERN char *p_slm; ///< 'selectmode'
EXTERN char *p_ssop; ///< 'sessionoptions'
EXTERN unsigned ssop_flags;
-
-#define SSOP_BUFFERS 0x001
-#define SSOP_WINPOS 0x002
-#define SSOP_RESIZE 0x004
-#define SSOP_WINSIZE 0x008
-#define SSOP_LOCALOPTIONS 0x010
-#define SSOP_OPTIONS 0x020
-#define SSOP_HELP 0x040
-#define SSOP_BLANK 0x080
-#define SSOP_GLOBALS 0x100
-#define SSOP_SLASH 0x200 // Deprecated, always set.
-#define SSOP_UNIX 0x400 // Deprecated, always set.
-#define SSOP_SESDIR 0x800
-#define SSOP_CURDIR 0x1000
-#define SSOP_FOLDS 0x2000
-#define SSOP_CURSOR 0x4000
-#define SSOP_TABPAGES 0x8000
-#define SSOP_TERMINAL 0x10000
-#define SSOP_SKIP_RTP 0x20000
-
EXTERN char *p_sh; ///< 'shell'
EXTERN char *p_shcf; ///< 'shellcmdflag'
EXTERN char *p_sp; ///< 'shellpipe'
@@ -636,13 +494,6 @@ EXTERN OptInt p_tpm; ///< 'tabpagemax'
EXTERN char *p_tal; ///< 'tabline'
EXTERN char *p_tpf; ///< 'termpastefilter'
EXTERN unsigned tpf_flags; ///< flags from 'termpastefilter'
-#define TPF_BS 0x001
-#define TPF_HT 0x002
-#define TPF_FF 0x004
-#define TPF_ESC 0x008
-#define TPF_DEL 0x010
-#define TPF_C0 0x020
-#define TPF_C1 0x040
EXTERN char *p_tfu; ///< 'tagfunc'
EXTERN char *p_spc; ///< 'spellcapcheck'
EXTERN char *p_spf; ///< 'spellfile'
@@ -655,28 +506,14 @@ EXTERN int p_sol; ///< 'startofline'
EXTERN char *p_su; ///< 'suffixes'
EXTERN char *p_swb; ///< 'switchbuf'
EXTERN unsigned swb_flags;
-// Keep in sync with p_swb_values in optionstr.c
-#define SWB_USEOPEN 0x001
-#define SWB_USETAB 0x002
-#define SWB_SPLIT 0x004
-#define SWB_NEWTAB 0x008
-#define SWB_VSPLIT 0x010
-#define SWB_USELAST 0x020
EXTERN char *p_spk; ///< 'splitkeep'
EXTERN char *p_syn; ///< 'syntax'
EXTERN char *p_tcl; ///< 'tabclose'
EXTERN unsigned tcl_flags; ///< flags from 'tabclose'
-#define TCL_LEFT 0x001
-#define TCL_USELAST 0x002
EXTERN OptInt p_ts; ///< 'tabstop'
EXTERN int p_tbs; ///< 'tagbsearch'
EXTERN char *p_tc; ///< 'tagcase'
EXTERN unsigned tc_flags; ///< flags from 'tagcase'
-#define TC_FOLLOWIC 0x01
-#define TC_IGNORE 0x02
-#define TC_MATCH 0x04
-#define TC_FOLLOWSCS 0x08
-#define TC_SMART 0x10
EXTERN OptInt p_tl; ///< 'taglength'
EXTERN int p_tr; ///< 'tagrelative'
EXTERN char *p_tags; ///< 'tags'
@@ -708,16 +545,10 @@ EXTERN char *p_vsts; ///< 'varsofttabstop'
EXTERN char *p_vts; ///< 'vartabstop'
EXTERN char *p_vdir; ///< 'viewdir'
EXTERN char *p_vop; ///< 'viewoptions'
-EXTERN unsigned vop_flags; ///< uses SSOP_ flags
+EXTERN unsigned vop_flags; ///< uses OptSsopFlags
EXTERN int p_vb; ///< 'visualbell'
EXTERN char *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
-#define VE_BLOCK 5U // includes "all"
-#define VE_INSERT 6U // includes "all"
-#define VE_ALL 4U
-#define VE_ONEMORE 8U
-#define VE_NONE 16U // "none"
-#define VE_NONEU 32U // "NONE"
EXTERN OptInt p_verbose; ///< 'verbose'
#ifdef IN_OPTION_C
char *p_vfile = empty_string_option; ///< used before options are initialized
@@ -727,9 +558,6 @@ extern char *p_vfile; ///< 'verbosefile'
EXTERN int p_warn; ///< 'warn'
EXTERN char *p_wop; ///< 'wildoptions'
EXTERN unsigned wop_flags;
-#define WOP_FUZZY 0x01
-#define WOP_TAGFILE 0x02
-#define WOP_PUM 0x04
EXTERN OptInt p_window; ///< 'window'
EXTERN char *p_wak; ///< 'winaltkeys'
EXTERN char *p_wig; ///< 'wildignore'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 9f86ef7489..dcd6e0a58b 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -7,13 +7,16 @@
--- @field alias? string|string[]
--- @field short_desc? string|fun(): string
--- @field varname? string
---- @field type vim.option_type|vim.option_type[]
+--- @field flags_varname? string
+--- @field type vim.option_type
--- @field immutable? boolean
--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean
--- @field enable_if? string
---- @field defaults? vim.option_defaults
+--- @field defaults? vim.option_defaults|vim.option_value|fun(): string
+--- @field values? vim.option_valid_values
+--- @field flags? true|table<string,integer>
--- @field secure? true
--- @field noglob? true
--- @field normal_fname_chars? true
@@ -27,7 +30,11 @@
--- @field no_mkrc? true
--- @field alloced? true
--- @field redraw? vim.option_redraw[]
+---
+--- If not provided and `values` is present, then is set to 'did_set_str_generic'
--- @field cb? string
+---
+--- If not provided and `values` is present, then is set to 'expand_set_str_generic'
--- @field expand_cb? string
--- @field tags? string[]
@@ -35,14 +42,15 @@
--- @field condition? string
--- string: #ifdef string
--- !string: #ifndef string
---- @field if_true integer|boolean|string|fun(): string
---- @field if_false? integer|boolean|string
+--- @field if_true vim.option_value|fun(): string
+--- @field if_false? vim.option_value
--- @field doc? string Default to show in options.txt
---- @field meta? integer|boolean|string Default to use in Lua meta files
+--- @field meta? string Default to use in Lua meta files
--- @alias vim.option_scope 'global'|'buf'|'win'
--- @alias vim.option_type 'boolean'|'number'|'string'
---- @alias vim.option_value boolean|number|string
+--- @alias vim.option_value boolean|integer|string
+--- @alias vim.option_valid_values (string|[string,vim.option_valid_values])[]
--- @alias vim.option_redraw
--- |'statuslines'
@@ -78,7 +86,7 @@ local function N_(s) -- luacheck: ignore 211 (currently unused)
end
-- luacheck: ignore 621
-return {
+local options = {
cstr = cstr,
--- @type string[]
valid_scopes = { 'global', 'buf', 'win' },
@@ -87,7 +95,7 @@ return {
options = {
{
abbreviation = 'al',
- defaults = { if_true = 224 },
+ defaults = 224,
full_name = 'aleph',
scope = { 'global' },
short_desc = N_('ASCII code of the letter Aleph (Hebrew)'),
@@ -96,7 +104,7 @@ return {
},
{
abbreviation = 'ari',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Allow CTRL-_ in Insert mode. This is default off, to avoid that users
that accidentally type CTRL-_ instead of SHIFT-_ get into reverse
@@ -111,7 +119,8 @@ return {
{
abbreviation = 'ambw',
cb = 'did_set_ambiwidth',
- defaults = { if_true = 'single' },
+ defaults = 'single',
+ values = { 'single', 'double' },
desc = [=[
Tells Vim what to do with characters with East Asian Width Class
Ambiguous (such as Euro, Registered Sign, Copyright Sign, Greek
@@ -144,7 +153,6 @@ return {
set to one of CJK locales. See Unicode Standard Annex #11
(https://www.unicode.org/reports/tr11).
]=],
- expand_cb = 'expand_set_ambiwidth',
full_name = 'ambiwidth',
redraw = { 'all_windows', 'ui_option' },
scope = { 'global' },
@@ -155,7 +163,7 @@ return {
{
abbreviation = 'arab',
cb = 'did_set_arabic',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
This option can be set to start editing Arabic text.
Setting this option will:
@@ -180,7 +188,7 @@ return {
},
{
abbreviation = 'arshape',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on and 'termbidi' is off, the required visual character
corrections that need to take place for displaying the Arabic language
@@ -205,7 +213,7 @@ return {
{
abbreviation = 'acd',
cb = 'did_set_autochdir',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, Vim will change the current working directory whenever you
open a file, switch buffers, delete a buffer or open/close a window.
@@ -222,7 +230,7 @@ return {
},
{
abbreviation = 'ai',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Copy indent from current line when starting a new line (typing <CR>
in Insert mode or when using the "o" or "O" command). If you do not
@@ -244,7 +252,7 @@ return {
},
{
abbreviation = 'ar',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When a file has been detected to have been changed outside of Vim and
it has not been changed inside of Vim, automatically read it again.
@@ -264,7 +272,7 @@ return {
},
{
abbreviation = 'aw',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Write the contents of the file, if it has been modified, on each
`:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`,
@@ -289,7 +297,7 @@ return {
},
{
abbreviation = 'awa',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Like 'autowrite', but also used for commands ":edit", ":enew", ":quit",
":qall", ":exit", ":xit", ":recover" and closing the Vim window.
@@ -305,7 +313,8 @@ return {
{
abbreviation = 'bg',
cb = 'did_set_background',
- defaults = { if_true = 'dark' },
+ defaults = 'dark',
+ values = { 'light', 'dark' },
desc = [=[
When set to "dark" or "light", adjusts the default color groups for
that background type. The |TUI| or other UI sets this on startup
@@ -330,7 +339,6 @@ return {
will change. To use other settings, place ":highlight" commands AFTER
the setting of the 'background' option.
]=],
- expand_cb = 'expand_set_background',
full_name = 'background',
scope = { 'global' },
short_desc = N_('"dark" or "light", used for highlight colors'),
@@ -340,7 +348,8 @@ return {
{
abbreviation = 'bs',
cb = 'did_set_backspace',
- defaults = { if_true = 'indent,eol,start' },
+ defaults = 'indent,eol,start',
+ values = { 'indent', 'eol', 'start', 'nostop' },
deny_duplicates = true,
desc = [=[
Influences the working of <BS>, <Del>, CTRL-W and CTRL-U in Insert
@@ -357,7 +366,6 @@ return {
When the value is empty, Vi compatible backspacing is used, none of
the ways mentioned for the items above are possible.
]=],
- expand_cb = 'expand_set_backspace',
full_name = 'backspace',
list = 'onecomma',
scope = { 'global' },
@@ -367,7 +375,7 @@ return {
},
{
abbreviation = 'bk',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Make a backup before overwriting a file. Leave it around after the
file has been successfully written. If you do not want to keep the
@@ -390,6 +398,8 @@ return {
abbreviation = 'bkc',
cb = 'did_set_backupcopy',
defaults = { condition = 'UNIX', if_false = 'auto', if_true = 'auto' },
+ values = { 'yes', 'auto', 'no', 'breaksymlink', 'breakhardlink' },
+ flags = true,
deny_duplicates = true,
desc = [=[
When writing a file and a backup is made, this option tells how it's
@@ -455,17 +465,17 @@ return {
the system may refuse to do this. In that case the "auto" value will
again not rename the file.
]=],
- expand_cb = 'expand_set_backupcopy',
full_name = 'backupcopy',
list = 'onecomma',
scope = { 'global', 'buf' },
short_desc = N_("make backup as a copy, don't rename the file"),
type = 'string',
varname = 'p_bkc',
+ flags_varname = 'bkc_flags',
},
{
abbreviation = 'bdir',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of directories for the backup file, separated with commas.
@@ -521,7 +531,7 @@ return {
{
abbreviation = 'bex',
cb = 'did_set_backupext_or_patchmode',
- defaults = { if_true = '~' },
+ defaults = '~',
desc = [=[
String which is appended to a file name to make the name of the
backup file. The default is quite unusual, because this avoids
@@ -585,8 +595,30 @@ return {
},
{
abbreviation = 'bo',
- cb = 'did_set_belloff',
- defaults = { if_true = 'all' },
+ defaults = 'all',
+ values = {
+ 'all',
+ 'backspace',
+ 'cursor',
+ 'complete',
+ 'copy',
+ 'ctrlg',
+ 'error',
+ 'esc',
+ 'ex',
+ 'hangul',
+ 'insertmode',
+ 'lang',
+ 'mess',
+ 'showmatch',
+ 'operator',
+ 'register',
+ 'shell',
+ 'spell',
+ 'term',
+ 'wildmode',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
Specifies for which events the bell will not be rung. It is a comma-
@@ -626,18 +658,18 @@ return {
indicate that an error occurred. It can be silenced by adding the
"error" keyword.
]=],
- expand_cb = 'expand_set_belloff',
full_name = 'belloff',
list = 'comma',
scope = { 'global' },
short_desc = N_('do not ring the bell for these reasons'),
type = 'string',
varname = 'p_bo',
+ flags_varname = 'bo_flags',
},
{
abbreviation = 'bin',
cb = 'did_set_binary',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
This option should be set before editing a binary file. You can also
use the |-b| Vim argument. When this option is switched on a few
@@ -675,7 +707,7 @@ return {
},
{
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When writing a file and the following conditions are met, a BOM (Byte
Order Mark) is prepended to the file:
@@ -708,6 +740,7 @@ return {
if_true = ' \t!@*-+;:,./?',
doc = '" ^I!@*-+;:,./?"',
},
+ flags = true,
desc = [=[
This option lets you choose which characters might cause a line
break if 'linebreak' is on. Only works for ASCII characters.
@@ -722,7 +755,7 @@ return {
},
{
abbreviation = 'bri',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Every wrapped line will continue visually indented (same amount of
space as the beginning of that line), thus preserving horizontal blocks
@@ -737,7 +770,9 @@ return {
{
abbreviation = 'briopt',
cb = 'did_set_breakindentopt',
- defaults = { if_true = '' },
+ defaults = '',
+ -- Keep this in sync with briopt_check().
+ values = { 'shift:', 'min:', 'sbr', 'list:', 'column:' },
deny_duplicates = true,
desc = [=[
Settings for 'breakindent'. It can consist of the following optional
@@ -768,7 +803,6 @@ return {
added for the 'showbreak' setting.
(default: off)
]=],
- expand_cb = 'expand_set_breakindentopt',
full_name = 'breakindentopt',
list = 'onecomma',
redraw = { 'current_buffer' },
@@ -799,7 +833,8 @@ return {
{
abbreviation = 'bh',
cb = 'did_set_bufhidden',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { '', 'hide', 'unload', 'delete', 'wipe' },
desc = [=[
This option specifies what happens when a buffer is no longer
displayed in a window:
@@ -821,7 +856,6 @@ return {
This option is used together with 'buftype' and 'swapfile' to specify
special kinds of buffers. See |special-buffers|.
]=],
- expand_cb = 'expand_set_bufhidden',
full_name = 'bufhidden',
noglob = true,
scope = { 'buf' },
@@ -832,7 +866,7 @@ return {
{
abbreviation = 'bl',
cb = 'did_set_buflisted',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When this option is set, the buffer shows up in the buffer list. If
it is reset it is not used for ":bnext", "ls", the Buffers menu, etc.
@@ -851,7 +885,17 @@ return {
{
abbreviation = 'bt',
cb = 'did_set_buftype',
- defaults = { if_true = '' },
+ defaults = '',
+ values = {
+ '',
+ 'acwrite',
+ 'help',
+ 'nofile',
+ 'nowrite',
+ 'quickfix',
+ 'terminal',
+ 'prompt',
+ },
desc = [=[
The value of this option specifies the type of a buffer:
<empty> normal buffer
@@ -898,7 +942,6 @@ return {
without saving. For writing there must be matching |BufWriteCmd|,
|FileWriteCmd| or |FileAppendCmd| autocommands.
]=],
- expand_cb = 'expand_set_buftype',
full_name = 'buftype',
noglob = true,
scope = { 'buf' },
@@ -909,8 +952,9 @@ return {
},
{
abbreviation = 'cmp',
- cb = 'did_set_casemap',
- defaults = { if_true = 'internal,keepascii' },
+ defaults = 'internal,keepascii',
+ values = { 'internal', 'keepascii' },
+ flags = true,
deny_duplicates = true,
desc = [=[
Specifies details about changing the case of letters. It may contain
@@ -923,17 +967,17 @@ return {
case mapping, the current locale is not effective.
This probably only matters for Turkish.
]=],
- expand_cb = 'expand_set_casemap',
full_name = 'casemap',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('specifies how case of letters is changed'),
type = 'string',
varname = 'p_cmp',
+ flags_varname = 'cmp_flags',
},
{
abbreviation = 'cdh',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, |:cd|, |:tcd| and |:lcd| without an argument changes the
current working directory to the |$HOME| directory like in Unix.
@@ -1007,7 +1051,7 @@ return {
varname = 'p_cedit',
},
{
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
|channel| connected to the buffer, or 0 if no channel is connected.
In a |:terminal| buffer this is the terminal channel.
@@ -1024,7 +1068,7 @@ return {
{
abbreviation = 'ccv',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
An expression that is used for character encoding conversion. It is
evaluated when a file that is to be read or has been written has a
@@ -1080,7 +1124,7 @@ return {
},
{
abbreviation = 'cin',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Enables automatic C program indenting. See 'cinkeys' to set the keys
that trigger reindenting in insert mode and 'cinoptions' to set your
@@ -1101,7 +1145,7 @@ return {
},
{
abbreviation = 'cink',
- defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
+ defaults = '0{,0},0),0],:,0#,!^F,o,O,e',
deny_duplicates = true,
desc = [=[
A list of keys that, when typed in Insert mode, cause reindenting of
@@ -1120,7 +1164,7 @@ return {
{
abbreviation = 'cino',
cb = 'did_set_cinoptions',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
The 'cinoptions' affect the way 'cindent' reindents lines in a C
@@ -1136,7 +1180,7 @@ return {
},
{
abbreviation = 'cinsd',
- defaults = { if_true = 'public,protected,private' },
+ defaults = 'public,protected,private',
deny_duplicates = true,
desc = [=[
Keywords that are interpreted as a C++ scope declaration by |cino-g|.
@@ -1154,7 +1198,7 @@ return {
},
{
abbreviation = 'cinw',
- defaults = { if_true = 'if,else,while,do,for,switch' },
+ defaults = 'if,else,while,do,for,switch',
deny_duplicates = true,
desc = [=[
These keywords start an extra indent in the next line when
@@ -1173,8 +1217,9 @@ return {
},
{
abbreviation = 'cb',
- cb = 'did_set_clipboard',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'unnamed', 'unnamedplus' },
+ flags = true,
desc = [=[
This option is a list of comma-separated names.
These names are recognized:
@@ -1200,18 +1245,18 @@ return {
"*". See |clipboard|.
]=],
deny_duplicates = true,
- expand_cb = 'expand_set_clipboard',
full_name = 'clipboard',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('use the clipboard as the unnamed register'),
type = 'string',
varname = 'p_cb',
+ flags_varname = 'cb_flags',
},
{
abbreviation = 'ch',
cb = 'did_set_cmdheight',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Number of screen lines to use for the command-line. Helps avoiding
|hit-enter| prompts.
@@ -1236,7 +1281,7 @@ return {
},
{
abbreviation = 'cwh',
- defaults = { if_true = 7 },
+ defaults = 7,
desc = [=[
Number of screen lines to use for the command-line window. |cmdwin|
]=],
@@ -1249,7 +1294,7 @@ return {
{
abbreviation = 'cc',
cb = 'did_set_colorcolumn',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
'colorcolumn' is a comma-separated list of screen columns that are
@@ -1304,7 +1349,7 @@ return {
{
abbreviation = 'com',
cb = 'did_set_comments',
- defaults = { if_true = 's1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-,fb:•' },
+ defaults = 's1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-,fb:•',
deny_duplicates = true,
desc = [=[
A comma-separated list of strings that can start a comment line. See
@@ -1322,7 +1367,7 @@ return {
{
abbreviation = 'cms',
cb = 'did_set_commentstring',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
A template for a comment. The "%s" in the value is replaced with the
comment text, and should be padded with a space when possible.
@@ -1337,7 +1382,7 @@ return {
},
{
abbreviation = 'cp',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'compatible',
scope = { 'global' },
short_desc = N_('No description'),
@@ -1347,7 +1392,8 @@ return {
{
abbreviation = 'cpt',
cb = 'did_set_complete',
- defaults = { if_true = '.,w,b,u,t' },
+ defaults = '.,w,b,u,t',
+ values = { '.', 'w', 'b', 'u', 'k', 'kspell', 's', 'i', 'd', ']', 't', 'U', 'f' },
deny_duplicates = true,
desc = [=[
This option specifies how keyword completion |ins-completion| works
@@ -1383,7 +1429,6 @@ return {
based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns
|i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions).
]=],
- expand_cb = 'expand_set_complete',
full_name = 'complete',
list = 'onecomma',
scope = { 'buf' },
@@ -1395,7 +1440,7 @@ return {
{
abbreviation = 'cfu',
cb = 'did_set_completefunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used for Insert mode completion
with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U|
@@ -1417,7 +1462,8 @@ return {
{
abbreviation = 'cia',
cb = 'did_set_completeitemalign',
- defaults = { if_true = 'abbr,kind,menu' },
+ defaults = 'abbr,kind,menu',
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of |complete-items| that controls the alignment
@@ -1437,7 +1483,20 @@ return {
{
abbreviation = 'cot',
cb = 'did_set_completeopt',
- defaults = { if_true = 'menu,preview' },
+ defaults = 'menu,preview',
+ values = {
+ 'menu',
+ 'menuone',
+ 'longest',
+ 'preview',
+ 'popup',
+ 'noinsert',
+ 'noselect',
+ 'fuzzy',
+ 'nosort',
+ 'preinsert',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of options for Insert mode completion
@@ -1469,9 +1528,9 @@ return {
a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
- noselect Do not select a match in the menu, force the user to
- select one from the menu. Only works in combination with
- "menu" or "menuone".
+ noselect Same as "noinsert", except that no menu item is
+ pre-selected. If both "noinsert" and "noselect" are
+ present, "noselect" has precedence.
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
@@ -1480,19 +1539,30 @@ return {
difference how completion candidates are reduced from the
list of alternatives, but not how the candidates are
collected (using different completion types).
+
+ nosort Disable sorting of completion candidates based on fuzzy
+ scores when "fuzzy" is enabled. Candidates will appear
+ in their original order.
+
+ preinsert
+ Preinsert the portion of the first candidate word that is
+ not part of the current completion leader and using the
+ |hl-ComplMatchIns| highlight group. Does not work when
+ "fuzzy" is also included.
]=],
- expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',
list = 'onecomma',
scope = { 'global', 'buf' },
short_desc = N_('options for Insert mode completion'),
type = 'string',
varname = 'p_cot',
+ flags_varname = 'cot_flags',
},
{
abbreviation = 'csl',
cb = 'did_set_completeslash',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { '', 'slash', 'backslash' },
desc = [=[
only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion:
@@ -1507,7 +1577,6 @@ return {
command line completion the global value is used.
]=],
enable_if = 'BACKSLASH_IN_FILENAME',
- expand_cb = 'expand_set_completeslash',
full_name = 'completeslash',
scope = { 'buf' },
type = 'string',
@@ -1516,7 +1585,7 @@ return {
{
abbreviation = 'cocu',
cb = 'did_set_concealcursor',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Sets the modes in which text in the cursor line can also be concealed.
When the current mode is listed then concealing happens just like in
@@ -1544,7 +1613,7 @@ return {
},
{
abbreviation = 'cole',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Determine how text with the "conceal" syntax attribute |:syn-conceal|
is shown:
@@ -1573,7 +1642,7 @@ return {
},
{
abbreviation = 'cf',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When 'confirm' is on, certain operations that would normally
fail because of unsaved changes to a buffer, e.g. ":q" and ":e",
@@ -1592,7 +1661,7 @@ return {
},
{
abbreviation = 'ci',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Copy the structure of the existing lines indent when autoindenting a
new line. Normally the new indent is reconstructed by a series of
@@ -1613,7 +1682,7 @@ return {
{
abbreviation = 'cpo',
cb = 'did_set_cpoptions',
- defaults = { if_true = macros('CPO_VIM', 'string') },
+ defaults = macros('CPO_VIM', 'string'),
desc = [=[
A sequence of single character flags. When a character is present
this indicates Vi-compatible behavior. This is used for things where
@@ -1855,7 +1924,7 @@ return {
},
{
abbreviation = 'crb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When this option is set, as the cursor in the current
window moves other cursorbound windows (windows that also have
@@ -1872,7 +1941,7 @@ return {
},
{
abbreviation = 'cuc',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Highlight the screen column of the cursor with CursorColumn
|hl-CursorColumn|. Useful to align text. Will make screen redrawing
@@ -1891,7 +1960,7 @@ return {
},
{
abbreviation = 'cul',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Highlight the text line of the cursor with CursorLine |hl-CursorLine|.
Useful to easily spot the cursor. Will make screen redrawing slower.
@@ -1907,7 +1976,14 @@ return {
{
abbreviation = 'culopt',
cb = 'did_set_cursorlineopt',
- defaults = { if_true = 'both' },
+ defaults = 'both',
+ -- Keep this in sync with fill_culopt_flags().
+ values = { 'line', 'screenline', 'number', 'both' },
+ flags = {
+ Line = 0x01,
+ Screenline = 0x02,
+ Number = 0x04,
+ },
deny_duplicates = true,
desc = [=[
Comma-separated list of settings for how 'cursorline' is displayed.
@@ -1924,7 +2000,6 @@ return {
"line" and "screenline" cannot be used together.
]=],
- expand_cb = 'expand_set_cursorlineopt',
full_name = 'cursorlineopt',
list = 'onecomma',
redraw = { 'current_window', 'highlight_only' },
@@ -1933,8 +2008,8 @@ return {
type = 'string',
},
{
- cb = 'did_set_debug',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'msg', 'throw', 'beep' },
desc = [=[
These values can be used:
msg Error messages that would otherwise be omitted will be given
@@ -1947,8 +2022,9 @@ return {
"msg" and "throw" are useful for debugging 'foldexpr', 'formatexpr' or
'indentexpr'.
]=],
- expand_cb = 'expand_set_debug',
+ -- TODO(lewis6991): bug, values currently cannot be combined
full_name = 'debug',
+ list = 'comma',
scope = { 'global' },
short_desc = N_('to "msg" to see all error messages'),
type = 'string',
@@ -1956,7 +2032,7 @@ return {
},
{
abbreviation = 'def',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Pattern to be used to find a macro definition. It is a search
pattern, just like for the "/" command. This option is used for the
@@ -1986,7 +2062,7 @@ return {
},
{
abbreviation = 'deco',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If editing Unicode and this option is set, backspace and Normal mode
"x" delete each combining character on its own. When it is off (the
@@ -2006,7 +2082,7 @@ return {
},
{
abbreviation = 'dict',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of file names, separated by commas, that are used to lookup words
@@ -2043,7 +2119,7 @@ return {
},
{
cb = 'did_set_diff',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Join the current window in the group of windows that shows differences
between files. See |diff-mode|.
@@ -2058,7 +2134,7 @@ return {
{
abbreviation = 'dex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to obtain a diff file (either ed-style
or unified-style) from two versions of a file. See |diff-diffexpr|.
@@ -2076,17 +2152,46 @@ return {
{
abbreviation = 'dip',
cb = 'did_set_diffopt',
- defaults = { if_true = 'internal,filler,closeoff' },
+ defaults = 'internal,filler,closeoff',
+ -- Keep this in sync with diffopt_changed().
+ values = {
+ 'filler',
+ 'context:',
+ 'iblank',
+ 'icase',
+ 'iwhite',
+ 'iwhiteall',
+ 'iwhiteeol',
+ 'horizontal',
+ 'vertical',
+ 'closeoff',
+ 'hiddenoff',
+ 'foldcolumn:',
+ 'followwrap',
+ 'internal',
+ 'indent-heuristic',
+ { 'algorithm:', { 'myers', 'minimal', 'patience', 'histogram' } },
+ 'linematch:',
+ },
deny_duplicates = true,
desc = [=[
Option settings for diff mode. It can consist of the following items.
All are optional. Items must be separated by a comma.
- filler Show filler lines, to keep the text
- synchronized with a window that has inserted
- lines at the same position. Mostly useful
- when windows are side-by-side and 'scrollbind'
- is set.
+ algorithm:{text} Use the specified diff algorithm with the
+ internal diff engine. Currently supported
+ algorithms are:
+ myers the default algorithm
+ minimal spend extra time to generate the
+ smallest possible diff
+ patience patience diff algorithm
+ histogram histogram diff algorithm
+
+ closeoff When a window is closed where 'diff' is set
+ and there is only one window remaining in the
+ same tab page with 'diff' set, execute
+ `:diffoff` in that window. This undoes a
+ `:diffsplit` command.
context:{n} Use a context of {n} lines between a change
and a fold that contains unchanged lines.
@@ -2097,6 +2202,23 @@ return {
value (999999) to disable folding completely.
See |fold-diff|.
+ filler Show filler lines, to keep the text
+ synchronized with a window that has inserted
+ lines at the same position. Mostly useful
+ when windows are side-by-side and 'scrollbind'
+ is set.
+
+ foldcolumn:{n} Set the 'foldcolumn' option to {n} when
+ starting diff mode. Without this 2 is used.
+
+ followwrap Follow the 'wrap' option and leave as it is.
+
+ horizontal Start diff mode with horizontal splits (unless
+ explicitly specified otherwise).
+
+ hiddenoff Do not use diff mode for a buffer when it
+ becomes hidden.
+
iblank Ignore changes where lines are all blank. Adds
the "-B" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
@@ -2110,6 +2232,17 @@ return {
are considered the same. Adds the "-i" flag
to the "diff" command if 'diffexpr' is empty.
+ indent-heuristic
+ Use the indent heuristic for the internal
+ diff library.
+
+ internal Use the internal diff library. This is
+ ignored when 'diffexpr' is set. *E960*
+ When running out of memory when writing a
+ buffer this item will be ignored for diffs
+ involving that buffer. Set the 'verbose'
+ option to see when this happens.
+
iwhite Ignore changes in amount of white space. Adds
the "-b" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
@@ -2129,56 +2262,19 @@ return {
of the "diff" command for what this does
exactly.
- horizontal Start diff mode with horizontal splits (unless
- explicitly specified otherwise).
+ linematch:{n} Align and mark changes between the most
+ similar lines between the buffers. When the
+ total number of lines in the diff hunk exceeds
+ {n}, the lines will not be aligned because for
+ very large diff hunks there will be a
+ noticeable lag. A reasonable setting is
+ "linematch:60", as this will enable alignment
+ for a 2 buffer diff hunk of 30 lines each,
+ or a 3 buffer diff hunk of 20 lines each.
vertical Start diff mode with vertical splits (unless
explicitly specified otherwise).
- closeoff When a window is closed where 'diff' is set
- and there is only one window remaining in the
- same tab page with 'diff' set, execute
- `:diffoff` in that window. This undoes a
- `:diffsplit` command.
-
- hiddenoff Do not use diff mode for a buffer when it
- becomes hidden.
-
- foldcolumn:{n} Set the 'foldcolumn' option to {n} when
- starting diff mode. Without this 2 is used.
-
- followwrap Follow the 'wrap' option and leave as it is.
-
- internal Use the internal diff library. This is
- ignored when 'diffexpr' is set. *E960*
- When running out of memory when writing a
- buffer this item will be ignored for diffs
- involving that buffer. Set the 'verbose'
- option to see when this happens.
-
- indent-heuristic
- 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:
- myers the default algorithm
- minimal spend extra time to generate the
- smallest possible diff
- patience patience diff algorithm
- histogram histogram diff algorithm
-
Examples: >vim
set diffopt=internal,filler,context:4
set diffopt=
@@ -2197,7 +2293,7 @@ return {
},
{
abbreviation = 'dg',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Enable the entering of digraphs in Insert mode with {char1} <BS>
{char2}. See |digraphs|.
@@ -2210,7 +2306,7 @@ return {
},
{
abbreviation = 'dir',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of directory names for the swap file, separated with commas.
@@ -2268,7 +2364,9 @@ return {
{
abbreviation = 'dy',
cb = 'did_set_display',
- defaults = { if_true = 'lastline' },
+ defaults = 'lastline',
+ values = { 'lastline', 'truncate', 'uhex', 'msgsep' },
+ flags = true,
deny_duplicates = true,
desc = [=[
Change the way text is displayed. This is a comma-separated list of
@@ -2289,7 +2387,6 @@ return {
The "@" character can be changed by setting the "lastline" item in
'fillchars'. The character is highlighted with |hl-NonText|.
]=],
- expand_cb = 'expand_set_display',
full_name = 'display',
list = 'onecomma',
redraw = { 'all_windows' },
@@ -2297,18 +2394,18 @@ return {
short_desc = N_('list of flags for how to display text'),
type = 'string',
varname = 'p_dy',
+ flags_varname = 'dy_flags',
},
{
abbreviation = 'ead',
- cb = 'did_set_eadirection',
- defaults = { if_true = 'both' },
+ defaults = 'both',
+ values = { 'both', 'ver', 'hor' },
desc = [=[
Tells when the 'equalalways' option applies:
ver vertically, width of windows is not affected
hor horizontally, height of windows is not affected
both width and height of windows is affected
]=],
- expand_cb = 'expand_set_eadirection',
full_name = 'eadirection',
scope = { 'global' },
short_desc = N_("in which direction 'equalalways' works"),
@@ -2317,7 +2414,7 @@ return {
},
{
abbreviation = 'ed',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'edcompatible',
scope = { 'global' },
short_desc = N_('No description'),
@@ -2326,8 +2423,8 @@ return {
},
{
abbreviation = 'emo',
- cb = 'did_set_ambiwidth',
- defaults = { if_true = true },
+ cb = 'did_set_emoji',
+ defaults = true,
desc = [=[
When on all Unicode emoji characters are considered to be full width.
This excludes "text emoji" characters, which are normally displayed as
@@ -2348,7 +2445,7 @@ return {
{
abbreviation = 'enc',
cb = 'did_set_encoding',
- defaults = { if_true = macros('ENC_DFLT', 'string') },
+ defaults = macros('ENC_DFLT', 'string'),
deny_in_modelines = true,
desc = [=[
String-encoding used internally and for |RPC| communication.
@@ -2365,7 +2462,7 @@ return {
{
abbreviation = 'eof',
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Indicates that a CTRL-Z character was found at the end of the file
when reading it. Normally only happens when 'fileformat' is "dos".
@@ -2385,7 +2482,7 @@ return {
{
abbreviation = 'eol',
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When writing a file and this option is off and the 'binary' option
is on, or 'fixeol' option is off, no <EOL> will be written for the
@@ -2411,7 +2508,7 @@ return {
{
abbreviation = 'ea',
cb = 'did_set_equalalways',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, all the windows are automatically made the same size after
splitting or closing a window. This also happens the moment the
@@ -2436,7 +2533,7 @@ return {
},
{
abbreviation = 'ep',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
External program to use for "=" command. When this option is empty
the internal formatting functions are used; either 'lisp', 'cindent'
@@ -2456,7 +2553,7 @@ return {
},
{
abbreviation = 'eb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Ring the bell (beep or screen flash) for error messages. This only
makes a difference for error messages, the bell will be used always
@@ -2472,7 +2569,7 @@ return {
},
{
abbreviation = 'ef',
- defaults = { if_true = macros('DFLT_ERRORFILE', 'string') },
+ defaults = macros('DFLT_ERRORFILE', 'string'),
desc = [=[
Name of the errorfile for the QuickFix mode (see |:cf|).
When the "-q" command-line argument is used, 'errorfile' is set to the
@@ -2512,7 +2609,7 @@ return {
{
abbreviation = 'ei',
cb = 'did_set_eventignore',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
A list of autocommand event names, which are to be ignored.
@@ -2532,7 +2629,7 @@ return {
},
{
abbreviation = 'et',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
In Insert mode: Use the appropriate number of spaces to insert a
<Tab>. Spaces are used in indents with the '>' and '<' commands and
@@ -2547,7 +2644,7 @@ return {
},
{
abbreviation = 'ex',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Automatically execute .nvim.lua, .nvimrc, and .exrc files in the
current directory, if the file is in the |trust| list. Use |:trust| to
@@ -2570,7 +2667,7 @@ return {
{
abbreviation = 'fenc',
cb = 'did_set_encoding',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
File-content encoding for the current buffer. Conversion is done with
iconv() or as specified with 'charconvert'.
@@ -2622,7 +2719,7 @@ return {
},
{
abbreviation = 'fencs',
- defaults = { if_true = 'ucs-bom,utf-8,default,latin1' },
+ defaults = 'ucs-bom,utf-8,default,latin1',
deny_duplicates = true,
desc = [=[
This is a list of character encodings considered when starting to edit
@@ -2689,6 +2786,7 @@ return {
if_false = 'unix',
doc = 'Windows: "dos", Unix: "unix"',
},
+ values = { 'unix', 'dos', 'mac' },
desc = [=[
This gives the <EOL> of the current buffer, which is used for
reading/writing the buffer from/to a file:
@@ -2706,7 +2804,6 @@ return {
option is set, because the file would be different when written.
This option cannot be changed when 'modifiable' is off.
]=],
- expand_cb = 'expand_set_fileformat',
full_name = 'fileformat',
no_mkrc = true,
redraw = { 'curswant', 'statuslines' },
@@ -2717,7 +2814,7 @@ return {
},
{
abbreviation = 'ffs',
- cb = 'did_set_fileformats',
+ cb = 'did_set_str_generic',
defaults = {
condition = 'USE_CRNL',
if_true = 'dos,unix',
@@ -2772,7 +2869,7 @@ return {
used.
Also see |file-formats|.
]=],
- expand_cb = 'expand_set_fileformat',
+ expand_cb = 'expand_set_str_generic',
full_name = 'fileformats',
list = 'onecomma',
scope = { 'global' },
@@ -2802,7 +2899,7 @@ return {
{
abbreviation = 'ft',
cb = 'did_set_filetype_or_syntax',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is set, the FileType autocommand event is triggered.
All autocommands that match with the value of this option will be
@@ -2837,7 +2934,7 @@ return {
{
abbreviation = 'fcs',
cb = 'did_set_chars_option',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Characters to fill the statuslines, vertical separators and special
@@ -2912,7 +3009,7 @@ return {
{
abbreviation = 'ffu',
cb = 'did_set_findfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Function that is called to obtain the filename(s) for the |:find|
command. When this option is empty, the internal |file-searching|
@@ -2972,7 +3069,7 @@ return {
{
abbreviation = 'fixeol',
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
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
@@ -2991,15 +3088,14 @@ return {
},
{
abbreviation = 'fcl',
- cb = 'did_set_foldclose',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'all' },
deny_duplicates = true,
desc = [=[
When set to "all", a fold is closed when the cursor isn't in it and
its level is higher than 'foldlevel'. Useful if you want folds to
automatically close when moving out of them.
]=],
- expand_cb = 'expand_set_foldclose',
full_name = 'foldclose',
list = 'onecomma',
redraw = { 'current_window' },
@@ -3010,8 +3106,29 @@ return {
},
{
abbreviation = 'fdc',
- cb = 'did_set_foldcolumn',
- defaults = { if_true = '0' },
+ defaults = '0',
+ values = {
+ 'auto',
+ 'auto:1',
+ 'auto:2',
+ 'auto:3',
+ 'auto:4',
+ 'auto:5',
+ 'auto:6',
+ 'auto:7',
+ 'auto:8',
+ 'auto:9',
+ '0',
+ '1',
+ '2',
+ '3',
+ '4',
+ '5',
+ '6',
+ '7',
+ '8',
+ '9',
+ },
desc = [=[
When and how to draw the foldcolumn. Valid values are:
"auto": resize to the minimum amount of folds to display.
@@ -3021,7 +3138,6 @@ return {
"[1-9]": to display a fixed number of columns
See |folding|.
]=],
- expand_cb = 'expand_set_foldcolumn',
full_name = 'foldcolumn',
redraw = { 'current_window' },
scope = { 'win' },
@@ -3030,7 +3146,7 @@ return {
},
{
abbreviation = 'fen',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When off, all folds are open. This option can be used to quickly
switch between showing all text unfolded and viewing the text with
@@ -3049,7 +3165,7 @@ return {
{
abbreviation = 'fde',
cb = 'did_set_foldexpr',
- defaults = { if_true = '0' },
+ defaults = '0',
desc = [=[
The expression used for when 'foldmethod' is "expr". It is evaluated
for each line to obtain its fold level. The context is set to the
@@ -3074,7 +3190,7 @@ return {
{
abbreviation = 'fdi',
cb = 'did_set_foldignore',
- defaults = { if_true = '#' },
+ defaults = '#',
desc = [=[
Used only when 'foldmethod' is "indent". Lines starting with
characters in 'foldignore' will get their fold level from surrounding
@@ -3090,7 +3206,7 @@ return {
{
abbreviation = 'fdl',
cb = 'did_set_foldlevel',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Sets the fold level: Folds with a higher level will be closed.
Setting this option to zero will close all folds. Higher numbers will
@@ -3106,7 +3222,7 @@ return {
},
{
abbreviation = 'fdls',
- defaults = { if_true = -1 },
+ defaults = -1,
desc = [=[
Sets 'foldlevel' when starting to edit another buffer in a window.
Useful to always start editing with all folds closed (value zero),
@@ -3128,7 +3244,7 @@ return {
{
abbreviation = 'fmr',
cb = 'did_set_foldmarker',
- defaults = { if_true = '{{{,}}}' },
+ defaults = '{{{,}}}',
deny_duplicates = true,
desc = [=[
The start and end marker used when 'foldmethod' is "marker". There
@@ -3147,7 +3263,8 @@ return {
{
abbreviation = 'fdm',
cb = 'did_set_foldmethod',
- defaults = { if_true = 'manual' },
+ defaults = 'manual',
+ values = { 'manual', 'expr', 'marker', 'indent', 'syntax', 'diff' },
desc = [=[
The kind of folding used for the current window. Possible values:
|fold-manual| manual Folds are created manually.
@@ -3157,7 +3274,6 @@ return {
|fold-syntax| syntax Syntax highlighting items specify folds.
|fold-diff| diff Fold text that is not changed.
]=],
- expand_cb = 'expand_set_foldmethod',
full_name = 'foldmethod',
redraw = { 'current_window' },
scope = { 'win' },
@@ -3167,7 +3283,7 @@ return {
{
abbreviation = 'fml',
cb = 'did_set_foldminlines',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Sets the number of screen lines above which a fold can be displayed
closed. Also for manually closed folds. With the default value of
@@ -3186,7 +3302,7 @@ return {
{
abbreviation = 'fdn',
cb = 'did_set_foldnestmax',
- defaults = { if_true = 20 },
+ defaults = 20,
desc = [=[
Sets the maximum nesting of folds for the "indent" and "syntax"
methods. This avoids that too many folds will be created. Using more
@@ -3200,8 +3316,21 @@ return {
},
{
abbreviation = 'fdo',
- cb = 'did_set_foldopen',
- defaults = { if_true = 'block,hor,mark,percent,quickfix,search,tag,undo' },
+ defaults = 'block,hor,mark,percent,quickfix,search,tag,undo',
+ values = {
+ 'all',
+ 'block',
+ 'hor',
+ 'mark',
+ 'percent',
+ 'quickfix',
+ 'search',
+ 'tag',
+ 'insert',
+ 'undo',
+ 'jump',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
Specifies for which type of commands folds will be opened, if the
@@ -3235,7 +3364,6 @@ return {
To close folds you can re-apply 'foldlevel' with the |zx| command or
set the 'foldclose' option to "all".
]=],
- expand_cb = 'expand_set_foldopen',
full_name = 'foldopen',
list = 'onecomma',
redraw = { 'curswant' },
@@ -3243,11 +3371,12 @@ return {
short_desc = N_('for which commands a fold will be opened'),
type = 'string',
varname = 'p_fdo',
+ flags_varname = 'fdo_flags',
},
{
abbreviation = 'fdt',
cb = 'did_set_optexpr',
- defaults = { if_true = 'foldtext()' },
+ defaults = 'foldtext()',
desc = [=[
An expression which is used to specify the text displayed for a closed
fold. The context is set to the script where 'foldexpr' was set,
@@ -3274,7 +3403,7 @@ return {
{
abbreviation = 'fex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to format a range of lines for the |gq|
operator or automatic formatting (see 'formatoptions'). When this
@@ -3326,7 +3455,7 @@ return {
},
{
abbreviation = 'flp',
- defaults = { if_true = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*' },
+ defaults = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*',
desc = [=[
A pattern that is used to recognize a list header. This is used for
the "n" flag in 'formatoptions'.
@@ -3347,7 +3476,7 @@ return {
{
abbreviation = 'fo',
cb = 'did_set_formatoptions',
- defaults = { if_true = macros('DFLT_FO_VIM', 'string') },
+ defaults = macros('DFLT_FO_VIM', 'string'),
desc = [=[
This is a sequence of letters which describes how automatic
formatting is to be done.
@@ -3366,7 +3495,7 @@ return {
},
{
abbreviation = 'fp',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
The name of an external program that will be used to format the lines
selected with the |gq| operator. The program must take the input on
@@ -3390,7 +3519,7 @@ return {
},
{
abbreviation = 'fs',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, the OS function fsync() will be called after saving a file
(|:write|, |writefile()|, …), |swap-file|, |undo-persistence| and |shada-file|.
@@ -3416,7 +3545,7 @@ return {
},
{
abbreviation = 'gd',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, the ":substitute" flag 'g' is default on. This means that
all matches in a line are substituted instead of one. When a 'g' flag
@@ -3440,7 +3569,7 @@ return {
},
{
abbreviation = 'gfm',
- defaults = { if_true = macros('DFLT_GREPFORMAT', 'string') },
+ defaults = macros('DFLT_GREPFORMAT', 'string'),
deny_duplicates = true,
desc = [=[
Format to recognize for the ":grep" command output.
@@ -3499,7 +3628,7 @@ return {
{
abbreviation = 'gcr',
cb = 'did_set_guicursor',
- defaults = { if_true = 'n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20' },
+ defaults = 'n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor',
deny_duplicates = true,
desc = [=[
Configures the cursor style for each mode. Works in the GUI and many
@@ -3528,6 +3657,7 @@ return {
ci Command-line Insert mode
cr Command-line Replace mode
sm showmatch in Insert mode
+ t Terminal mode
a all modes
The argument-list is a dash separated list of these arguments:
hor{N} horizontal bar, {N} percent of the character height
@@ -3544,7 +3674,8 @@ return {
cursor is not shown. Times are in msec. When one of
the numbers is zero, there is no blinking. E.g.: >vim
set guicursor=n:blinkon0
- < - Default is "blinkon0" for each mode.
+ <
+ Default is "blinkon0" for each mode.
{group-name}
Highlight group that decides the color and font of the
cursor.
@@ -3596,7 +3727,7 @@ return {
},
{
abbreviation = 'gfn',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This is a list of fonts which will be used for the GUI version of Vim.
In its simplest form the value is just one font name. When
@@ -3668,7 +3799,7 @@ return {
},
{
abbreviation = 'gfw',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Comma-separated list of fonts to be used for double-width characters.
@@ -3804,7 +3935,7 @@ return {
},
{
abbreviation = 'gtl',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty describes the text to use in a label of the GUI tab
pages line. When empty and when the result is empty Vim will use a
@@ -3830,7 +3961,7 @@ return {
},
{
abbreviation = 'gtt',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty describes the text to use in a tooltip for the GUI tab
pages line. When empty Vim will use a default tooltip.
@@ -3876,7 +4007,7 @@ return {
{
abbreviation = 'hh',
cb = 'did_set_helpheight',
- defaults = { if_true = 20 },
+ defaults = 20,
desc = [=[
Minimal initial height of the help window when it is opened with the
":help" command. The initial height of the help window is half of the
@@ -3921,7 +4052,7 @@ return {
},
{
abbreviation = 'hid',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When off a buffer is unloaded (including loss of undo information)
when it is |abandon|ed. When on a buffer becomes hidden when it is
@@ -3947,7 +4078,7 @@ return {
{
abbreviation = 'hl',
cb = 'did_set_highlight',
- defaults = { if_true = macros('HIGHLIGHT_INIT', 'string') },
+ defaults = macros('HIGHLIGHT_INIT', 'string'),
deny_duplicates = true,
full_name = 'highlight',
list = 'onecomma',
@@ -3958,11 +4089,11 @@ return {
},
{
abbreviation = 'hi',
- defaults = { if_true = 10000 },
+ defaults = 10000,
desc = [=[
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
- each of these histories (see |cmdline-editing| and 'msghistory' for
+ each of these histories (see |cmdline-editing| and 'messagesopt' for
the number of messages to remember).
The maximum value is 10000.
]=],
@@ -3974,7 +4105,7 @@ return {
},
{
abbreviation = 'hk',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'hkmap',
scope = { 'global' },
short_desc = N_('No description'),
@@ -3983,7 +4114,7 @@ return {
},
{
abbreviation = 'hkp',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'hkmapp',
scope = { 'global' },
short_desc = N_('No description'),
@@ -3993,7 +4124,7 @@ return {
{
abbreviation = 'hls',
cb = 'did_set_hlsearch',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When there is a previous search pattern, highlight all its matches.
The |hl-Search| highlight group determines the highlighting for all
@@ -4043,7 +4174,7 @@ return {
},
{
cb = 'did_set_iconstring',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is not empty, it will be used for the icon text of
the window. This happens only when the 'icon' option is on.
@@ -4063,7 +4194,7 @@ return {
{
abbreviation = 'ic',
cb = 'did_set_ignorecase',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Ignore case in search patterns, |cmdline-completion|, when
searching in the tags file, and |expr-==|.
@@ -4079,7 +4210,7 @@ return {
},
{
abbreviation = 'imc',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When set the Input Method is always on when starting to edit a command
line, unless entering a search pattern (see 'imsearch' for that).
@@ -4114,7 +4245,7 @@ return {
{
abbreviation = 'imi',
cb = 'did_set_iminsert',
- defaults = { if_true = macros('B_IMODE_NONE', 'number') },
+ defaults = macros('B_IMODE_NONE', 'number'),
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used in
Insert mode. Valid values:
@@ -4139,7 +4270,7 @@ return {
},
{
abbreviation = 'ims',
- defaults = { if_true = macros('B_IMODE_USE_INSERT', 'number') },
+ defaults = macros('B_IMODE_USE_INSERT', 'number'),
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used when
entering a search pattern. Valid values:
@@ -4162,7 +4293,8 @@ return {
{
abbreviation = 'icm',
cb = 'did_set_inccommand',
- defaults = { if_true = 'nosplit' },
+ defaults = 'nosplit',
+ values = { 'nosplit', 'split', '' },
desc = [=[
When nonempty, shows the effects of |:substitute|, |:smagic|,
|:snomagic| and user commands with the |:command-preview| flag as you
@@ -4178,7 +4310,6 @@ return {
'redrawtime') then 'inccommand' is automatically disabled until
|Command-line-mode| is done.
]=],
- expand_cb = 'expand_set_inccommand',
full_name = 'inccommand',
scope = { 'global' },
short_desc = N_('Live preview of substitution'),
@@ -4187,7 +4318,7 @@ return {
},
{
abbreviation = 'inc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Pattern to be used to find an include command. It is a search
pattern, just like for the "/" command (See |pattern|). This option
@@ -4209,7 +4340,7 @@ return {
{
abbreviation = 'inex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression to be used to transform the string found with the 'include'
option to a file name. Mostly useful to change "." to "/" for Java: >vim
@@ -4250,7 +4381,7 @@ return {
},
{
abbreviation = 'is',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
While typing a search command, show where the pattern, as it was typed
so far, matches. The matched string is highlighted. If the pattern
@@ -4293,7 +4424,7 @@ return {
{
abbreviation = 'inde',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to obtain the proper indent for a line.
It is used when a new line is created, for the |=| operator and
@@ -4345,7 +4476,7 @@ return {
},
{
abbreviation = 'indk',
- defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
+ defaults = '0{,0},0),0],:,0#,!^F,o,O,e',
deny_duplicates = true,
desc = [=[
A list of keys that, when typed in Insert mode, cause reindenting of
@@ -4362,7 +4493,7 @@ return {
},
{
abbreviation = 'inf',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When doing keyword completion in insert mode |ins-completion|, and
'ignorecase' is also on, the case of the match is adjusted depending
@@ -4381,7 +4512,7 @@ return {
},
{
abbreviation = 'im',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'insertmode',
scope = { 'global' },
short_desc = N_('No description'),
@@ -4487,7 +4618,7 @@ return {
{
abbreviation = 'isk',
cb = 'did_set_iskeyword',
- defaults = { if_true = '@,48-57,_,192-255' },
+ defaults = '@,48-57,_,192-255',
deny_duplicates = true,
desc = [=[
Keywords are used in searching and recognizing with many commands:
@@ -4513,7 +4644,7 @@ return {
{
abbreviation = 'isp',
cb = 'did_set_isopt',
- defaults = { if_true = '@,161-255' },
+ defaults = '@,161-255',
deny_duplicates = true,
desc = [=[
The characters given by this option are displayed directly on the
@@ -4553,7 +4684,7 @@ return {
},
{
abbreviation = 'js',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Insert two spaces after a '.', '?' and '!' with a join command.
Otherwise only one space is inserted.
@@ -4566,8 +4697,9 @@ return {
},
{
abbreviation = 'jop',
- cb = 'did_set_jumpoptions',
- defaults = { if_true = 'clean' },
+ defaults = 'clean',
+ values = { 'stack', 'view', 'clean' },
+ flags = true,
deny_duplicates = true,
desc = [=[
List of words that change the behavior of the |jumplist|.
@@ -4584,18 +4716,18 @@ return {
clean Remove unloaded buffers from the jumplist.
EXPERIMENTAL: this flag may change in the future.
]=],
- expand_cb = 'expand_set_jumpoptions',
full_name = 'jumpoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('Controls the behavior of the jumplist'),
type = 'string',
varname = 'p_jop',
+ flags_varname = 'jop_flags',
},
{
abbreviation = 'kmp',
cb = 'did_set_keymap',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Name of a keyboard mapping. See |mbyte-keymap|.
Setting this option to a valid keymap name has the side effect of
@@ -4615,7 +4747,8 @@ return {
{
abbreviation = 'km',
cb = 'did_set_keymodel',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'startsel', 'stopsel' },
deny_duplicates = true,
desc = [=[
List of comma-separated words, which enable special things that keys
@@ -4627,7 +4760,6 @@ return {
Special keys in this context are the cursor keys, <End>, <Home>,
<PageUp> and <PageDown>.
]=],
- expand_cb = 'expand_set_keymodel',
full_name = 'keymodel',
list = 'onecomma',
scope = { 'global' },
@@ -4668,7 +4800,7 @@ return {
{
abbreviation = 'lmap',
cb = 'did_set_langmap',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
This option allows switching your keyboard into a special language
@@ -4722,7 +4854,7 @@ return {
},
{
abbreviation = 'lm',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Language to use for menu translation. Tells which file is loaded
from the "lang" directory in 'runtimepath': >vim
@@ -4753,7 +4885,7 @@ return {
{
abbreviation = 'lnr',
cb = 'did_set_langnoremap',
- defaults = { if_true = true },
+ defaults = true,
full_name = 'langnoremap',
scope = { 'global' },
short_desc = N_("do not apply 'langmap' to mapped characters"),
@@ -4763,7 +4895,7 @@ return {
{
abbreviation = 'lrm',
cb = 'did_set_langremap',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When off, setting 'langmap' does not apply to characters resulting from
a mapping. If setting 'langmap' disables some of your mappings, make
@@ -4778,7 +4910,7 @@ return {
{
abbreviation = 'ls',
cb = 'did_set_laststatus',
- defaults = { if_true = 2 },
+ defaults = 2,
desc = [=[
The value of this option influences when the last window will have a
status line:
@@ -4798,7 +4930,7 @@ return {
},
{
abbreviation = 'lz',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When this option is set, the screen will not be redrawn while
executing macros, registers and other commands that have not been
@@ -4816,7 +4948,7 @@ return {
},
{
abbreviation = 'lbr',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If on, Vim will wrap long lines at a character in 'breakat' rather
than at the last character that fits on the screen. Unlike
@@ -4862,7 +4994,7 @@ return {
},
{
abbreviation = 'lsp',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
only in the GUI
Number of pixel lines inserted between characters. Useful if the font
@@ -4882,7 +5014,7 @@ return {
},
{
cb = 'did_set_lisp',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Lisp mode: When <Enter> is typed in insert mode set the indent for
the next line to Lisp standards (well, sort of). Also happens with
@@ -4902,7 +5034,8 @@ return {
{
abbreviation = 'lop',
cb = 'did_set_lispoptions',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'expr:0', 'expr:1' },
deny_duplicates = true,
desc = [=[
Comma-separated list of items that influence the Lisp indenting when
@@ -4913,7 +5046,6 @@ return {
Note that when using 'indentexpr' the `=` operator indents all the
lines, otherwise the first line is not indented (Vi-compatible).
]=],
- expand_cb = 'expand_set_lispoptions',
full_name = 'lispoptions',
list = 'onecomma',
scope = { 'buf' },
@@ -4940,7 +5072,7 @@ return {
varname = 'p_lispwords',
},
{
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
List mode: By default, show tabs as ">", trailing spaces as "-", and
non-breakable space characters as "+". Useful to see the difference
@@ -4968,7 +5100,7 @@ return {
{
abbreviation = 'lcs',
cb = 'did_set_chars_option',
- defaults = { if_true = 'tab:> ,trail:-,nbsp:+' },
+ defaults = 'tab:> ,trail:-,nbsp:+',
deny_duplicates = true,
desc = [=[
Strings to use in 'list' mode and for the |:list| command. It is a
@@ -5079,7 +5211,7 @@ return {
},
{
abbreviation = 'lpl',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on the plugin scripts are loaded when starting up |load-plugins|.
This option can be reset in your |vimrc| file to disable the loading
@@ -5094,7 +5226,7 @@ return {
varname = 'p_lpl',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Changes the special characters that can be used in search patterns.
See |pattern|.
@@ -5112,7 +5244,7 @@ return {
},
{
abbreviation = 'mef',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Name of the errorfile for the |:make| command (see |:make_makeprg|)
and the |:grep| command.
@@ -5137,7 +5269,7 @@ return {
{
abbreviation = 'menc',
cb = 'did_set_encoding',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Encoding used for reading the output of external commands. When empty,
encoding is not converted.
@@ -5160,7 +5292,7 @@ return {
},
{
abbreviation = 'mp',
- defaults = { if_true = 'make' },
+ defaults = 'make',
desc = [=[
Program to use for the ":make" command. See |:make_makeprg|.
This option may contain '%' and '#' characters (see |:_%| and |:_#|),
@@ -5189,7 +5321,7 @@ return {
{
abbreviation = 'mps',
cb = 'did_set_matchpairs',
- defaults = { if_true = '(:),{:},[:]' },
+ defaults = '(:),{:},[:]',
deny_duplicates = true,
desc = [=[
Characters that form pairs. The |%| command jumps from one to the
@@ -5217,7 +5349,7 @@ return {
},
{
abbreviation = 'mat',
- defaults = { if_true = 5 },
+ defaults = 5,
desc = [=[
Tenths of a second to show the matching paren, when 'showmatch' is
set. Note that this is not in milliseconds, like other options that
@@ -5231,7 +5363,7 @@ return {
},
{
abbreviation = 'mco',
- defaults = { if_true = macros('MAX_MCO', 'number') },
+ defaults = macros('MAX_MCO', 'number'),
full_name = 'maxcombine',
scope = { 'global' },
short_desc = N_('maximum nr of combining characters displayed'),
@@ -5240,7 +5372,7 @@ return {
},
{
abbreviation = 'mfd',
- defaults = { if_true = 100 },
+ defaults = 100,
desc = [=[
Maximum depth of function calls for user functions. This normally
catches endless recursion. When using a recursive function with
@@ -5259,7 +5391,7 @@ return {
},
{
abbreviation = 'mmd',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Maximum number of times a mapping is done without resulting in a
character to be used. This normally catches endless mappings, like
@@ -5276,7 +5408,7 @@ return {
},
{
abbreviation = 'mmp',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Maximum amount of memory (in Kbyte) to use for pattern matching.
The maximum value is about 2000000. Use this to work without a limit.
@@ -5299,7 +5431,7 @@ return {
},
{
abbreviation = 'mis',
- defaults = { if_true = 25 },
+ defaults = 25,
desc = [=[
Maximum number of items to use in a menu. Used for menus that are
generated from a list of items, e.g., the Buffers menu. Changing this
@@ -5312,9 +5444,43 @@ return {
varname = 'p_mis',
},
{
+ abbreviation = 'mopt',
+ cb = 'did_set_messagesopt',
+ defaults = 'hit-enter,history:500',
+ values = { 'hit-enter', 'wait:', 'history:' },
+ flags = true,
+ deny_duplicates = true,
+ desc = [=[
+ Option settings for outputting messages. It can consist of the
+ following items. Items must be separated by a comma.
+
+ hit-enter Use a |hit-enter| prompt when the message is longer than
+ 'cmdheight' size.
+
+ wait:{n} Instead of using a |hit-enter| prompt, simply wait for
+ {n} milliseconds so that the user has a chance to read
+ the message. The maximum value of {n} is 10000. Use
+ 0 to disable the wait (but then the user may miss an
+ important message).
+ This item is ignored when "hit-enter" is present, but
+ required when "hit-enter" is not present.
+
+ history:{n} Determines how many entries are remembered in the
+ |:messages| history. The maximum value is 10000.
+ Setting it to zero clears the message history.
+ This item must always be present.
+ ]=],
+ full_name = 'messagesopt',
+ list = 'onecommacolon',
+ scope = { 'global' },
+ short_desc = N_('options for outputting messages'),
+ type = 'string',
+ varname = 'p_mopt',
+ },
+ {
abbreviation = 'msm',
cb = 'did_set_mkspellmem',
- defaults = { if_true = '460000,2000,500' },
+ defaults = '460000,2000,500',
desc = [=[
Parameters for |:mkspell|. This tunes when to start compressing the
word tree. Compression can be slow when there are many words, but
@@ -5379,7 +5545,7 @@ return {
},
{
abbreviation = 'mle',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on allow some options that are an expression to be set in the
modeline. Check the option for whether it is affected by
@@ -5396,7 +5562,7 @@ return {
},
{
abbreviation = 'mls',
- defaults = { if_true = 5 },
+ defaults = 5,
desc = [=[
If 'modeline' is on 'modelines' gives the number of lines that is
checked for set commands. If 'modeline' is off or 'modelines' is zero
@@ -5412,7 +5578,7 @@ return {
{
abbreviation = 'ma',
cb = 'did_set_modifiable',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When off the buffer contents cannot be changed. The 'fileformat' and
'fileencoding' options also can't be changed.
@@ -5429,7 +5595,7 @@ return {
{
abbreviation = 'mod',
cb = 'did_set_modified',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, the buffer is considered to be modified. This option is set
when:
@@ -5462,7 +5628,7 @@ return {
varname = 'p_mod',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, listings pause when the whole screen is filled. You will get
the |more-prompt|. When this option is off there are no pauses, the
@@ -5476,7 +5642,7 @@ return {
},
{
cb = 'did_set_mouse',
- defaults = { if_true = 'nvi' },
+ defaults = 'nvi',
desc = [=[
Enables mouse support. For example, to enable the mouse in Normal mode
and Visual mode: >vim
@@ -5525,7 +5691,7 @@ return {
},
{
abbreviation = 'mousef',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
The window that the mouse pointer is on is automatically activated.
When changing the window layout or window focus in another way, the
@@ -5542,7 +5708,7 @@ return {
},
{
abbreviation = 'mh',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
only in the GUI
When on, the mouse pointer is hidden when characters are typed.
@@ -5557,8 +5723,8 @@ return {
},
{
abbreviation = 'mousem',
- cb = 'did_set_mousemodel',
- defaults = { if_true = 'popup_setpos' },
+ defaults = 'popup_setpos',
+ values = { 'extend', 'popup', 'popup_setpos' },
desc = [=[
Sets the model to use for the mouse. The name mostly specifies what
the right mouse button is used for:
@@ -5608,7 +5774,6 @@ return {
"g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
"g<RightMouse>" is "<C-RightMouse> ("CTRL-T")
]=],
- expand_cb = 'expand_set_mousemodel',
full_name = 'mousemodel',
scope = { 'global' },
short_desc = N_('changes meaning of mouse buttons'),
@@ -5617,7 +5782,7 @@ return {
},
{
abbreviation = 'mousemev',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, mouse move events are delivered to the input queue and are
available for mapping. The default, off, avoids the mouse movement
@@ -5634,7 +5799,8 @@ return {
},
{
cb = 'did_set_mousescroll',
- defaults = { if_true = 'ver:3,hor:6' },
+ defaults = 'ver:3,hor:6',
+ values = { 'hor:', 'ver:' },
desc = [=[
This option controls the number of lines / columns to scroll by when
scrolling with a mouse wheel (|scroll-mouse-wheel|). The option is
@@ -5654,7 +5820,6 @@ return {
< Will make Nvim scroll 5 lines at a time when scrolling vertically, and
scroll 2 columns at a time when scrolling horizontally.
]=],
- expand_cb = 'expand_set_mousescroll',
full_name = 'mousescroll',
list = 'comma',
scope = { 'global' },
@@ -5742,7 +5907,7 @@ return {
},
{
abbreviation = 'mouset',
- defaults = { if_true = 500 },
+ defaults = 500,
desc = [=[
Defines the maximum time in msec between two mouse clicks for the
second click to be recognized as a multi click.
@@ -5754,22 +5919,9 @@ return {
varname = 'p_mouset',
},
{
- abbreviation = 'mhi',
- defaults = { if_true = 500 },
- desc = [=[
- Determines how many entries are remembered in the |:messages| history.
- The maximum value is 10000.
- ]=],
- full_name = 'msghistory',
- scope = { 'global' },
- short_desc = N_('how many messages are remembered'),
- type = 'number',
- varname = 'p_mhi',
- },
- {
abbreviation = 'nf',
- cb = 'did_set_nrformats',
- defaults = { if_true = 'bin,hex' },
+ defaults = 'bin,hex',
+ values = { 'bin', 'octal', 'hex', 'alpha', 'unsigned', 'blank' },
deny_duplicates = true,
desc = [=[
This defines what bases Vim will consider for numbers when using the
@@ -5813,7 +5965,6 @@ return {
considered decimal. This also happens for numbers that are not
recognized as octal or hex.
]=],
- expand_cb = 'expand_set_nrformats',
full_name = 'nrformats',
list = 'onecomma',
scope = { 'buf' },
@@ -5824,7 +5975,7 @@ return {
{
abbreviation = 'nu',
cb = 'did_set_number_relativenumber',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Print the line number in front of each line. When the 'n' option is
excluded from 'cpoptions' a wrapped line will not use the column of
@@ -5857,7 +6008,7 @@ return {
{
abbreviation = 'nuw',
cb = 'did_set_numberwidth',
- defaults = { if_true = 4 },
+ defaults = 4,
desc = [=[
Minimal number of columns to use for the line number. Only relevant
when the 'number' or 'relativenumber' option is set or printing lines
@@ -5879,7 +6030,7 @@ return {
{
abbreviation = 'ofu',
cb = 'did_set_omnifunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used for Insert mode omni
completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O|
@@ -5902,7 +6053,7 @@ return {
},
{
abbreviation = 'odev',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
only for Windows
Enable reading and writing from devices. This may get Vim stuck on a
@@ -5920,7 +6071,7 @@ return {
{
abbreviation = 'opfunc',
cb = 'did_set_operatorfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be called by the |g@| operator.
See |:map-operator| for more info and an example. The value can be
@@ -5964,7 +6115,7 @@ return {
},
{
abbreviation = 'para',
- defaults = { if_true = 'IPLPPPQPP TPHPLIPpLpItpplpipbp' },
+ defaults = 'IPLPPPQPP TPHPLIPpLpItpplpipbp',
desc = [=[
Specifies the nroff macros that separate paragraphs. These are pairs
of two letters (see |object-motions|).
@@ -5977,7 +6128,7 @@ return {
},
{
cb = 'did_set_paste',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'paste',
pri_mkrc = true,
scope = { 'global' },
@@ -5987,7 +6138,7 @@ return {
},
{
abbreviation = 'pt',
- defaults = { if_true = '' },
+ defaults = '',
full_name = 'pastetoggle',
scope = { 'global' },
short_desc = N_('No description'),
@@ -5997,7 +6148,7 @@ return {
{
abbreviation = 'pex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to apply a patch to a file and generate
the resulting new version of the file. See |diff-patchexpr|.
@@ -6014,7 +6165,7 @@ return {
{
abbreviation = 'pm',
cb = 'did_set_backupext_or_patchmode',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty the oldest version of a file is kept. This can be used
to keep the original version of a file if you are changing files in a
@@ -6043,7 +6194,7 @@ return {
},
{
abbreviation = 'pa',
- defaults = { if_true = '.,,' },
+ defaults = '.,,',
deny_duplicates = true,
desc = [=[
This is a list of directories which will be searched when using the
@@ -6105,7 +6256,7 @@ return {
},
{
abbreviation = 'pi',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When changing the indent of the current line, preserve as much of the
indent structure as possible. Normally the indent is replaced by a
@@ -6128,7 +6279,7 @@ return {
},
{
abbreviation = 'pvh',
- defaults = { if_true = 12 },
+ defaults = 12,
desc = [=[
Default height for a preview window. Used for |:ptag| and associated
commands. Used for |CTRL-W_}| when no count is given.
@@ -6142,7 +6293,7 @@ return {
{
abbreviation = 'pvw',
cb = 'did_set_previewwindow',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Identifies the preview window. Only one window can have this option
set. It's normally not set directly, but by using one of the commands
@@ -6157,7 +6308,7 @@ return {
type = 'boolean',
},
{
- defaults = { if_true = true },
+ defaults = true,
full_name = 'prompt',
scope = { 'global' },
short_desc = N_('enable prompt in Ex mode'),
@@ -6167,7 +6318,7 @@ return {
{
abbreviation = 'pb',
cb = 'did_set_pumblend',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Enables pseudo-transparency for the |popup-menu|. Valid values are in
the range of 0 for fully opaque popupmenu (disabled) to 100 for fully
@@ -6191,7 +6342,7 @@ return {
},
{
abbreviation = 'ph',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Maximum number of items to show in the popup menu
(|ins-completion-menu|). Zero means "use available screen space".
@@ -6204,7 +6355,7 @@ return {
},
{
abbreviation = 'pw',
- defaults = { if_true = 15 },
+ defaults = 15,
desc = [=[
Minimum width for the popup menu (|ins-completion-menu|). If the
cursor column + 'pumwidth' exceeds screen width, the popup menu is
@@ -6218,7 +6369,7 @@ return {
},
{
abbreviation = 'pyx',
- defaults = { if_true = 3 },
+ defaults = 3,
desc = [=[
Specifies the python version used for pyx* functions and commands
|python_x|. As only Python 3 is supported, this always has the value
@@ -6237,7 +6388,7 @@ return {
{
abbreviation = 'qftf',
cb = 'did_set_quickfixtextfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used to get the text to display
in the quickfix and location list windows. This can be used to
@@ -6261,7 +6412,7 @@ return {
},
{
abbreviation = 'qe',
- defaults = { if_true = '\\' },
+ defaults = '\\',
desc = [=[
The characters that are used to escape quotes in a string. Used for
objects like a', a" and a` |a'|.
@@ -6278,7 +6429,7 @@ return {
{
abbreviation = 'ro',
cb = 'did_set_readonly',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If on, writes fail unless you use a '!'. Protects you from
accidentally overwriting a file. Default on when Vim is started
@@ -6299,8 +6450,16 @@ return {
},
{
abbreviation = 'rdb',
- cb = 'did_set_redrawdebug',
- defaults = { if_true = '' },
+ defaults = '',
+ values = {
+ 'compositor',
+ 'nothrottle',
+ 'invalid',
+ 'nodelta',
+ 'line',
+ 'flush',
+ },
+ flags = true,
desc = [=[
Flags to change the way redrawing works, for debugging purposes.
Most useful with 'writedelay' set to some reasonable value.
@@ -6339,14 +6498,15 @@ return {
short_desc = N_('Changes the way redrawing works (debug)'),
type = 'string',
varname = 'p_rdb',
+ flags_varname = 'rdb_flags',
},
{
abbreviation = 'rdt',
- defaults = { if_true = 2000 },
+ defaults = 2000,
desc = [=[
Time in milliseconds for redrawing the display. Applies to
- 'hlsearch', 'inccommand', |:match| highlighting and syntax
- highlighting.
+ 'hlsearch', 'inccommand', |:match| highlighting, syntax highlighting,
+ and async |LanguageTree:parse()|.
When redrawing takes more than this many milliseconds no further
matches will be highlighted.
For syntax highlighting the time applies per window. When over the
@@ -6362,7 +6522,7 @@ return {
},
{
abbreviation = 're',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
This selects the default regexp engine. |two-engines|
The possible values are:
@@ -6386,7 +6546,7 @@ return {
{
abbreviation = 'rnu',
cb = 'did_set_number_relativenumber',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Show the line number relative to the line with the cursor in front of
each line. Relative line numbers help you use the |count| you can
@@ -6413,7 +6573,7 @@ return {
type = 'boolean',
},
{
- defaults = { if_true = true },
+ defaults = true,
full_name = 'remap',
scope = { 'global' },
short_desc = N_('No description'),
@@ -6421,7 +6581,7 @@ return {
immutable = true,
},
{
- defaults = { if_true = 2 },
+ defaults = 2,
desc = [=[
Threshold for reporting number of lines changed. When the number of
changed lines is more than 'report' a message will be given for most
@@ -6437,7 +6597,7 @@ return {
},
{
abbreviation = 'ri',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Inserting characters in Insert mode will work backwards. See "typing
backwards" |ins-reverse|. This option can be toggled with the CTRL-_
@@ -6451,7 +6611,7 @@ return {
},
{
abbreviation = 'rl',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, display orientation becomes right-to-left, i.e., characters
that are stored in the file appear from the right to the left.
@@ -6471,8 +6631,8 @@ return {
},
{
abbreviation = 'rlc',
- cb = 'did_set_rightleftcmd',
- defaults = { if_true = 'search' },
+ defaults = 'search',
+ values = { 'search' },
desc = [=[
Each word in this option enables the command line editing to work in
right-to-left mode for a group of commands:
@@ -6482,8 +6642,8 @@ return {
This is useful for languages such as Hebrew, Arabic and Farsi.
The 'rightleft' option must be set for 'rightleftcmd' to take effect.
]=],
- expand_cb = 'expand_set_rightleftcmd',
full_name = 'rightleftcmd',
+ list = 'comma',
redraw = { 'current_window' },
scope = { 'win' },
short_desc = N_('commands for which editing works right-to-left'),
@@ -6491,7 +6651,7 @@ return {
},
{
abbreviation = 'ru',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Show the line and column number of the cursor position, separated by a
comma. When there is room, the relative position of the displayed
@@ -6526,7 +6686,7 @@ return {
{
abbreviation = 'ruf',
cb = 'did_set_rulerformat',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is not empty, it determines the content of the ruler
string, as displayed for the 'ruler' option.
@@ -6583,6 +6743,7 @@ return {
indent/ indent scripts |indent-expression|
keymap/ key mapping files |mbyte-keymap|
lang/ menu translations |:menutrans|
+ lsp/ LSP client configurations |lsp-config|
lua/ |Lua| plugins
menu.vim GUI menus |menu.vim|
pack/ packages |:packadd|
@@ -6697,7 +6858,7 @@ return {
{
abbreviation = 'scb',
cb = 'did_set_scrollbind',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
See also |scroll-binding|. When this option is set, scrolling the
current window also scrolls other scrollbind windows (windows that
@@ -6716,7 +6877,7 @@ return {
},
{
abbreviation = 'sj',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Minimal number of lines to scroll when the cursor gets off the
screen (e.g., with "j"). Not used for scroll commands (e.g., CTRL-E,
@@ -6733,7 +6894,7 @@ return {
},
{
abbreviation = 'so',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Minimal number of screen lines to keep above and below the cursor.
This will make some context visible around where you are working. If
@@ -6754,8 +6915,8 @@ return {
},
{
abbreviation = 'sbo',
- cb = 'did_set_scrollopt',
- defaults = { if_true = 'ver,jump' },
+ defaults = 'ver,jump',
+ values = { 'ver', 'hor', 'jump' },
deny_duplicates = true,
desc = [=[
This is a comma-separated list of words that specifies how
@@ -6786,7 +6947,6 @@ return {
When 'diff' mode is active there always is vertical scroll binding,
even when "ver" isn't there.
]=],
- expand_cb = 'expand_set_scrollopt',
full_name = 'scrollopt',
list = 'onecomma',
scope = { 'global' },
@@ -6796,7 +6956,7 @@ return {
},
{
abbreviation = 'sect',
- defaults = { if_true = 'SHNHH HUnhsh' },
+ defaults = 'SHNHH HUnhsh',
desc = [=[
Specifies the nroff macros that separate sections. These are pairs of
two letters (See |object-motions|). The default makes a section start
@@ -6809,7 +6969,7 @@ return {
varname = 'p_sections',
},
{
- defaults = { if_true = false },
+ defaults = false,
full_name = 'secure',
scope = { 'global' },
secure = true,
@@ -6820,7 +6980,8 @@ return {
{
abbreviation = 'sel',
cb = 'did_set_selection',
- defaults = { if_true = 'inclusive' },
+ defaults = 'inclusive',
+ values = { 'inclusive', 'exclusive', 'old' },
desc = [=[
This option defines the behavior of the selection. It is only used
in Visual and Select mode.
@@ -6836,11 +6997,12 @@ return {
selection.
When "old" is used and 'virtualedit' allows the cursor to move past
the end of line the line break still isn't included.
+ When "exclusive" is used, cursor position in visual mode will be
+ adjusted for inclusive motions |inclusive-motion-selection-exclusive|.
Note that when "exclusive" is used and selecting from the end
backwards, you cannot include the last character of a line, when
starting in Normal mode and 'virtualedit' empty.
]=],
- expand_cb = 'expand_set_selection',
full_name = 'selection',
scope = { 'global' },
short_desc = N_('what type of selection to use'),
@@ -6849,8 +7011,8 @@ return {
},
{
abbreviation = 'slm',
- cb = 'did_set_selectmode',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'mouse', 'key', 'cmd' },
deny_duplicates = true,
desc = [=[
This is a comma-separated list of words, which specifies when to start
@@ -6861,7 +7023,6 @@ return {
cmd when using "v", "V" or CTRL-V
See |Select-mode|.
]=],
- expand_cb = 'expand_set_selectmode',
full_name = 'selectmode',
list = 'onecomma',
scope = { 'global' },
@@ -6872,7 +7033,29 @@ return {
{
abbreviation = 'ssop',
cb = 'did_set_sessionoptions',
- defaults = { if_true = 'blank,buffers,curdir,folds,help,tabpages,winsize,terminal' },
+ defaults = 'blank,buffers,curdir,folds,help,tabpages,winsize,terminal',
+ -- Also used for 'viewoptions'.
+ values = {
+ 'buffers',
+ 'winpos',
+ 'resize',
+ 'winsize',
+ 'localoptions',
+ 'options',
+ 'help',
+ 'blank',
+ 'globals',
+ 'slash',
+ 'unix',
+ 'sesdir',
+ 'curdir',
+ 'folds',
+ 'cursor',
+ 'tabpages',
+ 'terminal',
+ 'skiprtp',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
Changes the effect of the |:mksession| command. It is a comma-
@@ -6913,13 +7096,13 @@ return {
If you leave out "options" many things won't work well after restoring
the session.
]=],
- expand_cb = 'expand_set_sessionoptions',
full_name = 'sessionoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('options for |:mksession|'),
type = 'string',
varname = 'p_ssop',
+ flags_varname = 'ssop_flags',
},
{
abbreviation = 'sd',
@@ -7056,7 +7239,7 @@ return {
{
abbreviation = 'sdf',
alias = { 'vif', 'viminfofile' },
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
When non-empty, overrides the file name used for |shada| (viminfo).
@@ -7310,7 +7493,7 @@ return {
},
{
abbreviation = 'stmp',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, use temp files for shell commands. When off use a pipe.
When using a pipe is not possible temp files are used anyway.
@@ -7331,7 +7514,7 @@ return {
},
{
abbreviation = 'sxe',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When 'shellxquote' is set to "(" then the characters listed in this
option will be escaped with a '^' character. This makes it possible
@@ -7374,7 +7557,7 @@ return {
},
{
abbreviation = 'sr',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Round indent to multiple of 'shiftwidth'. Applies to > and <
commands. CTRL-T and CTRL-D in Insert mode always round the indent to
@@ -7389,7 +7572,7 @@ return {
{
abbreviation = 'sw',
cb = 'did_set_shiftwidth_tabstop',
- defaults = { if_true = 8 },
+ defaults = 8,
desc = [=[
Number of spaces to use for each step of (auto)indent. Used for
|'cindent'|, |>>|, |<<|, etc.
@@ -7405,7 +7588,7 @@ return {
{
abbreviation = 'shm',
cb = 'did_set_shortmess',
- defaults = { if_true = 'ltToOCF' },
+ defaults = 'ltToOCF',
desc = [=[
This option helps to avoid all the |hit-enter| prompts caused by file
messages, for example with CTRL-G, and to avoid some other messages.
@@ -7475,7 +7658,7 @@ return {
{
abbreviation = 'sbr',
cb = 'did_set_showbreak',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
String to put at the start of lines that have been wrapped. Useful
values are "> " or "+++ ": >vim
@@ -7503,7 +7686,7 @@ return {
},
{
abbreviation = 'sc',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Show (partial) command in the last line of the screen. Set this
option off if your terminal is slow.
@@ -7526,7 +7709,8 @@ return {
{
abbreviation = 'sloc',
cb = 'did_set_showcmdloc',
- defaults = { if_true = 'last' },
+ defaults = 'last',
+ values = { 'last', 'statusline', 'tabline' },
desc = [=[
This option can be used to display the (partially) entered command in
another location. Possible values are:
@@ -7540,7 +7724,6 @@ return {
place the text. Without a custom 'statusline' or 'tabline' it will be
displayed in a convenient location.
]=],
- expand_cb = 'expand_set_showcmdloc',
full_name = 'showcmdloc',
scope = { 'global' },
short_desc = N_('change location of partial command'),
@@ -7549,7 +7732,7 @@ return {
},
{
abbreviation = 'sft',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When completing a word in insert mode (see |ins-completion|) from the
tags file, show both the tag name and a tidied-up form of the search
@@ -7568,7 +7751,7 @@ return {
},
{
abbreviation = 'sm',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When a bracket is inserted, briefly jump to the matching one. The
jump is only done if the match can be seen on the screen. The time to
@@ -7594,7 +7777,7 @@ return {
},
{
abbreviation = 'smd',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
If in Insert, Replace or Visual mode put a message on the last line.
The |hl-ModeMsg| highlight group determines the highlighting.
@@ -7609,7 +7792,7 @@ return {
{
abbreviation = 'stal',
cb = 'did_set_showtabline',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The value of this option specifies when the line with tab page labels
will be displayed:
@@ -7629,7 +7812,7 @@ return {
},
{
abbreviation = 'ss',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The minimal number of columns to scroll horizontally. Used only when
the 'wrap' option is off and the cursor is moved off of the screen.
@@ -7645,7 +7828,7 @@ return {
},
{
abbreviation = 'siso',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
The minimal number of screen columns to keep to the left and to the
right of the cursor if 'nowrap' is set. Setting this option to a
@@ -7677,7 +7860,31 @@ return {
{
abbreviation = 'scl',
cb = 'did_set_signcolumn',
- defaults = { if_true = 'auto' },
+ defaults = 'auto',
+ values = {
+ 'yes',
+ 'no',
+ 'auto',
+ 'auto:1',
+ 'auto:2',
+ 'auto:3',
+ 'auto:4',
+ 'auto:5',
+ 'auto:6',
+ 'auto:7',
+ 'auto:8',
+ 'auto:9',
+ 'yes:1',
+ 'yes:2',
+ 'yes:3',
+ 'yes:4',
+ 'yes:5',
+ 'yes:6',
+ 'yes:7',
+ 'yes:8',
+ 'yes:9',
+ 'number',
+ },
desc = [=[
When and how to draw the signcolumn. Valid values are:
"auto" only when there is a sign to display
@@ -7696,7 +7903,6 @@ return {
"number" display signs in the 'number' column. If the number
column is not present, then behaves like "auto".
]=],
- expand_cb = 'expand_set_signcolumn',
full_name = 'signcolumn',
redraw = { 'current_window' },
scope = { 'win' },
@@ -7705,7 +7911,7 @@ return {
},
{
abbreviation = 'scs',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Override the 'ignorecase' option if the search pattern contains upper
case characters. Only used when the search pattern is typed and
@@ -7722,7 +7928,7 @@ return {
},
{
abbreviation = 'si',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Do smart autoindenting when starting a new line. Works for C-like
programs, but can also be used for other languages. 'cindent' does
@@ -7752,7 +7958,7 @@ return {
},
{
abbreviation = 'sta',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, a <Tab> in front of a line inserts blanks according to
'shiftwidth'. 'tabstop' or 'softtabstop' is used in other places. A
@@ -7774,7 +7980,7 @@ return {
{
abbreviation = 'sms',
cb = 'did_set_smoothscroll',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Scrolling works with screen lines. When 'wrap' is set and the first
line in the window wraps part of it may not be visible, as if it is
@@ -7792,7 +7998,7 @@ return {
},
{
abbreviation = 'sts',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Number of spaces that a <Tab> counts for while performing editing
operations, like inserting a <Tab> or using <BS>. It "feels" like
@@ -7818,7 +8024,7 @@ return {
},
{
cb = 'did_set_spell',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on spell checking will be done. See |spell|.
The languages are specified with 'spelllang'.
@@ -7832,7 +8038,7 @@ return {
{
abbreviation = 'spc',
cb = 'did_set_spellcapcheck',
- defaults = { if_true = '[.?!]\\_[\\])\'"\\t ]\\+' },
+ defaults = '[.?!]\\_[\\])\'"\\t ]\\+',
desc = [=[
Pattern to locate the end of a sentence. The following word will be
checked to start with a capital letter. If not then it is highlighted
@@ -7854,7 +8060,7 @@ return {
{
abbreviation = 'spf',
cb = 'did_set_spellfile',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Name of the word list file where words are added for the |zg| and |zw|
@@ -7891,7 +8097,7 @@ return {
{
abbreviation = 'spl',
cb = 'did_set_spelllang',
- defaults = { if_true = 'en' },
+ defaults = 'en',
deny_duplicates = true,
desc = [=[
A comma-separated list of word list names. When the 'spell' option is
@@ -7943,7 +8149,9 @@ return {
{
abbreviation = 'spo',
cb = 'did_set_spelloptions',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'camel', 'noplainbuffer' },
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of options for spell checking:
@@ -7956,7 +8164,6 @@ return {
designated regions of the buffer are spellchecked in
this case.
]=],
- expand_cb = 'expand_set_spelloptions',
full_name = 'spelloptions',
list = 'onecomma',
redraw = { 'current_buffer', 'highlight_only' },
@@ -7968,7 +8175,9 @@ return {
{
abbreviation = 'sps',
cb = 'did_set_spellsuggest',
- defaults = { if_true = 'best' },
+ defaults = 'best',
+ -- Keep this in sync with spell_check_sps().
+ values = { 'best', 'fast', 'double', 'expr:', 'file:', 'timeout:' },
deny_duplicates = true,
desc = [=[
Methods used for spelling suggestions. Both for the |z=| command and
@@ -8038,7 +8247,6 @@ return {
security reasons.
]=],
expand = true,
- expand_cb = 'expand_set_spellsuggest',
full_name = 'spellsuggest',
list = 'onecomma',
scope = { 'global' },
@@ -8049,7 +8257,7 @@ return {
},
{
abbreviation = 'sb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, splitting a window will put the new window below the current
one. |:split|
@@ -8062,8 +8270,8 @@ return {
},
{
abbreviation = 'spk',
- cb = 'did_set_splitkeep',
- defaults = { if_true = 'cursor' },
+ defaults = 'cursor',
+ values = { 'cursor', 'screen', 'topline' },
desc = [=[
The value of this option determines the scroll behavior when opening,
closing or resizing horizontal splits.
@@ -8078,7 +8286,6 @@ return {
with the previous cursor position. For "screen", the text cannot always
be kept on the same screen line when 'wrap' is enabled.
]=],
- expand_cb = 'expand_set_splitkeep',
full_name = 'splitkeep',
scope = { 'global' },
short_desc = N_('determines scroll behavior for split windows'),
@@ -8087,7 +8294,7 @@ return {
},
{
abbreviation = 'spr',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, splitting a window will put the new window right of the
current one. |:vsplit|
@@ -8100,7 +8307,7 @@ return {
},
{
abbreviation = 'sol',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When "on" the commands listed below move the cursor to the first
non-blank of the line. When off the cursor is kept in the same column
@@ -8124,7 +8331,7 @@ return {
{
abbreviation = 'stc',
cb = 'did_set_statuscolumn',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
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.
@@ -8189,7 +8396,7 @@ return {
{
abbreviation = 'stl',
cb = 'did_set_statusline',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option determines the content of the status line.
Also see |status-line|.
@@ -8200,6 +8407,7 @@ return {
All fields except the {item} are optional. A single percent sign can
be given as "%%".
+ *stl-%!*
When the option starts with "%!" then it is used as an expression,
evaluated and the result is used as the option value. Example: >vim
set statusline=%!MyStatusLine()
@@ -8413,7 +8621,7 @@ return {
},
{
abbreviation = 'su',
- defaults = { if_true = '.bak,~,.o,.h,.info,.swp,.obj' },
+ defaults = '.bak,~,.o,.h,.info,.swp,.obj',
deny_duplicates = true,
desc = [=[
Files with these suffixes get a lower priority when multiple files
@@ -8436,7 +8644,7 @@ return {
},
{
abbreviation = 'sua',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Comma-separated list of suffixes, which are used when searching for a
@@ -8454,7 +8662,7 @@ return {
{
abbreviation = 'swf',
cb = 'did_set_swapfile',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Use a swapfile for the buffer. This option can be reset when a
swapfile is not wanted for a specific buffer. For example, with
@@ -8484,8 +8692,9 @@ return {
},
{
abbreviation = 'swb',
- cb = 'did_set_switchbuf',
- defaults = { if_true = 'uselast' },
+ defaults = 'uselast',
+ values = { 'useopen', 'usetab', 'split', 'newtab', 'vsplit', 'uselast' },
+ flags = true,
deny_duplicates = true,
desc = [=[
This option controls the behavior when switching between buffers.
@@ -8516,17 +8725,17 @@ return {
If a window has 'winfixbuf' enabled, 'switchbuf' is currently not
applied to the split window.
]=],
- expand_cb = 'expand_set_switchbuf',
full_name = 'switchbuf',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('sets behavior when switching to another buffer'),
type = 'string',
varname = 'p_swb',
+ flags_varname = 'swb_flags',
},
{
abbreviation = 'smc',
- defaults = { if_true = 3000 },
+ defaults = 3000,
desc = [=[
Maximum column in which to search for syntax items. In long lines the
text after this column is not highlighted and following lines may not
@@ -8545,7 +8754,7 @@ return {
{
abbreviation = 'syn',
cb = 'did_set_filetype_or_syntax',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is set, the syntax with this name is loaded, unless
syntax highlighting has been switched off with ":syntax off".
@@ -8581,8 +8790,9 @@ return {
},
{
abbreviation = 'tcl',
- cb = 'did_set_tabclose',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'left', 'uselast' },
+ flags = true,
deny_duplicates = true,
desc = [=[
This option controls the behavior when closing tab pages (e.g., using
@@ -8595,18 +8805,18 @@ return {
possible. This option takes precedence over the
others.
]=],
- expand_cb = 'expand_set_tabclose',
full_name = 'tabclose',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('which tab page to focus when closing a tab'),
type = 'string',
varname = 'p_tcl',
+ flags_varname = 'tcl_flags',
},
{
abbreviation = 'tal',
cb = 'did_set_tabline',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option determines the content of the tab pages
line at the top of the Vim window. When empty Vim will use a default
@@ -8639,7 +8849,7 @@ return {
},
{
abbreviation = 'tpm',
- defaults = { if_true = 50 },
+ defaults = 50,
desc = [=[
Maximum number of tab pages to be opened by the |-p| command line
argument or the ":tab all" command. |tabpage|
@@ -8653,7 +8863,7 @@ return {
{
abbreviation = 'ts',
cb = 'did_set_shiftwidth_tabstop',
- defaults = { if_true = 8 },
+ defaults = 8,
desc = [=[
Number of spaces that a <Tab> in the file counts for. Also see
the |:retab| command, and the 'softtabstop' option.
@@ -8704,7 +8914,7 @@ return {
},
{
abbreviation = 'tbs',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When searching for a tag (e.g., for the |:ta| command), Vim can either
use a binary search or a linear search in a tags file. Binary
@@ -8764,7 +8974,9 @@ return {
{
abbreviation = 'tc',
cb = 'did_set_tagcase',
- defaults = { if_true = 'followic' },
+ defaults = 'followic',
+ values = { 'followic', 'ignore', 'match', 'followscs', 'smart' },
+ flags = true,
desc = [=[
This option specifies how case is handled when searching the tags
file:
@@ -8774,17 +8986,17 @@ return {
match Match case
smart Ignore case unless an upper case letter is used
]=],
- expand_cb = 'expand_set_tagcase',
full_name = 'tagcase',
scope = { 'global', 'buf' },
short_desc = N_('how to handle case when searching in tags files'),
type = 'string',
varname = 'p_tc',
+ flags_varname = 'tc_flags',
},
{
abbreviation = 'tfu',
cb = 'did_set_tagfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
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
@@ -8805,7 +9017,7 @@ return {
},
{
abbreviation = 'tl',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
If non-zero, tags are significant up to this number of characters.
]=],
@@ -8817,7 +9029,7 @@ return {
},
{
abbreviation = 'tr',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
If on and using a tags file in another directory, file names in that
tags file are relative to the directory where the tags file is.
@@ -8830,7 +9042,7 @@ return {
},
{
abbreviation = 'tag',
- defaults = { if_true = './tags;,tags' },
+ defaults = './tags;,tags',
deny_duplicates = true,
desc = [=[
Filenames for the tag command, separated by spaces or commas. To
@@ -8862,7 +9074,7 @@ return {
},
{
abbreviation = 'tgst',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, the |tagstack| is used normally. When off, a ":tag" or
":tselect" command with an argument will not push the tag onto the
@@ -8880,7 +9092,7 @@ return {
},
{
abbreviation = 'tbidi',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
The terminal is in charge of Bi-directionality of text (as specified
by Unicode). The terminal is also expected to do the required shaping
@@ -8899,7 +9111,7 @@ return {
},
{
abbreviation = 'tenc',
- defaults = { if_true = '' },
+ defaults = '',
full_name = 'termencoding',
scope = { 'global' },
short_desc = N_('Terminal encoding'),
@@ -8908,7 +9120,7 @@ return {
},
{
abbreviation = 'tgc',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight|
attributes instead of "cterm" attributes. |guifg|
@@ -8927,8 +9139,9 @@ return {
},
{
abbreviation = 'tpf',
- cb = 'did_set_termpastefilter',
- defaults = { if_true = 'BS,HT,ESC,DEL' },
+ defaults = 'BS,HT,ESC,DEL',
+ values = { 'BS', 'HT', 'FF', 'ESC', 'DEL', 'C0', 'C1' },
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of options for specifying control characters
@@ -8950,15 +9163,15 @@ return {
C1 Control characters 0x80...0x9F
]=],
- expand_cb = 'expand_set_termpastefilter',
full_name = 'termpastefilter',
list = 'onecomma',
scope = { 'global' },
type = 'string',
varname = 'p_tpf',
+ flags_varname = 'tpf_flags',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
If the host terminal supports it, buffer all screen updates
made during a redraw cycle so that each screen is displayed in
@@ -8973,7 +9186,7 @@ return {
varname = 'p_termsync',
},
{
- defaults = { if_true = false },
+ defaults = false,
full_name = 'terse',
scope = { 'global' },
short_desc = N_('No description'),
@@ -8983,7 +9196,7 @@ return {
{
abbreviation = 'tw',
cb = 'did_set_textwidth',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Maximum width of text that is being inserted. A longer line will be
broken after white space to get this width. A zero value disables
@@ -9001,7 +9214,7 @@ return {
},
{
abbreviation = 'tsr',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of file names, separated by commas, that are used to lookup words
@@ -9031,7 +9244,7 @@ return {
{
abbreviation = 'tsrfu',
cb = 'did_set_thesaurusfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used for thesaurus completion
with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T| See |compl-thesaurusfunc|.
@@ -9051,7 +9264,7 @@ return {
},
{
abbreviation = 'top',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on: The tilde command "~" behaves like an operator.
]=],
@@ -9063,7 +9276,7 @@ return {
},
{
abbreviation = 'to',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
This option and 'timeoutlen' determine the behavior when part of a
mapped key sequence has been received. For example, if <c-f> is
@@ -9078,7 +9291,7 @@ return {
},
{
abbreviation = 'tm',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Time in milliseconds to wait for a mapped sequence to complete.
]=],
@@ -9090,7 +9303,7 @@ return {
},
{
cb = 'did_set_title_icon',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, the title of the window will be set to the value of
'titlestring' (if it is not empty), or to:
@@ -9112,7 +9325,7 @@ return {
},
{
cb = 'did_set_titlelen',
- defaults = { if_true = 85 },
+ defaults = 85,
desc = [=[
Gives the percentage of 'columns' to use for the length of the window
title. When the title is longer, only the end of the path name is
@@ -9131,7 +9344,7 @@ return {
varname = 'p_titlelen',
},
{
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
If not empty, this option will be used to set the window title when
exiting. Only if 'title' is enabled.
@@ -9148,7 +9361,7 @@ return {
},
{
cb = 'did_set_titlestring',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is not empty, it will be used for the title of the
window. This happens only when the 'title' option is on.
@@ -9157,6 +9370,10 @@ return {
expanded according to the rules used for 'statusline'. If it contains
an invalid '%' format, the value is used as-is and no error or warning
will be given when the value is set.
+
+ The default behaviour is equivalent to: >vim
+ set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim
+ <
This option cannot be set in a modeline when 'modelineexpr' is off.
Example: >vim
@@ -9180,7 +9397,7 @@ return {
varname = 'p_titlestring',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
This option and 'ttimeoutlen' determine the behavior when part of a
key code sequence has been received by the |TUI|.
@@ -9203,7 +9420,7 @@ return {
},
{
abbreviation = 'ttm',
- defaults = { if_true = 50 },
+ defaults = 50,
desc = [=[
Time in milliseconds to wait for a key code sequence to complete. Also
used for CTRL-\ CTRL-N and CTRL-\ CTRL-G when part of a command has
@@ -9218,7 +9435,7 @@ return {
},
{
abbreviation = 'tf',
- defaults = { if_true = true },
+ defaults = true,
full_name = 'ttyfast',
no_mkrc = true,
scope = { 'global' },
@@ -9228,7 +9445,7 @@ return {
},
{
abbreviation = 'udir',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of directory names for undo files, separated with commas.
@@ -9265,7 +9482,7 @@ return {
{
abbreviation = 'udf',
cb = 'did_set_undofile',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, Vim automatically saves undo history to an undo file when
writing a buffer to a file, and restores undo history from the same
@@ -9285,7 +9502,7 @@ return {
{
abbreviation = 'ul',
cb = 'did_set_undolevels',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Maximum number of changes that can be undone. Since undo information
is kept in memory, higher numbers will cause more memory to be used.
@@ -9313,7 +9530,7 @@ return {
},
{
abbreviation = 'ur',
- defaults = { if_true = 10000 },
+ defaults = 10000,
desc = [=[
Save the whole buffer for undo when reloading it. This applies to the
":e!" command and reloading for when the buffer changed outside of
@@ -9336,7 +9553,7 @@ return {
{
abbreviation = 'uc',
cb = 'did_set_updatecount',
- defaults = { if_true = 200 },
+ defaults = 200,
desc = [=[
After typing this many characters the swap file will be written to
disk. When zero, no swap file will be created at all (see chapter on
@@ -9358,7 +9575,7 @@ return {
},
{
abbreviation = 'ut',
- defaults = { if_true = 4000 },
+ defaults = 4000,
desc = [=[
If this many milliseconds nothing is typed the swap file will be
written to disk (see |crash-recovery|). Also used for the
@@ -9432,7 +9649,7 @@ return {
{
abbreviation = 'vsts',
cb = 'did_set_varsofttabstop',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
A list of the number of spaces that a <Tab> counts for while editing,
such as inserting a <Tab> or using <BS>. It "feels" like variable-
@@ -9460,7 +9677,7 @@ return {
{
abbreviation = 'vts',
cb = 'did_set_vartabstop',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
A list of the number of spaces that a <Tab> in the file counts for,
separated by commas. Each value corresponds to one tab, with the
@@ -9482,7 +9699,7 @@ return {
},
{
abbreviation = 'vbs',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Sets the verbosity level. Also set by |-V| and |:verbose|.
@@ -9521,7 +9738,7 @@ return {
{
abbreviation = 'vfile',
cb = 'did_set_verbosefile',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When not empty all messages are written in a file with this name.
When the file exists messages are appended.
@@ -9543,7 +9760,7 @@ return {
},
{
abbreviation = 'vdir',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Name of the directory where to store files for |:mkview|.
This option cannot be set from a |modeline| or in the |sandbox|, for
@@ -9559,8 +9776,9 @@ return {
},
{
abbreviation = 'vop',
- cb = 'did_set_viewoptions',
- defaults = { if_true = 'folds,cursor,curdir' },
+ cb = 'did_set_str_generic',
+ defaults = 'folds,cursor,curdir',
+ flags = true,
deny_duplicates = true,
desc = [=[
Changes the effect of the |:mkview| command. It is a comma-separated
@@ -9576,18 +9794,28 @@ return {
slash |deprecated| Always enabled. Uses "/" in filenames.
unix |deprecated| Always enabled. Uses "\n" line endings.
]=],
- expand_cb = 'expand_set_sessionoptions',
+ expand_cb = 'expand_set_str_generic',
full_name = 'viewoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('specifies what to save for :mkview'),
type = 'string',
varname = 'p_vop',
+ flags_varname = 'vop_flags',
},
{
abbreviation = 've',
cb = 'did_set_virtualedit',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'block', 'insert', 'all', 'onemore', 'none', 'NONE' },
+ flags = {
+ Block = 5,
+ Insert = 6,
+ All = 4,
+ Onemore = 8,
+ None = 16,
+ NoneU = 32,
+ },
deny_duplicates = true,
desc = [=[
A comma-separated list of these words:
@@ -9617,7 +9845,6 @@ return {
not get a warning for it.
When combined with other words, "none" is ignored.
]=],
- expand_cb = 'expand_set_virtualedit',
full_name = 'virtualedit',
list = 'onecomma',
redraw = { 'curswant' },
@@ -9625,10 +9852,11 @@ return {
short_desc = N_('when to use virtual editing'),
type = 'string',
varname = 'p_ve',
+ flags_varname = 've_flags',
},
{
abbreviation = 'vb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Use visual bell instead of beeping. Also see 'errorbells'.
]=],
@@ -9639,7 +9867,7 @@ return {
varname = 'p_vb',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Give a warning message when a shell command is used while the buffer
has been changed.
@@ -9653,7 +9881,7 @@ return {
{
abbreviation = 'ww',
cb = 'did_set_whichwrap',
- defaults = { if_true = 'b,s' },
+ defaults = 'b,s',
desc = [=[
Allow specified keys that move the cursor left/right to move to the
previous/next line when the cursor is on the first/last character in
@@ -9724,7 +9952,7 @@ return {
{
abbreviation = 'wcm',
cb = 'did_set_wildchar',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
'wildcharm' works exactly like 'wildchar', except that it is
recognized when used inside a macro. You can find "spare" command-line
@@ -9743,7 +9971,7 @@ return {
},
{
abbreviation = 'wig',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
A list of file patterns. A file that matches with one of these
@@ -9767,7 +9995,7 @@ return {
},
{
abbreviation = 'wic',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When set case is ignored when completing file names and directories.
Has no effect when 'fileignorecase' is set.
@@ -9782,7 +10010,7 @@ return {
},
{
abbreviation = 'wmnu',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When 'wildmenu' is on, command-line completion operates in an enhanced
mode. On pressing 'wildchar' (usually <Tab>) to invoke completion,
@@ -9831,7 +10059,10 @@ return {
{
abbreviation = 'wim',
cb = 'did_set_wildmode',
- defaults = { if_true = 'full' },
+ defaults = 'full',
+ -- Keep this in sync with check_opt_wim().
+ values = { 'full', 'longest', 'list', 'lastused' },
+ flags = true,
deny_duplicates = false,
desc = [=[
Completion mode that is used for the character specified with
@@ -9878,7 +10109,6 @@ return {
< Complete longest common string, then list alternatives.
More info here: |cmdline-completion|.
]=],
- expand_cb = 'expand_set_wildmode',
full_name = 'wildmode',
list = 'onecommacolon',
scope = { 'global' },
@@ -9888,8 +10118,9 @@ return {
},
{
abbreviation = 'wop',
- cb = 'did_set_wildoptions',
- defaults = { if_true = 'pum,tagfile' },
+ defaults = 'pum,tagfile',
+ values = { 'fuzzy', 'tagfile', 'pum' },
+ flags = true,
deny_duplicates = true,
desc = [=[
A list of words that change how |cmdline-completion| is done.
@@ -9910,18 +10141,18 @@ return {
d #define
f function
]=],
- expand_cb = 'expand_set_wildoptions',
full_name = 'wildoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('specifies how command line completion is done'),
type = 'string',
varname = 'p_wop',
+ flags_varname = 'wop_flags',
},
{
abbreviation = 'wak',
- cb = 'did_set_winaltkeys',
- defaults = { if_true = 'menu' },
+ defaults = 'menu',
+ values = { 'yes', 'menu', 'no' },
desc = [=[
only used in Win32
Some GUI versions allow the access to menu entries by using the ALT
@@ -9939,7 +10170,6 @@ return {
key is never used for the menu.
This option is not used for <F10>; on Win32.
]=],
- expand_cb = 'expand_set_winaltkeys',
full_name = 'winaltkeys',
scope = { 'global' },
short_desc = N_('when the windows system handles ALT keys'),
@@ -9949,7 +10179,7 @@ return {
{
abbreviation = 'wbr',
cb = 'did_set_winbar',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option enables the window bar and determines its
contents. The window bar is a bar that's shown at the top of every
@@ -9976,7 +10206,7 @@ return {
{
abbreviation = 'winbl',
cb = 'did_set_winblend',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Enables pseudo-transparency for a floating window. Valid values are in
the range of 0 for fully opaque window (disabled) to 100 for fully
@@ -10016,7 +10246,7 @@ return {
},
{
abbreviation = 'wfb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If enabled, the window and the buffer it is displaying are paired.
For example, attempting to change the buffer with |:edit| will fail.
@@ -10031,7 +10261,7 @@ return {
},
{
abbreviation = 'wfh',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Keep the window height when windows are opened or closed and
'equalalways' is set. Also for |CTRL-W_=|. Set by default for the
@@ -10046,7 +10276,7 @@ return {
},
{
abbreviation = 'wfw',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Keep the window width when windows are opened or closed and
'equalalways' is set. Also for |CTRL-W_=|.
@@ -10061,7 +10291,7 @@ return {
{
abbreviation = 'wh',
cb = 'did_set_winheight',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Minimal number of lines for the current window. This is not a hard
minimum, Vim will use fewer lines if there is not enough room. If the
@@ -10090,7 +10320,7 @@ return {
{
abbreviation = 'winhl',
cb = 'did_set_winhighlight',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Window-local highlights. Comma-delimited list of highlight
@@ -10122,7 +10352,7 @@ return {
{
abbreviation = 'wmh',
cb = 'did_set_winminheight',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The minimal height of a window, when it's not the current window.
This is a hard minimum, windows will never become smaller.
@@ -10143,7 +10373,7 @@ return {
{
abbreviation = 'wmw',
cb = 'did_set_winminwidth',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The minimal width of a window, when it's not the current window.
This is a hard minimum, windows will never become smaller.
@@ -10165,7 +10395,7 @@ return {
{
abbreviation = 'wiw',
cb = 'did_set_winwidth',
- defaults = { if_true = 20 },
+ defaults = 20,
desc = [=[
Minimal number of columns for the current window. This is not a hard
minimum, Vim will use fewer columns if there is not enough room. If
@@ -10186,7 +10416,7 @@ return {
},
{
cb = 'did_set_wrap',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
This option changes how text is displayed. It doesn't change the text
in the buffer, see 'textwidth' for that.
@@ -10212,7 +10442,7 @@ return {
},
{
abbreviation = 'wm',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Number of characters from the right window border where wrapping
starts. When typing text beyond this limit, an <EOL> will be inserted
@@ -10230,7 +10460,7 @@ return {
},
{
abbreviation = 'ws',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Searches wrap around the end of the file. Also applies to |]s| and
|[s|, searching for spelling mistakes.
@@ -10243,7 +10473,7 @@ return {
varname = 'p_ws',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Allows writing files. When not set, writing a file is not allowed.
Can be used for a view-only mode, where modifications to the text are
@@ -10259,7 +10489,7 @@ return {
},
{
abbreviation = 'wa',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Allows writing to any file with no need for "!" override.
]=],
@@ -10271,7 +10501,7 @@ return {
},
{
abbreviation = 'wb',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Make a backup before overwriting a file. The backup is removed after
the file was successfully written, unless the 'backup' option is
@@ -10294,7 +10524,7 @@ return {
},
{
abbreviation = 'wd',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Only takes effect together with 'redrawdebug'.
The number of milliseconds to wait after each line or each flush
@@ -10307,3 +10537,29 @@ return {
},
},
}
+
+--- @param o vim.option_meta
+local function preprocess(o)
+ if o.values then
+ o.cb = o.cb or 'did_set_str_generic'
+ o.expand_cb = o.expand_cb or 'expand_set_str_generic'
+ end
+
+ if type(o.alias) == 'string' then
+ o.alias = {
+ o.alias --[[@as string]],
+ }
+ end
+
+ if type(o.defaults) ~= 'table' then
+ o.defaults = {
+ if_true = o.defaults --[[@as string|boolean|number ]],
+ }
+ end
+end
+
+for _, o in ipairs(options.options) do
+ preprocess(o)
+end
+
+return options
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index bfb26a0be6..645bb23638 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
@@ -15,7 +16,6 @@
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_getln.h"
@@ -45,6 +45,7 @@
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
+#include "nvim/terminal.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -68,84 +69,6 @@ static const char e_wrong_number_of_characters_for_field_str[]
static const char e_wrong_character_width_for_field_str[]
= N_("E1512: Wrong character width for field \"%s\"");
-static char *(p_ambw_values[]) = { "single", "double", NULL };
-static char *(p_bg_values[]) = { "light", "dark", NULL };
-static char *(p_bkc_values[]) = { "yes", "auto", "no", "breaksymlink", "breakhardlink", NULL };
-static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete", "copy", "ctrlg", "error",
- "esc", "ex", "hangul", "insertmode", "lang", "mess", "showmatch",
- "operator", "register", "shell", "spell", "term", "wildmode",
- NULL };
-// Note: Keep this in sync with briopt_check()
-static char *(p_briopt_values[]) = { "shift:", "min:", "sbr", "list:", "column:", NULL };
-// Note: Keep this in sync with diffopt_changed()
-static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase",
- "iwhite", "iwhiteall", "iwhiteeol", "horizontal", "vertical",
- "closeoff", "hiddenoff", "foldcolumn:", "followwrap", "internal",
- "indent-heuristic", "linematch:", "algorithm:", NULL };
-static char *(p_dip_algorithm_values[]) = { "myers", "minimal", "patience", "histogram", NULL };
-static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", "blank", NULL };
-static char *(p_ff_values[]) = { "unix", "dos", "mac", NULL };
-static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
-static char *(p_cmp_values[]) = { "internal", "keepascii", NULL };
-// Note: Keep this in sync with fill_culopt_flags()
-static char *(p_culopt_values[]) = { "line", "screenline", "number", "both", NULL };
-static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep", NULL };
-static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent", "quickfix", "search",
- "tag", "insert", "undo", "jump", NULL };
-// Note: Keep this in sync with spell_check_sps()
-static char *(p_sps_values[]) = { "best", "fast", "double", "expr:", "file:", "timeout:", NULL };
-/// Also used for 'viewoptions'! Keep in sync with SSOP_ flags.
-static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "localoptions",
- "options", "help", "blank", "globals", "slash", "unix", "sesdir",
- "curdir", "folds", "cursor", "tabpages", "terminal", "skiprtp",
- NULL };
-// Keep in sync with SWB_ flags in option_vars.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 };
-// Keep in sync with TCL_ flags in option_vars.h
-static char *(p_tcl_values[]) = { "left", "uselast", NULL };
-static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
-// Note: Keep this in sync with check_opt_wim()
-static char *(p_wim_values[]) = { "full", "longest", "list", "lastused", NULL };
-static char *(p_wop_values[]) = { "fuzzy", "tagfile", "pum", NULL };
-static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
-static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL };
-static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
-static char *(p_slm_values[]) = { "mouse", "key", "cmd", NULL };
-static char *(p_km_values[]) = { "startsel", "stopsel", NULL };
-static char *(p_scbopt_values[]) = { "ver", "hor", "jump", NULL };
-static char *(p_debug_values[]) = { "msg", "throw", "beep", NULL };
-static char *(p_ead_values[]) = { "both", "ver", "hor", NULL };
-static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix", "help", "acwrite",
- "terminal", "prompt", NULL };
-static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", "wipe", NULL };
-static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL };
-static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
- "syntax", "diff", NULL };
-static char *(p_fcl_values[]) = { "all", NULL };
-static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "popup",
- "noinsert", "noselect", "fuzzy", NULL };
-#ifdef BACKSLASH_IN_FILENAME
-static char *(p_csl_values[]) = { "slash", "backslash", NULL };
-#endif
-
-static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto:3", "auto:4",
- "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "yes:1",
- "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8",
- "yes:9", "number", NULL };
-static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5",
- "auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4",
- "5", "6", "7", "8", "9", NULL };
-static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL };
-static char *(p_icm_values[]) = { "nosplit", "split", NULL };
-static char *(p_jop_values[]) = { "stack", "view", "clean", NULL };
-static char *(p_tpf_values[]) = { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
-static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", "line",
- "flush", NULL };
-static char *(p_sloc_values[]) = { "last", "statusline", "tabline", NULL };
-
/// All possible flags for 'shm'.
/// the literal chars before 0 are removed flags. these are safely ignored
static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES,
@@ -158,23 +81,23 @@ static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES,
/// option values.
void didset_string_options(void)
{
- opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
- opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true);
- opt_strings_flags(p_bo, p_bo_values, &bo_flags, true);
- opt_strings_flags(p_cot, p_cot_values, &cot_flags, true);
- opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true);
- opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
- opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
- opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
- opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
- opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
- opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
- opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
- opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
- opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
- opt_strings_flags(p_tcl, p_tcl_values, &tcl_flags, true);
- opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
- opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
+ check_str_opt(kOptCasemap, NULL);
+ check_str_opt(kOptBackupcopy, NULL);
+ check_str_opt(kOptBelloff, NULL);
+ check_str_opt(kOptCompleteopt, NULL);
+ check_str_opt(kOptSessionoptions, NULL);
+ check_str_opt(kOptViewoptions, NULL);
+ check_str_opt(kOptFoldopen, NULL);
+ check_str_opt(kOptDisplay, NULL);
+ check_str_opt(kOptJumpoptions, NULL);
+ check_str_opt(kOptRedrawdebug, NULL);
+ check_str_opt(kOptTagcase, NULL);
+ check_str_opt(kOptTermpastefilter, NULL);
+ check_str_opt(kOptVirtualedit, NULL);
+ check_str_opt(kOptSwitchbuf, NULL);
+ check_str_opt(kOptTabclose, NULL);
+ check_str_opt(kOptWildoptions, NULL);
+ check_str_opt(kOptClipboard, NULL);
}
char *illegal_char(char *errbuf, size_t errbuflen, int c)
@@ -300,7 +223,7 @@ int check_signcolumn(char *scl, win_T *wp)
return FAIL;
}
- if (check_opt_strings(val, p_scl_values, false) == OK) {
+ if (opt_strings_flags(val, opt_scl_values, NULL, false) == OK) {
if (wp == NULL) {
return OK;
}
@@ -428,7 +351,7 @@ bool check_illegal_path_names(char *val, uint32_t flags)
/// An option that accepts a list of flags is changed.
/// e.g. 'viewoptions', 'switchbuf', 'casemap', etc.
-static const char *did_set_opt_flags(char *val, char **values, unsigned *flagp, bool list)
+static const char *did_set_opt_flags(char *val, const char **values, unsigned *flagp, bool list)
{
if (opt_strings_flags(val, values, flagp, list) != OK) {
return e_invarg;
@@ -436,11 +359,40 @@ static const char *did_set_opt_flags(char *val, char **values, unsigned *flagp,
return NULL;
}
-/// An option that accepts a list of string values is changed.
-/// e.g. 'nrformats', 'scrollopt', 'wildoptions', etc.
-static const char *did_set_opt_strings(char *val, char **values, bool list)
+static const char **opt_values(OptIndex idx, size_t *values_len)
+{
+ OptIndex idx1 = idx == kOptViewoptions ? kOptSessionoptions
+ : idx == kOptFileformats ? kOptFileformat
+ : idx;
+
+ vimoption_T *opt = get_option(idx1);
+ if (values_len != NULL) {
+ *values_len = opt->values_len;
+ }
+ return opt->values;
+}
+
+static int check_str_opt(OptIndex idx, char **varp)
{
- return did_set_opt_flags(val, values, NULL, list);
+ vimoption_T *opt = get_option(idx);
+ if (varp == NULL) {
+ varp = opt->var;
+ }
+ bool list = opt->flags & (kOptFlagComma | kOptFlagOneComma);
+ const char **values = opt_values(idx, NULL);
+ return opt_strings_flags(*varp, values, opt->flags_var, list);
+}
+
+int expand_set_str_generic(optexpand_T *args, int *numMatches, char ***matches)
+{
+ size_t values_len;
+ const char **values = opt_values(args->oe_idx, &values_len);
+ return expand_set_opt_string(args, values, values_len, numMatches, matches);
+}
+
+const char *did_set_str_generic(optset_T *args)
+{
+ return check_str_opt(args->os_idx, args->os_varp) != OK ? e_invarg : NULL;
}
/// An option which is a list of flags is set. Valid values are in "flags".
@@ -456,7 +408,7 @@ static const char *did_set_option_listflag(char *val, char *flags, char *errbuf,
}
/// Expand an option that accepts a list of string values.
-static int expand_set_opt_string(optexpand_T *args, char **values, size_t numValues,
+static int expand_set_opt_string(optexpand_T *args, const char **values, size_t numValues,
int *numMatches, char ***matches)
{
regmatch_T *regmatch = args->oe_regmatch;
@@ -473,8 +425,10 @@ static int expand_set_opt_string(optexpand_T *args, char **values, size_t numVal
(*matches)[count++] = xstrdup(option_val);
}
- for (char **val = values; *val != NULL; val++) {
- if (include_orig_val && *option_val != NUL) {
+ for (const char **val = values; *val != NULL; val++) {
+ if (**val == NUL) {
+ continue; // Ignore empty
+ } else if (include_orig_val && *option_val != NUL) {
if (strcmp(*val, option_val) == 0) {
continue;
}
@@ -566,28 +520,30 @@ static int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatch
}
/// The 'ambiwidth' option is changed.
-const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_ambiwidth(optset_T *args)
{
- if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
return check_chars_options();
}
-int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char ***matches)
+/// The 'emoji' option is changed.
+const char *did_set_emoji(optset_T *args)
{
- return expand_set_opt_string(args,
- p_ambw_values,
- ARRAY_SIZE(p_ambw_values) - 1,
- numMatches,
- matches);
+ if (check_str_opt(kOptAmbiwidth, NULL) != OK) {
+ return e_invarg;
+ }
+ return check_chars_options();
}
/// The 'background' option is changed.
const char *did_set_background(optset_T *args)
{
- if (check_opt_strings(p_bg, p_bg_values, false) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
if (args->os_oldval.string.data[0] == *p_bg) {
@@ -609,16 +565,16 @@ const char *did_set_background(optset_T *args)
check_string_option(&p_bg);
init_highlight(false, false);
}
- return NULL;
-}
-int expand_set_background(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bg_values,
- ARRAY_SIZE(p_bg_values) - 1,
- numMatches,
- matches);
+ // Notify all terminal buffers that the background color changed so they can
+ // send a theme update notification
+ FOR_ALL_BUFFERS(buf) {
+ if (buf->terminal) {
+ terminal_notify_theme(buf->terminal, dark);
+ }
+ }
+
+ return NULL;
}
/// The 'backspace' option is changed.
@@ -628,19 +584,9 @@ const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED)
if (*p_bs != '2') {
return e_invarg;
}
- } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
- return e_invarg;
+ return NULL;
}
- return NULL;
-}
-
-int expand_set_backspace(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bs_values,
- ARRAY_SIZE(p_bs_values) - 1,
- numMatches,
- matches);
+ return did_set_str_generic(args);
}
/// The 'backupcopy' option is changed.
@@ -664,15 +610,15 @@ const char *did_set_backupcopy(optset_T *args)
// make the local value empty: use the global value
*flags = 0;
} else {
- if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
+ if (opt_strings_flags(bkc, opt_bkc_values, flags, true) != OK) {
return e_invarg;
}
- if (((*flags & BKC_AUTO) != 0)
- + ((*flags & BKC_YES) != 0)
- + ((*flags & BKC_NO) != 0) != 1) {
+ if (((*flags & kOptBkcFlagAuto) != 0)
+ + ((*flags & kOptBkcFlagYes) != 0)
+ + ((*flags & kOptBkcFlagNo) != 0) != 1) {
// Must have exactly one of "auto", "yes" and "no".
- opt_strings_flags(oldval, p_bkc_values, flags, true);
+ opt_strings_flags(oldval, opt_bkc_values, flags, true);
return e_invarg;
}
}
@@ -680,15 +626,6 @@ const char *did_set_backupcopy(optset_T *args)
return NULL;
}
-int expand_set_backupcopy(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bkc_values,
- ARRAY_SIZE(p_bkc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'backupext' or the 'patchmode' option is changed.
const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -700,21 +637,6 @@ const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-/// The 'belloff' option is changed.
-const char *did_set_belloff(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true);
-}
-
-int expand_set_belloff(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bo_values,
- ARRAY_SIZE(p_bo_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'breakat' option is changed.
const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -749,29 +671,11 @@ const char *did_set_breakindentopt(optset_T *args)
return NULL;
}
-int expand_set_breakindentopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_briopt_values,
- ARRAY_SIZE(p_briopt_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'bufhidden' option is changed.
const char *did_set_bufhidden(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false);
-}
-
-int expand_set_bufhidden(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bufhidden_values,
- ARRAY_SIZE(p_bufhidden_values) - 1,
- numMatches,
- matches);
+ return did_set_opt_flags(buf->b_p_bh, opt_bh_values, NULL, false);
}
/// The 'buftype' option is changed.
@@ -782,7 +686,7 @@ const char *did_set_buftype(optset_T *args)
// When 'buftype' is set, check for valid value.
if ((buf->terminal && buf->b_p_bt[0] != 't')
|| (!buf->terminal && buf->b_p_bt[0] == 't')
- || check_opt_strings(buf->b_p_bt, p_buftype_values, false) != OK) {
+ || opt_strings_flags(buf->b_p_bt, opt_bt_values, NULL, false) != OK) {
return e_invarg;
}
if (win->w_status_height || global_stl_height()) {
@@ -794,30 +698,6 @@ const char *did_set_buftype(optset_T *args)
return NULL;
}
-int expand_set_buftype(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_buftype_values,
- ARRAY_SIZE(p_buftype_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'casemap' option is changed.
-const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true);
-}
-
-int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_cmp_values,
- ARRAY_SIZE(p_cmp_values) - 1,
- numMatches,
- matches);
-}
-
/// The global 'listchars' or 'fillchars' option is changed.
static const char *did_set_global_chars_option(win_T *win, char *val, CharsOption what,
int opt_flags, char *errbuf, size_t errbuflen)
@@ -901,21 +781,6 @@ const char *did_set_cinoptions(optset_T *args)
return NULL;
}
-/// The 'clipboard' option is changed.
-const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true);
-}
-
-int expand_set_clipboard(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_cb_values,
- ARRAY_SIZE(p_cb_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'colorcolumn' option is changed.
const char *did_set_colorcolumn(optset_T *args)
{
@@ -1007,18 +872,6 @@ const char *did_set_complete(optset_T *args)
return NULL;
}
-int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_cpt_values[]) = {
- ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", "f", NULL
- };
- return expand_set_opt_string(args,
- p_cpt_values,
- ARRAY_SIZE(p_cpt_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'completeitemalign' option is changed.
const char *did_set_completeitemalign(optset_T *args)
{
@@ -1079,46 +932,28 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED)
buf->b_cot_flags = 0;
}
- if (check_opt_strings(cot, p_cot_values, true) != OK) {
+ if (opt_strings_flags(cot, opt_cot_values, NULL, true) != OK) {
return e_invarg;
}
- if (opt_strings_flags(cot, p_cot_values, flags, true) != OK) {
+ if (opt_strings_flags(cot, opt_cot_values, flags, true) != OK) {
return e_invarg;
}
return NULL;
}
-int expand_set_completeopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_cot_values,
- ARRAY_SIZE(p_cot_values) - 1,
- numMatches,
- matches);
-}
-
#ifdef BACKSLASH_IN_FILENAME
/// The 'completeslash' option is changed.
const char *did_set_completeslash(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- if (check_opt_strings(p_csl, p_csl_values, false) != OK
- || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) {
+ if (opt_strings_flags(p_csl, opt_csl_values, NULL, false) != OK
+ || opt_strings_flags(buf->b_p_csl, opt_csl_values, NULL, false) != OK) {
return e_invarg;
}
return NULL;
}
-
-int expand_set_completeslash(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_csl_values,
- ARRAY_SIZE(p_csl_values) - 1,
- numMatches,
- matches);
-}
#endif
/// The 'concealcursor' option is changed.
@@ -1161,37 +996,10 @@ const char *did_set_cursorlineopt(optset_T *args)
return NULL;
}
-int expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_culopt_values,
- ARRAY_SIZE(p_culopt_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'debug' option is changed.
-const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_debug, p_debug_values, false);
-}
-
-int expand_set_debug(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_debug_values,
- ARRAY_SIZE(p_debug_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'diffopt' option is changed.
const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED)
{
- if (diffopt_changed() == FAIL) {
- return e_invarg;
- }
- return NULL;
+ return diffopt_changed() == FAIL ? e_invarg : NULL;
}
int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches)
@@ -1204,56 +1012,29 @@ int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches)
if (xp->xp_pattern - args->oe_set_arg >= (int)algo_len
&& strncmp(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0) {
return expand_set_opt_string(args,
- p_dip_algorithm_values,
- ARRAY_SIZE(p_dip_algorithm_values) - 1,
+ opt_dip_algorithm_values,
+ ARRAY_SIZE(opt_dip_algorithm_values) - 1,
numMatches,
matches);
}
return FAIL;
}
- return expand_set_opt_string(args,
- p_dip_values,
- ARRAY_SIZE(p_dip_values) - 1,
- numMatches,
- matches);
+ return expand_set_str_generic(args, numMatches, matches);
}
/// The 'display' option is changed.
-const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_display(optset_T *args)
{
- if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
init_chartab();
msg_grid_validate();
return NULL;
}
-int expand_set_display(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_dy_values,
- ARRAY_SIZE(p_dy_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'eadirection' option is changed.
-const char *did_set_eadirection(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_ead, p_ead_values, false);
-}
-
-int expand_set_eadirection(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ead_values,
- ARRAY_SIZE(p_ead_values) - 1,
- numMatches,
- matches);
-}
-
/// One of the 'encoding', 'fileencoding' or 'makeencoding'
/// options is changed.
const char *did_set_encoding(optset_T *args)
@@ -1329,14 +1110,17 @@ int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches)
const char *did_set_fileformat(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- char **varp = (char **)args->os_varp;
const char *oldval = args->os_oldval.string.data;
int opt_flags = args->os_flags;
if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) {
return e_modifiable;
- } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
- return e_invarg;
}
+
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
+ }
+
redraw_titles();
// update flag in swap file
ml_setflags(buf);
@@ -1348,30 +1132,15 @@ const char *did_set_fileformat(optset_T *args)
return NULL;
}
-int expand_set_fileformat(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ff_values,
- ARRAY_SIZE(p_ff_values) - 1,
- numMatches,
- matches);
-}
-
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// fileformat options.
char *get_fileformat_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
- if (idx >= (int)ARRAY_SIZE(p_ff_values)) {
+ if (idx >= (int)ARRAY_SIZE(opt_ff_values)) {
return NULL;
}
- return p_ff_values[idx];
-}
-
-/// The 'fileformats' option is changed.
-const char *did_set_fileformats(optset_T *args)
-{
- return did_set_opt_strings(p_ffs, p_ff_values, true);
+ return (char *)opt_ff_values[idx];
}
/// The 'filetype' or the 'syntax' option is changed.
@@ -1392,40 +1161,6 @@ const char *did_set_filetype_or_syntax(optset_T *args)
return NULL;
}
-/// The 'foldclose' option is changed.
-const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_fcl, p_fcl_values, true);
-}
-
-int expand_set_foldclose(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fcl_values,
- ARRAY_SIZE(p_fcl_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'foldcolumn' option is changed.
-const char *did_set_foldcolumn(optset_T *args)
-{
- char **varp = (char **)args->os_varp;
- if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
- return e_invarg;
- }
- return NULL;
-}
-
-int expand_set_foldcolumn(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fdc_values,
- ARRAY_SIZE(p_fdc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'foldexpr' option is changed.
const char *did_set_foldexpr(optset_T *args)
{
@@ -1472,11 +1207,11 @@ const char *did_set_foldmarker(optset_T *args)
/// The 'foldmethod' option is changed.
const char *did_set_foldmethod(optset_T *args)
{
- win_T *win = (win_T *)args->os_win;
- char **varp = (char **)args->os_varp;
- if (check_opt_strings(*varp, p_fdm_values, false) != OK || **varp == NUL) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
+ win_T *win = (win_T *)args->os_win;
foldUpdateAll(win);
if (foldmethodIsDiff(win)) {
newFoldLevel();
@@ -1484,30 +1219,6 @@ const char *did_set_foldmethod(optset_T *args)
return NULL;
}
-int expand_set_foldmethod(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fdm_values,
- ARRAY_SIZE(p_fdm_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'foldopen' option is changed.
-const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true);
-}
-
-int expand_set_foldopen(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fdo_values,
- ARRAY_SIZE(p_fdo_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'formatoptions' option is changed.
const char *did_set_formatoptions(optset_T *args)
{
@@ -1586,16 +1297,7 @@ const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED)
if (cmdpreview) {
return e_invarg;
}
- return did_set_opt_strings(p_icm, p_icm_values, false);
-}
-
-int expand_set_inccommand(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_icm_values,
- ARRAY_SIZE(p_icm_values) - 1,
- numMatches,
- matches);
+ return did_set_str_generic(args);
}
/// The 'iskeyword' option is changed.
@@ -1629,21 +1331,6 @@ const char *did_set_isopt(optset_T *args)
return NULL;
}
-/// The 'jumpoptions' option is changed.
-const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true);
-}
-
-int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_jop_values,
- ARRAY_SIZE(p_jop_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'keymap' option has changed.
const char *did_set_keymap(optset_T *args)
{
@@ -1699,23 +1386,15 @@ const char *did_set_keymap(optset_T *args)
/// The 'keymodel' option is changed.
const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED)
{
- if (check_opt_strings(p_km, p_km_values, true) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
km_stopsel = (vim_strchr(p_km, 'o') != NULL);
km_startsel = (vim_strchr(p_km, 'a') != NULL);
return NULL;
}
-int expand_set_keymodel(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_km_values,
- ARRAY_SIZE(p_km_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'lispoptions' option is changed.
const char *did_set_lispoptions(optset_T *args)
{
@@ -1727,16 +1406,6 @@ const char *did_set_lispoptions(optset_T *args)
return NULL;
}
-int expand_set_lispoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_lop_values[]) = { "expr:0", "expr:1", NULL };
- return expand_set_opt_string(args,
- p_lop_values,
- ARRAY_SIZE(p_lop_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'matchpairs' option is changed.
const char *did_set_matchpairs(optset_T *args)
{
@@ -1764,6 +1433,15 @@ const char *did_set_matchpairs(optset_T *args)
return NULL;
}
+/// Process the updated 'messagesopt' option value.
+const char *did_set_messagesopt(optset_T *args FUNC_ATTR_UNUSED)
+{
+ if (messagesopt_changed() == FAIL) {
+ return e_invarg;
+ }
+ return NULL;
+}
+
/// The 'mkspellmem' option is changed.
const char *did_set_mkspellmem(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1786,21 +1464,6 @@ int expand_set_mouse(optexpand_T *args, int *numMatches, char ***matches)
return expand_set_opt_listflag(args, MOUSE_ALL, numMatches, matches);
}
-/// The 'mousemodel' option is changed.
-const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_mousem, p_mousem_values, false);
-}
-
-int expand_set_mousemodel(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_mousem_values,
- ARRAY_SIZE(p_mousem_values) - 1,
- numMatches,
- matches);
-}
-
/// Handle setting 'mousescroll'.
/// @return error message, NULL if it's OK.
const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED)
@@ -1866,33 +1529,6 @@ const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_mousescroll(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_mousescroll_values[]) = { "hor:", "ver:", NULL };
- return expand_set_opt_string(args,
- p_mousescroll_values,
- ARRAY_SIZE(p_mousescroll_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'nrformats' option is changed.
-const char *did_set_nrformats(optset_T *args)
-{
- char **varp = (char **)args->os_varp;
-
- return did_set_opt_strings(*varp, p_nf_values, true);
-}
-
-int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_nf_values,
- ARRAY_SIZE(p_nf_values) - 1,
- numMatches,
- matches);
-}
-
/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
const char *did_set_optexpr(optset_T *args)
@@ -1909,61 +1545,18 @@ const char *did_set_optexpr(optset_T *args)
return NULL;
}
-/// The 'redrawdebug' option is changed.
-const char *did_set_redrawdebug(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_rdb, p_rdb_values, &rdb_flags, true);
-}
-
-/// The 'rightleftcmd' option is changed.
-const char *did_set_rightleftcmd(optset_T *args)
-{
- char **varp = (char **)args->os_varp;
-
- // Currently only "search" is a supported value.
- if (**varp != NUL && strcmp(*varp, "search") != 0) {
- return e_invarg;
- }
-
- return NULL;
-}
-
-int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_rlc_values[]) = { "search", NULL };
- return expand_set_opt_string(args,
- p_rlc_values,
- ARRAY_SIZE(p_rlc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'rulerformat' option is changed.
const char *did_set_rulerformat(optset_T *args)
{
return did_set_statustabline_rulerformat(args, true, false);
}
-/// The 'scrollopt' option is changed.
-const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_sbo, p_scbopt_values, true);
-}
-
-int expand_set_scrollopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_scbopt_values,
- ARRAY_SIZE(p_scbopt_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'selection' option is changed.
const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED)
{
- if (*p_sel == NUL || check_opt_strings(p_sel, p_sel_values, false) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
if (VIsual_active) {
// Visual selection may be drawn differently.
@@ -1972,54 +1565,22 @@ const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_selection(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_sel_values,
- ARRAY_SIZE(p_sel_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'selectmode' option is changed.
-const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_slm, p_slm_values, true);
-}
-
-int expand_set_selectmode(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_slm_values,
- ARRAY_SIZE(p_slm_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'sessionoptions' option is changed.
const char *did_set_sessionoptions(optset_T *args)
{
- if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
- if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
+ if ((ssop_flags & kOptSsopFlagCurdir) && (ssop_flags & kOptSsopFlagSesdir)) {
// Don't allow both "sesdir" and "curdir".
const char *oldval = args->os_oldval.string.data;
- opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
+ opt_strings_flags(oldval, opt_ssop_values, &ssop_flags, true);
return e_invarg;
}
return NULL;
}
-int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ssop_values,
- ARRAY_SIZE(p_ssop_values) - 1,
- numMatches,
- matches);
-}
-
const char *did_set_shada(optset_T *args)
{
char *errbuf = args->os_errbuf;
@@ -2099,7 +1660,7 @@ const char *did_set_showbreak(optset_T *args)
/// The 'showcmdloc' option is changed.
const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED)
{
- const char *errmsg = did_set_opt_strings(p_sloc, p_sloc_values, false);
+ const char *errmsg = did_set_str_generic(args);
if (errmsg == NULL) {
comp_col();
@@ -2108,15 +1669,6 @@ const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED)
return errmsg;
}
-int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_sloc_values,
- ARRAY_SIZE(p_sloc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'signcolumn' option is changed.
const char *did_set_signcolumn(optset_T *args)
{
@@ -2134,15 +1686,6 @@ const char *did_set_signcolumn(optset_T *args)
return NULL;
}
-int expand_set_signcolumn(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_scl_values,
- ARRAY_SIZE(p_scl_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'spellcapcheck' option is changed.
const char *did_set_spellcapcheck(optset_T *args)
{
@@ -2185,25 +1728,16 @@ const char *did_set_spelloptions(optset_T *args)
const char *val = args->os_newval.string.data;
if (!(opt_flags & OPT_LOCAL)
- && opt_strings_flags(val, p_spo_values, &spo_flags, true) != OK) {
+ && opt_strings_flags(val, opt_spo_values, &spo_flags, true) != OK) {
return e_invarg;
}
if (!(opt_flags & OPT_GLOBAL)
- && opt_strings_flags(val, p_spo_values, &win->w_s->b_p_spo_flags, true) != OK) {
+ && opt_strings_flags(val, opt_spo_values, &win->w_s->b_p_spo_flags, true) != OK) {
return e_invarg;
}
return NULL;
}
-int expand_set_spelloptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_spo_values,
- ARRAY_SIZE(p_spo_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'spellsuggest' option is changed.
const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -2213,30 +1747,6 @@ const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_sps_values,
- ARRAY_SIZE(p_sps_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'splitkeep' option is changed.
-const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_spk, p_spk_values, false);
-}
-
-int expand_set_splitkeep(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_spk_values,
- ARRAY_SIZE(p_spk_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'statuscolumn' option is changed.
const char *did_set_statuscolumn(optset_T *args)
{
@@ -2275,7 +1785,11 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf
if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
ru_wid = wid;
} else {
- errmsg = check_stl_option(p_ruf);
+ // Validate the flags in 'rulerformat' only if it doesn't point to
+ // a custom function ("%!" flag).
+ if ((*varp)[1] != '!') {
+ errmsg = check_stl_option(p_ruf);
+ }
}
} else if (rulerformat || s[0] != '%' || s[1] != '!') {
// check 'statusline', 'winbar', 'tabline' or 'statuscolumn'
@@ -2288,36 +1802,6 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf
return errmsg;
}
-/// The 'switchbuf' option is changed.
-const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true);
-}
-
-int expand_set_switchbuf(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_swb_values,
- ARRAY_SIZE(p_swb_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'tabclose' option is changed.
-const char *did_set_tabclose(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_tcl, p_tcl_values, &tcl_flags, true);
-}
-
-int expand_set_tabclose(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_tcl_values,
- ARRAY_SIZE(p_tcl_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'tabline' option is changed.
const char *did_set_tabline(optset_T *args)
{
@@ -2344,37 +1828,12 @@ const char *did_set_tagcase(optset_T *args)
if ((opt_flags & OPT_LOCAL) && *p == NUL) {
// make the local value empty: use the global value
*flags = 0;
- } else if (*p == NUL
- || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
+ } else if (opt_strings_flags(p, opt_tc_values, flags, false) != OK) {
return e_invarg;
}
return NULL;
}
-int expand_set_tagcase(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_tc_values,
- ARRAY_SIZE(p_tc_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'termpastefilter' option is changed.
-const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true);
-}
-
-int expand_set_termpastefilter(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_tpf_values,
- ARRAY_SIZE(p_tpf_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'titlestring' or the 'iconstring' option is changed.
static const char *did_set_titleiconstring(optset_T *args, int flagval)
{
@@ -2471,12 +1930,6 @@ const char *did_set_verbosefile(optset_T *args)
return NULL;
}
-/// The 'viewoptions' option is changed.
-const char *did_set_viewoptions(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_vop, p_ssop_values, &vop_flags, true);
-}
-
/// The 'virtualedit' option is changed.
const char *did_set_virtualedit(optset_T *args)
{
@@ -2494,7 +1947,7 @@ const char *did_set_virtualedit(optset_T *args)
// make the local value empty: use the global value
*flags = 0;
} else {
- if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
+ if (opt_strings_flags(ve, opt_ve_values, flags, true) != OK) {
return e_invarg;
} else if (strcmp(ve, args->os_oldval.string.data) != 0) {
// Recompute cursor position in case the new 've' setting
@@ -2506,15 +1959,6 @@ const char *did_set_virtualedit(optset_T *args)
return NULL;
}
-int expand_set_virtualedit(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ve_values,
- ARRAY_SIZE(p_ve_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'whichwrap' option is changed.
const char *did_set_whichwrap(optset_T *args)
{
@@ -2539,48 +1983,6 @@ const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_wildmode(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_wim_values,
- ARRAY_SIZE(p_wim_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'wildoptions' option is changed.
-const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true);
-}
-
-int expand_set_wildoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_wop_values,
- ARRAY_SIZE(p_wop_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'winaltkeys' option is changed.
-const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED)
-{
- if (*p_wak == NUL || check_opt_strings(p_wak, p_wak_values, false) != OK) {
- return e_invarg;
- }
- return NULL;
-}
-
-int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_wak_values,
- ARRAY_SIZE(p_wak_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'winbar' option is changed.
const char *did_set_winbar(optset_T *args)
{
@@ -2603,16 +2005,6 @@ int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches)
return expand_set_opt_generic(args, get_highlight_name, numMatches, matches);
}
-/// Check an option that can be a range of string values.
-///
-/// @param list when true: accept a list of values
-///
-/// @return OK for correct value, FAIL otherwise. Empty is always OK.
-static int check_opt_strings(char *val, char **values, int list)
-{
- return opt_strings_flags(val, values, NULL, list);
-}
-
/// Handle an option that can be a range of string values.
/// Set a flag in "*flagp" for each string present.
///
@@ -2621,11 +2013,14 @@ static int check_opt_strings(char *val, char **values, int list)
/// @param list when true: accept a list of values
///
/// @return OK for correct value, FAIL otherwise. Empty is always OK.
-static int opt_strings_flags(const char *val, char **values, unsigned *flagp, bool list)
+static int opt_strings_flags(const char *val, const char **values, unsigned *flagp, bool list)
{
unsigned new_flags = 0;
- while (*val) {
+ // If not list and val is empty, then force one iteration of the while loop
+ bool iter_one = (*val == NUL) && !list;
+
+ while (*val || iter_one) {
for (unsigned i = 0;; i++) {
if (values[i] == NULL) { // val not found in values[]
return FAIL;
@@ -2640,6 +2035,9 @@ static int opt_strings_flags(const char *val, char **values, unsigned *flagp, bo
break; // check next item in val list
}
}
+ if (iter_one) {
+ break;
+ }
}
if (flagp != NULL) {
*flagp = new_flags;
@@ -2651,7 +2049,7 @@ static int opt_strings_flags(const char *val, char **values, unsigned *flagp, bo
/// @return OK if "p" is a valid fileformat name, FAIL otherwise.
int check_ff_value(char *p)
{
- return check_opt_strings(p, p_ff_values, false);
+ return opt_strings_flags(p, opt_ff_values, NULL, false);
}
static const char e_conflicts_with_value_of_listchars[]
@@ -2689,49 +2087,54 @@ static schar_T get_encoded_char_adv(const char **p)
}
struct chars_tab {
- schar_T *cp; ///< char value
- const char *name; ///< char id
- const char *def; ///< default value
- const char *fallback; ///< default value when "def" isn't single-width
+ schar_T *cp; ///< char value
+ String name; ///< char id
+ const char *def; ///< default value
+ const char *fallback; ///< default value when "def" isn't single-width
};
+#define CHARSTAB_ENTRY(cp, name, def, fallback) \
+ { (cp), { name, STRLEN_LITERAL(name) }, def, fallback }
+
static fcs_chars_T fcs_chars;
static const struct chars_tab fcs_tab[] = {
- { &fcs_chars.stl, "stl", " ", NULL },
- { &fcs_chars.stlnc, "stlnc", " ", NULL },
- { &fcs_chars.wbr, "wbr", " ", NULL },
- { &fcs_chars.horiz, "horiz", "─", "-" },
- { &fcs_chars.horizup, "horizup", "┴", "-" },
- { &fcs_chars.horizdown, "horizdown", "┬", "-" },
- { &fcs_chars.vert, "vert", "│", "|" },
- { &fcs_chars.vertleft, "vertleft", "┤", "|" },
- { &fcs_chars.vertright, "vertright", "├", "|" },
- { &fcs_chars.verthoriz, "verthoriz", "┼", "+" },
- { &fcs_chars.fold, "fold", "·", "-" },
- { &fcs_chars.foldopen, "foldopen", "-", NULL },
- { &fcs_chars.foldclosed, "foldclose", "+", NULL },
- { &fcs_chars.foldsep, "foldsep", "│", "|" },
- { &fcs_chars.diff, "diff", "-", NULL },
- { &fcs_chars.msgsep, "msgsep", " ", NULL },
- { &fcs_chars.eob, "eob", "~", NULL },
- { &fcs_chars.lastline, "lastline", "@", NULL },
+ CHARSTAB_ENTRY(&fcs_chars.stl, "stl", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.stlnc, "stlnc", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.wbr, "wbr", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.horiz, "horiz", "─", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.horizup, "horizup", "┴", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.horizdown, "horizdown", "┬", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.vert, "vert", "│", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.vertleft, "vertleft", "┤", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.vertright, "vertright", "├", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.verthoriz, "verthoriz", "┼", "+"),
+ CHARSTAB_ENTRY(&fcs_chars.fold, "fold", "·", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.foldopen, "foldopen", "-", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.foldclosed, "foldclose", "+", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.foldsep, "foldsep", "│", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.diff, "diff", "-", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.msgsep, "msgsep", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.eob, "eob", "~", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.lastline, "lastline", "@", NULL),
};
static lcs_chars_T lcs_chars;
static const struct chars_tab lcs_tab[] = {
- { &lcs_chars.eol, "eol", NULL, NULL },
- { &lcs_chars.ext, "extends", NULL, NULL },
- { &lcs_chars.nbsp, "nbsp", NULL, NULL },
- { &lcs_chars.prec, "precedes", NULL, NULL },
- { &lcs_chars.space, "space", NULL, NULL },
- { &lcs_chars.tab2, "tab", NULL, NULL },
- { &lcs_chars.lead, "lead", NULL, NULL },
- { &lcs_chars.trail, "trail", NULL, NULL },
- { &lcs_chars.conceal, "conceal", NULL, NULL },
- { NULL, "multispace", NULL, NULL },
- { NULL, "leadmultispace", NULL, NULL },
+ CHARSTAB_ENTRY(&lcs_chars.eol, "eol", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.ext, "extends", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.nbsp, "nbsp", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.prec, "precedes", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.space, "space", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.tab2, "tab", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.lead, "lead", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.trail, "trail", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.conceal, "conceal", NULL, NULL),
+ CHARSTAB_ENTRY(NULL, "multispace", NULL, NULL),
+ CHARSTAB_ENTRY(NULL, "leadmultispace", NULL, NULL),
};
+#undef CHARSTAB_ENTRY
+
static char *field_value_err(char *errbuf, size_t errbuflen, const char *fmt, const char *field)
{
if (errbuf == NULL) {
@@ -2812,13 +2215,13 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
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 && p[len] == ':')) {
+ if (!(strncmp(p, tab[i].name.data,
+ tab[i].name.size) == 0 && p[tab[i].name.size] == ':')) {
continue;
}
- if (what == kListchars && strcmp(tab[i].name, "multispace") == 0) {
- const char *s = p + len + 1;
+ const char *s = p + tab[i].name.size + 1;
+ if (what == kListchars && strcmp(tab[i].name.data, "multispace") == 0) {
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
@@ -2828,7 +2231,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
if (c1 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
multispace_len++;
}
@@ -2836,7 +2239,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
// lcs-multispace cannot be an empty string
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
p = s;
} else {
@@ -2852,8 +2255,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
break;
}
- if (what == kListchars && strcmp(tab[i].name, "leadmultispace") == 0) {
- const char *s = p + len + 1;
+ if (what == kListchars && strcmp(tab[i].name.data, "leadmultispace") == 0) {
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
@@ -2863,7 +2265,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
if (c1 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
lead_multispace_len++;
}
@@ -2871,7 +2273,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
// lcs-leadmultispace cannot be an empty string
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
p = s;
} else {
@@ -2887,17 +2289,16 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
break;
}
- const char *s = p + len + 1;
if (*s == NUL) {
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
schar_T c2 = 0;
schar_T c3 = 0;
@@ -2905,20 +2306,20 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
if (*s == NUL) {
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
c2 = get_encoded_char_adv(&s);
if (c2 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
if (c3 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
}
}
@@ -2938,7 +2339,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
} else {
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
}
@@ -2969,22 +2370,22 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
/// 'fillchars' option.
char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
- if (idx >= (int)ARRAY_SIZE(fcs_tab)) {
+ if (idx < 0 || idx >= (int)ARRAY_SIZE(fcs_tab)) {
return NULL;
}
- return (char *)fcs_tab[idx].name;
+ return fcs_tab[idx].name.data;
}
/// Function given to ExpandGeneric() to obtain possible arguments of the
/// 'listchars' option.
char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
- if (idx >= (int)ARRAY_SIZE(lcs_tab)) {
+ if (idx < 0 || idx >= (int)ARRAY_SIZE(lcs_tab)) {
return NULL;
}
- return (char *)lcs_tab[idx].name;
+ return lcs_tab[idx].name.data;
}
/// Check all global and local values of 'listchars' and 'fillchars'.
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index ccf6c9554a..3126881266 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -15,7 +15,6 @@
#include "nvim/cmdexpand.h"
#include "nvim/cmdexpand_defs.h"
#include "nvim/eval.h"
-#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
@@ -581,7 +580,7 @@ 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 *restrict srcp, char *restrict dst, int dstlen, bool esc, bool one,
+void expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen, bool esc, bool one,
char *prefix)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 1981d0dfd4..1b202fdc59 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -8,16 +8,13 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
-#include <stdint.h>
+#include <string.h>
#include <uv.h>
#include "auto/config.h"
-#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
#include "nvim/memory.h"
-#include "nvim/message.h"
#include "nvim/os/fileio.h"
#include "nvim/os/fs.h"
#include "nvim/os/os_defs.h"
@@ -28,7 +25,7 @@
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/fileio.c.generated.h"
+# include "os/fileio.c.generated.h" // IWYU pragma: keep
#endif
/// Open file
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index d0da37b8e7..451994241d 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -370,8 +370,8 @@ static bool is_executable_in_path(const char *name, char **abspath)
char *path = xstrdup(path_env);
#endif
- size_t buf_len = strlen(name) + strlen(path) + 2;
- char *buf = xmalloc(buf_len);
+ const size_t bufsize = strlen(name) + strlen(path) + 2;
+ char *buf = xmalloc(bufsize);
// Walk through all entries in $PATH to check if "name" exists there and
// is an executable file.
@@ -382,7 +382,7 @@ static bool is_executable_in_path(const char *name, char **abspath)
// Combine the $PATH segment with `name`.
xmemcpyz(buf, p, (size_t)(e - p));
- (void)append_path(buf, name, buf_len);
+ (void)append_path(buf, name, bufsize);
#ifdef MSWIN
if (is_executable_ext(buf, abspath)) {
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 2d17581bac..3259fb500b 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -13,7 +14,6 @@
#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_defs.h"
#include "nvim/globals.h"
@@ -29,6 +29,7 @@
#include "nvim/profile.h"
#include "nvim/state.h"
#include "nvim/state_defs.h"
+#include "nvim/types_defs.h"
#define READ_BUFFER_SIZE 0xfff
#define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN)
@@ -402,6 +403,7 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
if (type != KS_EXTRA
|| !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
+ || (mouse_code >= KE_X1MOUSE && mouse_code <= KE_X2RELEASE)
|| (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT)
|| mouse_code == KE_MOUSEMOVE)) {
return bufsize;
diff --git a/src/nvim/os/nvim.rc b/src/nvim/os/nvim.rc
new file mode 100644
index 0000000000..e838c93c16
--- /dev/null
+++ b/src/nvim/os/nvim.rc
@@ -0,0 +1,2 @@
+#include "winuser.h"
+2 RT_MANIFEST nvim.manifest
diff --git a/src/nvim/os/pty_proc_unix.c b/src/nvim/os/pty_proc_unix.c
index 3bca065d2d..10a464bbc3 100644
--- a/src/nvim/os/pty_proc_unix.c
+++ b/src/nvim/os/pty_proc_unix.c
@@ -30,6 +30,7 @@
#endif
#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/eval/typval.h"
#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 81b75dc4d3..d60d0b3e55 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -700,6 +700,7 @@ int os_call_shell(char *cmd, int opts, char *extra_args)
}
if (!emsg_silent && exitcode != 0 && !(opts & kShellOptSilent)) {
+ msg_ext_set_kind("shell_ret");
msg_puts(_("\nshell returned "));
msg_outnum(exitcode);
msg_putchar('\n');
@@ -1067,7 +1068,7 @@ static void out_data_ring(const char *output, size_t size)
}
if (output == NULL && size == SIZE_MAX) { // Print mode
- out_data_append_to_screen(last_skipped, &last_skipped_len, true);
+ out_data_append_to_screen(last_skipped, &last_skipped_len, STDOUT_FILENO, true);
return;
}
@@ -1095,14 +1096,15 @@ static void out_data_ring(const char *output, size_t size)
/// @param output Data to append to screen lines.
/// @param count Size of data.
/// @param eof If true, there will be no more data output.
-static void out_data_append_to_screen(const char *output, size_t *count, bool eof)
+static void out_data_append_to_screen(const char *output, size_t *count, int fd, bool eof)
FUNC_ATTR_NONNULL_ALL
{
const char *p = output;
const char *end = output + *count;
+ msg_ext_set_kind(fd == STDERR_FILENO ? "shell_err" : "shell_out");
while (p < end) {
if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) {
- msg_putchar_hl((uint8_t)(*p), 0);
+ msg_putchar_hl((uint8_t)(*p), fd == STDERR_FILENO ? HLF_E : 0);
p++;
} else {
// Note: this is not 100% precise:
@@ -1118,7 +1120,7 @@ static void out_data_append_to_screen(const char *output, size_t *count, bool eo
goto end;
}
- msg_outtrans_len(p, i, 0, false);
+ msg_outtrans_len(p, i, fd == STDERR_FILENO ? HLF_E : 0, false);
p += i;
}
}
@@ -1133,7 +1135,7 @@ static size_t out_data_cb(RStream *stream, const char *ptr, size_t count, void *
// Save the skipped output. If it is the final chunk, we display it later.
out_data_ring(ptr, count);
} else if (count > 0) {
- out_data_append_to_screen(ptr, &count, eof);
+ out_data_append_to_screen(ptr, &count, stream->s.fd, eof);
}
return count;
@@ -1206,10 +1208,11 @@ static void read_input(StringBuilder *buf)
size_t len = 0;
linenr_T lnum = curbuf->b_op_start.lnum;
char *lp = ml_get(lnum);
+ size_t lplen = (size_t)ml_get_len(lnum);
while (true) {
- size_t l = strlen(lp + written);
- if (l == 0) {
+ lplen -= written;
+ if (lplen == 0) {
len = 0;
} else if (lp[written] == NL) {
// NL -> NUL translation
@@ -1217,11 +1220,11 @@ static void read_input(StringBuilder *buf)
kv_push(*buf, NUL);
} else {
char *s = vim_strchr(lp + written, NL);
- len = s == NULL ? l : (size_t)(s - (lp + written));
+ len = s == NULL ? lplen : (size_t)(s - (lp + written));
kv_concat_len(*buf, lp + written, len);
}
- if (len == l) {
+ if (len == lplen) {
// Finished a line, add a NL, unless this line should not have one.
if (lnum != curbuf->b_op_end.lnum
|| (!curbuf->b_p_bin && curbuf->b_p_fixeol)
@@ -1234,6 +1237,7 @@ static void read_input(StringBuilder *buf)
break;
}
lp = ml_get(lnum);
+ lplen = (size_t)ml_get_len(lnum);
written = 0;
} else if (len > 0) {
written += len;
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index ecedf144e5..98d13fc9d1 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -187,8 +187,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
switch (signum) {
#ifdef SIGPWR
case SIGPWR:
- // Signal of a power failure(eg batteries low), flush the swap files to
- // be safe
+ // Signal of a power failure (eg batteries low), flush the swap files to be safe
ml_sync_all(false, false, true);
break;
#endif
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 80890acb7d..3df77571a1 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -8,6 +8,7 @@
#include "auto/config.h"
#include "nvim/ascii_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
@@ -1874,11 +1875,12 @@ void path_fix_case(char *name)
return;
}
+ size_t taillen = strlen(tail);
const char *entry;
while ((entry = 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)) {
+ if (STRICMP(tail, entry) == 0 && taillen == strlen(entry)) {
char newname[MAXPATHL + 1];
// Verify the inode is equal.
@@ -2071,7 +2073,7 @@ char *path_shorten_fname(char *full_path, char *dir_name)
/// @param[in] flags Flags passed to expand_wildcards().
///
/// @returns OK when *file is set to allocated array of matches
-/// and *num_file(can be zero) to the number of matches.
+/// and *num_file (can be zero) to the number of matches.
/// 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 "".
@@ -2269,14 +2271,12 @@ int append_path(char *path, const char *to_append, size_t max_len)
// Combine the path segments, separated by a slash.
if (current_length > 0 && !vim_ispathsep_nocolon(path[current_length - 1])) {
- current_length += 1; // Count the trailing slash.
-
// +1 for the NUL at the end.
- if (current_length + 1 > max_len) {
- return FAIL;
+ if (current_length + STRLEN_LITERAL(PATHSEPSTR) + 1 > max_len) {
+ return FAIL; // No space for trailing slash.
}
-
- xstrlcat(path, PATHSEPSTR, max_len);
+ xstrlcpy(path + current_length, PATHSEPSTR, max_len - current_length);
+ current_length += STRLEN_LITERAL(PATHSEPSTR);
}
// +1 for the NUL at the end.
@@ -2284,7 +2284,7 @@ int append_path(char *path, const char *to_append, size_t max_len)
return FAIL;
}
- xstrlcat(path, to_append, max_len);
+ xstrlcpy(path + current_length, to_append, max_len - current_length);
return OK;
}
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index 64acd93f57..3d537a51f2 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -1353,8 +1353,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Patrone kan nie deur letters afgebaken word nie"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "vervang met %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "vervang met %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up (^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Onderbreek) "
diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po
index 44975d9161..2162f13592 100644
--- a/src/nvim/po/ca.po
+++ b/src/nvim/po/ca.po
@@ -1241,8 +1241,8 @@ msgstr "E146: Les expressions regulars no poden estar delimitades per lletres"
# amb o per + tecles. eac
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "substituir amb %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "substituir amb %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/cs.cp1250.po b/src/nvim/po/cs.cp1250.po
index 43b6c82960..4939a150ac 100644
--- a/src/nvim/po/cs.cp1250.po
+++ b/src/nvim/po/cs.cp1250.po
@@ -1255,8 +1255,8 @@ msgstr "E146: Regulrn vrazy nesm bt oddleny psmeny"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradit za %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradit za %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po
index 7df69e061f..9cb552c6de 100644
--- a/src/nvim/po/cs.po
+++ b/src/nvim/po/cs.po
@@ -1255,8 +1255,8 @@ msgstr "E146: Regulrn vrazy nesm bt oddleny psmeny"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradit za %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradit za %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po
index c65477bf13..ba7ffa9f7b 100644
--- a/src/nvim/po/da.po
+++ b/src/nvim/po/da.po
@@ -984,8 +984,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Regulære udtryk kan ikke afgrænses af bogstaver"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "erstat med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "erstat med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Afbrudt) "
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index 0d1195eecf..0eb72b9565 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -666,8 +666,8 @@ msgstr "E146: Regulre Ausdrcke knnen nicht durch Buchstaben begrenzt werden"
#: ../ex_cmds.c:3958
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "ersetze durch %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "ersetze durch %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4373
msgid "(Interrupted) "
diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po
index 7b849d4e68..f93dcad206 100644
--- a/src/nvim/po/en_GB.po
+++ b/src/nvim/po/en_GB.po
@@ -1199,8 +1199,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Regular expressions cannot be delimited by letters"
#: ../ex_cmds.c:3964
-#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
+#, c-format"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgstr ""
#: ../ex_cmds.c:4379
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 5cd18ad24b..4033ff8a43 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -950,8 +950,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Ne eblas limigi regulesprimon per literoj"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "ĉu anstataŭigi per %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "ĉu anstataŭigi per %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Interrompita) "
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 292ca15264..b67c275bf3 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -1243,8 +1243,8 @@ msgstr "E146: Las expresiones regulares no se pueden delimitar con letras"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "¿Reemplazar con %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "¿Reemplazar con %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index c9cad6cc69..e1700d61ff 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -1521,8 +1521,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Säännöllistä ilmausta ei voi rajata kirjaimilla"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "korvaa kohteella %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "korvaa kohteella %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Keskeytetty)"
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 92d2ad215f..0dae00f3a3 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -855,8 +855,8 @@ msgstr ""
"lettres"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "remplacer par %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "remplacer par %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Interrompu) "
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index 7a5893b5f2..902bf9fae1 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -972,8 +972,8 @@ msgstr ""
"E146: N cheadatear litreacha mar theormharcir ar shloinn ionadaochta"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "cuir %s ina ionad (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "cuir %s? ina ionad (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Idirbhriste) "
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index d02052ec39..5caaa3c1f0 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -1227,8 +1227,8 @@ msgstr "E146: Le espressioni regolari non possono essere delimitate da lettere"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "sostituire con %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "sostituire con %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index 78f927c21d..02bd36efbb 100644
--- a/src/nvim/po/ja.euc-jp.po
+++ b/src/nvim/po/ja.euc-jp.po
@@ -867,8 +867,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: ɽʸǶڤ뤳ȤǤޤ"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s ִޤ? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s ִޤ? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(ޤޤ) "
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index 1a493c0336..8d417d222a 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -2998,8 +2998,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: 正規表現は文字で区切ることができません"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s に置換しますか? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s に置換しますか? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(割込まれました) "
diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po
index 03dff518f3..66463713d7 100644
--- a/src/nvim/po/ko.UTF-8.po
+++ b/src/nvim/po/ko.UTF-8.po
@@ -1221,8 +1221,8 @@ msgstr "E146: 정규표현식은 글자로 구분될 수 없습니다"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s(으)로 바꿈 (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s(으)로 바꿈? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po
index d512789246..6f10dcc232 100644
--- a/src/nvim/po/nb.po
+++ b/src/nvim/po/nb.po
@@ -1236,8 +1236,8 @@ msgstr "E146: Regulre uttrykk kan ikke bli adskilt av bokstaver"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "Erstatt med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "Erstatt med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po
index f6ce5ddc9f..75d516a67d 100644
--- a/src/nvim/po/nl.po
+++ b/src/nvim/po/nl.po
@@ -1223,8 +1223,8 @@ msgstr "E146: reguliere expressies kunnen niet begrensd worden door letters"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "vervang door %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "vervang door %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po
index d512789246..6f10dcc232 100644
--- a/src/nvim/po/no.po
+++ b/src/nvim/po/no.po
@@ -1236,8 +1236,8 @@ msgstr "E146: Regulre uttrykk kan ikke bli adskilt av bokstaver"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "Erstatt med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "Erstatt med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po
index b7200a7ba8..521edb383d 100644
--- a/src/nvim/po/pl.UTF-8.po
+++ b/src/nvim/po/pl.UTF-8.po
@@ -1207,8 +1207,8 @@ msgstr "E146: Wzorce regularne nie mogą być rozgraniczane literami"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "zamień na %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "zamień na %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po
index 3f2f87991c..4e7225fc9c 100644
--- a/src/nvim/po/pt_BR.po
+++ b/src/nvim/po/pt_BR.po
@@ -4216,8 +4216,8 @@ msgstr "E146: Expresses regulares no podem ser delimitadas por letras"
#: ../ex_cmds.c:3958
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "substituir por %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "substituir por %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4373
msgid "(Interrupted) "
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index 64c18f890c..90dc5b2d8e 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -1215,8 +1215,8 @@ msgstr "E146: Регулярные выражения не могут разде
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "заменить на %s? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "заменить на %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po
index 9510a357f0..394b5aff01 100644
--- a/src/nvim/po/sk.cp1250.po
+++ b/src/nvim/po/sk.cp1250.po
@@ -1224,8 +1224,8 @@ msgstr "E146: Regulrne vrazy nesm by oddelen psmenami"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradi %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradi %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po
index 21791e0061..79ed3e9539 100644
--- a/src/nvim/po/sk.po
+++ b/src/nvim/po/sk.po
@@ -1224,8 +1224,8 @@ msgstr "E146: Regulrne vrazy nesm by oddelen psmenami"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradi %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradi %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index 1cbb37eeb1..79b6747921 100644
--- a/src/nvim/po/sr.po
+++ b/src/nvim/po/sr.po
@@ -1201,8 +1201,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Регуларни изрази не могу да се раздвајају словима"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "заменити са %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "заменити са %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Прекинуто)"
diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po
index 89a7717746..285dadb595 100644
--- a/src/nvim/po/sv.po
+++ b/src/nvim/po/sv.po
@@ -2628,8 +2628,8 @@ msgstr "E146: Reguljra uttryck kan inte vara tskilda av bokstver"
#: ../ex_cmds.c:3958
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "erstt med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "erstt med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4373
msgid "(Interrupted) "
diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po
index 5b9a549cb2..ea41cf56b3 100644
--- a/src/nvim/po/tr.po
+++ b/src/nvim/po/tr.po
@@ -1867,8 +1867,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Düzenli ifadeler harflerle sınırlandırılamaz"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s ile değiştir (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s ile değiştir? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Yarıda kesildi) "
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index 6f2f90ad2c..cb422cc2b6 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -3008,8 +3008,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Регулярні вирази не можна розділяти літерами"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "Замінити на %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "Замінити на %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Перервано) "
diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po
index 3e88ac446c..b976d39647 100644
--- a/src/nvim/po/vi.po
+++ b/src/nvim/po/vi.po
@@ -4,219 +4,147 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Vim 6.3 \n"
+"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2005-02-30 21:37+0400\n"
-"Last-Translator: Phan Vinh Thinh <teppi@vnlinux.org>\n"
+"POT-Creation-Date: 2024-12-03 22:06+0100\n"
+"PO-Revision-Date: 2024-12-03 22:07+0100\n"
+"Last-Translator: Phạm Bình An <111893501+brianhuster@users.noreply.github.com>\n"
"Language-Team: Phan Vinh Thinh <teppi@vnlinux.org>\n"
-"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../api/private/helpers.c:201
-#, fuzzy
-msgid "Unable to get option value"
-msgstr "E258: Không thể trả lời cho máy con"
-
-#: ../api/private/helpers.c:204
-msgid "internal error: unknown option type"
-msgstr ""
-
-#: ../buffer.c:92
-msgid "[Location List]"
-msgstr ""
-
-#: ../buffer.c:93
-msgid "[Quickfix List]"
-msgstr ""
-
-#: ../buffer.c:94
-msgid "E855: Autocommands caused command to abort"
-msgstr ""
-
-#: ../buffer.c:135
msgid "E82: Cannot allocate any buffer, exiting..."
msgstr "E82: Không thể phân chia bộ nhớ thậm chí cho một bộ đệm, thoát..."
-#: ../buffer.c:138
msgid "E83: Cannot allocate buffer, using other one..."
msgstr "E83: Không thể phân chia bộ nhớ cho bộ đệm, sử dụng bộ đệm khác..."
-#: ../buffer.c:763
msgid "E515: No buffers were unloaded"
msgstr "E515: Không có bộ đệm nào được bỏ nạp từ bộ nhớ"
-#: ../buffer.c:765
msgid "E516: No buffers were deleted"
msgstr "E516: Không có bộ đệm nào bị xóa"
-#: ../buffer.c:767
msgid "E517: No buffers were wiped out"
msgstr "E517: Không có bộ đệm nào được làm sạch"
-#: ../buffer.c:772
msgid "1 buffer unloaded"
msgstr "1 bộ đệm được bỏ nạp từ bộ nhớ"
-#: ../buffer.c:774
#, c-format
msgid "%d buffers unloaded"
msgstr "%d bộ đệm được bỏ nạp từ bộ nhớ"
-#: ../buffer.c:777
msgid "1 buffer deleted"
msgstr "1 bộ đệm bị xóa"
-#: ../buffer.c:779
#, c-format
msgid "%d buffers deleted"
msgstr "%d bộ đệm được bỏ nạp"
-#: ../buffer.c:782
msgid "1 buffer wiped out"
msgstr "1 bộ đệm được làm sạch"
-#: ../buffer.c:784
#, c-format
msgid "%d buffers wiped out"
msgstr "%d bộ đệm được làm sạch"
-#: ../buffer.c:806
-msgid "E90: Cannot unload last buffer"
-msgstr "E90: Không thể bỏ nạp từ bộ nhớ bộ đệm cuối cùng"
-
-#: ../buffer.c:874
msgid "E84: No modified buffer found"
msgstr "E84: Không tìm thấy bộ đệm có thay đổi"
-#. back where we started, didn't find anything.
-#: ../buffer.c:903
msgid "E85: There is no listed buffer"
msgstr "E85: Không có bộ đệm được liệt kê"
-#: ../buffer.c:913
#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: Bộ đệm %<PRId64> không tồn tại"
+msgid "E86: Buffer %ld does not exist"
+msgstr "E86: Bộ đệm %ld không tồn tại"
-#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: Đây là bộ đệm cuối cùng"
-#: ../buffer.c:917
msgid "E88: Cannot go before first buffer"
msgstr "E88: Đây là bộ đệm đầu tiên"
-#: ../buffer.c:945
#, c-format
-msgid ""
-"E89: No write since last change for buffer %<PRId64> (add ! to override)"
+msgid "E89: No write since last change for buffer %ld (add ! to override)"
msgstr ""
-"E89: Thay đổi trong bộ đệm %<PRId64> chưa được ghi lại (thêm ! để thoát ra "
-"bằng mọi giá)"
+"E89: Thay đổi trong bộ đệm %ld chưa được ghi lại (thêm ! để thoát ra bằng "
+"mọi giá)"
+
+msgid "E90: Cannot unload last buffer"
+msgstr "E90: Không thể bỏ nạp từ bộ nhớ bộ đệm cuối cùng"
-#. wrap around (may cause duplicates)
-#: ../buffer.c:1423
msgid "W14: Warning: List of file names overflow"
msgstr "W14: Cảnh báo: Danh sách tên tập tin quá đầy"
-#: ../buffer.c:1555 ../quickfix.c:3361
#, c-format
-msgid "E92: Buffer %<PRId64> not found"
-msgstr "E92: Bộ đệm %<PRId64> không được tìm thấy"
+msgid "E92: Buffer %ld not found"
+msgstr "E92: Bộ đệm %ld không được tìm thấy"
-#: ../buffer.c:1798
#, c-format
msgid "E93: More than one match for %s"
msgstr "E93: Tìm thấy vài tương ứng với %s"
-#: ../buffer.c:1800
#, c-format
msgid "E94: No matching buffer for %s"
msgstr "E94: Không có bộ đệm tương ứng với %s"
-#: ../buffer.c:2161
#, c-format
-msgid "line %<PRId64>"
-msgstr "dòng %<PRId64>"
+msgid "line %ld"
+msgstr "dòng %ld"
-#: ../buffer.c:2233
msgid "E95: Buffer with this name already exists"
msgstr "E95: Đã có bộ đệm với tên như vậy"
-#: ../buffer.c:2498
msgid " [Modified]"
msgstr " [Đã thay đổi]"
-#: ../buffer.c:2501
msgid "[Not edited]"
msgstr "[Chưa soạn thảo]"
-#: ../buffer.c:2504
msgid "[New file]"
msgstr "[Tập tin mới]"
-#: ../buffer.c:2505
msgid "[Read errors]"
msgstr "[Lỗi đọc]"
-#: ../buffer.c:2506 ../buffer.c:3217 ../fileio.c:1807 ../screen.c:4895
-msgid "[RO]"
-msgstr "[Chỉ đọc]"
-
-#: ../buffer.c:2507 ../fileio.c:1807
msgid "[readonly]"
msgstr "[chỉ đọc]"
-#: ../buffer.c:2524
#, c-format
msgid "1 line --%d%%--"
msgstr "1 dòng --%d%%--"
-#: ../buffer.c:2526
#, c-format
-msgid "%<PRId64> lines --%d%%--"
-msgstr "%<PRId64> dòng --%d%%--"
+msgid "%ld lines --%d%%--"
+msgstr "%ld dòng --%d%%--"
-#: ../buffer.c:2530
#, c-format
-msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
-msgstr "dòng %<PRId64> của %<PRId64> --%d%%-- cột "
+msgid "line %ld of %ld --%d%%-- col "
+msgstr "dòng %ld của %ld --%d%%-- cột "
-#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
-#, fuzzy
-msgid "[No Name]"
+msgid "[No file]"
msgstr "[Không có tập tin]"
-#. must be a help buffer
-#: ../buffer.c:2667
msgid "help"
msgstr "trợ giúp"
-#: ../buffer.c:3225 ../screen.c:4883
-#, fuzzy
-msgid "[Help]"
+msgid "[help]"
msgstr "[trợ giúp]"
-#: ../buffer.c:3254 ../screen.c:4887
msgid "[Preview]"
msgstr "[Xem trước]"
-#: ../buffer.c:3528
msgid "All"
msgstr "Tất cả"
-#: ../buffer.c:3528
msgid "Bot"
msgstr "Cuối"
-#: ../buffer.c:3531
msgid "Top"
msgstr "Đầu"
-#: ../buffer.c:4244
msgid ""
"\n"
"# Buffer list:\n"
@@ -224,11 +152,12 @@ msgstr ""
"\n"
"# Danh sách bộ đệm:\n"
-#: ../buffer.c:4289
-msgid "[Scratch]"
-msgstr ""
+msgid "[Error List]"
+msgstr "[Danh sách lỗi]"
+
+msgid "[No File]"
+msgstr "[Không có tập tin]"
-#: ../buffer.c:4529
msgid ""
"\n"
"--- Signs ---"
@@ -236,806 +165,300 @@ msgstr ""
"\n"
"--- Ký hiệu ---"
-#: ../buffer.c:4538
#, c-format
msgid "Signs for %s:"
msgstr "Ký hiệu cho %s:"
-#: ../buffer.c:4543
#, c-format
-msgid " line=%<PRId64> id=%d name=%s"
-msgstr " dòng=%<PRId64> id=%d tên=%s"
-
-#: ../cursor_shape.c:68
-msgid "E545: Missing colon"
-msgstr "E545: Thiếu dấu hai chấm"
-
-#: ../cursor_shape.c:70 ../cursor_shape.c:94
-msgid "E546: Illegal mode"
-msgstr "E546: Chế độ không cho phép"
-
-#: ../cursor_shape.c:134
-msgid "E548: digit expected"
-msgstr "E548: yêu cầu một số"
+msgid " line=%ld id=%d name=%s"
+msgstr " dòng=%ld id=%d tên=%s"
-#: ../cursor_shape.c:138
-msgid "E549: Illegal percentage"
-msgstr "E549: Tỷ lệ phần trăm không cho phép"
-
-#: ../diff.c:146
#, c-format
-msgid "E96: Can not diff more than %<PRId64> buffers"
-msgstr ""
-"E96: Chỉ có thể theo dõi sự khác nhau trong nhiều nhất %<PRId64> bộ đệm"
-
-#: ../diff.c:753
-#, fuzzy
-msgid "E810: Cannot read or write temp files"
-msgstr "E557: Không thể mở tập tin termcap"
+msgid "E96: Can not diff more than %ld buffers"
+msgstr "E96: Chỉ có thể theo dõi sự khác nhau trong nhiều nhất %ld bộ đệm"
-#: ../diff.c:755
msgid "E97: Cannot create diffs"
msgstr "E97: Không thể tạo tập tin khác biệt (diff)"
-#: ../diff.c:966
-#, fuzzy
-msgid "E816: Cannot read patch output"
-msgstr "E98: Không thể đọc dữ liệu ra của lệnh diff"
+msgid "Patch file"
+msgstr "Tập tin vá lỗi (patch)"
-#: ../diff.c:1220
msgid "E98: Cannot read diff output"
msgstr "E98: Không thể đọc dữ liệu ra của lệnh diff"
-#: ../diff.c:2081
msgid "E99: Current buffer is not in diff mode"
msgstr "E99: Bộ đệm hiện thời không nằm trong chế độ khác biệt (diff)"
-#: ../diff.c:2100
-#, fuzzy
-msgid "E793: No other buffer in diff mode is modifiable"
-msgstr "E100: Không còn bộ đệm trong chế độ khác biệt (diff) nào nữa"
-
-#: ../diff.c:2102
msgid "E100: No other buffer in diff mode"
msgstr "E100: Không còn bộ đệm trong chế độ khác biệt (diff) nào nữa"
-#: ../diff.c:2112
msgid "E101: More than two buffers in diff mode, don't know which one to use"
-msgstr ""
-"E101: Có nhiều hơn hai bộ đệm trong chế độ khác biệt (diff), không biết chọn"
+msgstr "E101: Có nhiều hơn hai bộ đệm trong chế độ khác biệt (diff), không biết nên chọn cái nào"
-#: ../diff.c:2141
#, c-format
msgid "E102: Can't find buffer \"%s\""
msgstr "E102: Không tìm thấy bộ đệm \"%s\""
-#: ../diff.c:2152
#, c-format
msgid "E103: Buffer \"%s\" is not in diff mode"
msgstr "E103: Bộ đệm \"%s\" không nằm trong chế độ khác biệt (diff)"
-#: ../diff.c:2193
-msgid "E787: Buffer changed unexpectedly"
-msgstr ""
-
-#: ../digraph.c:1598
msgid "E104: Escape not allowed in digraph"
msgstr "E104: Không cho phép dùng ký tự thoát Escape trong chữ ghép"
-#: ../digraph.c:1760
msgid "E544: Keymap file not found"
msgstr "E544: Không tìm thấy tập tin sơ đồ bàn phím"
-#: ../digraph.c:1785
msgid "E105: Using :loadkeymap not in a sourced file"
msgstr "E105: Câu lệnh :loadkeymap được sử dụng ngoài tập tin script"
-#: ../digraph.c:1821
-msgid "E791: Empty keymap entry"
-msgstr ""
-
-#: ../edit.c:82
msgid " Keyword completion (^N^P)"
msgstr " Tự động kết thúc cho từ khóa (^N^P)"
-#. ctrl_x_mode == 0, ^P/^N compl.
-#: ../edit.c:83
-#, fuzzy
-msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+msgid " ^X mode (^E^Y^L^]^F^I^K^D^V^N^P)"
msgstr " Chế độ ^X (^E^Y^L^]^F^I^K^D^V^N^P)"
-#: ../edit.c:85
+msgid " Keyword Local completion (^N^P)"
+msgstr " Tự động kết thúc nội bộ cho từ khóa (^N^P)"
+
msgid " Whole line completion (^L^N^P)"
msgstr " Tự động kết thúc cho cả dòng (^L^N^P)"
-#: ../edit.c:86
msgid " File name completion (^F^N^P)"
msgstr " Tự động kết thúc tên tập tin (^F^N^P)"
-#: ../edit.c:87
msgid " Tag completion (^]^N^P)"
msgstr " Tự động kết thúc thẻ đánh dấu (^]^N^P)"
-#: ../edit.c:88
msgid " Path pattern completion (^N^P)"
msgstr " Tự động kết thúc mẫu đường dẫn (^N^P)"
-#: ../edit.c:89
msgid " Definition completion (^D^N^P)"
msgstr " Tự động kết thúc định nghĩa (^D^N^P)"
-#: ../edit.c:91
msgid " Dictionary completion (^K^N^P)"
msgstr " Tự động kết thúc theo từ điển (^K^N^P)"
-#: ../edit.c:92
msgid " Thesaurus completion (^T^N^P)"
msgstr " Tự động kết thúc từ đồng âm (^T^N^P)"
-#: ../edit.c:93
msgid " Command-line completion (^V^N^P)"
msgstr " Tự động kết thúc dòng lệnh (^V^N^P)"
-#: ../edit.c:94
-#, fuzzy
-msgid " User defined completion (^U^N^P)"
-msgstr " Tự động kết thúc cho cả dòng (^L^N^P)"
-
-#: ../edit.c:95
-#, fuzzy
-msgid " Omni completion (^O^N^P)"
-msgstr " Tự động kết thúc thẻ đánh dấu (^]^N^P)"
-
-#: ../edit.c:96
-#, fuzzy
-msgid " Spelling suggestion (^S^N^P)"
-msgstr " Tự động kết thúc cho cả dòng (^L^N^P)"
-
-#: ../edit.c:97
-msgid " Keyword Local completion (^N^P)"
-msgstr " Tự động kết thúc nội bộ cho từ khóa (^N^P)"
-
-#: ../edit.c:100
msgid "Hit end of paragraph"
msgstr "Kết thúc của đoạn văn"
-#: ../edit.c:101
-msgid "E839: Completion function changed window"
-msgstr ""
-
-#: ../edit.c:102
-msgid "E840: Completion function deleted text"
-msgstr ""
+msgid "'thesaurus' option is empty"
+msgstr "Không đưa ra giá trị của tùy chọn 'thesaurus'"
-#: ../edit.c:1847
msgid "'dictionary' option is empty"
msgstr "Không đưa ra giá trị của tùy chọn 'dictionary'"
-#: ../edit.c:1848
-msgid "'thesaurus' option is empty"
-msgstr "Không đưa ra giá trị của tùy chọn 'thesaurus'"
-
-#: ../edit.c:2655
#, c-format
msgid "Scanning dictionary: %s"
msgstr "Quét từ điển: %s"
-#: ../edit.c:3079
msgid " (insert) Scroll (^E/^Y)"
msgstr " (chèn) Cuộn (^E/^Y)"
-#: ../edit.c:3081
msgid " (replace) Scroll (^E/^Y)"
msgstr " (thay thế) Cuộn (^E/^Y)"
-#: ../edit.c:3587
#, c-format
msgid "Scanning: %s"
msgstr "Quét: %s"
-#: ../edit.c:3614
msgid "Scanning tags."
msgstr "Tìm kiếm trong số thẻ đánh dấu."
-#: ../edit.c:4519
msgid " Adding"
msgstr " Thêm"
-#. 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 "-- Tìm kiếm..."
-#: ../edit.c:4618
msgid "Back at original"
msgstr "Từ ban đầu"
-#: ../edit.c:4621
msgid "Word from other line"
msgstr "Từ của dòng khác"
-#: ../edit.c:4624
msgid "The only match"
msgstr "Tương ứng duy nhất"
-#: ../edit.c:4680
#, c-format
msgid "match %d of %d"
msgstr "Tương ứng %d của %d"
-#: ../edit.c:4684
#, c-format
msgid "match %d"
msgstr "Tương ứng %d"
-#: ../eval.c:137
-#, fuzzy
-msgid "E18: Unexpected characters in :let"
-msgstr "E18: Ở trước '=' có các ký tự không mong đợi"
-
-#: ../eval.c:138
-#, fuzzy, c-format
-msgid "E684: list index out of range: %<PRId64>"
-msgstr "E322: số thứ tự dòng vượt quá giới hạn : %<PRId64>"
-
-#: ../eval.c:139
#, c-format
-msgid "E121: Undefined variable: %s"
-msgstr "E121: Biến không xác định: %s"
-
-#: ../eval.c:140
-msgid "E111: Missing ']'"
-msgstr "E111: Thiếu ']'"
+msgid "E106: Unknown variable: \"%s\""
+msgstr "E106: Biến không biết: \"%s\""
-#: ../eval.c:141
-#, fuzzy, c-format
-msgid "E686: Argument of %s must be a List"
-msgstr "E487: Tham số phải là một số dương"
-
-#: ../eval.c:143
-#, fuzzy, c-format
-msgid "E712: Argument of %s must be a List or Dictionary"
-msgstr "E487: Tham số phải là một số dương"
-
-#: ../eval.c:144
-#, fuzzy
-msgid "E713: Cannot use empty key for Dictionary"
-msgstr "E214: Không tìm thấy tập tin tạm thời (temp) để ghi nhớ"
-
-#: ../eval.c:145
-#, fuzzy
-msgid "E714: List required"
-msgstr "E471: Cần chỉ ra tham số"
-
-#: ../eval.c:146
-#, fuzzy
-msgid "E715: Dictionary required"
-msgstr "E129: Cần tên hàm số"
-
-#: ../eval.c:147
-#, c-format
-msgid "E118: Too many arguments for function: %s"
-msgstr "E118: Quá nhiều tham số cho hàm: %s"
-
-#: ../eval.c:148
-#, c-format
-msgid "E716: Key not present in Dictionary: %s"
-msgstr ""
-
-#: ../eval.c:150
-#, c-format
-msgid "E122: Function %s already exists, add ! to replace it"
-msgstr "E122: Hàm số %s đã có, hãy thêm ! để thay thế nó."
-
-#: ../eval.c:151
-#, fuzzy
-msgid "E717: Dictionary entry already exists"
-msgstr "E95: Đã có bộ đệm với tên như vậy"
-
-#: ../eval.c:152
-#, fuzzy
-msgid "E718: Funcref required"
-msgstr "E129: Cần tên hàm số"
-
-#: ../eval.c:153
-#, fuzzy
-msgid "E719: Cannot use [:] with a Dictionary"
-msgstr "E360: Không chạy được shell với tùy chọn -f"
-
-#: ../eval.c:154
-#, c-format
-msgid "E734: Wrong variable type for %s="
-msgstr ""
-
-#: ../eval.c:155
-#, fuzzy, c-format
-msgid "E130: Unknown function: %s"
-msgstr "E117: Hàm số không biết: %s"
-
-#: ../eval.c:156
-#, c-format
-msgid "E461: Illegal variable name: %s"
-msgstr "E461: Tên biến không cho phép: %s"
-
-#: ../eval.c:157
-msgid "E806: using Float as a String"
-msgstr ""
-
-#: ../eval.c:1830
-msgid "E687: Less targets than List items"
-msgstr ""
-
-#: ../eval.c:1834
-msgid "E688: More targets than List items"
-msgstr ""
-
-#: ../eval.c:1906
-msgid "Double ; in list of variables"
-msgstr ""
-
-#: ../eval.c:2078
-#, fuzzy, c-format
-msgid "E738: Can't list variables for %s"
-msgstr "E138: Không thể ghi tập tin viminfo %s!"
-
-#: ../eval.c:2391
-msgid "E689: Can only index a List or Dictionary"
-msgstr ""
-
-#: ../eval.c:2396
-msgid "E708: [:] must come last"
-msgstr ""
-
-#: ../eval.c:2439
-msgid "E709: [:] requires a List value"
-msgstr ""
-
-#: ../eval.c:2674
-msgid "E710: List value has more items than target"
-msgstr ""
-
-#: ../eval.c:2678
-msgid "E711: List value has not enough items"
-msgstr ""
-
-#: ../eval.c:2867
-#, fuzzy
-msgid "E690: Missing \"in\" after :for"
-msgstr "E69: Thiếu ] sau %s%%["
-
-#: ../eval.c:3063
#, c-format
msgid "E107: Missing parentheses: %s"
msgstr "E107: Thiếu dấu ngoặc: %s"
-#: ../eval.c:3263
#, c-format
msgid "E108: No such variable: \"%s\""
msgstr "E108: Không có biến như vậy: \"%s\""
-#: ../eval.c:3333
-msgid "E743: variable nested too deep for (un)lock"
-msgstr ""
-
-#: ../eval.c:3630
msgid "E109: Missing ':' after '?'"
msgstr "E109: Thiếu ':' sau '?'"
-#: ../eval.c:3893
-msgid "E691: Can only compare List with List"
-msgstr ""
-
-#: ../eval.c:3895
-#, fuzzy
-msgid "E692: Invalid operation for Lists"
-msgstr "E449: Nhận được một biểu thức không cho phép"
-
-#: ../eval.c:3915
-msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr ""
-
-#: ../eval.c:3917
-#, fuzzy
-msgid "E736: Invalid operation for Dictionary"
-msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
-
-#: ../eval.c:3932
-msgid "E693: Can only compare Funcref with Funcref"
-msgstr ""
-
-#: ../eval.c:3934
-#, fuzzy
-msgid "E694: Invalid operation for Funcrefs"
-msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
-
-#: ../eval.c:4277
-#, fuzzy
-msgid "E804: Cannot use '%' with Float"
-msgstr "E360: Không chạy được shell với tùy chọn -f"
-
-#: ../eval.c:4478
msgid "E110: Missing ')'"
msgstr "E110: Thiếu ')'"
-#: ../eval.c:4609
-#, fuzzy
-msgid "E695: Cannot index a Funcref"
-msgstr "E90: Không thể bỏ nạp từ bộ nhớ bộ đệm cuối cùng"
+msgid "E111: Missing ']'"
+msgstr "E111: Thiếu ']'"
-#: ../eval.c:4839
#, c-format
msgid "E112: Option name missing: %s"
msgstr "E112: Không đưa ra tên tùy chọn: %s"
-#: ../eval.c:4855
#, c-format
msgid "E113: Unknown option: %s"
msgstr "E113: Tùy chọn không biết: %s"
-#: ../eval.c:4904
#, c-format
msgid "E114: Missing quote: %s"
msgstr "E114: Thiếu ngoặc kép: %s"
-#: ../eval.c:5020
#, c-format
msgid "E115: Missing quote: %s"
msgstr "E115: Thiếu ngoặc kép: %s"
-#: ../eval.c:5084
-#, fuzzy, c-format
-msgid "E696: Missing comma in List: %s"
-msgstr "E405: Thiếu dấu bằng: %s"
-
-#: ../eval.c:5091
-#, fuzzy, c-format
-msgid "E697: Missing end of List ']': %s"
-msgstr "E398: Thiếu '=': %s"
-
-#: ../eval.c:6475
-#, fuzzy, c-format
-msgid "E720: Missing colon in Dictionary: %s"
-msgstr "E405: Thiếu dấu bằng: %s"
-
-#: ../eval.c:6499
-#, c-format
-msgid "E721: Duplicate key in Dictionary: \"%s\""
-msgstr ""
-
-#: ../eval.c:6517
-#, fuzzy, c-format
-msgid "E722: Missing comma in Dictionary: %s"
-msgstr "E527: Thiếu dấu phẩy"
-
-#: ../eval.c:6524
-#, fuzzy, c-format
-msgid "E723: Missing end of Dictionary '}': %s"
-msgstr "E126: Thiếu lệnh :endfunction"
-
-#: ../eval.c:6555
-#, fuzzy
-msgid "E724: variable nested too deep for displaying"
-msgstr "E22: Các script lồng vào nhau quá sâu"
-
-#: ../eval.c:7188
-#, fuzzy, c-format
-msgid "E740: Too many arguments for function %s"
-msgstr "E118: Quá nhiều tham số cho hàm: %s"
-
-#: ../eval.c:7190
#, c-format
msgid "E116: Invalid arguments for function %s"
msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
-#: ../eval.c:7377
#, c-format
msgid "E117: Unknown function: %s"
msgstr "E117: Hàm số không biết: %s"
-#: ../eval.c:7383
+#, c-format
+msgid "E118: Too many arguments for function: %s"
+msgstr "E118: Quá nhiều tham số cho hàm: %s"
+
#, c-format
msgid "E119: Not enough arguments for function: %s"
msgstr "E119: Không đủ tham số cho hàm: %s"
-#: ../eval.c:7387
#, c-format
msgid "E120: Using <SID> not in a script context: %s"
msgstr "E120: Sử dụng <SID> ngoài script: %s"
-#: ../eval.c:7391
-#, c-format
-msgid "E725: Calling dict function without Dictionary: %s"
-msgstr ""
-
-#: ../eval.c:7453
-#, fuzzy
-msgid "E808: Number or Float required"
-msgstr "E521: Sau dấu = cần đưa ra một số"
-
-#: ../eval.c:7503
-#, fuzzy
-msgid "add() argument"
-msgstr "Tham số không được phép cho"
-
-#: ../eval.c:7907
-#, fuzzy
-msgid "E699: Too many arguments"
-msgstr "Có quá nhiều tham số soạn thảo"
-
-#: ../eval.c:8073
-#, fuzzy
-msgid "E785: complete() can only be used in Insert mode"
-msgstr "E328: Trình đơn chỉ có trong chế độ khác"
-
-#: ../eval.c:8156
msgid "&Ok"
msgstr "&Ok"
-#: ../eval.c:8676
-#, fuzzy, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E227: đã có ánh xạ cho %s"
-
-#: ../eval.c:8692
-msgid "extend() argument"
-msgstr ""
-
-#: ../eval.c:8915
-#, fuzzy
-msgid "map() argument"
-msgstr " vim [các tham số] "
-
-#: ../eval.c:8916
-msgid "filter() argument"
-msgstr ""
-
-#: ../eval.c:9229
#, c-format
msgid "+-%s%3ld lines: "
msgstr "+-%s%3ld dòng: "
-#: ../eval.c:9291
-#, fuzzy, c-format
-msgid "E700: Unknown function: %s"
-msgstr "E117: Hàm số không biết: %s"
+msgid ""
+"&OK\n"
+"&Cancel"
+msgstr ""
+"&OK\n"
+"&Hủy bỏ"
-#: ../eval.c:10729
msgid "called inputrestore() more often than inputsave()"
msgstr "Hàm số inputrestore() được gọi nhiều hơn hàm inputsave()"
-#: ../eval.c:10771
-#, fuzzy
-msgid "insert() argument"
-msgstr "Có quá nhiều tham số soạn thảo"
-
-#: ../eval.c:10841
-#, fuzzy
-msgid "E786: Range not allowed"
-msgstr "E481: Không cho phép sử dụng phạm vi"
-
-#: ../eval.c:11140
-#, fuzzy
-msgid "E701: Invalid type for len()"
-msgstr "E596: Phông chữ không đúng"
-
-#: ../eval.c:11980
-msgid "E726: Stride is zero"
-msgstr ""
-
-#: ../eval.c:11982
-msgid "E727: Start past end"
-msgstr ""
-
-#: ../eval.c:12024 ../eval.c:15297
-msgid "<empty>"
-msgstr ""
-
-#: ../eval.c:12282
-msgid "remove() argument"
-msgstr ""
-
-#: ../eval.c:12466
msgid "E655: Too many symbolic links (cycle?)"
msgstr "E655: Quá nhiều liên kết tượng trưng (vòng lặp?)"
-#: ../eval.c:12593
-msgid "reverse() argument"
-msgstr ""
+msgid "E240: No connection to Vim server"
+msgstr "E240: Không có kết nối với máy chủ Vim"
-#: ../eval.c:13721
-msgid "sort() argument"
-msgstr ""
+msgid "E277: Unable to read a server reply"
+msgstr "E277: Máy chủ không trả lời"
-#: ../eval.c:13721
-#, fuzzy
-msgid "uniq() argument"
-msgstr "Tham số không được phép cho"
-
-#: ../eval.c:13776
-#, fuzzy
-msgid "E702: Sort compare function failed"
-msgstr "E237: Chọn máy in không thành công"
+msgid "E258: Unable to send to client"
+msgstr "E258: Không thể trả lời cho máy con"
-#: ../eval.c:13806
-msgid "E882: Uniq compare function failed"
-msgstr ""
+#, c-format
+msgid "E241: Unable to send to %s"
+msgstr "E241: Không thể gửi tin nhắn tới %s"
-#: ../eval.c:14085
msgid "(Invalid)"
msgstr "(Không đúng)"
-#: ../eval.c:14590
-#, fuzzy
-msgid "E677: Error writing temp file"
-msgstr "E208: Lỗi ghi nhớ vào \"%s\""
-
-#: ../eval.c:16159
-msgid "E805: Using a Float as a Number"
-msgstr ""
-
-#: ../eval.c:16162
-msgid "E703: Using a Funcref as a Number"
-msgstr ""
-
-#: ../eval.c:16170
-msgid "E745: Using a List as a Number"
-msgstr ""
-
-#: ../eval.c:16173
-msgid "E728: Using a Dictionary as a Number"
-msgstr ""
-
-#: ../eval.c:16259
-msgid "E729: using Funcref as a String"
-msgstr ""
-
-#: ../eval.c:16262
-#, fuzzy
-msgid "E730: using List as a String"
-msgstr "E374: Thiếu ] trong chuỗi định dạng"
-
-#: ../eval.c:16265
-msgid "E731: using Dictionary as a String"
-msgstr ""
-
-#: ../eval.c:16619
-#, fuzzy, c-format
-msgid "E706: Variable type mismatch for: %s"
-msgstr "E93: Tìm thấy vài tương ứng với %s"
-
-#: ../eval.c:16705
-#, fuzzy, c-format
-msgid "E795: Cannot delete variable %s"
-msgstr "E46: Không thay đổi được biến chỉ đọc \"%s\""
-
-#: ../eval.c:16724
-#, fuzzy, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-
-#: ../eval.c:16732
#, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr ""
+msgid "E121: Undefined variable: %s"
+msgstr "E121: Biến không xác định: %s"
-#: ../eval.c:16763
#, c-format
-msgid "E741: Value is locked: %s"
-msgstr ""
-
-#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
-msgid "Unknown"
-msgstr "Không rõ"
-
-#: ../eval.c:16768
-#, fuzzy, c-format
-msgid "E742: Cannot change value of %s"
-msgstr "E284: Không đặt được giá trị nội dung nhập vào (IC)"
+msgid "E461: Illegal variable name: %s"
+msgstr "E461: Tên biến không cho phép: %s"
-#: ../eval.c:16838
-msgid "E698: variable nested too deep for making a copy"
-msgstr ""
+#, c-format
+msgid "E122: Function %s already exists, add ! to replace it"
+msgstr "E122: Hàm số %s đã có, hãy thêm ! để thay thế nó."
-#: ../eval.c:17249
#, c-format
msgid "E123: Undefined function: %s"
msgstr "E123: Hàm số không xác định: %s"
-#: ../eval.c:17260
#, c-format
msgid "E124: Missing '(': %s"
msgstr "E124: Thiếu '(': %s"
-#: ../eval.c:17293
-#, fuzzy
-msgid "E862: Cannot use g: here"
-msgstr "E284: Không đặt được giá trị nội dung nhập vào (IC)"
-
-#: ../eval.c:17312
#, c-format
msgid "E125: Illegal argument: %s"
msgstr "E125: Tham số không cho phép: %s"
-#: ../eval.c:17323
-#, fuzzy, c-format
-msgid "E853: Duplicate argument name: %s"
-msgstr "E154: Thẻ ghi lặp lại \"%s\" trong tập tin %s"
-
-#: ../eval.c:17416
msgid "E126: Missing :endfunction"
msgstr "E126: Thiếu lệnh :endfunction"
-#: ../eval.c:17537
-#, fuzzy, c-format
-msgid "E707: Function name conflicts with variable: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-
-#: ../eval.c:17549
#, c-format
msgid "E127: Cannot redefine function %s: It is in use"
msgstr "E127: Không thể định nghĩa lại hàm số %s: hàm đang được sử dụng"
-#: ../eval.c:17604
-#, fuzzy, c-format
-msgid "E746: Function name does not match script file name: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-
-#: ../eval.c:17716
msgid "E129: Function name required"
msgstr "E129: Cần tên hàm số"
-#: ../eval.c:17824
-#, fuzzy, c-format
-msgid "E128: Function name must start with a capital or \"s:\": %s"
+#, c-format
+msgid "E128: Function name must start with a capital: %s"
msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-#: ../eval.c:17833
-#, fuzzy, c-format
-msgid "E884: Function name cannot contain a colon: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
+#, c-format
+msgid "E130: Undefined function: %s"
+msgstr "E130: Hàm số %s chưa xác định"
-#: ../eval.c:18336
#, c-format
msgid "E131: Cannot delete function %s: It is in use"
msgstr "E131: Không thể xóa hàm số %s: Hàm đang được sử dụng"
-#: ../eval.c:18441
msgid "E132: Function call depth is higher than 'maxfuncdepth'"
msgstr "E132: Độ sâu của lời gọi hàm số lớn hơn giá trị 'maxfuncdepth'"
-#: ../eval.c:18568
#, c-format
msgid "calling %s"
msgstr "lời gọi %s"
-#: ../eval.c:18651
#, c-format
msgid "%s aborted"
msgstr "%s dừng"
-#: ../eval.c:18653
#, c-format
-msgid "%s returning #%<PRId64>"
-msgstr "%s trả lại #%<PRId64>"
+msgid "%s returning #%ld"
+msgstr "%s trả lại #%ld"
-#: ../eval.c:18670
-#, fuzzy, c-format
-msgid "%s returning %s"
+#, c-format
+msgid "%s returning \"%s\""
msgstr "%s trả lại \"%s\""
-#: ../eval.c:18691 ../ex_cmds2.c:2695
#, c-format
msgid "continuing in %s"
msgstr "tiếp tục trong %s"
-#: ../eval.c:18795
msgid "E133: :return not inside a function"
msgstr "E133: lệnh :return ở ngoài một hàm"
-#: ../eval.c:19159
msgid ""
"\n"
"# global variables:\n"
@@ -1043,114 +466,260 @@ msgstr ""
"\n"
"# biến toàn cầu:\n"
-#: ../eval.c:19254
-msgid ""
-"\n"
-"\tLast set from "
+msgid "Entering Debug mode. Type \"cont\" to continue."
+msgstr "Bật chế độ sửa lỗi (Debug). Gõ \"cont\" để tiếp tục."
+
+#, c-format
+msgid "line %ld: %s"
+msgstr "dòng %ld: %s"
+
+#, c-format
+msgid "cmd: %s"
+msgstr "câu lệnh: %s"
+
+#, c-format
+msgid "Breakpoint in \"%s%s\" line %ld"
+msgstr "Điểm dừng trên \"%s%s\" dòng %ld"
+
+#, c-format
+msgid "E161: Breakpoint not found: %s"
+msgstr "E161: Không tìm thấy điểm dừng: %s"
+
+msgid "No breakpoints defined"
+msgstr "Điểm dừng không được xác định"
+
+#, c-format
+msgid "%3d %s %s line %ld"
+msgstr "%3d %s %s dòng %ld"
+
+msgid "Save As"
+msgstr "Ghi nhớ như"
+
+#, c-format
+msgid "Save changes to \"%.*s\"?"
+msgstr "Ghi nhớ thay đổi vào \"%.*s\"?"
+
+msgid "Untitled"
+msgstr "Chưa đặt tên"
+
+#, c-format
+msgid "E162: No write since last change for buffer \"%s\""
+msgstr "E162: Thay đổi chưa được ghi nhớ trong bộ đệm \"%s\""
+
+msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr ""
-"\n"
-"\tLần cuối cùng tùy chọn thay đổi vào "
+"Cảnh báo: Chuyển tới bộ đệm khác không theo ý muốn (hãy kiểm tra câu lệnh tự "
+"động)"
-#: ../eval.c:19272
-#, fuzzy
-msgid "No old files"
-msgstr "Không có tập tin được tính đến"
+msgid "E163: There is only one file to edit"
+msgstr "E163: Chỉ có một tập tin để soạn thảo"
+
+msgid "E164: Cannot go before first file"
+msgstr "E164: Đây là tập tin đầu tiên"
+
+msgid "E165: Cannot go beyond last file"
+msgstr "E165: Đây là tập tin cuối cùng"
+
+# TODO: Capitalise first word of message?
+msgid "E666: Compiler not supported: %s"
+msgstr "E666: trình biên dịch không được hỗ trợ: %s"
+
+#, c-format
+msgid "Searching for \"%s\" in \"%s\""
+msgstr "Tìm kiếm \"%s\" trong \"%s\""
+
+#, c-format
+msgid "Searching for \"%s\""
+msgstr "Tìm kiếm \"%s\""
+
+#, c-format
+msgid "not found in 'runtimepath': \"%s\""
+msgstr "không tìm thấy trong 'runtimepath': \"%s\""
+
+msgid "Source Vim script"
+msgstr "Thực hiện script của Vim"
+
+#, c-format
+msgid "Cannot source a directory: \"%s\""
+msgstr "Không thể thực hiện một thư mục: \"%s\""
+
+#, c-format
+msgid "could not source \"%s\""
+msgstr "không thực hiện được \"%s\""
+
+#, c-format
+msgid "line %ld: could not source \"%s\""
+msgstr "dòng %ld: không thực hiện được \"%s\""
+
+#, c-format
+msgid "sourcing \"%s\""
+msgstr "thực hiện \"%s\""
+
+#, c-format
+msgid "line %ld: sourcing \"%s\""
+msgstr "dòng %ld: thực hiện \"%s\""
+
+#, c-format
+msgid "finished sourcing %s"
+msgstr "thực hiện xong %s"
+
+msgid "W15: Warning: Wrong line separator, ^M may be missing"
+msgstr "W15: Cảnh báo: Ký tự phân cách dòng không đúng. Rất có thể thiếu ^M"
+
+msgid "E167: :scriptencoding used outside of a sourced file"
+msgstr "E167: Lệnh :scriptencoding sử dụng ngoài tập tin script"
+
+msgid "E168: :finish used outside of a sourced file"
+msgstr "E168: Lệnh :finish sử dụng ngoài tập tin script"
+
+#, c-format
+msgid "Page %d"
+msgstr "Trang %d"
+
+msgid "No text to be printed"
+msgstr "Không có gì để in"
+
+#, c-format
+msgid "Printing page %d (%d%%)"
+msgstr "In trang %d (%d%%)"
+
+#, c-format
+msgid " Copy %d of %d"
+msgstr " Sao chép %d của %d"
+
+#, c-format
+msgid "Printed: %s"
+msgstr "Đã in: %s"
+
+msgid "Printing aborted"
+msgstr "In bị dừng"
+
+msgid "E455: Error writing to PostScript output file"
+msgstr "E455: Lỗi ghi nhớ vào tập tin PostScript"
+
+#, c-format
+msgid "E624: Can't open file \"%s\""
+msgstr "E624: Không thể mở tập tin \"%s\""
+
+#, 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\""
+
+# TODO: Capitalise first word of message?
+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"
+
+# TODO: Capitalise first word of message?
+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ợ"
+
+#, 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"
+
+msgid "E324: Can't open PostScript output file"
+msgstr "E324: Không thể mở tập tin PostScript"
+
+#, c-format
+msgid "E456: Can't open file \"%s\""
+msgstr "E456: Không thể mở tập tin \"%s\""
+
+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\""
+
+#, 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\""
+
+#, c-format
+msgid "E620: Unable to convert from multi-byte to \"%s\" encoding"
+msgstr "E620: Không thể chuyển từ các ký tự nhiều byte thành bảng mã \"%s\""
+
+msgid "Sending to printer..."
+msgstr "Gửi tới máy in..."
+
+msgid "E365: Failed to print PostScript file"
+msgstr "E365: In tập tin PostScript không thành công"
+
+msgid "Print job sent."
+msgstr "Đã gửi công việc in."
+
+#, c-format
+msgid "Current %slanguage: \"%s\""
+msgstr "Ngôn ngữ %shiện thời: \"%s\""
+
+#, c-format
+msgid "E197: Cannot set language to \"%s\""
+msgstr "E197: Không thể thay đổi ngôn ngữ thành \"%s\""
-#: ../ex_cmds.c:122
#, c-format
msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
msgstr "<%s>%s%s %d, Hex %02x, Octal %03o"
-#: ../ex_cmds.c:145
#, c-format
msgid "> %d, Hex %04x, Octal %o"
msgstr "> %d, Hex %04x, Octal %o"
-#: ../ex_cmds.c:146
#, c-format
msgid "> %d, Hex %08x, Octal %o"
msgstr "> %d, Hex %08x, Octal %o"
-#: ../ex_cmds.c:684
msgid "E134: Move lines into themselves"
msgstr "E134: Di chuyển các dòng lên chính chúng"
-#: ../ex_cmds.c:747
msgid "1 line moved"
msgstr "Đã di chuyển 1 dòng"
-#: ../ex_cmds.c:749
#, c-format
-msgid "%<PRId64> lines moved"
-msgstr "Đã di chuyển %<PRId64> dòng"
+msgid "%ld lines moved"
+msgstr "Đã di chuyển %ld dòng"
-#: ../ex_cmds.c:1175
#, c-format
-msgid "%<PRId64> lines filtered"
-msgstr "Đã lọc %<PRId64> dòng"
+msgid "%ld lines filtered"
+msgstr "Đã lọc %ld dòng"
-#: ../ex_cmds.c:1194
msgid "E135: *Filter* Autocommands must not change current buffer"
msgstr "E135: Các lệnh tự động *Filter* không được thay đổi bộ đệm hiện thời"
-#: ../ex_cmds.c:1244
msgid "[No write since last change]\n"
msgstr "[Thay đổi chưa được ghi nhớ]\n"
-#: ../ex_cmds.c:1424
#, c-format
msgid "%sviminfo: %s in line: "
msgstr "%sviminfo: %s trên dòng: "
-#: ../ex_cmds.c:1431
msgid "E136: viminfo: Too many errors, skipping rest of file"
msgstr "E136: viminfo: Quá nhiều lỗi, phần còn lại của tập tin sẽ được bỏ qua"
-#: ../ex_cmds.c:1458
#, c-format
msgid "Reading viminfo file \"%s\"%s%s%s"
msgstr "Đọc tập tin viminfo \"%s\"%s%s%s"
-#: ../ex_cmds.c:1460
msgid " info"
msgstr " thông tin"
-#: ../ex_cmds.c:1461
msgid " marks"
msgstr " dấu hiệu"
-#: ../ex_cmds.c:1462
-#, fuzzy
-msgid " oldfiles"
-msgstr "Không có tập tin được tính đến"
-
-#: ../ex_cmds.c:1463
msgid " FAILED"
msgstr " KHÔNG THÀNH CÔNG"
-#. avoid a wait_return for this message, it's annoying
-#: ../ex_cmds.c:1541
#, c-format
msgid "E137: Viminfo file is not writable: %s"
msgstr "E137: Thiếu quyền ghi lên tập tin viminfo: %s"
-#: ../ex_cmds.c:1626
#, c-format
msgid "E138: Can't write viminfo file %s!"
msgstr "E138: Không thể ghi tập tin viminfo %s!"
-#: ../ex_cmds.c:1635
#, c-format
msgid "Writing viminfo file \"%s\""
msgstr "Ghi tập tin viminfo \"%s\""
-#. Write the info:
-#: ../ex_cmds.c:1720
#, c-format
msgid "# This viminfo file was generated by Vim %s.\n"
msgstr "# Tập tin viminfo này được tự động tạo bởi Vim %s.\n"
-#: ../ex_cmds.c:1722
msgid ""
"# You may edit it if you're careful!\n"
"\n"
@@ -1158,137 +727,88 @@ msgstr ""
"# Bạn có thể sửa tập tin này, nhưng hãy thận trọng!\n"
"\n"
-#: ../ex_cmds.c:1723
msgid "# Value of 'encoding' when this file was written\n"
msgstr "# Giá trị của tùy chọn 'encoding' vào thời điểm ghi tập tin\n"
-#: ../ex_cmds.c:1800
msgid "Illegal starting char"
msgstr "Ký tự đầu tiên không cho phép"
-#: ../ex_cmds.c:2162
+msgid "E139: File is loaded in another buffer"
+msgstr "E139: Tập tin được nạp trong bộ đệm khác"
+
msgid "Write partial file?"
msgstr "Ghi nhớ một phần tập tin?"
-#: ../ex_cmds.c:2166
msgid "E140: Use ! to write partial buffer"
msgstr "E140: Sử dụng ! để ghi nhớ một phần bộ đệm"
-#: ../ex_cmds.c:2281
-#, fuzzy, c-format
-msgid "Overwrite existing file \"%s\"?"
-msgstr "Ghi đè lên tập tin đã có \"%.*s\"?"
-
-#: ../ex_cmds.c:2317
#, c-format
-msgid "Swap file \"%s\" exists, overwrite anyway?"
-msgstr ""
-
-#: ../ex_cmds.c:2326
-#, fuzzy, c-format
-msgid "E768: Swap file exists: %s (:silent! overrides)"
-msgstr "E13: Tập tin đã tồn tại (thêm ! để ghi chèn)"
+msgid "Overwrite existing file \"%.*s\"?"
+msgstr "Ghi đè lên tập tin đã có \"%.*s\"?"
-#: ../ex_cmds.c:2381
#, c-format
-msgid "E141: No file name for buffer %<PRId64>"
-msgstr "E141: Không có tên tập tin cho bộ đệm %<PRId64>"
+msgid "E141: No file name for buffer %ld"
+msgstr "E141: Không có tên tập tin cho bộ đệm %ld"
-#: ../ex_cmds.c:2412
msgid "E142: File not written: Writing is disabled by 'write' option"
msgstr "E142: Tập tin chưa được ghi nhớ: Ghi nhớ bị tắt bởi tùy chọn 'write'"
-#: ../ex_cmds.c:2434
-#, fuzzy, c-format
+#, c-format
msgid ""
-"'readonly' option is set for \"%s\".\n"
+"'readonly' option is set for \"%.*s\".\n"
"Do you wish to write anyway?"
msgstr ""
"Tùy chọn 'readonly' được đặt cho \"%.*s\".\n"
"Ghi nhớ bằng mọi giá?"
-#: ../ex_cmds.c:2439
-#, c-format
-msgid ""
-"File permissions of \"%s\" are read-only.\n"
-"It may still be possible to write it.\n"
-"Do you wish to try?"
-msgstr ""
-
-#: ../ex_cmds.c:2451
-#, fuzzy, c-format
-msgid "E505: \"%s\" is read-only (add ! to override)"
-msgstr "là tập tin chỉ đọc (thêm ! để ghi nhớ bằng mọi giá)"
+msgid "Edit File"
+msgstr "Soạn thảo tập tin"
-#: ../ex_cmds.c:3120
#, c-format
msgid "E143: Autocommands unexpectedly deleted new buffer %s"
msgstr "E143: Các lệnh tự động xóa bộ đệm mới ngoài ý muốn %s"
-#: ../ex_cmds.c:3313
-msgid "E144: non-numeric argument to :z"
+# TODO: Capitalise first word of message?
+msgid "E144: Non-numeric argument to :z"
msgstr "E144: Tham số của lệnh :z phải là số"
-#: ../ex_cmds.c:3498
+msgid "E145: Shell commands not allowed in rvim"
+msgstr "E145: Không cho phép sử dụng lệnh shell trong rvim."
+
msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Không thể phân cách biểu thức chính quy bằng chữ cái"
-#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "thay thế bằng %s? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "thay thế bằng %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
-#: ../ex_cmds.c:4379
msgid "(Interrupted) "
msgstr "(bị dừng)"
-#: ../ex_cmds.c:4384
-#, fuzzy
-msgid "1 match"
-msgstr "; tương ứng "
-
-#: ../ex_cmds.c:4384
msgid "1 substitution"
msgstr "1 thay thế"
-#: ../ex_cmds.c:4387
-#, fuzzy, c-format
-msgid "%<PRId64> matches"
-msgstr "%<PRId64> thay đổi"
-
-#: ../ex_cmds.c:4388
#, c-format
-msgid "%<PRId64> substitutions"
-msgstr "%<PRId64> thay thế"
+msgid "%ld substitutions"
+msgstr "%ld thay thế"
-#: ../ex_cmds.c:4392
msgid " on 1 line"
msgstr " trên 1 dòng"
-#: ../ex_cmds.c:4395
#, c-format
-msgid " on %<PRId64> lines"
-msgstr " trên %<PRId64> dòng"
+msgid " on %ld lines"
+msgstr " trên %ld dòng"
-#: ../ex_cmds.c:4438
msgid "E147: Cannot do :global recursive"
msgstr "E147: Không thực hiện được lệnh :global đệ qui"
-#: ../ex_cmds.c:4467
msgid "E148: Regular expression missing from global"
msgstr "E148: Thiếu biểu thức chính quy trong lệnh :global"
-#: ../ex_cmds.c:4508
#, c-format
msgid "Pattern found in every line: %s"
msgstr "Tìm thấy tương ứng trên mọi dòng: %s"
-#: ../ex_cmds.c:4510
-#, fuzzy, c-format
-msgid "Pattern not found: %s"
-msgstr "Không tìm thấy mẫu (pattern)"
-
-#: ../ex_cmds.c:4587
msgid ""
"\n"
"# Last Substitute String:\n"
@@ -1298,342 +818,138 @@ msgstr ""
"# Chuỗi thay thế cuối cùng:\n"
"$"
-#: ../ex_cmds.c:4679
msgid "E478: Don't panic!"
msgstr "E478: Hãy bình tĩnh, đừng hoảng hốt!"
-#: ../ex_cmds.c:4717
#, c-format
msgid "E661: Sorry, no '%s' help for %s"
msgstr "E661: Rất tiếc, không có trợ giúp '%s' cho %s"
-#: ../ex_cmds.c:4719
#, c-format
msgid "E149: Sorry, no help for %s"
msgstr "E149: Rất tiếc không có trợ giúp cho %s"
-#: ../ex_cmds.c:4751
#, c-format
msgid "Sorry, help file \"%s\" not found"
msgstr "Xin lỗi, không tìm thấy tập tin trợ giúp \"%s\""
-#: ../ex_cmds.c:5323
#, c-format
msgid "E150: Not a directory: %s"
msgstr "E150: %s không phải là một thư mục"
-#: ../ex_cmds.c:5446
#, c-format
msgid "E152: Cannot open %s for writing"
msgstr "E152: Không thể mở %s để ghi"
-#: ../ex_cmds.c:5471
#, c-format
msgid "E153: Unable to open %s for reading"
msgstr "E153: Không thể mở %s để đọc"
-#: ../ex_cmds.c:5500
#, c-format
msgid "E670: Mix of help file encodings within a language: %s"
msgstr ""
"E670: Tập tin trợ giúp sử dụng nhiều bảng mã khác nhau cho một ngôn ngữ: %s"
-#: ../ex_cmds.c:5565
-#, fuzzy, c-format
-msgid "E154: Duplicate tag \"%s\" in file %s/%s"
+#, c-format
+msgid "E154: Duplicate tag \"%s\" in file %s"
msgstr "E154: Thẻ ghi lặp lại \"%s\" trong tập tin %s"
-#: ../ex_cmds.c:5687
#, c-format
msgid "E160: Unknown sign command: %s"
msgstr "E160: Câu lệnh ký hiệu không biết: %s"
-#: ../ex_cmds.c:5704
msgid "E156: Missing sign name"
msgstr "E156: Thiếu tên ký hiệu"
-#: ../ex_cmds.c:5746
msgid "E612: Too many signs defined"
msgstr "E612: Định nghĩa quá nhiều ký hiệu"
-#: ../ex_cmds.c:5813
#, c-format
msgid "E239: Invalid sign text: %s"
msgstr "E239: Văn bản ký hiệu không thích hợp: %s"
-#: ../ex_cmds.c:5844 ../ex_cmds.c:6035
#, c-format
msgid "E155: Unknown sign: %s"
msgstr "E155: Ký hiệu không biết: %s"
-#: ../ex_cmds.c:5877
msgid "E159: Missing sign number"
msgstr "E159: Thiếu số của ký hiệu"
-#: ../ex_cmds.c:5971
#, c-format
msgid "E158: Invalid buffer name: %s"
msgstr "E158: Tên bộ đệm không đúng: %s"
-#: ../ex_cmds.c:6008
#, c-format
-msgid "E157: Invalid sign ID: %<PRId64>"
-msgstr "E157: ID của ký hiệu không đúng: %<PRId64>"
+msgid "E157: Invalid sign ID: %ld"
+msgstr "E157: ID của ký hiệu không đúng: %ld"
+
+msgid " (NOT FOUND)"
+msgstr " (KHÔNG TÌM THẤY)"
-#: ../ex_cmds.c:6066
msgid " (not supported)"
msgstr " (không được hỗ trợ)"
-#: ../ex_cmds.c:6169
msgid "[Deleted]"
msgstr "[bị xóa]"
-#: ../ex_cmds2.c:139
-msgid "Entering Debug mode. Type \"cont\" to continue."
-msgstr "Bật chế độ sửa lỗi (Debug). Gõ \"cont\" để tiếp tục."
-
-#: ../ex_cmds2.c:143 ../ex_docmd.c:759
-#, c-format
-msgid "line %<PRId64>: %s"
-msgstr "dòng %<PRId64>: %s"
-
-#: ../ex_cmds2.c:145
-#, c-format
-msgid "cmd: %s"
-msgstr "câu lệnh: %s"
-
-#: ../ex_cmds2.c:322
-#, c-format
-msgid "Breakpoint in \"%s%s\" line %<PRId64>"
-msgstr "Điểm dừng trên \"%s%s\" dòng %<PRId64>"
-
-#: ../ex_cmds2.c:581
-#, c-format
-msgid "E161: Breakpoint not found: %s"
-msgstr "E161: Không tìm thấy điểm dừng: %s"
-
-#: ../ex_cmds2.c:611
-msgid "No breakpoints defined"
-msgstr "Điểm dừng không được xác định"
-
-#: ../ex_cmds2.c:617
-#, c-format
-msgid "%3d %s %s line %<PRId64>"
-msgstr "%3d %s %s dòng %<PRId64>"
-
-#: ../ex_cmds2.c:942
-msgid "E750: First use \":profile start {fname}\""
-msgstr ""
-
-#: ../ex_cmds2.c:1269
-#, fuzzy, c-format
-msgid "Save changes to \"%s\"?"
-msgstr "Ghi nhớ thay đổi vào \"%.*s\"?"
-
-#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851
-msgid "Untitled"
-msgstr "Chưa đặt tên"
-
-#: ../ex_cmds2.c:1421
-#, c-format
-msgid "E162: No write since last change for buffer \"%s\""
-msgstr "E162: Thay đổi chưa được ghi nhớ trong bộ đệm \"%s\""
-
-#: ../ex_cmds2.c:1480
-msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
-msgstr ""
-"Cảnh báo: Chuyển tới bộ đệm khác không theo ý muốn (hãy kiểm tra câu lệnh tự "
-"động)"
-
-#: ../ex_cmds2.c:1826
-msgid "E163: There is only one file to edit"
-msgstr "E163: Chỉ có một tập tin để soạn thảo"
-
-#: ../ex_cmds2.c:1828
-msgid "E164: Cannot go before first file"
-msgstr "E164: Đây là tập tin đầu tiên"
-
-#: ../ex_cmds2.c:1830
-msgid "E165: Cannot go beyond last file"
-msgstr "E165: Đây là tập tin cuối cùng"
-
-#: ../ex_cmds2.c:2175
-#, c-format
-msgid "E666: compiler not supported: %s"
-msgstr "E666: trình biên dịch không được hỗ trợ: %s"
-
-#: ../ex_cmds2.c:2257
-#, c-format
-msgid "Searching for \"%s\" in \"%s\""
-msgstr "Tìm kiếm \"%s\" trong \"%s\""
-
-#: ../ex_cmds2.c:2284
-#, c-format
-msgid "Searching for \"%s\""
-msgstr "Tìm kiếm \"%s\""
-
-#: ../ex_cmds2.c:2307
-#, c-format
-msgid "not found in 'runtimepath': \"%s\""
-msgstr "không tìm thấy trong 'runtimepath': \"%s\""
-
-#: ../ex_cmds2.c:2472
-#, c-format
-msgid "Cannot source a directory: \"%s\""
-msgstr "Không thể thực hiện một thư mục: \"%s\""
-
-#: ../ex_cmds2.c:2518
-#, c-format
-msgid "could not source \"%s\""
-msgstr "không thực hiện được \"%s\""
-
-#: ../ex_cmds2.c:2520
-#, c-format
-msgid "line %<PRId64>: could not source \"%s\""
-msgstr "dòng %<PRId64>: không thực hiện được \"%s\""
-
-#: ../ex_cmds2.c:2535
-#, c-format
-msgid "sourcing \"%s\""
-msgstr "thực hiện \"%s\""
-
-#: ../ex_cmds2.c:2537
-#, c-format
-msgid "line %<PRId64>: sourcing \"%s\""
-msgstr "dòng %<PRId64>: thực hiện \"%s\""
-
-#: ../ex_cmds2.c:2693
-#, c-format
-msgid "finished sourcing %s"
-msgstr "thực hiện xong %s"
-
-#: ../ex_cmds2.c:2765
-#, fuzzy
-msgid "modeline"
-msgstr "Thêm 1 dòng"
-
-#: ../ex_cmds2.c:2767
-#, fuzzy
-msgid "--cmd argument"
-msgstr " vim [các tham số] "
-
-#: ../ex_cmds2.c:2769
-#, fuzzy
-msgid "-c argument"
-msgstr " vim [các tham số] "
-
-#: ../ex_cmds2.c:2771
-msgid "environment variable"
-msgstr ""
-
-#: ../ex_cmds2.c:2773
-#, fuzzy
-msgid "error handler"
-msgstr "Lỗi và sự gián đoạn"
-
-#: ../ex_cmds2.c:3020
-msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: Cảnh báo: Ký tự phân cách dòng không đúng. Rất có thể thiếu ^M"
-
-#: ../ex_cmds2.c:3139
-msgid "E167: :scriptencoding used outside of a sourced file"
-msgstr "E167: Lệnh :scriptencoding sử dụng ngoài tập tin script"
-
-#: ../ex_cmds2.c:3166
-msgid "E168: :finish used outside of a sourced file"
-msgstr "E168: Lệnh :finish sử dụng ngoài tập tin script"
-
-#: ../ex_cmds2.c:3389
-#, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "Ngôn ngữ %shiện thời: \"%s\""
-
-#: ../ex_cmds2.c:3404
-#, c-format
-msgid "E197: Cannot set language to \"%s\""
-msgstr "E197: Không thể thay đổi ngôn ngữ thành \"%s\""
-
-#. don't redisplay the window
-#. don't wait for return
-#: ../ex_docmd.c:387
msgid "Entering Ex mode. Type \"visual\" to go to Normal mode."
msgstr ""
"Chuyển vào chế độ Ex. Để chuyển về chế độ Thông thường hãy gõ \"visual\""
-#: ../ex_docmd.c:428
msgid "E501: At end-of-file"
msgstr "E501: Ở cuối tập tin"
-#: ../ex_docmd.c:513
msgid "E169: Command too recursive"
msgstr "E169: Câu lệnh quá đệ quy"
-#: ../ex_docmd.c:1006
#, c-format
msgid "E605: Exception not caught: %s"
msgstr "E605: Trường hợp đặc biệt không được xử lý: %s"
-#: ../ex_docmd.c:1085
msgid "End of sourced file"
msgstr "Kết thúc tập tin script"
-#: ../ex_docmd.c:1086
msgid "End of function"
msgstr "Kết thúc của hàm số"
-#: ../ex_docmd.c:1628
msgid "E464: Ambiguous use of user-defined command"
msgstr "E464: Sự sử dụng không rõ ràng câu lệnh do người dùng định nghĩa"
-#: ../ex_docmd.c:1638
msgid "E492: Not an editor command"
msgstr "E492: Không phải là câu lệnh của trình soạn thảo"
-#: ../ex_docmd.c:1729
msgid "E493: Backwards range given"
msgstr "E493: Đưa ra phạm vi ngược lại"
-#: ../ex_docmd.c:1733
msgid "Backwards range given, OK to swap"
msgstr "Đưa ra phạm vi ngược lại, thay đổi vị trí hai giới hạn"
-#. append
-#. typed wrong
-#: ../ex_docmd.c:1787
msgid "E494: Use w or w>>"
msgstr "E494: Hãy sử dụng w hoặc w>>"
-#: ../ex_docmd.c:3454
-msgid "E319: The command is not available in this version"
+msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: Xin lỗi, câu lệnh này không có trong phiên bản này"
-#: ../ex_docmd.c:3752
msgid "E172: Only one file name allowed"
msgstr "E172: Chỉ cho phép sử dụng một tên tập tin"
-#: ../ex_docmd.c:4238
msgid "1 more file to edit. Quit anyway?"
msgstr "Còn 1 tập tin nữa cần soạn thảo. Thoát?"
-#: ../ex_docmd.c:4242
#, c-format
msgid "%d more files to edit. Quit anyway?"
msgstr "Còn %d tập tin nữa chưa soạn thảo. Thoát?"
-#: ../ex_docmd.c:4248
msgid "E173: 1 more file to edit"
msgstr "E173: 1 tập tin nữa chờ soạn thảo."
-#: ../ex_docmd.c:4250
#, c-format
-msgid "E173: %<PRId64> more files to edit"
-msgstr "E173: %<PRId64> tập tin nữa chưa soạn thảo."
+msgid "E173: %ld more files to edit"
+msgstr "E173: %ld tập tin nữa chưa soạn thảo."
-#: ../ex_docmd.c:4320
msgid "E174: Command already exists: add ! to replace it"
msgstr "E174: Đã có câu lệnh: Thêm ! để thay thế"
-#: ../ex_docmd.c:4432
msgid ""
"\n"
" Name Args Range Complete Definition"
@@ -1641,352 +957,252 @@ msgstr ""
"\n"
" Tên\t\tTham_số Phạm_vi Phần_phụ Định_nghĩa"
-#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
msgstr "Không tìm thấy câu lệnh do người dùng định nghĩa"
-#: ../ex_docmd.c:4538
msgid "E175: No attribute specified"
msgstr "E175: Không có tham số được chỉ ra"
-#: ../ex_docmd.c:4583
msgid "E176: Invalid number of arguments"
msgstr "E176: Số lượng tham số không đúng"
-#: ../ex_docmd.c:4594
msgid "E177: Count cannot be specified twice"
msgstr "E177: Số đếm không thể được chỉ ra hai lần"
-#: ../ex_docmd.c:4603
msgid "E178: Invalid default value for count"
msgstr "E178: Giá trị của số đếm theo mặc định không đúng"
-#: ../ex_docmd.c:4625
-#, fuzzy
-msgid "E179: argument required for -complete"
+# TODO: Capitalise first word of message?
+msgid "E179: Argument required for complete"
msgstr "E179: yêu cầu đưa ra tham số để kết thúc"
-#: ../ex_docmd.c:4635
+#, c-format
+msgid "E180: Invalid complete value: %s"
+msgstr "E180: Giá trị phần phụ không đúng: %s"
+
+msgid "E468: Completion argument only allowed for custom completion"
+msgstr ""
+"E468: Tham số tự động kết thúc chỉ cho phép sử dụng với phần phụ đặc biệt"
+
+msgid "E467: Custom completion requires a function argument"
+msgstr "E467: Phần phục đặc biệt yêu cầu một tham số của hàm"
+
#, c-format
msgid "E181: Invalid attribute: %s"
msgstr "E181: Thuộc tính không đúng: %s"
-#: ../ex_docmd.c:4678
msgid "E182: Invalid command name"
msgstr "E182: Tên câu lệnh không đúng"
-#: ../ex_docmd.c:4691
msgid "E183: User defined commands must start with an uppercase letter"
msgstr "E183: Câu lệnh người dùng định nghĩa phải bắt đầu với một ký tự hoa"
-#: ../ex_docmd.c:4696
-#, fuzzy
-msgid "E841: Reserved name, cannot be used for user defined command"
-msgstr "E464: Sự sử dụng không rõ ràng câu lệnh do người dùng định nghĩa"
-
-#: ../ex_docmd.c:4751
#, c-format
msgid "E184: No such user-defined command: %s"
msgstr "E184: Không có câu lệnh người dùng định nghĩa như vậy: %s"
-#: ../ex_docmd.c:5219
#, c-format
-msgid "E180: Invalid complete value: %s"
-msgstr "E180: Giá trị phần phụ không đúng: %s"
-
-#: ../ex_docmd.c:5225
-msgid "E468: Completion argument only allowed for custom completion"
-msgstr ""
-"E468: Tham số tự động kết thúc chỉ cho phép sử dụng với phần phụ đặc biệt"
-
-#: ../ex_docmd.c:5231
-msgid "E467: Custom completion requires a function argument"
-msgstr "E467: Phần phục đặc biệt yêu cầu một tham số của hàm"
-
-#: ../ex_docmd.c:5257
-#, fuzzy, c-format
-msgid "E185: Cannot find color scheme '%s'"
+msgid "E185: Cannot find color scheme %s"
msgstr "E185: Không tin thấy sơ đồ màu sắc %s"
-#: ../ex_docmd.c:5263
msgid "Greetings, Vim user!"
msgstr "Xin chào người dùng Vim!"
-#: ../ex_docmd.c:5431
-#, fuzzy
-msgid "E784: Cannot close last tab page"
-msgstr "E444: Không được đóng cửa sổ cuối cùng"
-
-#: ../ex_docmd.c:5462
-#, fuzzy
-msgid "Already only one tab page"
-msgstr "Chỉ có một cửa sổ"
-
-#: ../ex_docmd.c:6004
-#, fuzzy, c-format
-msgid "Tab page %d"
-msgstr "Trang %d"
+msgid "Edit File in new window"
+msgstr "Soạn thảo tập tin trong cửa sổ mới"
-#: ../ex_docmd.c:6295
msgid "No swap file"
msgstr "Không có tập tin swap"
-#: ../ex_docmd.c:6478
-#, fuzzy
-msgid "E747: Cannot change directory, buffer is modified (add ! to override)"
-msgstr ""
-"E509: Không tạo được tập tin lưu trữ (thêm ! để bỏ qua việc kiểm tra lại)"
+msgid "Append File"
+msgstr "Thêm tập tin"
-#: ../ex_docmd.c:6485
msgid "E186: No previous directory"
msgstr "E186: Không có thư mục trước"
-#: ../ex_docmd.c:6530
msgid "E187: Unknown"
msgstr "E187: Không rõ"
-#: ../ex_docmd.c:6610
msgid "E465: :winsize requires two number arguments"
msgstr "E465: câu lệnh :winsize yêu cầu hai tham số bằng số"
-#: ../ex_docmd.c:6655
+#, c-format
+msgid "Window position: X %d, Y %d"
+msgstr "Vị trí cửa sổ: X %d, Y %d"
+
msgid "E188: Obtaining window position not implemented for this platform"
msgstr "E188: Trên hệ thống này việc xác định vị trí cửa sổ không làm việc"
-#: ../ex_docmd.c:6662
msgid "E466: :winpos requires two number arguments"
msgstr "E466: câu lệnh :winpos yêu câu hai tham số bằng số"
-#: ../ex_docmd.c:7241
-#, fuzzy, c-format
-msgid "E739: Cannot create directory: %s"
-msgstr "Không thể thực hiện một thư mục: \"%s\""
+msgid "Save Redirection"
+msgstr "Chuyển hướng ghi nhớ"
+
+msgid "Save View"
+msgstr "Ghi nhớ vẻ ngoài"
+
+msgid "Save Session"
+msgstr "Ghi nhớ buổi làm việc"
+
+msgid "Save Setup"
+msgstr "Ghi nhớ cấu hình"
-#: ../ex_docmd.c:7268
#, c-format
msgid "E189: \"%s\" exists (add ! to override)"
msgstr "E189: \"%s\" đã có (thêm !, để ghi đè)"
-#: ../ex_docmd.c:7273
#, c-format
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: Không mở được \"%s\" để ghi nhớ"
-#. set mark
-#: ../ex_docmd.c:7294
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr "E191: Tham số phải là một chữ cái hoặc dấu ngoặc thẳng/ngược"
-#: ../ex_docmd.c:7333
msgid "E192: Recursive use of :normal too deep"
msgstr "E192: Sử dụng đệ quy lệnh :normal quá sâu"
-#: ../ex_docmd.c:7807
msgid "E194: No alternate file name to substitute for '#'"
msgstr "E194: Không có tên tập tin tương đương để thay thế '#'"
-#: ../ex_docmd.c:7841
-msgid "E495: no autocommand file name to substitute for \"<afile>\""
+# TODO: Capitalise first word of message?
+msgid "E495: No autocommand file name to substitute for \"<afile>\""
msgstr "E495: Không có tên tập tin câu lệnh tự động để thay thế \"<afile>\""
-#: ../ex_docmd.c:7850
-msgid "E496: no autocommand buffer number to substitute for \"<abuf>\""
+# TODO: Capitalise first word of message?
+msgid "E496: No autocommand buffer number to substitute for \"<abuf>\""
msgstr ""
"E496: Không có số thứ tự bộ đệm câu lệnh tự động để thay thế \"<abuf>\""
-#: ../ex_docmd.c:7861
-msgid "E497: no autocommand match name to substitute for \"<amatch>\""
+# TODO: Capitalise first word of message?
+msgid "E497: No autocommand match name to substitute for \"<amatch>\""
msgstr "E497: Không có tên tương ứng câu lệnh tự động để thay thế \"<amatch>\""
-#: ../ex_docmd.c:7870
-msgid "E498: no :source file name to substitute for \"<sfile>\""
-msgstr "E498: không có tên tập tin :source để thay thế \"<sfile>\""
-
-#: ../ex_docmd.c:7876
-#, fuzzy
-msgid "E842: no line number to use for \"<slnum>\""
+# TODO: Capitalise first word of message?
+msgid "E498: No :source file name to substitute for \"<sfile>\""
msgstr "E498: không có tên tập tin :source để thay thế \"<sfile>\""
-#: ../ex_docmd.c:7903
-#, fuzzy, c-format
+#, no-c-format
msgid "E499: Empty file name for '%' or '#', only works with \":p:h\""
msgstr "E499: Tên tập tin rỗng cho '%' hoặc '#', chỉ làm việc với \":p:h\""
-#: ../ex_docmd.c:7905
msgid "E500: Evaluates to an empty string"
msgstr "E500: Kết quả của biểu thức là một chuỗi rỗng"
-#: ../ex_docmd.c:8838
msgid "E195: Cannot open viminfo file for reading"
msgstr "E195: Không thể mở tập tin viminfo để đọc"
-#: ../ex_eval.c:464
+msgid "E196: No digraphs in this version"
+msgstr "E196: Trong phiên bản này chữ ghép không được hỗ trợ"
+
msgid "E608: Cannot :throw exceptions with 'Vim' prefix"
msgstr ""
"E608: Không thể thực hiện lệnh :throw cho những ngoại lệ với tiền tố 'Vim'"
-#. always scroll up, don't overwrite
-#: ../ex_eval.c:496
#, c-format
msgid "Exception thrown: %s"
msgstr "Trường hợp ngoại lệ: %s"
-#: ../ex_eval.c:545
#, c-format
msgid "Exception finished: %s"
msgstr "Kết thúc việc xử lý trường hợp ngoại lệ: %s"
-#: ../ex_eval.c:546
#, c-format
msgid "Exception discarded: %s"
msgstr "Trường hợp ngoại lệ bị bỏ qua: %s"
-#: ../ex_eval.c:588 ../ex_eval.c:634
#, c-format
-msgid "%s, line %<PRId64>"
-msgstr "%s, dòng %<PRId64>"
+msgid "%s, line %ld"
+msgstr "%s, dòng %ld"
-#. always scroll up, don't overwrite
-#: ../ex_eval.c:608
#, c-format
msgid "Exception caught: %s"
msgstr "Xử lý trường hợp ngoại lệ: %s"
-#: ../ex_eval.c:676
#, c-format
msgid "%s made pending"
msgstr "%s thực hiện việc chờ đợi"
-#: ../ex_eval.c:679
#, c-format
msgid "%s resumed"
msgstr "%s được phục hồi lại"
-#: ../ex_eval.c:683
#, c-format
msgid "%s discarded"
msgstr "%s bị bỏ qua"
-#: ../ex_eval.c:708
msgid "Exception"
msgstr "Trường hợp ngoại lệ"
-#: ../ex_eval.c:713
msgid "Error and interrupt"
msgstr "Lỗi và sự gián đoạn"
-#: ../ex_eval.c:715
msgid "Error"
msgstr "Lỗi"
-#. if (pending & CSTP_INTERRUPT)
-#: ../ex_eval.c:717
msgid "Interrupt"
msgstr "Sự gián đoạn"
-#: ../ex_eval.c:795
+# TODO: Capitalise first word of message?
msgid "E579: :if nesting too deep"
msgstr "E579: :if xếp lồng vào nhau quá sâu"
-#: ../ex_eval.c:830
msgid "E580: :endif without :if"
msgstr "E580: :endif không có :if"
-#: ../ex_eval.c:873
msgid "E581: :else without :if"
msgstr "E581: :else không có :if"
-#: ../ex_eval.c:876
msgid "E582: :elseif without :if"
msgstr "E582: :elseif không có :if"
-#: ../ex_eval.c:880
-msgid "E583: multiple :else"
+# TODO: Capitalise first word of message?
+msgid "E583: Multiple :else"
msgstr "E583: phát hiện vài :else"
-#: ../ex_eval.c:883
msgid "E584: :elseif after :else"
msgstr "E584: :elseif sau :else"
-#: ../ex_eval.c:941
-#, fuzzy
-msgid "E585: :while/:for nesting too deep"
+msgid "E585: :while nesting too deep"
msgstr "E585: :while xếp lồng vào nhau quá sâu"
-#: ../ex_eval.c:1028
-#, fuzzy
-msgid "E586: :continue without :while or :for"
+msgid "E586: :continue without :while"
msgstr "E586: :continue không có :while"
-#: ../ex_eval.c:1061
-#, fuzzy
-msgid "E587: :break without :while or :for"
+msgid "E587: :break without :while"
msgstr "E587: :break không có :while"
-#: ../ex_eval.c:1102
-#, fuzzy
-msgid "E732: Using :endfor with :while"
-msgstr "E170: Thiếu câu lệnh :endwhile"
-
-#: ../ex_eval.c:1104
-#, fuzzy
-msgid "E733: Using :endwhile with :for"
-msgstr "E170: Thiếu câu lệnh :endwhile"
-
-#: ../ex_eval.c:1247
msgid "E601: :try nesting too deep"
msgstr "E601: :try xếp lồng vào nhau quá sâu"
-#: ../ex_eval.c:1317
msgid "E603: :catch without :try"
msgstr "E603: :catch không có :try"
-#. Give up for a ":catch" after ":finally" and ignore it.
-#. * Just parse.
-#: ../ex_eval.c:1332
msgid "E604: :catch after :finally"
msgstr "E604: :catch đứng sau :finally"
-#: ../ex_eval.c:1451
msgid "E606: :finally without :try"
msgstr "E606: :finally không có :try"
-#. Give up for a multiple ":finally" and ignore it.
-#: ../ex_eval.c:1467
-msgid "E607: multiple :finally"
+# TODO: Capitalise first word of message?
+msgid "E607: Multiple :finally"
msgstr "E607: phát hiện vài :finally"
-#: ../ex_eval.c:1571
msgid "E602: :endtry without :try"
msgstr "E602: :endtry không có :try"
-#: ../ex_eval.c:2026
msgid "E193: :endfunction not inside a function"
msgstr "E193: lệnh :endfunction chỉ được sử dụng trong một hàm số"
-#: ../ex_getln.c:1643
-#, fuzzy
-msgid "E788: Not allowed to edit another buffer now"
-msgstr "E48: Không cho phép trong hộp cát (sandbox)"
-
-#: ../ex_getln.c:1656
-#, fuzzy
-msgid "E811: Not allowed to change buffer information now"
-msgstr "E94: Không có bộ đệm tương ứng với %s"
-
-#: ../ex_getln.c:3178
msgid "tagname"
msgstr "tên thẻ ghi"
-#: ../ex_getln.c:3181
msgid " kind file\n"
msgstr " loại tập tin\n"
-#: ../ex_getln.c:4799
msgid "'history' option is zero"
msgstr "giá trị của tùy chọn 'history' bằng không"
-#: ../ex_getln.c:5046
#, c-format
msgid ""
"\n"
@@ -1995,312 +1211,201 @@ msgstr ""
"\n"
"# %s, Lịch sử (bắt đầu từ mới nhất tới cũ nhất):\n"
-#: ../ex_getln.c:5047
msgid "Command Line"
msgstr "Dòng lệnh"
-#: ../ex_getln.c:5048
msgid "Search String"
msgstr "Chuỗi tìm kiếm"
-#: ../ex_getln.c:5049
msgid "Expression"
msgstr "Biểu thức"
-#: ../ex_getln.c:5050
msgid "Input Line"
msgstr "Dòng nhập"
-#: ../ex_getln.c:5117
msgid "E198: cmd_pchar beyond the command length"
msgstr "E198: cmd_pchar lớn hơn chiều dài câu lệnh"
-#: ../ex_getln.c:5279
msgid "E199: Active window or buffer deleted"
msgstr "E199: Cửa sổ hoặc bộ đệm hoạt động bị xóa"
-#: ../file_search.c:203
-msgid "E854: path too long for completion"
-msgstr ""
-
-#: ../file_search.c:446
-#, c-format
-msgid ""
-"E343: Invalid path: '**[number]' must be at the end of the path or be "
-"followed by '%s'."
-msgstr ""
-"E343: Đường dẫn đưa ra không đúng: '**[số]' phải ở cuối đường dẫn hoặc theo "
-"sau bởi '%s'"
-
-#: ../file_search.c:1505
-#, c-format
-msgid "E344: Can't find directory \"%s\" in cdpath"
-msgstr "E344: Không tìm thấy thư mục \"%s\" để chuyển thư mục"
-
-#: ../file_search.c:1508
-#, c-format
-msgid "E345: Can't find file \"%s\" in path"
-msgstr "E345: Không tìm thấy tập tin \"%s\" trong đường dẫn"
-
-#: ../file_search.c:1512
-#, c-format
-msgid "E346: No more directory \"%s\" found in cdpath"
-msgstr "E346: Trong đường dẫn thay đổi thư mục không còn có thư mục \"%s\" nữa"
-
-#: ../file_search.c:1515
-#, c-format
-msgid "E347: No more file \"%s\" found in path"
-msgstr "E347: Trong đường dẫn path không còn có tập tin \"%s\" nữa"
-
-#: ../fileio.c:137
-#, fuzzy
-msgid "E812: Autocommands changed buffer or buffer name"
-msgstr "E135: Các lệnh tự động *Filter* không được thay đổi bộ đệm hiện thời"
-
-#: ../fileio.c:368
msgid "Illegal file name"
msgstr "Tên tập tin không cho phép"
-#: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578
msgid "is a directory"
msgstr "là một thư mục"
-#: ../fileio.c:397
msgid "is not a file"
msgstr "không phải là một tập tin"
-#: ../fileio.c:508 ../fileio.c:3522
msgid "[New File]"
msgstr "[Tập tin mới]"
-#: ../fileio.c:511
-msgid "[New DIRECTORY]"
-msgstr ""
-
-#: ../fileio.c:529 ../fileio.c:532
-msgid "[File too big]"
-msgstr ""
-
-#: ../fileio.c:534
msgid "[Permission Denied]"
msgstr "[Truy cập bị từ chối]"
-#: ../fileio.c:653
msgid "E200: *ReadPre autocommands made the file unreadable"
msgstr ""
"E200: Câu lệnh tự động *ReadPre làm cho tập tin trở thành không thể đọc"
-#: ../fileio.c:655
msgid "E201: *ReadPre autocommands must not change current buffer"
msgstr "E201: Câu lệnh tự động *ReadPre không được thay đổi bộ đệm hoạt động"
-#: ../fileio.c:672
-msgid "Nvim: Reading from stdin...\n"
+msgid "Vim: Reading from stdin...\n"
msgstr "Vim: Đọc từ đầu vào tiêu chuẩn stdin...\n"
-#. Re-opening the original file failed!
-#: ../fileio.c:909
+msgid "Reading from stdin..."
+msgstr "Đọc từ đầu vào tiêu chuẩn stdin..."
+
msgid "E202: Conversion made file unreadable!"
msgstr "E202: Sự biến đổi làm cho tập tin trở thành không thể đọc!"
-#. fifo or socket
-#: ../fileio.c:1782
msgid "[fifo/socket]"
msgstr "[fifo/socket]"
-#. fifo
-#: ../fileio.c:1788
msgid "[fifo]"
msgstr "[fifo]"
-#. or socket
-#: ../fileio.c:1794
msgid "[socket]"
msgstr "[socket]"
-#. or character special
-#: ../fileio.c:1801
-#, fuzzy
-msgid "[character special]"
-msgstr "1 ký tự"
+msgid "[RO]"
+msgstr "[Chỉ đọc]"
-#: ../fileio.c:1815
msgid "[CR missing]"
msgstr "[thiếu ký tự CR]"
-#: ../fileio.c:1819
+msgid "[NL found]"
+msgstr "[tìm thấy ký tự NL]"
+
msgid "[long lines split]"
msgstr "[dòng dài được chia nhỏ]"
-#: ../fileio.c:1823 ../fileio.c:3512
msgid "[NOT converted]"
msgstr "[KHÔNG được chuyển đổi]"
-#: ../fileio.c:1826 ../fileio.c:3515
msgid "[converted]"
msgstr "[đã chuyển bảng mã]"
-#: ../fileio.c:1831
-#, fuzzy, c-format
-msgid "[CONVERSION ERROR in line %<PRId64>]"
-msgstr "[BYTE KHÔNG CHO PHÉP trên dòng %<PRId64>]"
+msgid "[crypted]"
+msgstr "[đã mã hóa]"
+
+msgid "[CONVERSION ERROR]"
+msgstr "[LỖI CHUYỂN BẢNG MÃ]"
-#: ../fileio.c:1835
#, c-format
-msgid "[ILLEGAL BYTE in line %<PRId64>]"
-msgstr "[BYTE KHÔNG CHO PHÉP trên dòng %<PRId64>]"
+msgid "[ILLEGAL BYTE in line %ld]"
+msgstr "[BYTE KHÔNG CHO PHÉP trên dòng %ld]"
-#: ../fileio.c:1838
msgid "[READ ERRORS]"
msgstr "[LỖI ĐỌC]"
-#: ../fileio.c:2104
msgid "Can't find temp file for conversion"
msgstr "Không tìm thấy tập tin tạm thời (temp) để chuyển bảng mã"
-#: ../fileio.c:2110
msgid "Conversion with 'charconvert' failed"
msgstr "Chuyển đổi nhờ 'charconvert' không được thực hiện"
-#: ../fileio.c:2113
msgid "can't read output of 'charconvert'"
msgstr "không đọc được đầu ra của 'charconvert'"
-#: ../fileio.c:2437
-#, fuzzy
-msgid "E676: No matching autocommands for acwrite buffer"
-msgstr "Không có câu lệnh tự động tương ứng"
-
-#: ../fileio.c:2466
msgid "E203: Autocommands deleted or unloaded buffer to be written"
msgstr "E203: Câu lệnh tự động đã xóa hoặc bỏ nạp bộ đệm cần ghi nhớ"
-#: ../fileio.c:2486
msgid "E204: Autocommand changed number of lines in unexpected way"
msgstr "E204: Câu lệnh tự động đã thay đổ số dòng theo cách không mong muốn"
-#: ../fileio.c:2548 ../fileio.c:2565
+msgid "NetBeans disallows writes of unmodified buffers"
+msgstr "NetBeans không cho phép ghi nhớ bộ đệm chưa có thay đổi nào"
+
+msgid "Partial writes disallowed for NetBeans buffers"
+msgstr "Ghi nhớ một phần bộ đệm NetBeans không được cho phép"
+
msgid "is not a file or writable device"
msgstr "không phải là một tập tin thay một thiết bị có thể ghi nhớ"
-#: ../fileio.c:2601
msgid "is read-only (add ! to override)"
msgstr "là tập tin chỉ đọc (thêm ! để ghi nhớ bằng mọi giá)"
-#: ../fileio.c:2886
msgid "E506: Can't write to backup file (add ! to override)"
msgstr ""
"E506: Không thể ghi nhớ vào tập tin lưu trữ (thêm ! để ghi nhớ bằng mọi giá"
-#: ../fileio.c:2898
msgid "E507: Close error for backup file (add ! to override)"
msgstr "E507: Lỗi đóng tập tin lưu trữ (thêm ! để bỏ qua việc kiểm tra lại)"
-#: ../fileio.c:2901
msgid "E508: Can't read file for backup (add ! to override)"
msgstr ""
"E508: Không đọc được tập tin lưu trữ (thêm ! để bỏ qua việc kiểm tra lại)"
-#: ../fileio.c:2923
msgid "E509: Cannot create backup file (add ! to override)"
msgstr ""
"E509: Không tạo được tập tin lưu trữ (thêm ! để bỏ qua việc kiểm tra lại)"
-#: ../fileio.c:3008
msgid "E510: Can't make backup file (add ! to override)"
msgstr ""
"E510: Không tạo được tập tin lưu trữ (thêm ! để bỏ qua việc kiểm tra lại)"
-#. Can't write without a tempfile!
-#: ../fileio.c:3121
msgid "E214: Can't find temp file for writing"
msgstr "E214: Không tìm thấy tập tin tạm thời (temp) để ghi nhớ"
-#: ../fileio.c:3134
msgid "E213: Cannot convert (add ! to write without conversion)"
msgstr ""
"E213: Không thể chuyển đổi bảng mã (thêm ! để ghi nhớ mà không chuyển đổi)"
-#: ../fileio.c:3169
msgid "E166: Can't open linked file for writing"
msgstr "E166: Không thể mở tập tin liên kết để ghi nhớ"
-#: ../fileio.c:3173
msgid "E212: Can't open file for writing"
msgstr "E212: Không thể mở tập tin để ghi nhớ"
-#: ../fileio.c:3363
msgid "E667: Fsync failed"
msgstr "E667: Không thực hiện thành công hàm số fsync()"
-#: ../fileio.c:3398
msgid "E512: Close failed"
msgstr "E512: Thao tác đóng không thành công"
-#: ../fileio.c:3436
-#, fuzzy
-msgid "E513: write error, conversion failed (make 'fenc' empty to override)"
+# TODO: Capitalise first word of message?
+msgid "E513: Write error, conversion failed"
msgstr "E513: Lỗi ghi nhớ, biến đổi không thành công"
-#: ../fileio.c:3441
-#, c-format
-msgid ""
-"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
-"override)"
-msgstr ""
-
-#: ../fileio.c:3448
-msgid "E514: write error (file system full?)"
+# TODO: Capitalise first word of message?
+msgid "E514: Write error (file system full?)"
msgstr "E514: lỗi ghi nhớ (không còn chỗ trống?)"
-#: ../fileio.c:3506
msgid " CONVERSION ERROR"
msgstr " LỖI BIẾN ĐỔI"
-#: ../fileio.c:3509
-#, fuzzy, c-format
-msgid " in line %<PRId64>;"
-msgstr "dòng %<PRId64>"
-
-#: ../fileio.c:3519
msgid "[Device]"
msgstr "[Thiết bị]"
-#: ../fileio.c:3522
msgid "[New]"
msgstr "[Mới]"
-#: ../fileio.c:3535
msgid " [a]"
msgstr " [a]"
-#: ../fileio.c:3535
msgid " appended"
msgstr " đã thêm"
-#: ../fileio.c:3537
msgid " [w]"
msgstr " [w]"
-#: ../fileio.c:3537
msgid " written"
msgstr " đã ghi"
-#: ../fileio.c:3579
msgid "E205: Patchmode: can't save original file"
msgstr "E205: Chế độ vá lỗi (patch): không thể ghi nhớ tập tin gốc"
-#: ../fileio.c:3602
-msgid "E206: patchmode: can't touch empty original file"
+# TODO: Capitalise first word of message?
+msgid "E206: Patchmode: can't touch empty original file"
msgstr ""
"E206: Chế độ vá lỗi (patch): không thể thay đổi tham số của tập tin gốc "
"trống rỗng"
-#: ../fileio.c:3616
msgid "E207: Can't delete backup file"
msgstr "E207: Không thể xóa tập tin lưu trữ (backup)"
-#: ../fileio.c:3672
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
@@ -2308,97 +1413,73 @@ msgstr ""
"\n"
"CẢNH BÁO: Tập tin gốc có thể bị mất hoặc bị hỏng\n"
-#: ../fileio.c:3675
msgid "don't quit the editor until the file is successfully written!"
msgstr ""
"đừng thoát khởi trình soạn thảo, khi tập tin còn chưa được ghi nhớ thành cồng"
-#: ../fileio.c:3795
msgid "[dos]"
msgstr "[dos]"
-#: ../fileio.c:3795
msgid "[dos format]"
msgstr "[định dạng dos]"
-#: ../fileio.c:3801
msgid "[mac]"
msgstr "[mac]"
-#: ../fileio.c:3801
msgid "[mac format]"
msgstr "[định dạng mac]"
-#: ../fileio.c:3807
msgid "[unix]"
msgstr "[unix]"
-#: ../fileio.c:3807
msgid "[unix format]"
msgstr "[định dạng unix]"
-#: ../fileio.c:3831
msgid "1 line, "
msgstr "1 dòng, "
-#: ../fileio.c:3833
#, c-format
-msgid "%<PRId64> lines, "
-msgstr "%<PRId64> dòng, "
+msgid "%ld lines, "
+msgstr "%ld dòng, "
-#: ../fileio.c:3836
msgid "1 character"
msgstr "1 ký tự"
-#: ../fileio.c:3838
#, c-format
-msgid "%<PRId64> characters"
-msgstr "%<PRId64> ký tự"
+msgid "%ld characters"
+msgstr "%ld ký tự"
-#: ../fileio.c:3849
msgid "[noeol]"
msgstr "[noeol]"
-#: ../fileio.c:3849
msgid "[Incomplete last line]"
msgstr "[Dòng cuối cùng không đầy đủ]"
-#. don't overwrite messages here
-#. must give this prompt
-#. don't use emsg() here, don't want to flush the buffers
-#: ../fileio.c:3865
msgid "WARNING: The file has been changed since reading it!!!"
msgstr "CẢNH BÁO: Tập tin đã thay đổi so với thời điểm đọc!!!"
-#: ../fileio.c:3867
msgid "Do you really want to write to it"
msgstr "Bạn có chắc muốn ghi nhớ vào tập tin này"
-#: ../fileio.c:4648
#, c-format
msgid "E208: Error writing to \"%s\""
msgstr "E208: Lỗi ghi nhớ vào \"%s\""
-#: ../fileio.c:4655
#, c-format
msgid "E209: Error closing \"%s\""
msgstr "E209: Lỗi đóng \"%s\""
-#: ../fileio.c:4657
#, c-format
msgid "E210: Error reading \"%s\""
msgstr "E210: Lỗi đọc \"%s\""
-#: ../fileio.c:4883
msgid "E246: FileChangedShell autocommand deleted buffer"
msgstr "E246: Bộ đệm bị xóa khi thực hiện câu lệnh tự động FileChangedShell"
-#: ../fileio.c:4894
-#, fuzzy, c-format
-msgid "E211: File \"%s\" no longer available"
+#, c-format
+msgid "E211: Warning: File \"%s\" no longer available"
msgstr "E211: Cảnh báo: Tập tin \"%s\" không còn truy cập được nữa"
-#: ../fileio.c:4906
#, c-format
msgid ""
"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as "
@@ -2407,44 +1488,28 @@ msgstr ""
"W12: Cảnh báo: Tập tin \"%s\" và bộ đệm Vim đã thay đổi không phụ thuộc vào "
"nhau"
-#: ../fileio.c:4907
-#, fuzzy
-msgid "See \":help W12\" for more info."
-msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
-
-#: ../fileio.c:4910
#, c-format
msgid "W11: Warning: File \"%s\" has changed since editing started"
msgstr ""
"W11: Cảnh báo: Tập tin \"%s\" đã thay đổi sau khi việc soạn thảo bắt đầu"
-#: ../fileio.c:4911
-msgid "See \":help W11\" for more info."
-msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
-
-#: ../fileio.c:4914
#, c-format
msgid "W16: Warning: Mode of file \"%s\" has changed since editing started"
msgstr ""
"W16: Cảnh báo: chế độ truy cập tới tập tin \"%s\" đã thay đổi sau khi bắt "
"đầu soạn thảo"
-#: ../fileio.c:4915
-#, fuzzy
-msgid "See \":help W16\" for more info."
-msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
-
-#: ../fileio.c:4927
#, c-format
msgid "W13: Warning: File \"%s\" has been created after editing started"
msgstr ""
"W13: Cảnh báo: tập tin \"%s\" được tạo ra sau khi việc soạn thảo bắt đầu"
-#: ../fileio.c:4947
+msgid "See \":help W11\" for more info."
+msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
+
msgid "Warning"
msgstr "Cảnh báo"
-#: ../fileio.c:4948
msgid ""
"&OK\n"
"&Load File"
@@ -2452,48 +1517,33 @@ msgstr ""
"&OK\n"
"&Nạp tập tin"
-#: ../fileio.c:5065
#, c-format
msgid "E462: Could not prepare for reloading \"%s\""
msgstr "E462: Không thể chuẩn bị để nạp lại \"%s\""
-#: ../fileio.c:5078
#, c-format
msgid "E321: Could not reload \"%s\""
msgstr "E321: Không thể nạp lại \"%s\""
-#: ../fileio.c:5601
msgid "--Deleted--"
msgstr "--Bị xóa--"
-#: ../fileio.c:5732
-#, c-format
-msgid "auto-removing autocommand: %s <buffer=%d>"
-msgstr ""
-
-#. the group doesn't exist
-#: ../fileio.c:5772
#, c-format
msgid "E367: No such group: \"%s\""
msgstr "E367: Nhóm \"%s\" không tồn tại"
-#: ../fileio.c:5897
#, c-format
msgid "E215: Illegal character after *: %s"
msgstr "E215: Ký tự không cho phép sau *: %s"
-#: ../fileio.c:5905
#, c-format
msgid "E216: No such event: %s"
msgstr "E216: Sự kiện không có thật: %s"
-#: ../fileio.c:5907
#, c-format
msgid "E216: No such group or event: %s"
msgstr "E216: Nhóm hoặc sự kiện không có thật: %s"
-#. Highlight title
-#: ../fileio.c:6090
msgid ""
"\n"
"--- Autocommands ---"
@@ -2501,681 +1551,400 @@ msgstr ""
"\n"
"--- Câu lệnh tự động ---"
-#: ../fileio.c:6293
-#, fuzzy, c-format
-msgid "E680: <buffer=%d>: invalid buffer number "
-msgstr "số của bộ đệm không đúng"
-
-#: ../fileio.c:6370
msgid "E217: Can't execute autocommands for ALL events"
msgstr "E217: Không thể thực hiện câu lệnh tự động cho MỌI sự kiện"
-#: ../fileio.c:6393
msgid "No matching autocommands"
msgstr "Không có câu lệnh tự động tương ứng"
-#: ../fileio.c:6831
-msgid "E218: autocommand nesting too deep"
+# TODO: Capitalise first word of message?
+msgid "E218: Autocommand nesting too deep"
msgstr "E218: câu lệnh tự động xếp lồng vào nhau quá xâu"
-#: ../fileio.c:7143
#, c-format
msgid "%s Autocommands for \"%s\""
msgstr "%s câu lệnh tự động cho \"%s\""
-#: ../fileio.c:7149
#, c-format
msgid "Executing %s"
msgstr "Thực hiện %s"
-#: ../fileio.c:7211
#, c-format
msgid "autocommand %s"
msgstr "câu lệnh tự động %s"
-#: ../fileio.c:7795
msgid "E219: Missing {."
msgstr "E219: Thiếu {."
-#: ../fileio.c:7797
msgid "E220: Missing }."
msgstr "E220: Thiếu }."
-#: ../fold.c:93
msgid "E490: No fold found"
msgstr "E490: Không tìm thấy nếp gấp"
-#: ../fold.c:544
msgid "E350: Cannot create fold with current 'foldmethod'"
msgstr ""
"E350: Không thể tạo nếp gấp với giá trị hiện thời của tùy chọn 'foldmethod'"
-#: ../fold.c:546
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr ""
"E351: Không thể xóa nếp gấp với giá trị hiện thời của tùy chọn 'foldmethod'"
-#: ../fold.c:1784
-#, c-format
-msgid "+--%3ld lines folded "
-msgstr "+--%3ld dòng được gấp"
-
-#. buffer has already been read
-#: ../getchar.c:273
msgid "E222: Add to read buffer"
msgstr "E222: Thêm vào bộ đệm đang đọc"
-#: ../getchar.c:2040
-msgid "E223: recursive mapping"
+# TODO: Capitalise first word of message?
+msgid "E223: Recursive mapping"
msgstr "E223: ánh xạ đệ quy"
-#: ../getchar.c:2849
-#, c-format
-msgid "E224: global abbreviation already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E224: Global abbreviation already exists for %s"
msgstr "E224: đã có sự viết tắt toàn cầu cho %s"
-#: ../getchar.c:2852
-#, c-format
-msgid "E225: global mapping already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E225: Global mapping already exists for %s"
msgstr "E225: đã có ánh xạ toàn cầu cho %s"
-#: ../getchar.c:2952
-#, c-format
-msgid "E226: abbreviation already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E226: Abbreviation already exists for %s"
msgstr "E226: đã có sự viết tắt cho %s"
-#: ../getchar.c:2955
-#, c-format
-msgid "E227: mapping already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E227: Mapping already exists for %s"
msgstr "E227: đã có ánh xạ cho %s"
-#: ../getchar.c:3008
msgid "No abbreviation found"
msgstr "Không tìm thấy viết tắt"
-#: ../getchar.c:3010
msgid "No mapping found"
msgstr "Không tìm thấy ánh xạ"
-#: ../getchar.c:3974
msgid "E228: makemap: Illegal mode"
msgstr "E228: makemap: Chế độ không cho phép"
-#. key value of 'cedit' option
-#. type of cmdline window or 0
-#. result of cmdline window or 0
-#: ../globals.h:924
-msgid "--No lines in buffer--"
-msgstr "-- Không có dòng nào trong bộ đệm --"
-
-#.
-#. * The error messages that can be shared are included here.
-#. * Excluded are errors that are only used once and debugging messages.
-#.
-#: ../globals.h:996
-msgid "E470: Command aborted"
-msgstr "E470: Câu lệnh bị dừng"
-
-#: ../globals.h:997
-msgid "E471: Argument required"
-msgstr "E471: Cần chỉ ra tham số"
-
-#: ../globals.h:998
-msgid "E10: \\ should be followed by /, ? or &"
-msgstr "E10: Sau \\ phải là các ký tự /, ? hoặc &"
-
-#: ../globals.h:1000
-msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits"
-msgstr "E11: Lỗi trong cửa sổ dòng lệnh; <CR> thực hiện, CTRL-C thoát"
-
-#: ../globals.h:1002
-msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search"
-msgstr ""
-"E12: Câu lệnh không cho phép từ exrc/vimrc trong thư mục hiện thời hoặc "
-"trong tìm kiếm thẻ ghi"
+msgid "<cannot open> "
+msgstr "<không thể mở> "
-#: ../globals.h:1003
-msgid "E171: Missing :endif"
-msgstr "E171: Thiếu câu lệnh :endif"
-
-#: ../globals.h:1004
-msgid "E600: Missing :endtry"
-msgstr "E600: Thiếu câu lệnh :endtry"
-
-#: ../globals.h:1005
-msgid "E170: Missing :endwhile"
-msgstr "E170: Thiếu câu lệnh :endwhile"
+#, c-format
+msgid "E616: vim_SelFile: can't get font %s"
+msgstr "E616: vim_SelFile: không tìm thấy phông chữ %s"
-#: ../globals.h:1006
-#, fuzzy
-msgid "E170: Missing :endfor"
-msgstr "E171: Thiếu câu lệnh :endif"
+msgid "E614: vim_SelFile: can't return to current directory"
+msgstr "E614: vim_SelFile: không trở lại được thư mục hiện thời"
-#: ../globals.h:1007
-msgid "E588: :endwhile without :while"
-msgstr "E588: Câu lệnh :endwhile không có lệnh :while (1 cặp)"
+msgid "Pathname:"
+msgstr "Đường dẫn tới tập tin:"
-#: ../globals.h:1008
-#, fuzzy
-msgid "E588: :endfor without :for"
-msgstr "E580: :endif không có :if"
+msgid "E615: vim_SelFile: can't get current directory"
+msgstr "E615: vim_SelFile: không tìm thấy thư mục hiện thời"
-#: ../globals.h:1009
-msgid "E13: File exists (add ! to override)"
-msgstr "E13: Tập tin đã tồn tại (thêm ! để ghi chèn)"
+msgid "OK"
+msgstr "Đồng ý"
-#: ../globals.h:1010
-msgid "E472: Command failed"
-msgstr "E472: Không thực hiện thành công câu lệnh"
+msgid "Cancel"
+msgstr "Hủy bỏ"
-#: ../globals.h:1011
-msgid "E473: Internal error"
-msgstr "E473: Lỗi nội bộ"
+msgid "Vim dialog"
+msgstr "Hộp thoại Vim"
-#: ../globals.h:1012
-msgid "Interrupted"
-msgstr "Bị gián đoạn"
+msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
+msgstr "Thanh cuộn: Không thể xác định hình học của thanh cuộn."
-#: ../globals.h:1013
-msgid "E14: Invalid address"
-msgstr "E14: Địa chỉ không cho phép"
+msgid "E232: Cannot create BalloonEval with both message and callback"
+msgstr "E232: Không tạo được BalloonEval với cả thông báo và lời gọi ngược lại"
-#: ../globals.h:1014
-msgid "E474: Invalid argument"
-msgstr "E474: Tham số không cho phép"
+msgid "E229: Cannot start the GUI"
+msgstr "E229: Không chạy được giao diện đồ họa GUI"
-#: ../globals.h:1015
#, c-format
-msgid "E475: Invalid argument: %s"
-msgstr "E475: Tham số không cho phép: %s"
+msgid "E230: Cannot read from \"%s\""
+msgstr "E230: Không đọc được từ \"%s\""
-#: ../globals.h:1016
-#, c-format
-msgid "E15: Invalid expression: %s"
-msgstr "E15: Biểu thức không cho phép: %s"
+msgid "E665: Cannot start GUI, no valid font found"
+msgstr ""
+"E665: Không chạy được giao diện đồ họa GUI, đưa ra phông chữ không đúng"
-#: ../globals.h:1017
-msgid "E16: Invalid range"
-msgstr "E16: Vùng không cho phép"
+msgid "E231: 'guifontwide' invalid"
+msgstr "E231: 'guifontwide' có giá trị không đúng"
-#: ../globals.h:1018
-msgid "E476: Invalid command"
-msgstr "E476: Câu lệnh không cho phép"
+msgid "E599: Value of 'imactivatekey' is invalid"
+msgstr "E599: Giá trị của 'imactivatekey' không đúng"
-#: ../globals.h:1019
#, c-format
-msgid "E17: \"%s\" is a directory"
-msgstr "E17: \"%s\" là mộ thư mục"
+msgid "E254: Cannot allocate color %s"
+msgstr "E254: Không chỉ định được màu %s"
-#: ../globals.h:1020
-#, fuzzy
-msgid "E900: Invalid job id"
-msgstr "E49: Kích thước thanh cuộn không cho phép"
+msgid "Vim dialog..."
+msgstr "Hộp thoại Vim..."
-#: ../globals.h:1021
-msgid "E901: Job table is full"
+msgid ""
+"&Yes\n"
+"&No\n"
+"&Cancel"
msgstr ""
+"&Có\n"
+"&Không\n"
+"&Dừng"
-#: ../globals.h:1024
-#, c-format
-msgid "E364: Library call failed for \"%s()\""
-msgstr "E364: Gọi hàm số \"%s()\" của thư viện không thành công"
+msgid "Input _Methods"
+msgstr "Phương pháp _nhập liệu"
-#: ../globals.h:1026
-msgid "E19: Mark has invalid line number"
-msgstr "E19: Dấu hiệu chỉ đến một số thứ tự dòng không đúng"
+msgid "VIM - Search and Replace..."
+msgstr "VIM - Tìm kiếm và thay thế..."
-#: ../globals.h:1027
-msgid "E20: Mark not set"
-msgstr "E20: Dấu hiệu không được xác định"
+msgid "VIM - Search..."
+msgstr "VIM - Tìm kiếm..."
-#: ../globals.h:1029
-msgid "E21: Cannot make changes, 'modifiable' is off"
-msgstr "E21: Không thể thay đổi, vì tùy chọn 'modifiable' bị tắt"
+msgid "Find what:"
+msgstr "Tìm kiếm gì:"
-#: ../globals.h:1030
-msgid "E22: Scripts nested too deep"
-msgstr "E22: Các script lồng vào nhau quá sâu"
+msgid "Replace with:"
+msgstr "Thay thế bởi:"
-#: ../globals.h:1031
-msgid "E23: No alternate file"
-msgstr "E23: Không có tập tin xen kẽ"
+msgid "Match whole word only"
+msgstr "Chỉ tìm tương ứng hoàn toàn với từ"
-#: ../globals.h:1032
-msgid "E24: No such abbreviation"
-msgstr "E24: Không có chữ viết tắt như vậy"
+msgid "Match case"
+msgstr "Có tính kiểu chữ"
-#: ../globals.h:1033
-msgid "E477: No ! allowed"
-msgstr "E477: Không cho phép !"
+msgid "Direction"
+msgstr "Hướng"
-#: ../globals.h:1035
-msgid "E25: Nvim does not have a built-in GUI"
-msgstr "E25: Không sử dụng được giao diện đồ họa vì không chọn khi biên dịch"
+msgid "Up"
+msgstr "Lên"
-#: ../globals.h:1036
-#, c-format
-msgid "E28: No such highlight group name: %s"
-msgstr "E28: Nhóm chiếu sáng cú pháp %s không tồn tại"
+msgid "Down"
+msgstr "Xuống"
-#: ../globals.h:1037
-msgid "E29: No inserted text yet"
-msgstr "E29: Tạm thời chưa có văn bản được chèn"
+msgid "Find Next"
+msgstr "Tìm tiếp"
-#: ../globals.h:1038
-msgid "E30: No previous command line"
-msgstr "E30: Không có dòng lệnh trước"
+msgid "Replace"
+msgstr "Thay thế"
-#: ../globals.h:1039
-msgid "E31: No such mapping"
-msgstr "E31: Không có ánh xạ (mapping) như vậy"
+msgid "Replace All"
+msgstr "Thay thế tất cả"
-#: ../globals.h:1040
-msgid "E479: No match"
-msgstr "E479: Không có tương ứng"
+msgid "Vim: Received \"die\" request from session manager\n"
+msgstr "Vim: Nhận được yêu cầu \"chết\" (dừng) từ trình quản lý màn hình\n"
-#: ../globals.h:1041
-#, c-format
-msgid "E480: No match: %s"
-msgstr "E480: Không có tương ứng: %s"
+msgid "Vim: Main window unexpectedly destroyed\n"
+msgstr "Vim: Cửa sổ chính đã bị đóng đột ngột\n"
-#: ../globals.h:1042
-msgid "E32: No file name"
-msgstr "E32: Không có tên tập tin"
+msgid "Font Selection"
+msgstr "Chọn phông chữ"
-#: ../globals.h:1044
-msgid "E33: No previous substitute regular expression"
-msgstr "E33: Không có biểu thức chính quy trước để thay thế"
+msgid "Used CUT_BUFFER0 instead of empty selection"
+msgstr "Sử dụng CUT_BUFFER0 thay cho lựa chọn trống rỗng"
-#: ../globals.h:1045
-msgid "E34: No previous command"
-msgstr "E34: Không có câu lệnh trước"
+msgid "Filter"
+msgstr "Đầu lọc"
-#: ../globals.h:1046
-msgid "E35: No previous regular expression"
-msgstr "E35: Không có biểu thức chính quy trước"
+msgid "Directories"
+msgstr "Thư mục"
-#: ../globals.h:1047
-msgid "E481: No range allowed"
-msgstr "E481: Không cho phép sử dụng phạm vi"
+msgid "Help"
+msgstr "Trợ giúp"
-#: ../globals.h:1048
-msgid "E36: Not enough room"
-msgstr "E36: Không đủ chỗ trống"
+msgid "Files"
+msgstr "Tập tin"
-#: ../globals.h:1049
-#, c-format
-msgid "E482: Can't create file %s"
-msgstr "E482: Không tạo được tập tin %s"
+msgid "Selection"
+msgstr "Lựa chọn"
-#: ../globals.h:1050
-msgid "E483: Can't get temp file name"
-msgstr "E483: Không nhận được tên tập tin tạm thời (temp)"
+msgid "Undo"
+msgstr "Hủy thao tác"
-#: ../globals.h:1051
#, c-format
-msgid "E484: Can't open file %s"
-msgstr "E484: Không mở được tập tin %s"
+msgid "E671: Cannot find window title \"%s\""
+msgstr "E671: Không tìm được tiêu đề cửa sổ \"%s\""
-#: ../globals.h:1052
#, c-format
-msgid "E485: Can't read file %s"
-msgstr "E485: Không đọc được tập tin %s"
+msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
+msgstr "E243: Tham số không được hỗ trợ: \"-%s\"; Hãy sử dụng phiên bản OLE."
-#: ../globals.h:1054
-msgid "E37: No write since last change (add ! to override)"
-msgstr "E37: Thay đổi chưa được ghi nhớ (thêm ! để bỏ qua ghi nhớ)"
+msgid "E672: Unable to open window inside MDI application"
+msgstr "E672: Không mở được cửa sổ bên trong ứng dụng MDI"
-#: ../globals.h:1055
-#, fuzzy
-msgid "E37: No write since last change"
-msgstr "[Thay đổi chưa được ghi nhớ]\n"
+msgid "Find string (use '\\\\' to find a '\\')"
+msgstr "Tìm kiếm chuỗi (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-#: ../globals.h:1056
-msgid "E38: Null argument"
-msgstr "E38: Tham sô bằng 0"
+msgid "Find & Replace (use '\\\\' to find a '\\')"
+msgstr "Tìm kiếm và Thay thế (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-#: ../globals.h:1057
-msgid "E39: Number expected"
-msgstr "E39: Yêu cầu một số"
+msgid "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
+msgstr ""
+"Vim E458: Không chỉ định được bản ghi trong bảng màu, một vài màu có thể "
+"hiển thị không chính xác"
-#: ../globals.h:1058
#, c-format
-msgid "E40: Can't open errorfile %s"
-msgstr "E40: Không mở được tập tin lỗi %s"
+msgid "E250: Fonts for the following charsets are missing in fontset %s:"
+msgstr "E250: Trong bộ phông chữ %s thiếu phông cho các bảng mã sau:"
-#: ../globals.h:1059
-msgid "E41: Out of memory!"
-msgstr "E41: Không đủ bộ nhớ!"
-
-#: ../globals.h:1060
-msgid "Pattern not found"
-msgstr "Không tìm thấy mẫu (pattern)"
+#, c-format
+msgid "E252: Fontset name: %s"
+msgstr "E252: Bộ phông chữ: %s"
-#: ../globals.h:1061
#, c-format
-msgid "E486: Pattern not found: %s"
-msgstr "E486: Không tìm thấy mẫu (pattern): %s"
+msgid "Font '%s' is not fixed-width"
+msgstr "Phông chữ '%s' không phải là phông có độ rộng cố định (fixed-width)"
-#: ../globals.h:1062
-msgid "E487: Argument must be positive"
-msgstr "E487: Tham số phải là một số dương"
+#, c-format
+msgid "E253: Fontset name: %s\n"
+msgstr "E253: Bộ phông chữ: %s\n"
-#: ../globals.h:1064
-msgid "E459: Cannot go back to previous directory"
-msgstr "E459: Không quay lại được thư mục trước đó"
+#, c-format
+msgid "Font0: %s\n"
+msgstr "Font0: %s\n"
-#: ../globals.h:1066
-msgid "E42: No Errors"
-msgstr "E42: Không có lỗi"
+#, c-format
+msgid "Font1: %s\n"
+msgstr "Font1: %s\n"
-#: ../globals.h:1067
-msgid "E776: No location list"
+#, c-format
+msgid "Font%ld width is not twice that of font0\n"
msgstr ""
+"Chiều rộng phông chữ font%ld phải lớn hơn hai lần so với chiều rộng font0\n"
-#: ../globals.h:1068
-msgid "E43: Damaged match string"
-msgstr "E43: Chuỗi tương ứng bị hỏng"
-
-#: ../globals.h:1069
-msgid "E44: Corrupted regexp program"
-msgstr "E44: Chương trình xử lý biểu thức chính quy bị hỏng"
-
-#: ../globals.h:1071
-msgid "E45: 'readonly' option is set (add ! to override)"
-msgstr "E45: Tùy chọn 'readonly' được bật (Hãy thêm ! để lờ đi)"
-
-#: ../globals.h:1073
-#, fuzzy, c-format
-msgid "E46: Cannot change read-only variable \"%s\""
-msgstr "E46: Không thay đổi được biến chỉ đọc \"%s\""
-
-#: ../globals.h:1075
-#, fuzzy, c-format
-msgid "E794: Cannot set variable in the sandbox: \"%s\""
-msgstr "E46: Không thay đổi được biến chỉ đọc \"%s\""
-
-#: ../globals.h:1076
-msgid "E47: Error while reading errorfile"
-msgstr "E47: Lỗi khi đọc tập tin lỗi"
-
-#: ../globals.h:1078
-msgid "E48: Not allowed in sandbox"
-msgstr "E48: Không cho phép trong hộp cát (sandbox)"
-
-#: ../globals.h:1080
-msgid "E523: Not allowed here"
-msgstr "E523: Không cho phép ở đây"
-
-#: ../globals.h:1082
-msgid "E359: Screen mode setting not supported"
-msgstr "E359: Chế độ màn hình không được hỗ trợ"
-
-#: ../globals.h:1083
-msgid "E49: Invalid scroll size"
-msgstr "E49: Kích thước thanh cuộn không cho phép"
-
-#: ../globals.h:1084
-msgid "E91: 'shell' option is empty"
-msgstr "E91: Tùy chọn 'shell' là một chuỗi rỗng"
-
-#: ../globals.h:1085
-msgid "E255: Couldn't read in sign data!"
-msgstr "E255: Không đọc được dữ liệu về ký tự!"
-
-#: ../globals.h:1086
-msgid "E72: Close error on swap file"
-msgstr "E72: Lỗi đóng tập tin trao đổi (swap)"
-
-#: ../globals.h:1087
-msgid "E73: tag stack empty"
-msgstr "E73: đống thẻ ghi rỗng"
-
-#: ../globals.h:1088
-msgid "E74: Command too complex"
-msgstr "E74: Câu lệnh quá phức tạp"
-
-#: ../globals.h:1089
-msgid "E75: Name too long"
-msgstr "E75: Tên quá dài"
-
-#: ../globals.h:1090
-msgid "E76: Too many ["
-msgstr "E76: Quá nhiều ký tự ["
-
-#: ../globals.h:1091
-msgid "E77: Too many file names"
-msgstr "E77: Quá nhiều tên tập tin"
-
-#: ../globals.h:1092
-msgid "E488: Trailing characters"
-msgstr "E488: Ký tự thừa ở đuôi"
-
-#: ../globals.h:1093
-msgid "E78: Unknown mark"
-msgstr "E78: Dấu hiệu không biết"
-
-#: ../globals.h:1094
-msgid "E79: Cannot expand wildcards"
-msgstr "E79: Không thực hiện được phép thế theo wildcard"
-
-#: ../globals.h:1096
-msgid "E591: 'winheight' cannot be smaller than 'winminheight'"
-msgstr "E591: giá trị của 'winheight' không thể nhỏ hơn 'winminheight'"
-
-#: ../globals.h:1098
-msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'"
-msgstr "E592: giá trị của 'winwidth' không thể nhỏ hơn 'winminwidth'"
-
-#: ../globals.h:1099
-msgid "E80: Error while writing"
-msgstr "E80: Lỗi khi ghi nhớ"
-
-#: ../globals.h:1100
-msgid "Zero count"
-msgstr "Giá trị của bộ đếm bằng 0"
-
-#: ../globals.h:1101
-msgid "E81: Using <SID> not in a script context"
-msgstr "E81: Sử dụng <SID> ngoài phạm vi script"
-
-#: ../globals.h:1102
-#, fuzzy, c-format
-msgid "E685: Internal error: %s"
-msgstr "E473: Lỗi nội bộ"
+#, c-format
+msgid "Font0 width: %ld\n"
+msgstr "Chiều rộng font0: %ld\n"
-#: ../globals.h:1104
-msgid "E363: pattern uses more memory than 'maxmempattern'"
+#, c-format
+msgid ""
+"Font1 width: %ld\n"
+"\n"
msgstr ""
+"Chiều rộng font1: %ld\n"
+"\n"
-#: ../globals.h:1105
-#, fuzzy
-msgid "E749: empty buffer"
-msgstr "E279: Đây không phải là bộ đệm SNiFF+"
-
-#: ../globals.h:1108
-#, fuzzy
-msgid "E682: Invalid search pattern or delimiter"
-msgstr "E383: Chuỗi tìm kiếm không đúng: %s"
-
-#: ../globals.h:1109
-msgid "E139: File is loaded in another buffer"
-msgstr "E139: Tập tin được nạp trong bộ đệm khác"
-
-#: ../globals.h:1110
-#, fuzzy, c-format
-msgid "E764: Option '%s' is not set"
-msgstr "E236: Phông chữ \"%s\" không có độ rộng cố định (fixed-width)"
-
-#: ../globals.h:1111
-#, fuzzy
-msgid "E850: Invalid register name"
-msgstr "E354: Tên sổ đăng ký không cho phép: '%s'"
-
-#: ../globals.h:1114
-msgid "search hit TOP, continuing at BOTTOM"
-msgstr "tìm kiếm sẽ được tiếp tục từ CUỐI tài liệu"
+msgid "E256: Hangul automata ERROR"
+msgstr "E256: LỖI máy tự động Hangual (tiếng Hàn)"
-#: ../globals.h:1115
-msgid "search hit BOTTOM, continuing at TOP"
-msgstr "tìm kiếm sẽ được tiếp tục từ ĐẦU tài liệu"
-
-#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Thêm một cơ sở dữ liệu mới"
-#: ../if_cscope.c:87
msgid "Query for a pattern"
msgstr "Yêu cầu theo một mẫu"
-#: ../if_cscope.c:89
msgid "Show this message"
msgstr "Hiển thị thông báo này"
-#: ../if_cscope.c:91
msgid "Kill a connection"
msgstr "Hủy kết nối"
-#: ../if_cscope.c:93
msgid "Reinit all connections"
msgstr "Khởi đầu lại tất cả các kết nối"
-#: ../if_cscope.c:95
msgid "Show connections"
msgstr "Hiển thị kết nối"
-#: ../if_cscope.c:101
#, c-format
msgid "E560: Usage: cs[cope] %s"
msgstr "E560: Sử dụng: cs[cope] %s"
-#: ../if_cscope.c:225
msgid "This cscope command does not support splitting the window.\n"
msgstr "Câu lệnh cscope này không hỗ trợ việc chia (split) cửa sổ.\n"
-#: ../if_cscope.c:266
msgid "E562: Usage: cstag <ident>"
msgstr "E562: Sử dụng: cstag <tên>"
-#: ../if_cscope.c:313
-msgid "E257: cstag: tag not found"
+# TODO: Capitalise first word of message?
+msgid "E257: cstag: Tag not found"
msgstr "E257: cstag: không tìm thấy thẻ ghi"
-#: ../if_cscope.c:461
#, c-format
msgid "E563: stat(%s) error: %d"
msgstr "E563: lỗi stat(%s): %d"
-#: ../if_cscope.c:551
+msgid "E563: stat error"
+msgstr "E563: lỗi stat"
+
#, c-format
msgid "E564: %s is not a directory or a valid cscope database"
msgstr ""
"E564: %s không phải là một thư mục hoặc một cơ sở dữ liệu cscope thích hợp"
-#: ../if_cscope.c:566
#, c-format
msgid "Added cscope database %s"
msgstr "Đã thêm cơ sở dữ liệu cscope %s"
-#: ../if_cscope.c:616
-#, c-format
-msgid "E262: error reading cscope connection %<PRId64>"
-msgstr "E262: lỗi lấy thông tin từ kết nối cscope %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E262: Error reading cscope connection %ld"
+msgstr "E262: lỗi lấy thông tin từ kết nối cscope %ld"
-#: ../if_cscope.c:711
-msgid "E561: unknown cscope search type"
+# TODO: Capitalise first word of message?
+msgid "E561: Unknown cscope search type"
msgstr "E561: không rõ loại tìm kiếm cscope"
-#: ../if_cscope.c:752 ../if_cscope.c:789
msgid "E566: Could not create cscope pipes"
msgstr "E566: Không tạo được đường ống (pipe) cho cscope"
-#: ../if_cscope.c:767
msgid "E622: Could not fork for cscope"
msgstr "E622: Không thực hiện được fork() cho cscope"
-#: ../if_cscope.c:849
-#, fuzzy
-msgid "cs_create_connection setpgid failed"
-msgstr "thực hiện cs_create_connection không thành công"
-
-#: ../if_cscope.c:853 ../if_cscope.c:889
msgid "cs_create_connection exec failed"
msgstr "thực hiện cs_create_connection không thành công"
-#: ../if_cscope.c:863 ../if_cscope.c:902
+msgid "E623: Could not spawn cscope process"
+msgstr "E623: Chạy tiến trình cscope không thành công"
+
msgid "cs_create_connection: fdopen for to_fp failed"
msgstr "cs_create_connection: thực hiện fdopen cho to_fp không thành công"
-#: ../if_cscope.c:865 ../if_cscope.c:906
msgid "cs_create_connection: fdopen for fr_fp failed"
msgstr "cs_create_connection: thực hiện fdopen cho fr_fp không thành công"
-#: ../if_cscope.c:890
-msgid "E623: Could not spawn cscope process"
-msgstr "E623: Chạy tiến trình cscope không thành công"
-
-#: ../if_cscope.c:932
-msgid "E567: no cscope connections"
+# TODO: Capitalise first word of message?
+msgid "E567: No cscope connections"
msgstr "E567: không có kết nối với cscope"
-#: ../if_cscope.c:1009
-#, c-format
-msgid "E469: invalid cscopequickfix flag %c for %c"
-msgstr "E469: cờ cscopequickfix %c cho %c không chính xác"
-
-#: ../if_cscope.c:1058
-#, c-format
-msgid "E259: no matches found for cscope query %s of %s"
+# TODO: Capitalise first word of message?
+msgid "E259: No matches found for cscope query %s of %s"
msgstr "E259: không tìm thấy tương ứng với yêu cầu cscope %s cho %s"
-#: ../if_cscope.c:1142
+# TODO: Capitalise first word of message?
+msgid "E469: Invalid cscopequickfix flag %c for %c"
+msgstr "E469: cờ cscopequickfix %c cho %c không chính xác"
+
msgid "cscope commands:\n"
msgstr "các lệnh cscope:\n"
-#: ../if_cscope.c:1150
-#, fuzzy, c-format
-msgid "%-5s: %s%*s (Usage: %s)"
+#, c-format
+msgid "%-5s: %-30s (Usage: %s)"
msgstr "%-5s: %-30s (Sử dụng: %s)"
-#: ../if_cscope.c:1155
-msgid ""
-"\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 ""
+# TODO: Capitalise first word of message?
+msgid "E625: Cannot open cscope database: %s"
+msgstr "E625: không mở được cơ sở dữ liệu cscope: %s"
-#: ../if_cscope.c:1226
-msgid "E568: duplicate cscope database not added"
+# TODO: Capitalise first word of message?
+msgid "E626: Cannot get cscope database information"
+msgstr "E626: không lấy được thông tin về cơ sở dữ liệu cscope"
+
+# TODO: Capitalise first word of message?
+msgid "E568: Duplicate cscope database not added"
msgstr "E568: cơ sở dữ liệu này của cscope đã được gắn vào từ trước"
-#: ../if_cscope.c:1335
-#, c-format
-msgid "E261: cscope connection %s not found"
+msgid "E569: maximum number of cscope connections reached"
+msgstr "E569: đã đạt tới số kết nối lớn nhất cho phép với cscope"
+
+# TODO: Capitalise first word of message?
+msgid "E261: Cscope connection %s not found"
msgstr "E261: kết nối với cscope %s không được tìm thấy"
-#: ../if_cscope.c:1364
#, c-format
msgid "cscope connection %s closed"
msgstr "kết nối %s với cscope đã bị đóng"
-#. should not reach here
-#: ../if_cscope.c:1486
-msgid "E570: fatal error in cs_manage_matches"
+# TODO: Capitalise first word of message?
+msgid "E570: Fatal error in cs_manage_matches"
msgstr "E570: lỗi nặng trong cs_manage_matches"
-#: ../if_cscope.c:1693
#, c-format
msgid "Cscope tag: %s"
msgstr "Thẻ ghi cscope: %s"
-#: ../if_cscope.c:1711
msgid ""
"\n"
" # line"
@@ -3183,90 +1952,305 @@ msgstr ""
"\n"
" # dòng"
-#: ../if_cscope.c:1713
msgid "filename / context / line\n"
msgstr "tên tập tin / nội dung / dòng\n"
-#: ../if_cscope.c:1809
#, c-format
msgid "E609: Cscope error: %s"
msgstr "E609: Lỗi cscope: %s"
-#: ../if_cscope.c:2053
msgid "All cscope databases reset"
msgstr "Khởi động lại tất cả cơ sở dữ liệu cscope"
-#: ../if_cscope.c:2123
msgid "no cscope connections\n"
msgstr "không có kết nối với cscope\n"
-#: ../if_cscope.c:2126
msgid " # pid database name prepend path\n"
msgstr " # pid tên cơ sở dữ liệu đường dẫn ban đầu\n"
-#: ../main.c:144
-#, fuzzy
-msgid "Unknown option argument"
+msgid ""
+"E263: Sorry, this command is disabled, the Python library could not be "
+"loaded."
+msgstr ""
+"E263: Rất tiếc câu lệnh này không làm việc, vì thư viện Python chưa được nạp."
+
+msgid "E659: Cannot invoke Python recursively"
+msgstr "E659: Không thể gọi Python một cách đệ quy"
+
+msgid "can't delete OutputObject attributes"
+msgstr "Không xóa được thuộc tính OutputObject"
+
+msgid "softspace must be an integer"
+msgstr "giá trị softspace phải là một số nguyên"
+
+msgid "invalid attribute"
+msgstr "thuộc tính không đúng"
+
+msgid "writelines() requires list of strings"
+msgstr "writelines() yêu cầu một danh sách các chuỗi"
+
+msgid "E264: Python: Error initialising I/O objects"
+msgstr "E264: Python: Lỗi khi bắt đầu sử dụng vật thể I/O"
+
+msgid "invalid expression"
+msgstr "biểu thức không đúng"
+
+msgid "expressions disabled at compile time"
+msgstr "biểu thức bị tắt khi biên dịch"
+
+msgid "attempt to refer to deleted buffer"
+msgstr "cố chỉ đến bộ đệm đã bị xóa"
+
+msgid "line number out of range"
+msgstr "số thứ tự của dòng vượt quá giới hạn"
+
+#, c-format
+msgid "<buffer object (deleted) at %8lX>"
+msgstr "<vật thể của bộ đệm (bị xóa) tại %8lX>"
+
+msgid "invalid mark name"
+msgstr "tên dấu hiệu không đúng"
+
+msgid "no such buffer"
+msgstr "không có bộ đệm như vậy"
+
+msgid "attempt to refer to deleted window"
+msgstr "cố chỉ đến cửa sổ đã bị đóng"
+
+msgid "readonly attribute"
+msgstr "thuộc tính chỉ đọc"
+
+msgid "cursor position outside buffer"
+msgstr "vị trí con trỏ nằm ngoài bộ đệm"
+
+#, c-format
+msgid "<window object (deleted) at %.8lX>"
+msgstr "<vật thể của cửa sổ (bị xóa) tại %.8lX>"
+
+#, c-format
+msgid "<window object (unknown) at %.8lX>"
+msgstr "<vật thể của cửa sổ (không rõ) tại %.8lX>"
+
+#, c-format
+msgid "<window %d>"
+msgstr "<cửa sổ %d>"
+
+msgid "no such window"
+msgstr "không có cửa sổ như vậy"
+
+msgid "cannot save undo information"
+msgstr "không ghi được thông tin về việc hủy thao tác"
+
+msgid "cannot delete line"
+msgstr "không xóa được dòng"
+
+msgid "cannot replace line"
+msgstr "không thay thế được dòng"
+
+msgid "cannot insert line"
+msgstr "không chèn được dòng"
+
+msgid "string cannot contain newlines"
+msgstr "chuỗi không thể chứa ký tự dòng mới"
+
+msgid ""
+"E266: Sorry, this command is disabled, the Ruby library could not be loaded."
+msgstr ""
+"E266: Rất tiếc câu lệnh này không làm việc, vì thư viện Ruby chưa đượcnạp."
+
+# TODO: Capitalise first word of message?
+msgid "E273: Unknown longjmp status %d"
+msgstr "E273: không rõ trạng thái của longjmp %d"
+
+msgid "Toggle implementation/definition"
+msgstr "Bật tắt giữa thi hành/định nghĩa"
+
+msgid "Show base class of"
+msgstr "Hiển thị hạng cơ bản của"
+
+msgid "Show overridden member function"
+msgstr "Hiển thị hàm số bị nạp đè lên"
+
+msgid "Retrieve from file"
+msgstr "Nhận từ tập tin"
+
+msgid "Retrieve from project"
+msgstr "Nhận từ dự án"
+
+msgid "Retrieve from all projects"
+msgstr "Nhận từ tất cả các dự án"
+
+msgid "Retrieve"
+msgstr "Nhận"
+
+msgid "Show source of"
+msgstr "Hiển thị mã nguồn"
+
+msgid "Find symbol"
+msgstr "Tìm ký hiệu"
+
+msgid "Browse class"
+msgstr "Duyệt hạng"
+
+msgid "Show class in hierarchy"
+msgstr "Hiển thị hạng trong hệ thống cấp bậc"
+
+msgid "Show class in restricted hierarchy"
+msgstr "Hiển thị hạng trong hệ thống cấp bậc giới hạn"
+
+msgid "Xref refers to"
+msgstr "Xref chỉ đến"
+
+msgid "Xref referred by"
+msgstr "Liên kết đến xref từ"
+
+msgid "Xref has a"
+msgstr "Xref có một"
+
+msgid "Xref used by"
+msgstr "Xref được sử dụng bởi"
+
+msgid "Show docu of"
+msgstr "Hiển thị docu của"
+
+msgid "Generate docu for"
+msgstr "Tạo docu cho"
+
+msgid "not "
+msgstr "không "
+
+msgid "connected"
+msgstr "được kết nối"
+
+
+msgid "invalid buffer number"
+msgstr "số của bộ đệm không đúng"
+
+msgid "not implemented yet"
+msgstr "tạm thời chưa được thực thi"
+
+msgid "unknown option"
+msgstr "tùy chọn không rõ"
+
+msgid "cannot set line(s)"
+msgstr "không thể đặt (các) dòng"
+
+msgid "mark not set"
+msgstr "dấu hiệu chưa được đặt"
+
+#, c-format
+msgid "row %d column %d"
+msgstr "hàng %d cột %d"
+
+msgid "cannot insert/append line"
+msgstr "không thể chèn hoặc thêm dòng"
+
+msgid "unknown flag: "
+msgstr "cờ không biết: "
+
+msgid "unknown vimOption"
+msgstr "không rõ tùy chọn vimOption"
+
+msgid "keyboard interrupt"
+msgstr "sự gián đoạn của bàn phím"
+
+msgid "Vim error"
+msgstr "lỗi của vim"
+
+msgid "cannot create buffer/window command: object is being deleted"
+msgstr "không tạo được câu lệnh của bộ đệm hay của cửa sổ: vật thể đang bị xóa"
+
+msgid ""
+"cannot register callback command: buffer/window is already being deleted"
+msgstr "không đăng ký được câu lệnh gọi ngược: bộ đệm hoặc cửa sổ đang bị xóa"
+
+msgid ""
+"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim."
+"org"
+msgstr ""
+"E280: LỖI NẶNG CỦA TCL: bị hỏng danh sách liên kết!? Hãy thông báo việc "
+"nàyđến danh sách thư (mailing list) vim-dev@vim.org"
+
+msgid "cannot register callback command: buffer/window reference not found"
+msgstr ""
+"không đăng ký được câu lệnh gọi ngược: không tìm thấy liên kết đến bộ đệm "
+"hoặc cửa sổ"
+
+msgid ""
+"E571: Sorry, this command is disabled: the Tcl library could not be loaded."
+msgstr ""
+"E571: Rất tiếc là câu lệnh này không làm việc, vì thư viện Tcl chưa được nạp"
+
+msgid ""
+"E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim.org"
+msgstr ""
+"E281: LỖI TCL: mã thoát ra không phải là một số nguyên!? Hãy thông báo điều "
+"này đến danh sách thư (mailing list) vim-dev@vim.org"
+
+msgid "cannot get line"
+msgstr "không nhận được dòng"
+
+msgid "Unable to register a command server name"
+msgstr "Không đăng ký được một tên cho máy chủ câu lệnh"
+
+msgid "E248: Failed to send command to the destination program"
+msgstr "E248: Gửi câu lệnh vào chương trình khác không thành công"
+
+#, c-format
+msgid "E573: Invalid server id used: %s"
+msgstr "E573: Sử dụng id máy chủ không đúng: %s"
+
+msgid "E251: VIM instance registry property is badly formed. Deleted!"
+msgstr "E251: Thuộc tính đăng ký của Vim được định dạng không đúng. Xóa!"
+
+msgid "Unknown option"
msgstr "Tùy chọn không biết"
-#: ../main.c:146
msgid "Too many edit arguments"
msgstr "Có quá nhiều tham số soạn thảo"
-#: ../main.c:148
msgid "Argument missing after"
msgstr "Thiếu tham số sau"
-#: ../main.c:150
-#, fuzzy
-msgid "Garbage after option argument"
+msgid "Garbage after option"
msgstr "Rác sau tùy chọn"
-#: ../main.c:152
msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"
msgstr ""
"Quá nhiều tham số \"+câu lệnh\", \"-c câu lệnh\" hoặc \"--cmd câu lệnh\""
-#: ../main.c:154
msgid "Invalid argument for"
msgstr "Tham số không được phép cho"
-#: ../main.c:294
-#, c-format
-msgid "%d files to edit\n"
-msgstr "%d tập tin để soạn thảo\n"
+msgid "This Vim was not compiled with the diff feature."
+msgstr "Vim không được biên dịch với tính năng hỗ trợ xem khác biệt (diff)."
-#: ../main.c:1342
msgid "Attempt to open script file again: \""
msgstr "Thử mở tập tin script một lần nữa: \""
-#: ../main.c:1350
msgid "Cannot open for reading: \""
msgstr "Không mở để đọc được: \""
-#: ../main.c:1393
msgid "Cannot open for script output: \""
msgstr "Không mở cho đầu ra script được: \""
-#: ../main.c:1622
+#, c-format
+msgid "%d files to edit\n"
+msgstr "%d tập tin để soạn thảo\n"
+
msgid "Vim: Warning: Output is not to a terminal\n"
msgstr "Vim: Cảnh báo: Đầu ra không hướng tới một terminal\n"
-#: ../main.c:1624
msgid "Vim: Warning: Input is not from a terminal\n"
msgstr "Vim: Cảnh báo: Đầu vào không phải đến từ một terminal\n"
-#. just in case..
-#: ../main.c:1891
msgid "pre-vimrc command line"
msgstr "dòng lệnh chạy trước khi thực hiện vimrc"
-#: ../main.c:1964
#, c-format
msgid "E282: Cannot read from \"%s\""
msgstr "E282: Không đọc được từ \"%s\""
-#: ../main.c:2149
msgid ""
"\n"
"More info with: \"vim -h\"\n"
@@ -3274,23 +2258,18 @@ msgstr ""
"\n"
"Xem thông tin chi tiết với: \"vim -h\"\n"
-#: ../main.c:2178
msgid "[file ..] edit specified file(s)"
msgstr "[tập tin ..] soạn thảo (các) tập tin chỉ ra"
-#: ../main.c:2179
msgid "- read text from stdin"
msgstr "- đọc văn bản từ đầu vào stdin"
-#: ../main.c:2180
msgid "-t tag edit file where tag is defined"
msgstr "-t thẻ ghi soạn thảo tập tin từ chỗ thẻ ghi chỉ ra"
-#: ../main.c:2181
msgid "-q [errorfile] edit file with first error"
msgstr "-q [tập tin lỗi] soạn thảo tập tin với lỗi đầu tiên"
-#: ../main.c:2187
msgid ""
"\n"
"\n"
@@ -3300,11 +2279,9 @@ msgstr ""
"\n"
"Sử dụng:"
-#: ../main.c:2189
msgid " vim [arguments] "
msgstr " vim [các tham số] "
-#: ../main.c:2193
msgid ""
"\n"
" or:"
@@ -3312,7 +2289,6 @@ msgstr ""
"\n"
" hoặc:"
-#: ../main.c:2196
msgid ""
"\n"
"\n"
@@ -3322,187 +2298,322 @@ msgstr ""
"\n"
"Tham số:\n"
-#: ../main.c:2197
msgid "--\t\t\tOnly file names after this"
msgstr "--\t\t\tSau tham số chỉ đưa ra tên tập tin"
-#: ../main.c:2199
msgid "--literal\t\tDon't expand wildcards"
msgstr "--literal\t\tKhông thực hiện việc mở rộng wildcard"
-#: ../main.c:2201
+msgid "-register\t\tRegister this gvim for OLE"
+msgstr "-register\t\tĐăng ký gvim này cho OLE"
+
+msgid "-unregister\t\tUnregister gvim for OLE"
+msgstr "-unregister\t\tBỏ đăng ký gvim này cho OLE"
+
+msgid "-g\t\t\tRun using GUI (like \"gvim\")"
+msgstr "-g\t\t\tSử dụng giao diện đồ họa GUI (giống \"gvim\")"
+
+msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
+msgstr ""
+"-f hoặc --nofork\tTrong chương trình hoạt động: Không thực hiện fork khi "
+"chạy GUI"
+
msgid "-v\t\t\tVi mode (like \"vi\")"
msgstr "-v\t\t\tChế độ Vi (giống \"vi\")"
-#: ../main.c:2202
msgid "-e\t\t\tEx mode (like \"ex\")"
msgstr "-e\t\t\tChế độ Ex (giống \"ex\")"
-#: ../main.c:2203
-msgid "-E\t\t\tImproved Ex mode"
-msgstr ""
-
-#: ../main.c:2204
msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")"
msgstr "-s\t\t\tChế độ ít đưa thông báo (gói) (chỉ dành cho \"ex\")"
-#: ../main.c:2205
msgid "-d\t\t\tDiff mode (like \"vimdiff\")"
msgstr "-d\t\t\tChế độ khác biệt, diff (giống \"vimdiff\")"
-#: ../main.c:2206
msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
msgstr "-y\t\t\tChế độ đơn giản (giống \"evim\", không có chế độ)"
-#: ../main.c:2207
msgid "-R\t\t\tReadonly mode (like \"view\")"
msgstr "-R\t\t\tChế độ chỉ đọc (giống \"view\")"
-#: ../main.c:2209
+msgid "-Z\t\t\tRestricted mode (like \"rvim\")"
+msgstr "-Z\t\t\tChế độ hạn chế (giống \"rvim\")"
+
msgid "-m\t\t\tModifications (writing files) not allowed"
msgstr "-m\t\t\tKhông có khả năng ghi nhớ thay đổi (ghi nhớ tập tin)"
-#: ../main.c:2210
msgid "-M\t\t\tModifications in text not allowed"
msgstr "-M\t\t\tKhông có khả năng thay đổi văn bản"
-#: ../main.c:2211
msgid "-b\t\t\tBinary mode"
msgstr "-b\t\t\tChế độ nhị phân (binary)"
-#: ../main.c:2212
msgid "-l\t\t\tLisp mode"
msgstr "-l\t\t\tChế độ Lisp"
-#: ../main.c:2213
msgid "-C\t\t\tCompatible with Vi: 'compatible'"
msgstr "-C\t\t\tChế độ tương thích với Vi: 'compatible'"
-#: ../main.c:2214
msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'"
msgstr "-N\t\t\tChế độ không tương thích hoàn toàn với Vi: 'nocompatible'"
-#: ../main.c:2215
-msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
-msgstr ""
+msgid "-V[N]\t\tVerbose level"
+msgstr "-V[N]\t\tMức độ chi tiết của thông báo"
-#: ../main.c:2216
msgid "-D\t\t\tDebugging mode"
msgstr "-D\t\t\tChế độ sửa lỗi (debug)"
-#: ../main.c:2217
msgid "-n\t\t\tNo swap file, use memory only"
msgstr "-n\t\t\tKhông sử dụng tập tin swap, chỉ sử dụng bộ nhớ"
-#: ../main.c:2218
msgid "-r\t\t\tList swap files and exit"
msgstr "-r\t\t\tLiệt kê các tập tin swap rồi thoát"
-#: ../main.c:2219
msgid "-r (with file name)\tRecover crashed session"
msgstr "-r (với tên tập tin)\tPhục hồi lần soạn thảo gặp sự cố"
-#: ../main.c:2220
msgid "-L\t\t\tSame as -r"
msgstr "-L\t\t\tGiống với -r"
-#: ../main.c:2221
-msgid "-A\t\t\tstart in Arabic mode"
+msgid "-f\t\t\tDon't use newcli to open window"
+msgstr "-f\t\t\tKhông sử dụng newcli để mở cửa sổ"
+
+msgid "-dev <device>\t\tUse <device> for I/O"
+msgstr "-dev <thiết bị>\t\tSử dụng <thiết bị> cho I/O"
+
+msgid "-A\t\t\tStart in Arabic mode"
msgstr "-A\t\t\tKhởi động vào chế độ Ả Rập"
-#: ../main.c:2222
msgid "-H\t\t\tStart in Hebrew mode"
msgstr "-H\t\t\tKhởi động vào chế độ Do thái"
-#: ../main.c:2223
msgid "-F\t\t\tStart in Farsi mode"
msgstr "-F\t\t\tKhởi động vào chế độ Farsi"
-#: ../main.c:2224
msgid "-T <terminal>\tSet terminal type to <terminal>"
msgstr "-T <terminal>\tĐặt loại terminal thành <terminal>"
-#: ../main.c:2225
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\t\tSử dụng <vimrc> thay thế cho mọi .vimrc"
-#: ../main.c:2226
+msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
+msgstr "-U <gvimrc>\t\tSử dụng <gvimrc> thay thế cho mọi .gvimrc"
+
msgid "--noplugin\t\tDon't load plugin scripts"
msgstr "--noplugin\t\tKhông nạp bất kỳ script môđun nào"
-#: ../main.c:2227
-#, fuzzy
-msgid "-p[N]\t\tOpen N tab pages (default: one for each file)"
-msgstr "-o[N]\t\tMở N cửa sổ (theo mặc định: mỗi cửa sổ cho một tập tin)"
-
-#: ../main.c:2228
msgid "-o[N]\t\tOpen N windows (default: one for each file)"
msgstr "-o[N]\t\tMở N cửa sổ (theo mặc định: mỗi cửa sổ cho một tập tin)"
-#: ../main.c:2229
msgid "-O[N]\t\tLike -o but split vertically"
msgstr "-O[N]\t\tGiống với -o nhưng phân chia theo đường thẳng đứng"
-#: ../main.c:2230
msgid "+\t\t\tStart at end of file"
msgstr "+\t\t\tBắt đầu soạn thảo từ cuối tập tin"
-#: ../main.c:2231
msgid "+<lnum>\t\tStart at line <lnum>"
msgstr "+<lnum>\t\tBắt đầu soạn thảo từ dòng thứ <lnum> (số thứ tự của dòng)"
-#: ../main.c:2232
msgid "--cmd <command>\tExecute <command> before loading any vimrc file"
msgstr "--cmd <câu lệnh>\tThực hiện <câu lệnh> trước khi nạp tập tin vimrc"
-#: ../main.c:2233
msgid "-c <command>\t\tExecute <command> after loading the first file"
msgstr "-c <câu lệnh>\t\tThực hiện <câu lệnh> sau khi nạp tập tin đầu tiên"
-#: ../main.c:2235
msgid "-S <session>\t\tSource file <session> after loading the first file"
msgstr "-S <session>\t\tThực hiện <session> sau khi nạp tập tin đầu tiên"
-#: ../main.c:2236
msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>"
msgstr ""
"-s <scriptin>\tĐọc các lệnh của chế độ Thông thường từ tập tin <scriptin>"
-#: ../main.c:2237
msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>"
msgstr "-w <scriptout>\tThêm tất cả các lệnh đã gõ vào tập tin <scriptout>"
-#: ../main.c:2238
msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>"
msgstr "-W <scriptout>\tGhi nhớ tất cả các lệnh đã gõ vào tập tin <scriptout>"
-#: ../main.c:2240
-msgid "--startuptime <file>\tWrite startup timing messages to <file>"
+msgid "-x\t\t\tEdit encrypted files"
+msgstr "-x\t\t\tSoạn thảo tập tin đã mã hóa"
+
+msgid "-display <display>\tConnect Vim to this particular X-server"
+msgstr "-display <màn hình>\tKết nối Vim tới máy chủ X đã chỉ ra"
+
+msgid "-X\t\t\tDo not connect to X server"
+msgstr "-X\t\t\tKhông thực hiện việc kết nối tới máy chủ X"
+
+msgid "--remote <files>\tEdit <files> in a Vim server if possible"
+msgstr "--remote <tập tin>\tSoạn thảo <tập tin> trên máy chủ Vim nếu có thể"
+
+msgid "--remote-silent <files> Same, don't complain if there is no server"
+msgstr ""
+"--remote-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có máy chủ"
+
+msgid ""
+"--remote-wait <files> As --remote but wait for files to have been edited"
+msgstr "--remote-wait <tập tin> Cũng như --remote, nhưng chờ sự kết thúc"
+
+msgid ""
+"--remote-wait-silent <files> Same, don't complain if there is no server"
+msgstr ""
+"--remote-wait-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có máy "
+"chủ"
+
+msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
+msgstr "--remote-send <phím>\tGửi <phím> lên máy chủ Vim và thoát"
+
+msgid "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
msgstr ""
+"--remote-expr <biểu thức>\tTính <biểu thức> trên máy chủ Vim và in ra kết quả"
+
+msgid "--serverlist\t\tList available Vim server names and exit"
+msgstr "--serverlist\t\tHiển thị danh sách máy chủ Vim và thoát"
+
+msgid "--servername <name>\tSend to/become the Vim server <name>"
+msgstr "--servername <tên>\tGửi lên (hoặc trở thành) máy chủ Vim với <tên>"
-#: ../main.c:2242
msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
msgstr "-i <viminfo>\t\tSử dụng tập tin <viminfo> thay cho .viminfo"
-#: ../main.c:2243
msgid "-h or --help\tPrint Help (this message) and exit"
msgstr "-h hoặc --help\tHiển thị Trợ giúp (thông tin này) và thoát"
-#: ../main.c:2244
msgid "--version\t\tPrint version information and exit"
msgstr "--version\t\tĐưa ra thông tin về phiên bản Vim và thoát"
-#: ../mark.c:676
+msgid ""
+"\n"
+"Arguments recognised by gvim (Motif version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản Motif):\n"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (neXtaw version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản neXtaw):\n"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (Athena version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản Athena):\n"
+
+msgid "-display <display>\tRun Vim on <display>"
+msgstr "-display <màn hình>\tChạy Vim trong <màn hình> đã chỉ ra"
+
+msgid "-iconic\t\tStart Vim iconified"
+msgstr "-iconic\t\tChạy Vim ở dạng thu nhỏ"
+
+msgid "-name <name>\t\tUse resource as if vim was <name>"
+msgstr "-name <tên>\t\tSử dụng tài nguyên giống như khi vim có <tên>"
+
+msgid "\t\t\t (Unimplemented)\n"
+msgstr "\t\t\t (Chưa được thực thi)\n"
+
+msgid "-background <color>\tUse <color> for the background (also: -bg)"
+msgstr "-background <màu>\tSử dụng <màu> chỉ ra cho nền (cũng như: -bg)"
+
+msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
+msgstr ""
+"-foreground <màu>\tSử dụng <màu> cho văn bản thông thường (cũng như: -fg)"
+
+msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
+msgstr ""
+"-font <phông>\t\tSử dụng <phông> chữ cho văn bản thông thường (cũng như: -fn)"
+
+msgid "-boldfont <font>\tUse <font> for bold text"
+msgstr "-boldfont <phông>\tSử dụng <phông> chữ cho văn bản in đậm"
+
+msgid "-italicfont <font>\tUse <font> for italic text"
+msgstr "-italicfont <phông>\tSử dụng <phông> chữ cho văn bản in nghiêng"
+
+msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
+msgstr "-geometry <kích thước>\tSử dụng <kích thước> ban đầu (cũng như: -geom)"
+
+msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
+msgstr ""
+"-borderwidth <rộng>\tSử dụng đường viền có chiều <rộng> (cũng như: -bw)"
+
+msgid "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
+msgstr ""
+"-scrollbarwidth <rộng> Sử dụng thanh cuộn với chiều <rộng> (cũng như: -sw)"
+
+msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
+msgstr ""
+"-menuheight <cao>\tSử dụng thanh trình đơn với chiều <cao> (cũng như: -mh)"
+
+msgid "-reverse\t\tUse reverse video (also: -rv)"
+msgstr "-reverse\t\tSử dụng chế độ video đảo ngược (cũng như: -rv)"
+
+msgid "+reverse\t\tDon't use reverse video (also: +rv)"
+msgstr "+reverse\t\tKhông sử dụng chế độ video đảo ngược (cũng như: +rv)"
+
+msgid "-xrm <resource>\tSet the specified resource"
+msgstr "-xrm <tài nguyên>\tĐặt <tài nguyên> chỉ ra"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (RISC OS version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản RISC OS):\n"
+
+msgid "--columns <number>\tInitial width of window in columns"
+msgstr "--columns <số>\tChiều rộng ban đầu của cửa sổ tính theo số cột"
+
+msgid "--rows <number>\tInitial height of window in rows"
+msgstr "--rows <số>\tChiều cao ban đầu của cửa sổ tính theo số dòng"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (GTK+ version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản GTK+):\n"
+
+msgid "-display <display>\tRun Vim on <display> (also: --display)"
+msgstr ""
+"-display <màn hình>\tChạy Vim trên <màn hình> chỉ ra (cũng như: --display)"
+
+msgid "--role <role>\tSet a unique role to identify the main window"
+msgstr "--role <vai trò>\tĐặt <vai trò> duy nhất để nhận diện cửa sổ chính"
+
+msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
+msgstr "--socketid <xid>\tMở Vim bên trong thành phần GTK khác"
+
+msgid "-P <parent title>\tOpen Vim inside parent application"
+msgstr "-P <tiêu đề của mẹ>\tMở Vim bên trong ứng dụng mẹ"
+
+msgid "No display"
+msgstr "Không có màn hình"
+
+msgid ": Send failed.\n"
+msgstr ": Gửi không thành công.\n"
+
+msgid ": Send failed. Trying to execute locally\n"
+msgstr ": Gửi không thành công. Thử thực hiện nội bộ\n"
+
+#, c-format
+msgid "%d of %d edited"
+msgstr "đã soạn thảo %d từ %d"
+
+msgid "No display: Send expression failed.\n"
+msgstr "Không có màn hình: gửi biểu thức không thành công.\n"
+
+msgid ": Send expression failed.\n"
+msgstr ": Gửi biểu thức không thành công.\n"
+
msgid "No marks set"
msgstr "Không có dấu hiệu nào được đặt."
-#: ../mark.c:678
#, c-format
msgid "E283: No marks matching \"%s\""
msgstr "E283: Không có dấu hiệu tương ứng với \"%s\""
-#. Highlight title
-#: ../mark.c:687
msgid ""
"\n"
"mark line col file/text"
@@ -3510,8 +2621,6 @@ msgstr ""
"\n"
"nhãn dòng cột tập tin/văn bản"
-#. Highlight title
-#: ../mark.c:789
msgid ""
"\n"
" jump line col file/text"
@@ -3519,8 +2628,6 @@ msgstr ""
"\n"
" bước_nhảy dòng cột tập tin/văn bản"
-#. Highlight title
-#: ../mark.c:831
msgid ""
"\n"
"change line col text"
@@ -3528,7 +2635,6 @@ msgstr ""
"\n"
"thay_đổi dòng cột văn_bản"
-#: ../mark.c:1238
msgid ""
"\n"
"# File marks:\n"
@@ -3536,8 +2642,6 @@ msgstr ""
"\n"
"# Nhãn của tập tin:\n"
-#. Write the jumplist with -'
-#: ../mark.c:1271
msgid ""
"\n"
"# Jumplist (newest first):\n"
@@ -3545,7 +2649,6 @@ msgstr ""
"\n"
"# Danh sách bước nhảy (mới hơn đứng trước):\n"
-#: ../mark.c:1352
msgid ""
"\n"
"# History of marks within files (newest to oldest):\n"
@@ -3553,88 +2656,94 @@ msgstr ""
"\n"
"# Lịch sử các nhãn trong tập tin (từ mới nhất đến cũ nhất):\n"
-#: ../mark.c:1431
msgid "Missing '>'"
msgstr "Thiếu '>'"
-#: ../memfile.c:426
-msgid "E293: block was not locked"
+msgid "E543: Not a valid codepage"
+msgstr "E543: Bảng mã không cho phép"
+
+msgid "E284: Cannot set IC values"
+msgstr "E284: Không đặt được giá trị nội dung nhập vào (IC)"
+
+msgid "E285: Failed to create input context"
+msgstr "E285: Không tạo được nội dung nhập vào"
+
+msgid "E286: Failed to open input method"
+msgstr "E286: Việc thử mở phương pháp nhập không thành công"
+
+msgid "E287: Warning: Could not set destroy callback to IM"
+msgstr ""
+"E287: Cảnh báo: không đặt được sự gọi ngược hủy diệt thành phương pháp nhập"
+
+# TODO: Capitalise first word of message?
+msgid "E288: Input method doesn't support any style"
+msgstr "E288: phương pháp nhập không hỗ trợ bất kỳ phong cách (style) nào"
+
+# TODO: Capitalise first word of message?
+msgid "E289: Input method doesn't support my preedit type"
+msgstr "E289: phương pháp nhập không hỗ trợ loại soạn thảo trước của Vim"
+
+msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
+msgstr "E291: GTK+ cũ hơn 1.2.3. Vùng chỉ trạng thái không làm việc"
+
+# TODO: Capitalise first word of message?
+msgid "E293: Block was not locked"
msgstr "E293: khối chưa bị khóa"
-#: ../memfile.c:799
msgid "E294: Seek error in swap file read"
msgstr "E294: Lỗi tìm kiếm khi đọc tập tin trao đổi (swap)"
-#: ../memfile.c:803
msgid "E295: Read error in swap file"
msgstr "E295: Lỗi đọc tập tin trao đổi (swap)"
-#: ../memfile.c:849
msgid "E296: Seek error in swap file write"
msgstr "E296: Lỗi tìm kiếm khi ghi nhớ tập tin trao đổi (swap)"
-#: ../memfile.c:865
msgid "E297: Write error in swap file"
msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
-#: ../memfile.c:1036
msgid "E300: Swap file already exists (symlink attack?)"
msgstr ""
"E300: Tập tin trao đổi (swap) đã tồn tại (sử dụng liên kết mềm tấn công?)"
-#: ../memline.c:318
msgid "E298: Didn't get block nr 0?"
msgstr "E298: Chưa lấy khối số 0?"
-#: ../memline.c:361
msgid "E298: Didn't get block nr 1?"
msgstr "E298: Chưa lấy khối số 12?"
-#: ../memline.c:377
msgid "E298: Didn't get block nr 2?"
msgstr "E298: Chưa lấy khối số 2?"
-#. could not (re)open the swap file, what can we do????
-#: ../memline.c:465
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: Ối, mất tập tin trao đổi (swap)!!!"
-#: ../memline.c:477
msgid "E302: Could not rename swap file"
msgstr "E302: Không đổi được tên tập tin trao đổi (swap)"
-#: ../memline.c:554
#, c-format
msgid "E303: Unable to open swap file for \"%s\", recovery impossible"
msgstr ""
"E303: Không mở được tập tin trao đổi (swap) cho \"%s\", nên không thể phục "
"hồi"
-#: ../memline.c:666
-#, fuzzy
-msgid "E304: ml_upd_block0(): Didn't get block 0??"
+msgid "E304: ml_timestamp: Didn't get block 0??"
msgstr "E304: ml_timestamp: Chưa lấy khối số 0??"
-#. no swap files found
-#: ../memline.c:830
#, c-format
msgid "E305: No swap file found for %s"
msgstr "E305: Không tìm thấy tập tin trao đổi (swap) cho %s"
-#: ../memline.c:839
msgid "Enter number of swap file to use (0 to quit): "
msgstr "Hãy nhập số của tập tin trao đổi (swap) muốn sử dụng (0 để thoát): "
-#: ../memline.c:879
#, c-format
msgid "E306: Cannot open %s"
msgstr "E306: Không mở được %s"
-#: ../memline.c:897
msgid "Unable to read block 0 from "
msgstr "Không thể đọc khối số 0 từ "
-#: ../memline.c:900
msgid ""
"\n"
"Maybe no changes were made or Vim did not update the swap file."
@@ -3642,28 +2751,22 @@ msgstr ""
"\n"
"Chưa có thay đổi nào hoặc Vim không thể cập nhật tập tin trao đổi (swap)"
-#: ../memline.c:909
msgid " cannot be used with this version of Vim.\n"
msgstr " không thể sử dụng trong phiên bản Vim này.\n"
-#: ../memline.c:911
msgid "Use Vim version 3.0.\n"
msgstr "Hãy sử dụng Vim phiên bản 3.0.\n"
-#: ../memline.c:916
#, c-format
msgid "E307: %s does not look like a Vim swap file"
msgstr "E307: %s không phải là tập tin trao đổi (swap) của Vim"
-#: ../memline.c:922
msgid " cannot be used on this computer.\n"
msgstr " không thể sử dụng trên máy tính này.\n"
-#: ../memline.c:924
msgid "The file was created on "
msgstr "Tập tin đã được tạo trên "
-#: ../memline.c:928
msgid ""
",\n"
"or the file has been damaged."
@@ -3671,85 +2774,63 @@ msgstr ""
",\n"
"hoặc tập tin đã bị hỏng."
-#: ../memline.c:945
-msgid " has been damaged (page size is smaller than minimum value).\n"
-msgstr ""
-
-#: ../memline.c:974
#, c-format
msgid "Using swap file \"%s\""
msgstr "Đang sử dụng tập tin trao đổi (swap) \"%s\""
-#: ../memline.c:980
#, c-format
msgid "Original file \"%s\""
msgstr "Tập tin gốc \"%s\""
-#: ../memline.c:995
msgid "E308: Warning: Original file may have been changed"
msgstr "E308: Cảnh báo: Tập tin gốc có thể đã bị thay đổi"
-#: ../memline.c:1061
#, c-format
msgid "E309: Unable to read block 1 from %s"
msgstr "E309: Không đọc được khối số 1 từ %s"
-#: ../memline.c:1065
msgid "???MANY LINES MISSING"
msgstr "???THIẾU NHIỀU DÒNG"
-#: ../memline.c:1076
msgid "???LINE COUNT WRONG"
msgstr "???GIÁ TRỊ CỦA SỐ ĐẾM DÒNG BỊ SAI"
-#: ../memline.c:1082
msgid "???EMPTY BLOCK"
msgstr "???KHỐI RỖNG"
-#: ../memline.c:1103
msgid "???LINES MISSING"
msgstr "???THIẾU DÒNG"
-#: ../memline.c:1128
#, c-format
msgid "E310: Block 1 ID wrong (%s not a .swp file?)"
msgstr "E310: Khối 1 ID sai (%s không phải là tập tin .swp?)"
-#: ../memline.c:1133
msgid "???BLOCK MISSING"
msgstr "???THIẾU KHỐI"
-#: ../memline.c:1147
msgid "??? from here until ???END lines may be messed up"
msgstr "??? từ đây tới ???CUỐI, các dòng có thể đã bị hỏng"
-#: ../memline.c:1164
msgid "??? from here until ???END lines may have been inserted/deleted"
msgstr "??? từ đây tới ???CUỐI, các dòng có thể đã bị chèn hoặc xóa"
-#: ../memline.c:1181
msgid "???END"
msgstr "???CUỐI"
-#: ../memline.c:1238
msgid "E311: Recovery Interrupted"
msgstr "E311: Việc phục hồi bị gián đoạn"
-#: ../memline.c:1243
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
msgstr ""
"E312: Phát hiện ra lỗi trong khi phục hồi; hãy xem những dòng bắt đầu với ???"
-#: ../memline.c:1245
msgid "See \":help E312\" for more information."
msgstr "Hãy xem thông tin bổ sung trong trợ giúp \":help E312\""
-#: ../memline.c:1249
msgid "Recovery completed. You should check if everything is OK."
msgstr "Việc phục hồi đã hoàn thành. Nên kiểm tra xem mọi thứ có ổn không."
-#: ../memline.c:1251
msgid ""
"\n"
"(You might want to write out this file under another name\n"
@@ -3757,71 +2838,49 @@ msgstr ""
"\n"
"(Có thể ghi nhớ tập tin với tên khác và so sánh với tập\n"
-#: ../memline.c:1252
-#, fuzzy
-msgid "and run diff with the original file to check for changes)"
+msgid "and run diff with the original file to check for changes)\n"
msgstr "gốc bằng chương trình diff).\n"
-#: ../memline.c:1254
-msgid "Recovery completed. Buffer contents equals file contents."
-msgstr ""
-
-#: ../memline.c:1255
-#, fuzzy
msgid ""
-"\n"
-"You may want to delete the .swp file now.\n"
+"Delete the .swp file afterwards.\n"
"\n"
msgstr ""
"Sau đó hãy xóa tập tin .swp.\n"
"\n"
-#. use msg() to start the scrolling properly
-#: ../memline.c:1327
msgid "Swap files found:"
msgstr "Tìm thấy tập tin trao đổi (swap):"
-#: ../memline.c:1446
msgid " In current directory:\n"
msgstr " Trong thư mục hiện thời:\n"
-#: ../memline.c:1448
msgid " Using specified name:\n"
msgstr " Với tên chỉ ra:\n"
-#: ../memline.c:1450
msgid " In directory "
msgstr " Trong thư mục "
-#: ../memline.c:1465
msgid " -- none --\n"
msgstr " -- không --\n"
-#: ../memline.c:1527
msgid " owned by: "
msgstr " người sở hữu: "
-#: ../memline.c:1529
msgid " dated: "
msgstr " ngày: "
-#: ../memline.c:1532 ../memline.c:3231
msgid " dated: "
msgstr " ngày: "
-#: ../memline.c:1548
msgid " [from Vim version 3.0]"
msgstr " [từ Vim phiên bản 3.0]"
-#: ../memline.c:1550
msgid " [does not look like a Vim swap file]"
msgstr " [không phải là tập tin trao đổi (swap) của Vim]"
-#: ../memline.c:1552
msgid " file name: "
msgstr " tên tập tin: "
-#: ../memline.c:1558
msgid ""
"\n"
" modified: "
@@ -3829,15 +2888,12 @@ msgstr ""
"\n"
" thay đổi: "
-#: ../memline.c:1559
msgid "YES"
msgstr "CÓ"
-#: ../memline.c:1559
msgid "no"
msgstr "không"
-#: ../memline.c:1562
msgid ""
"\n"
" user name: "
@@ -3845,11 +2901,9 @@ msgstr ""
"\n"
" tên người dùng: "
-#: ../memline.c:1568
msgid " host name: "
msgstr " tên máy: "
-#: ../memline.c:1570
msgid ""
"\n"
" host name: "
@@ -3857,7 +2911,6 @@ msgstr ""
"\n"
" tên máy: "
-#: ../memline.c:1575
msgid ""
"\n"
" process ID: "
@@ -3865,11 +2918,16 @@ msgstr ""
"\n"
" ID tiến trình: "
-#: ../memline.c:1579
msgid " (still running)"
msgstr " (vẫn đang chạy)"
-#: ../memline.c:1586
+msgid ""
+"\n"
+" [not usable with this version of Vim]"
+msgstr ""
+"\n"
+" [không sử dụng được với phiên bản này của Vim]"
+
msgid ""
"\n"
" [not usable on this computer]"
@@ -3877,97 +2935,75 @@ msgstr ""
"\n"
" [không sử dụng được trên máy tính này]"
-#: ../memline.c:1590
msgid " [cannot be read]"
msgstr " [không đọc được]"
-#: ../memline.c:1593
msgid " [cannot be opened]"
msgstr " [không mở được]"
-#: ../memline.c:1698
msgid "E313: Cannot preserve, there is no swap file"
msgstr "E313: Không cập nhật được tập tin trao đổi (swap) vì không tìm thấy nó"
-#: ../memline.c:1747
msgid "File preserved"
msgstr "Đã cập nhật tập tin trao đổi (swap)"
-#: ../memline.c:1749
msgid "E314: Preserve failed"
msgstr "E314: Cập nhật không thành công"
-#: ../memline.c:1819
-#, c-format
-msgid "E315: ml_get: invalid lnum: %<PRId64>"
-msgstr "E315: ml_get: giá trị lnum không đúng: %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E315: ml_get: Invalid lnum: %ld"
+msgstr "E315: ml_get: giá trị lnum không đúng: %ld"
-#: ../memline.c:1851
-#, c-format
-msgid "E316: ml_get: cannot find line %<PRId64>"
-msgstr "E316: ml_get: không tìm được dòng %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E316: ml_get: Cannot find line %ld"
+msgstr "E316: ml_get: không tìm được dòng %ld"
-#: ../memline.c:2236
-msgid "E317: pointer block id wrong 3"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong 3"
msgstr "E317: Giá trị của pointer khối số 3 không đúng"
-#: ../memline.c:2311
msgid "stack_idx should be 0"
msgstr "giá trị stack_idx phải bằng 0"
-#: ../memline.c:2369
msgid "E318: Updated too many blocks?"
msgstr "E318: Đã cập nhật quá nhiều khối?"
-#: ../memline.c:2511
-msgid "E317: pointer block id wrong 4"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong 4"
msgstr "E317: Giá trị của pointer khối số 4 không đúng"
-#: ../memline.c:2536
msgid "deleted block 1?"
msgstr "đã xóa khối số 1?"
-#: ../memline.c:2707
#, c-format
-msgid "E320: Cannot find line %<PRId64>"
-msgstr "E320: Không tìm được dòng %<PRId64>"
+msgid "E320: Cannot find line %ld"
+msgstr "E320: Không tìm được dòng %ld"
-#: ../memline.c:2916
-msgid "E317: pointer block id wrong"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong"
msgstr "E317: giá trị của pointer khối không đúng"
-#: ../memline.c:2930
msgid "pe_line_count is zero"
msgstr "giá trị pe_line_count bằng không"
-#: ../memline.c:2955
-#, c-format
-msgid "E322: line number out of range: %<PRId64> past the end"
-msgstr "E322: số thứ tự dòng vượt quá giới hạn : %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E322: Line number out of range: %ld past the end"
+msgstr "E322: số thứ tự dòng vượt quá giới hạn : %ld"
-#: ../memline.c:2959
-#, c-format
-msgid "E323: line count wrong in block %<PRId64>"
-msgstr "E323: giá trị đếm dòng không đúng trong khối %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E323: Line count wrong in block %ld"
+msgstr "E323: giá trị đếm dòng không đúng trong khối %ld"
-#: ../memline.c:2999
msgid "Stack size increases"
msgstr "Kích thước của đống tăng lên"
-#: ../memline.c:3038
-msgid "E317: pointer block id wrong 2"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong 2"
msgstr "E317: Giá trị của cái chỉ (pointer) khối số 2 không đúng"
-#: ../memline.c:3070
-#, c-format
-msgid "E773: Symlink loop for \"%s\""
-msgstr ""
-
-#: ../memline.c:3221
msgid "E325: ATTENTION"
msgstr "E325: CHÚ Ý"
-#: ../memline.c:3222
msgid ""
"\n"
"Found a swap file by the name \""
@@ -3975,45 +3011,37 @@ msgstr ""
"\n"
"Tìm thấy một tập tin trao đổi (swap) với tên \""
-#: ../memline.c:3226
msgid "While opening file \""
msgstr "Khi mở tập tin: \""
-#: ../memline.c:3239
msgid " NEWER than swap file!\n"
msgstr " MỚI hơn so với tập tin trao đổi (swap)\n"
-#: ../memline.c:3244
-#, fuzzy
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."
+"(1) Another program may be editing the same file.\n"
+" If this is the case, be careful not to end up with two\n"
+" different instances of the same file when making changes.\n"
msgstr ""
"\n"
"(1) Rất có thể một chương trình khác đang soạn thảo tập tin.\n"
" Nếu như vậy, hãy cẩn thận khi thay đổi, làm sao để không thu\n"
" được hai phương án khác nhau của cùng một tập tin.\n"
-#: ../memline.c:3245
-#, fuzzy
-msgid " Quit, or continue with caution.\n"
+msgid " Quit, or continue with caution.\n"
msgstr " Thoát hoặc tiếp tục với sự cẩn thận.\n"
-#: ../memline.c:3246
-#, fuzzy
-msgid "(2) An edit session for this file crashed.\n"
+msgid ""
+"\n"
+"(2) An edit session for this file crashed.\n"
msgstr ""
"\n"
"(2) Lần soạn thảo trước của tập tin này gặp sự cố.\n"
-#: ../memline.c:3247
-msgid " If this is the case, use \":recover\" or \"nvim -r "
+msgid " If this is the case, use \":recover\" or \"vim -r "
msgstr ""
-" Trong trường hợp này, hãy sử dụng câu lệnh \":recover\" hoặc \"nvim -r "
+" Trong trường hợp này, hãy sử dụng câu lệnh \":recover\" hoặc \"vim -r "
-#: ../memline.c:3249
msgid ""
"\"\n"
" to recover the changes (see \":help recovery\").\n"
@@ -4021,12 +3049,10 @@ msgstr ""
"\"\n"
" để phục hồi những thay đổi (hãy xem \":help recovery\").\n"
-#: ../memline.c:3250
msgid " If you did this already, delete the swap file \""
msgstr ""
" Nếu đã thực hiện thao tác này rồi, thì hãy xóa tập tin trao đổi (swap) \""
-#: ../memline.c:3252
msgid ""
"\"\n"
" to avoid this message.\n"
@@ -4034,23 +3060,18 @@ msgstr ""
"\"\n"
" để tránh sự xuất hiện của thông báo này trong tương lai.\n"
-#: ../memline.c:3450 ../memline.c:3452
msgid "Swap file \""
msgstr "Tập tin trao đổi (swap) \""
-#: ../memline.c:3451 ../memline.c:3455
msgid "\" already exists!"
msgstr "\" đã có rồi!"
-#: ../memline.c:3457
msgid "VIM - ATTENTION"
msgstr "VIM - CHÚ Ý"
-#: ../memline.c:3459
msgid "Swap file already exists!"
msgstr "Tập tin trao đổi (swap) đã rồi!"
-#: ../memline.c:3464
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
@@ -4064,75 +3085,44 @@ msgstr ""
"&Q Thoát\n"
"&A Gián đoạn"
-#: ../memline.c:3467
-#, fuzzy
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
"&Recover\n"
-"&Delete it\n"
"&Quit\n"
-"&Abort"
+"&Abort\n"
+"&Delete it"
msgstr ""
"&O Mở chỉ để đọc\n"
"&E Vẫn soạn thảo\n"
"&R Phục hồi\n"
"&Q Thoát\n"
-"&A Gián đoạn"
+"&A Gián đoạn&D Xóa nó"
-#.
-#. * 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
msgid "E326: Too many swap files found"
msgstr "E326: Tìm thấy quá nhiều tập tin trao đổi (swap)"
-#: ../memory.c:227
-#, c-format
-msgid "E342: Out of memory! (allocating %<PRIu64> bytes)"
-msgstr "E342: Không đủ bộ nhớ! (phân chia %<PRIu64> byte)"
-
-#: ../menu.c:62
msgid "E327: Part of menu-item path is not sub-menu"
msgstr ""
"E327: Một phần của đường dẫn tới phần tử của trình đơn không phải là trình "
"đơn con"
-#: ../menu.c:63
msgid "E328: Menu only exists in another mode"
msgstr "E328: Trình đơn chỉ có trong chế độ khác"
-#: ../menu.c:64
-#, fuzzy, c-format
-msgid "E329: No menu \"%s\""
+msgid "E329: No menu of that name"
msgstr "E329: Không có trình đơn với tên như vậy"
-#. Only a mnemonic or accelerator is not valid.
-#: ../menu.c:329
-msgid "E792: Empty menu name"
-msgstr ""
-
-#: ../menu.c:340
msgid "E330: Menu path must not lead to a sub-menu"
msgstr "E330: Đường dẫn tới trình đơn không được đưa tới trình đơn con"
-#: ../menu.c:365
msgid "E331: Must not add menu items directly to menu bar"
msgstr ""
"E331: Các phần tử của trình đơn không thể thêm trực tiếp vào thanh trình đơn"
-#: ../menu.c:370
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: Cái phân chia không thể là một phần của đường dẫn tới trình đơn"
-#. Now we have found the matching menu, and we list the mappings
-#. Highlight title
-#: ../menu.c:762
msgid ""
"\n"
"--- Menus ---"
@@ -4140,70 +3130,62 @@ msgstr ""
"\n"
"--- Trình đơn ---"
-#: ../menu.c:1313
+msgid "Tear off this menu"
+msgstr "Chia cắt trình đơn này"
+
msgid "E333: Menu path must lead to a menu item"
msgstr "E333: Đường dẫn tới trình đơn phải đưa tới một phần tử cuả trình đơn"
-#: ../menu.c:1330
#, c-format
msgid "E334: Menu not found: %s"
msgstr "E334: Không tìm thấy trình đơn: %s"
-#: ../menu.c:1396
#, c-format
msgid "E335: Menu not defined for %s mode"
msgstr "E335: Trình đơn không được định nghĩa cho chế độ %s"
-#: ../menu.c:1426
msgid "E336: Menu path must lead to a sub-menu"
msgstr "E336: Đường dẫn tới trình đơn phải đưa tới một trình đơn con"
-#: ../menu.c:1447
msgid "E337: Menu not found - check menu names"
msgstr "E337: Không tìm thấy trình đơn - hãy kiểm tra tên trình đơn"
-#: ../message.c:423
#, c-format
msgid "Error detected while processing %s:"
msgstr "Phát hiện lỗi khi xử lý %s:"
-#: ../message.c:445
#, c-format
msgid "line %4ld:"
msgstr "dòng %4ld:"
-#: ../message.c:617
-#, c-format
-msgid "E354: Invalid register name: '%s'"
-msgstr "E354: Tên sổ đăng ký không cho phép: '%s'"
+msgid "[string too long]"
+msgstr "[chuỗi quá dài]"
+
+msgid "Messages maintainer: The Vim Project"
+msgstr ""
+"Bản dịch các thông báo sang tiếng Việt: Phan Vĩnh Thịnh <teppi@vnlinux.org>"
-#: ../message.c:986
msgid "Interrupt: "
msgstr "Gián đoạn: "
-#: ../message.c:988
-#, fuzzy
-msgid "Press ENTER or type command to continue"
-msgstr "Nhấn phím ENTER hoặc nhập câu lệnh để tiếp tục"
+msgid "Hit ENTER to continue"
+msgstr "Nhấn phím ENTER để tiếp tục"
-#: ../message.c:1843
-#, fuzzy, c-format
-msgid "%s line %<PRId64>"
-msgstr "%s, dòng %<PRId64>"
+msgid "Hit ENTER or type command to continue"
+msgstr "Nhấn phím ENTER hoặc nhập câu lệnh để tiếp tục"
-#: ../message.c:2392
msgid "-- More --"
msgstr "-- Còn nữa --"
-#: ../message.c:2398
-msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr ""
+msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
+msgstr " (RET/BS: dòng, SPACE/b: trang, d/u: nửa trang, q: thoát)"
+
+msgid " (RET: line, SPACE: page, d: half page, q: quit)"
+msgstr " (RET: dòng, SPACE: trang, d: nửa trang, q: thoát)"
-#: ../message.c:3021 ../message.c:3031
msgid "Question"
msgstr "Câu hỏi"
-#: ../message.c:3023
msgid ""
"&Yes\n"
"&No"
@@ -4211,17 +3193,6 @@ msgstr ""
"&Có\n"
"&Không"
-#: ../message.c:3033
-msgid ""
-"&Yes\n"
-"&No\n"
-"&Cancel"
-msgstr ""
-"&Có\n"
-"&Không\n"
-"&Dừng"
-
-#: ../message.c:3045
msgid ""
"&Yes\n"
"&No\n"
@@ -4234,181 +3205,233 @@ msgstr ""
"&Vứt bỏ tất cả\n"
"&Dừng lại"
-#: ../message.c:3058
-#, fuzzy
-msgid "E766: Insufficient arguments for printf()"
-msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
+msgid "Save File dialog"
+msgstr "Ghi nhớ tập tin"
-#: ../message.c:3119
-msgid "E807: Expected Float argument for printf()"
-msgstr ""
+msgid "Open File dialog"
+msgstr "Mở tập tin"
-#: ../message.c:3873
-#, fuzzy
-msgid "E767: Too many arguments to printf()"
-msgstr "E118: Quá nhiều tham số cho hàm: %s"
+msgid "E338: Sorry, no file browser in console mode"
+msgstr ""
+"E338: Xin lỗi nhưng không có trình duyệt tập tin trong chế độ kênh giao tác "
+"(console)"
-#: ../misc1.c:2256
msgid "W10: Warning: Changing a readonly file"
msgstr "W10: Cảnh báo: Thay đổi một tập tin chỉ có quyền đọc"
-#: ../misc1.c:2537
-msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr ""
-
-#: ../misc1.c:2539
-msgid "Type number and <Enter> (empty cancels): "
-msgstr ""
-
-#: ../misc1.c:2585
msgid "1 more line"
msgstr "Thêm 1 dòng"
-#: ../misc1.c:2588
msgid "1 line less"
msgstr "Bớt 1 dòng"
-#: ../misc1.c:2593
#, c-format
-msgid "%<PRId64> more lines"
-msgstr "Thêm %<PRId64> dòng"
+msgid "%ld more lines"
+msgstr "Thêm %ld dòng"
-#: ../misc1.c:2596
#, c-format
-msgid "%<PRId64> fewer lines"
-msgstr "Bớt %<PRId64> dòng"
+msgid "%ld fewer lines"
+msgstr "Bớt %ld dòng"
-#: ../misc1.c:2599
msgid " (Interrupted)"
msgstr " (Bị gián đoạn)"
-#: ../misc1.c:2635
-msgid "Beep!"
+msgid "Vim: preserving files...\n"
+msgstr "Vim: ghi nhớ các tập tin...\n"
+
+msgid "Vim: Finished.\n"
+msgstr "Vim: Đã xong.\n"
+
+msgid "ERROR: "
+msgstr "LỖI: "
+
+#, c-format
+msgid ""
+"\n"
+"[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n"
msgstr ""
+"\n"
+"[byte] tổng phân phối-còn trống %lu-%lu, sử dụng %lu, píc sử dụng %lu\n"
+
+#, c-format
+msgid ""
+"[calls] total re/malloc()'s %lu, total free()'s %lu\n"
+"\n"
+msgstr ""
+"[gọi] tổng re/malloc() %lu, tổng free() %lu\n"
+"\n"
+
+msgid "E340: Line is becoming too long"
+msgstr "E340: Dòng đang trở thành quá dài"
+
+#, c-format
+msgid "E341: Internal error: lalloc(%ld, )"
+msgstr "E341: Lỗi nội bộ: lalloc(%ld, )"
+
+#, c-format
+msgid "E342: Out of memory! (allocating %lu bytes)"
+msgstr "E342: Không đủ bộ nhớ! (phân chia %lu byte)"
-#: ../misc2.c:738
#, c-format
msgid "Calling shell to execute: \"%s\""
msgstr "Gọi shell để thực hiện: \"%s\""
-#: ../normal.c:183
-msgid "E349: No identifier under cursor"
-msgstr "E349: Không có tên ở vị trí con trỏ"
+msgid "E545: Missing colon"
+msgstr "E545: Thiếu dấu hai chấm"
-#: ../normal.c:1866
-#, fuzzy
-msgid "E774: 'operatorfunc' is empty"
-msgstr "E91: Tùy chọn 'shell' là một chuỗi rỗng"
+msgid "E546: Illegal mode"
+msgstr "E546: Chế độ không cho phép"
+
+msgid "E547: Illegal mouseshape"
+msgstr "E547: Dạng trỏ chuột không cho phép"
+
+# TODO: Capitalise first word of message?
+msgid "E548: Digit expected"
+msgstr "E548: yêu cầu một số"
+
+msgid "E549: Illegal percentage"
+msgstr "E549: Tỷ lệ phần trăm không cho phép"
+
+msgid "Enter encryption key: "
+msgstr "Nhập mật khẩu để mã hóa: "
+
+msgid "Enter same key again: "
+msgstr " Nhập lại mật khẩu:"
+
+msgid "Keys don't match!"
+msgstr "Hai mật khẩu không trùng nhau!"
+
+#, c-format
+msgid ""
+"E343: Invalid path: '**[number]' must be at the end of the path or be "
+"followed by '%s'."
+msgstr ""
+"E343: Đường dẫn đưa ra không đúng: '**[số]' phải ở cuối đường dẫn hoặc theo "
+"sau bởi '%s'"
+
+#, c-format
+msgid "E344: Can't find directory \"%s\" in cdpath"
+msgstr "E344: Không tìm thấy thư mục \"%s\" để chuyển thư mục"
+
+#, c-format
+msgid "E345: Can't find file \"%s\" in path"
+msgstr "E345: Không tìm thấy tập tin \"%s\" trong đường dẫn"
+
+#, c-format
+msgid "E346: No more directory \"%s\" found in cdpath"
+msgstr "E346: Trong đường dẫn thay đổi thư mục không còn có thư mục \"%s\" nữa"
+
+#, c-format
+msgid "E347: No more file \"%s\" found in path"
+msgstr "E347: Trong đường dẫn path không còn có tập tin \"%s\" nữa"
+
+msgid "E550: Missing colon"
+msgstr "E550: Thiếu dấu hai chấm"
+
+msgid "E551: Illegal component"
+msgstr "E551: Thành phần không cho phép"
+
+# TODO: Capitalise first word of message?
+msgid "E552: Digit expected"
+msgstr "E552: Cần chỉ ra một số"
+
+msgid "Cannot connect to Netbeans #2"
+msgstr "Không kết nối được với Netbeans #2"
+
+msgid "Cannot connect to Netbeans"
+msgstr "Không kết nối được với NetBeans"
+
+#, c-format
+msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
+msgstr ""
+"E668: Chế độ truy cập thông tin về liên kết với NetBeans không đúng: \"%s\""
+
+msgid "read from Netbeans socket"
+msgstr "đọc từ socket NetBeans"
+
+#, c-format
+msgid "E658: NetBeans connection lost for buffer %ld"
+msgstr "E658: Bị mất liên kết với NetBeans cho bộ đệm %ld"
-#: ../normal.c:2637
msgid "Warning: terminal cannot highlight"
msgstr "Cảnh báo: terminal không thực hiện được sự chiếu sáng"
-#: ../normal.c:2807
msgid "E348: No string under cursor"
msgstr "E348: Không có chuỗi ở vị trí con trỏ"
-#: ../normal.c:3937
+msgid "E349: No identifier under cursor"
+msgstr "E349: Không có tên ở vị trí con trỏ"
+
msgid "E352: Cannot erase folds with current 'foldmethod'"
msgstr ""
"E352: Không thể tẩy xóa nếp gấp với giá trị hiện thời của tùy chọn "
"'foldmethod'"
-#: ../normal.c:5897
-msgid "E664: changelist is empty"
+# TODO: Capitalise first word of message?
+msgid "E664: Changelist is empty"
msgstr "E664: danh sách những thay đổi trống rỗng"
-#: ../normal.c:5899
msgid "E662: At start of changelist"
msgstr "E662: Ở đầu danh sách những thay đổi"
-#: ../normal.c:5901
msgid "E663: At end of changelist"
msgstr "E663: Ở cuối danh sách những thay đổi"
-#: ../normal.c:7053
-msgid "Type :quit<Enter> to exit Nvim"
+msgid "Type :quit<Enter> to exit Vim"
msgstr "Gõ :quit<Enter> để thoát khỏi Vim"
-#: ../ops.c:248
#, c-format
msgid "1 line %sed 1 time"
msgstr "Trên 1 dòng %s 1 lần"
-#: ../ops.c:250
#, c-format
msgid "1 line %sed %d times"
msgstr "Trên 1 dòng %s %d lần"
-#: ../ops.c:253
#, c-format
-msgid "%<PRId64> lines %sed 1 time"
-msgstr "Trên %<PRId64> dòng %s 1 lần"
+msgid "%ld lines %sed 1 time"
+msgstr "Trên %ld dòng %s 1 lần"
-#: ../ops.c:256
#, c-format
-msgid "%<PRId64> lines %sed %d times"
-msgstr "Trên %<PRId64> dòng %s %d lần"
+msgid "%ld lines %sed %d times"
+msgstr "Trên %ld dòng %s %d lần"
-#: ../ops.c:592
#, c-format
-msgid "%<PRId64> lines to indent... "
-msgstr "Thụt đầu %<PRId64> dòng..."
+msgid "%ld lines to indent... "
+msgstr "Thụt đầu %ld dòng..."
-#: ../ops.c:634
msgid "1 line indented "
msgstr "Đã thụt đầu 1 dòng"
-#: ../ops.c:636
#, c-format
-msgid "%<PRId64> lines indented "
-msgstr "%<PRId64> dòng đã thụt đầu"
-
-#: ../ops.c:938
-#, fuzzy
-msgid "E748: No previously used register"
-msgstr "E186: Không có thư mục trước"
+msgid "%ld lines indented "
+msgstr "%ld dòng đã thụt đầu"
-#. must display the prompt
-#: ../ops.c:1433
msgid "cannot yank; delete anyway"
msgstr "sao chép không thành công; đã xóa"
-#: ../ops.c:1929
msgid "1 line changed"
msgstr "1 dòng đã thay đổi"
-#: ../ops.c:1931
#, c-format
-msgid "%<PRId64> lines changed"
-msgstr "%<PRId64> đã thay đổi"
+msgid "%ld lines changed"
+msgstr "%ld đã thay đổi"
-#: ../ops.c:2521
-#, fuzzy
-msgid "block of 1 line yanked"
-msgstr "đã sao chép 1 dòng"
+#, c-format
+msgid "freeing %ld lines"
+msgstr "đã làm sạch %ld dòng"
-#: ../ops.c:2523
msgid "1 line yanked"
msgstr "đã sao chép 1 dòng"
-#: ../ops.c:2525
-#, fuzzy, c-format
-msgid "block of %<PRId64> lines yanked"
-msgstr "đã sao chép %<PRId64> dòng"
-
-#: ../ops.c:2528
#, c-format
-msgid "%<PRId64> lines yanked"
-msgstr "đã sao chép %<PRId64> dòng"
+msgid "%ld lines yanked"
+msgstr "đã sao chép %ld dòng"
-#: ../ops.c:2710
#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: Trong sổ đăng ký %s không có gì hết"
-#. Highlight title
-#: ../ops.c:3185
msgid ""
"\n"
"--- Registers ---"
@@ -4416,11 +3439,9 @@ msgstr ""
"\n"
"--- Sổ đăng ký ---"
-#: ../ops.c:4455
msgid "Illegal register name"
msgstr "Tên sổ đăng ký không cho phép"
-#: ../ops.c:4533
msgid ""
"\n"
"# Registers:\n"
@@ -4428,194 +3449,154 @@ msgstr ""
"\n"
"# Sổ đăng ký:\n"
-#: ../ops.c:4575
#, c-format
msgid "E574: Unknown register type %d"
msgstr "E574: Loại sổ đăng ký không biết %d"
-#: ../ops.c:5089
#, c-format
-msgid "%<PRId64> Cols; "
-msgstr "%<PRId64> Cột; "
+msgid "E354: Invalid register name: '%s'"
+msgstr "E354: Tên sổ đăng ký không cho phép: '%s'"
-#: ../ops.c:5097
#, c-format
-msgid ""
-"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
-"%<PRId64> of %<PRId64> Bytes"
-msgstr ""
-"Chọn %s%<PRId64> của %<PRId64> Dòng; %<PRId64> của %<PRId64> Từ; %<PRId64> "
-"của %<PRId64> Byte"
-
-#: ../ops.c:5105
-#, fuzzy, c-format
-msgid ""
-"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
-"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes"
-msgstr ""
-"Chọn %s%<PRId64> của %<PRId64> Dòng; %<PRId64> của %<PRId64> Từ; %<PRId64> "
-"của %<PRId64> Byte"
+msgid "%ld Cols; "
+msgstr "%ld Cột; "
-#: ../ops.c:5123
#, c-format
-msgid ""
-"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
-"%<PRId64> of %<PRId64>"
-msgstr ""
-"Cột %s của %s; Dòng %<PRId64> của %<PRId64>; Từ %<PRId64> của %<PRId64>; "
-"Byte %<PRId64> của %<PRId64>"
+msgid "Selected %s%ld of %ld Lines; %ld of %ld Words; %ld of %ld Bytes"
+msgstr "Chọn %s%ld của %ld Dòng; %ld của %ld Từ; %ld của %ld Byte"
-#: ../ops.c:5133
-#, fuzzy, c-format
-msgid ""
-"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char "
-"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>"
-msgstr ""
-"Cột %s của %s; Dòng %<PRId64> của %<PRId64>; Từ %<PRId64> của %<PRId64>; "
-"Byte %<PRId64> của %<PRId64>"
+#, c-format
+msgid "Col %s of %s; Line %ld of %ld; Word %ld of %ld; Byte %ld of %ld"
+msgstr "Cột %s của %s; Dòng %ld của %ld; Từ %ld của %ld; Byte %ld của %ld"
-#: ../ops.c:5146
#, c-format
-msgid "(+%<PRId64> for BOM)"
-msgstr "(+%<PRId64> cho BOM)"
+msgid "(+%ld for BOM)"
+msgstr "(+%ld cho BOM)"
-#: ../option.c:1238
msgid "%<%f%h%m%=Page %N"
msgstr "%<%f%h%m%=Trang %N"
-#: ../option.c:1574
msgid "Thanks for flying Vim"
msgstr "Xin cảm ơn đã sử dụng Vim"
-#. found a mismatch: skip
-#: ../option.c:2698
msgid "E518: Unknown option"
msgstr "E518: Tùy chọn không biết"
-#: ../option.c:2709
msgid "E519: Option not supported"
msgstr "E519: Tùy chọn không được hỗ trợ"
-#: ../option.c:2740
msgid "E520: Not allowed in a modeline"
msgstr "E520: Không cho phép trên dòng chế độ (modeline)"
-#: ../option.c:2815
-msgid "E846: Key code not set"
+msgid ""
+"\n"
+"\tLast set from "
msgstr ""
+"\n"
+"\tLần cuối cùng tùy chọn thay đổi vào "
-#: ../option.c:2924
msgid "E521: Number required after ="
msgstr "E521: Sau dấu = cần đưa ra một số"
-#: ../option.c:3226 ../option.c:3864
msgid "E522: Not found in termcap"
msgstr "E522: Không tìm thấy trong termcap"
-#: ../option.c:3335
#, c-format
msgid "E539: Illegal character <%s>"
msgstr "E539: Ký tự không cho phép <%s>"
-#: ../option.c:3862
msgid "E529: Cannot set 'term' to empty string"
msgstr "E529: Giá trị của tùy chọn 'term' không thể là một chuỗi trống rỗng"
-#: ../option.c:3885
+msgid "E530: Cannot change term in GUI"
+msgstr "E530: Không thể thay đổi terminal trong giao diện đồ họa GUI"
+
+msgid "E531: Use \":gui\" to start the GUI"
+msgstr "E531: Hãy sử dụng \":gui\" để chạy giao diện đồ họa GUI"
+
msgid "E589: 'backupext' and 'patchmode' are equal"
msgstr "E589: giá trị của tùy chọn 'backupext' và 'patchmode' bằng nhau"
-#: ../option.c:3964
-msgid "E834: Conflicts with value of 'listchars'"
-msgstr ""
+msgid "E617: Cannot be changed in the GTK+ 2 GUI"
+msgstr "E617: Không thể thay đổi trong giao diện đồ họa GTK+ 2"
-#: ../option.c:3966
-msgid "E835: Conflicts with value of 'fillchars'"
-msgstr ""
-
-#: ../option.c:4163
msgid "E524: Missing colon"
msgstr "E524: Thiếu dấu hai chấm"
-#: ../option.c:4165
msgid "E525: Zero length string"
msgstr "E525: Chuỗi có độ dài bằng không"
-#: ../option.c:4220
#, c-format
msgid "E526: Missing number after <%s>"
msgstr "E526: Thiếu một số sau <%s>"
-#: ../option.c:4232
msgid "E527: Missing comma"
msgstr "E527: Thiếu dấu phẩy"
-#: ../option.c:4239
msgid "E528: Must specify a ' value"
msgstr "E528: Cần đưa ra một giá trị cho '"
-#: ../option.c:4271
msgid "E595: contains unprintable or wide character"
msgstr "E595: chứa ký tự không in ra hoặc ký tự với chiều rộng gấp đôi"
-#: ../option.c:4469
+msgid "E596: Invalid font(s)"
+msgstr "E596: Phông chữ không đúng"
+
+# TODO: Capitalise first word of message?
+msgid "E597: Can't select fontset"
+msgstr "E597: không chọn được bộ phông chữ"
+
+msgid "E598: Invalid fontset"
+msgstr "E598: Bộ phông chữ không đúng"
+
+# TODO: Capitalise first word of message?
+msgid "E533: Can't select wide font"
+msgstr "E533: không chọn được phông chữ với các ký tự có chiều rộng gấp đôi"
+
+msgid "E534: Invalid wide font"
+msgstr "E534: Phông chữ, với ký tự có chiều rộng gấp đôi, không đúng"
+
#, c-format
msgid "E535: Illegal character after <%c>"
msgstr "E535: Ký tự sau <%c> không chính xác"
-#: ../option.c:4534
-msgid "E536: comma required"
+# TODO: Capitalise first word of message?
+msgid "E536: Comma required"
msgstr "E536: cầu có dấu phẩy"
-#: ../option.c:4543
#, c-format
msgid "E537: 'commentstring' must be empty or contain %s"
msgstr "E537: Giá trị của tùy chọn 'commentstring' phải rỗng hoặc chứa %s"
-#: ../option.c:4928
+msgid "E538: No mouse support"
+msgstr "E538: Chuột không được hỗ trợ"
+
msgid "E540: Unclosed expression sequence"
msgstr "E540: Dãy các biểu thức không đóng"
-#: ../option.c:4932
-msgid "E541: too many items"
-msgstr "E541: quá nhiều phần tử"
-#: ../option.c:4934
-msgid "E542: unbalanced groups"
+# TODO: Capitalise first word of message?
+msgid "E542: Unbalanced groups"
msgstr "E542: các nhóm không cân bằng"
-#: ../option.c:5148
msgid "E590: A preview window already exists"
msgstr "E590: Cửa sổ xem trước đã có"
-#: ../option.c:5311
msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'"
msgstr "W17: Tiếng Ả Rập yêu cầu sử dụng UTF-8, hãy nhập ':set encoding=utf-8'"
-#: ../option.c:5623
#, c-format
msgid "E593: Need at least %d lines"
msgstr "E593: Cần ít nhất %d dòng"
-#: ../option.c:5631
#, c-format
msgid "E594: Need at least %d columns"
msgstr "E594: Cần ít nhất %d cột"
-#: ../option.c:6011
#, c-format
msgid "E355: Unknown option: %s"
msgstr "E355: Tùy chọn không biết: %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
-msgid "E521: Number required: &%s = '%s'"
-msgstr "E521: Sau dấu = cần đưa ra một số"
-
-#: ../option.c:6149
msgid ""
"\n"
"--- Terminal codes ---"
@@ -4623,7 +3604,6 @@ msgstr ""
"\n"
"--- Mã terminal ---"
-#: ../option.c:6151
msgid ""
"\n"
"--- Global option values ---"
@@ -4631,7 +3611,6 @@ msgstr ""
"\n"
"--- Giá trị tùy chọn toàn cầu ---"
-#: ../option.c:6153
msgid ""
"\n"
"--- Local option values ---"
@@ -4639,7 +3618,6 @@ msgstr ""
"\n"
"--- Giá trị tùy chọn nội bộ ---"
-#: ../option.c:6155
msgid ""
"\n"
"--- Options ---"
@@ -4647,21 +3625,127 @@ msgstr ""
"\n"
"--- Tùy chọn ---"
-#: ../option.c:6816
msgid "E356: get_varp ERROR"
msgstr "E356: LỖI get_varp"
-#: ../option.c:7696
#, c-format
msgid "E357: 'langmap': Matching character missing for %s"
msgstr "E357: 'langmap': Thiếu ký tự tương ứng cho %s"
-#: ../option.c:7715
#, c-format
msgid "E358: 'langmap': Extra characters after semicolon: %s"
msgstr "E358: 'langmap': Thừa ký tự sau dấu chấm phẩy: %s"
-#: ../os/shell.c:194
+msgid "cannot open "
+msgstr "không mở được "
+
+msgid "VIM: Can't open window!\n"
+msgstr "VIM: Không mở được cửa sổ!\n"
+
+msgid "Need Amigados version 2.04 or later\n"
+msgstr "Cần Amigados phiên bản 2.04 hoặc mới hơn\n"
+
+#, c-format
+msgid "Need %s version %ld\n"
+msgstr "Cần %s phiên bản %ld\n"
+
+msgid "Cannot open NIL:\n"
+msgstr "Không mở được NIL:\n"
+
+msgid "Cannot create "
+msgstr "Không tạo được "
+
+#, c-format
+msgid "Vim exiting with %d\n"
+msgstr "Thoát Vim với mã %d\n"
+
+msgid "cannot change console mode ?!\n"
+msgstr "không thay đổi được chế độ kênh giao tác (console)?!\n"
+
+msgid "mch_get_shellsize: not a console??\n"
+msgstr "mch_get_shellsize: không phải là kênh giao tác (console)??\n"
+
+msgid "E360: Cannot execute shell with -f option"
+msgstr "E360: Không chạy được shell với tùy chọn -f"
+
+msgid "Cannot execute "
+msgstr "Không chạy được "
+
+msgid "shell "
+msgstr "shell "
+
+msgid " returned\n"
+msgstr " thoát\n"
+
+msgid "ANCHOR_BUF_SIZE too small."
+msgstr "Giá trị ANCHOR_BUF_SIZE quá nhỏ."
+
+msgid "I/O ERROR"
+msgstr "LỖI I/O (NHẬP/XUẤT)"
+
+msgid "...(truncated)"
+msgstr "...(bị cắt bớt)"
+
+msgid "'columns' is not 80, cannot execute external commands"
+msgstr "Tùy chọn 'columns' khác 80, chương trình ngoại trú không thể thực hiện"
+
+msgid "E237: Printer selection failed"
+msgstr "E237: Chọn máy in không thành công"
+
+#, c-format
+msgid "to %s on %s"
+msgstr "tới %s trên %s"
+
+#, c-format
+msgid "E613: Unknown printer font: %s"
+msgstr "E613: Không rõ phông chữ của máy in: %s"
+
+#, c-format
+msgid "E238: Print error: %s"
+msgstr "E238: Lỗi in: %s"
+
+msgid "Unknown"
+msgstr "Không rõ"
+
+#, c-format
+msgid "Printing '%s'"
+msgstr "Đang in '%s'"
+
+#, c-format
+msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
+msgstr "E244: Tên bảng mã không cho phép \"%s\" trong tên phông chữ \"%s\""
+
+#, c-format
+msgid "E245: Illegal char '%c' in font name \"%s\""
+msgstr "E245: Ký tự không cho phép '%c' trong tên phông chữ \"%s\""
+
+msgid "Vim: Double signal, exiting\n"
+msgstr "Vim: Tín hiệu đôi, thoát\n"
+
+#, c-format
+msgid "Vim: Caught deadly signal %s\n"
+msgstr "Vim: Nhận được tín hiệu chết %s\n"
+
+msgid "Vim: Caught deadly signal\n"
+msgstr "Vim: Nhận được tín hiệu chết\n"
+
+#, c-format
+msgid "Opening the X display took %ld msec"
+msgstr "Mở màn hình X mất %ld mili giây"
+
+msgid ""
+"\n"
+"Vim: Got X error\n"
+msgstr ""
+"\n"
+"Vim: Lỗi X\n"
+
+msgid "Testing the X display failed"
+msgstr "Kiểm tra màn hình X không thành công"
+
+msgid "Opening the X display timed out"
+msgstr "Không mở được màn hình X trong thời gian cho phép (time out)"
+
msgid ""
"\n"
"Cannot execute shell "
@@ -4669,7 +3753,13 @@ msgstr ""
"\n"
"Không chạy được shell "
-#: ../os/shell.c:439
+msgid ""
+"\n"
+"Cannot execute shell sh\n"
+msgstr ""
+"\n"
+"Không chạy được shell sh\n"
+
msgid ""
"\n"
"shell returned "
@@ -4677,959 +3767,387 @@ msgstr ""
"\n"
"shell dừng làm việc "
-#: ../os_unix.c:465 ../os_unix.c:471
msgid ""
"\n"
-"Could not get security context for "
+"Cannot create pipes\n"
msgstr ""
+"\n"
+"Không tạo được đường ống (pipe)\n"
-#: ../os_unix.c:479
msgid ""
"\n"
-"Could not set security context for "
+"Cannot fork\n"
msgstr ""
+"\n"
+"Không thực hiện được fork()\n"
-#: ../os_unix.c:1558 ../os_unix.c:1647
-#, c-format
-msgid "dlerror = \"%s\""
+msgid ""
+"\n"
+"Command terminated\n"
msgstr ""
+"\n"
+"Câu lệnh bị gián đoạn\n"
+
+msgid "XSMP lost ICE connection"
+msgstr "XSMP mất kết nối ICE"
+
+msgid "Opening the X display failed"
+msgstr "Mở màn hình X không thành công"
+
+msgid "XSMP handling save-yourself request"
+msgstr "XSMP xử lý yêu cầu tự động ghi nhớ"
+
+msgid "XSMP opening connection"
+msgstr "XSMP mở kết nối"
+
+msgid "XSMP ICE connection watch failed"
+msgstr "XSMP mất theo dõi kết nối ICE"
-#: ../path.c:1449
#, c-format
-msgid "E447: Can't find file \"%s\" in path"
-msgstr "E447: Không tìm thấy tập tin \"%s\" trong đường dẫn"
+msgid "XSMP SmcOpenConnection failed: %s"
+msgstr "XSMP thực hiện SmcOpenConnection không thành công: %s"
+
+msgid "At line"
+msgstr "Tại dòng"
+
+msgid "Could not allocate memory for command line."
+msgstr "Không phân chia được bộ nhớ cho dòng lệnh."
+
+msgid "VIM Error"
+msgstr "Lỗi VIM"
+
+msgid "Could not load vim32.dll!"
+msgstr "Không nạp được vim32.dll!"
+
+msgid "Could not fix up function pointers to the DLL!"
+msgstr "Không sửa được cái chỉ (pointer) hàm số tới DLL!"
+
+#, c-format
+msgid "shell returned %d"
+msgstr "thoát shell với mã %d"
+
+#, c-format
+msgid "Vim: Caught %s event\n"
+msgstr "Vim: Nhận được sự kiện %s\n"
+
+msgid "close"
+msgstr "đóng"
+
+msgid "logoff"
+msgstr "thoát"
+
+msgid "shutdown"
+msgstr "tắt máy"
+
+msgid "E371: Command not found"
+msgstr "E371: Câu lệnh không tìm thấy"
+
+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 ""
+"Không tìm thấy VIMRUN.EXE trong $PATH.\n"
+"Lệnh ngoại trú sẽ không dừng lại sau khi hoàn thành.\n"
+"Thông tin chi tiết xem trong :help win32-vimrun"
+
+msgid "Vim Warning"
+msgstr "Cảnh báo Vim"
-#: ../quickfix.c:359
#, c-format
msgid "E372: Too many %%%c in format string"
msgstr "E372: Quá nhiều %%%c trong chuỗi định dạng"
-#: ../quickfix.c:371
#, c-format
msgid "E373: Unexpected %%%c in format string"
msgstr "E373: Không mong đợi %%%c trong chuỗi định dạng"
-#: ../quickfix.c:420
msgid "E374: Missing ] in format string"
msgstr "E374: Thiếu ] trong chuỗi định dạng"
-#: ../quickfix.c:431
#, c-format
msgid "E375: Unsupported %%%c in format string"
msgstr "E375: %%%c không được hỗ trợ trong chuỗi định dạng"
-#: ../quickfix.c:448
#, c-format
msgid "E376: Invalid %%%c in format string prefix"
msgstr "E376: Không cho phép %%%c trong tiền tố của chuỗi định dạng"
-#: ../quickfix.c:454
#, c-format
msgid "E377: Invalid %%%c in format string"
msgstr "E377: Không cho phép %%%c trong chuỗi định dạng"
-#. nothing found
-#: ../quickfix.c:477
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: Trong giá trị 'errorformat' thiếu mẫu (pattern)"
-#: ../quickfix.c:695
msgid "E379: Missing or empty directory name"
msgstr "E379: Tên thư mục không được đưa ra hoặc bằng một chuỗi rỗng"
-#: ../quickfix.c:1305
msgid "E553: No more items"
msgstr "E553: Không còn phần tử nào nữa"
-#: ../quickfix.c:1674
#, c-format
msgid "(%d of %d)%s%s: "
msgstr "(%d của %d)%s%s: "
-#: ../quickfix.c:1676
msgid " (line deleted)"
msgstr " (dòng bị xóa)"
-#: ../quickfix.c:1863
msgid "E380: At bottom of quickfix stack"
msgstr "E380: Ở dưới của đống sửa nhanh"
-#: ../quickfix.c:1869
msgid "E381: At top of quickfix stack"
msgstr "E381: Ở đầu của đống sửa nhanh"
-#: ../quickfix.c:1880
#, c-format
msgid "error list %d of %d; %d errors"
msgstr "danh sách lỗi %d của %d; %d lỗi"
-#: ../quickfix.c:2427
msgid "E382: Cannot write, 'buftype' option is set"
msgstr "E382: Không ghi nhớ được, giá trị 'buftype' không phải là chuỗi rỗng"
-#: ../quickfix.c:2812
-msgid "E683: File name missing or invalid pattern"
-msgstr ""
-
-#: ../quickfix.c:2911
-#, fuzzy, c-format
-msgid "Cannot open file \"%s\""
-msgstr "E624: Không thể mở tập tin \"%s\""
+# TODO: Capitalise first word of message?
+msgid "E369: Invalid item in %s%%[]"
+msgstr "E369: phần tử không cho phép trong %s%%[]"
-#: ../quickfix.c:3429
-#, fuzzy
-msgid "E681: Buffer is not loaded"
-msgstr "1 bộ đệm được bỏ nạp từ bộ nhớ"
+msgid "E339: Pattern too long"
+msgstr "E339: Mẫu (pattern) quá dài"
-#: ../quickfix.c:3487
-#, fuzzy
-msgid "E777: String or List expected"
-msgstr "E548: yêu cầu một số"
+msgid "E50: Too many \\z("
+msgstr "E50: Quá nhiều \\z("
-#: ../regexp.c:359
#, c-format
-msgid "E369: invalid item in %s%%[]"
-msgstr "E369: phần tử không cho phép trong %s%%[]"
+msgid "E51: Too many %s("
+msgstr "E51: Quá nhiều %s("
-#: ../regexp.c:374
-#, fuzzy, c-format
-msgid "E769: Missing ] after %s["
-msgstr "E69: Thiếu ] sau %s%%["
+msgid "E52: Unmatched \\z("
+msgstr "E52: Không có cặp cho \\z("
-#: ../regexp.c:375
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: Không có cặp cho %s%%("
-#: ../regexp.c:376
#, c-format
msgid "E54: Unmatched %s("
msgstr "E54: Không có cặp cho %s("
-#: ../regexp.c:377
#, c-format
msgid "E55: Unmatched %s)"
msgstr "E55: Không có cặp cho %s)"
-#: ../regexp.c:378
-msgid "E66: \\z( not allowed here"
-msgstr "E66: \\z( không thể sử dụng ở đây"
-
-#: ../regexp.c:379
-msgid "E67: \\z1 et al. not allowed here"
-msgstr "E67: \\z1 và tương tự không được sử dụng ở đây"
-
-#: ../regexp.c:380
-#, c-format
-msgid "E69: Missing ] after %s%%["
-msgstr "E69: Thiếu ] sau %s%%["
-
-#: ../regexp.c:381
#, c-format
-msgid "E70: Empty %s%%[]"
-msgstr "E70: %s%%[] rỗng"
-
-#: ../regexp.c:1209 ../regexp.c:1224
-msgid "E339: Pattern too long"
-msgstr "E339: Mẫu (pattern) quá dài"
+msgid "E56: %s* operand could be empty"
+msgstr "E56: operand %s* không thể rỗng"
-#: ../regexp.c:1371
-msgid "E50: Too many \\z("
-msgstr "E50: Quá nhiều \\z("
-
-#: ../regexp.c:1378
#, c-format
-msgid "E51: Too many %s("
-msgstr "E51: Quá nhiều %s("
+msgid "E57: %s+ operand could be empty"
+msgstr "E57: operand %s+ không thể rỗng"
-#: ../regexp.c:1427
-msgid "E52: Unmatched \\z("
-msgstr "E52: Không có cặp cho \\z("
+# TODO: Capitalise first word of message?
+msgid "E59: Invalid character after %s@"
+msgstr "E59: ký tự không cho phép sau %s@"
-#: ../regexp.c:1637
#, c-format
-msgid "E59: invalid character after %s@"
-msgstr "E59: ký tự không cho phép sau %s@"
+msgid "E58: %s{ operand could be empty"
+msgstr "E58: operand %s{ không thể rỗng"
-#: ../regexp.c:1672
#, c-format
msgid "E60: Too many complex %s{...}s"
msgstr "E60: Quá nhiều cấu trúc phức tạp %s{...}"
-#: ../regexp.c:1687
#, c-format
msgid "E61: Nested %s*"
msgstr "E61: %s* lồng vào"
-#: ../regexp.c:1690
#, c-format
msgid "E62: Nested %s%c"
msgstr "E62: %s%c lồng vào"
-#: ../regexp.c:1800
-msgid "E63: invalid use of \\_"
+# TODO: Capitalise first word of message?
+msgid "E63: Invalid use of \\_"
msgstr "E63: không cho phép sử dụng \\_"
-#: ../regexp.c:1850
#, c-format
msgid "E64: %s%c follows nothing"
msgstr "E64: %s%c không theo sau gì cả"
-#: ../regexp.c:1902
msgid "E65: Illegal back reference"
msgstr "E65: Không cho phép liên kết ngược lại"
-#: ../regexp.c:1943
+msgid "E66: \\z( not allowed here"
+msgstr "E66: \\z( không thể sử dụng ở đây"
+
+msgid "E67: \\z1 - \\z9 not allowed here"
+msgstr "E67: \\z1 và tương tự không được sử dụng ở đây"
+
msgid "E68: Invalid character after \\z"
msgstr "E68: Ký tự không cho phép sau \\z"
-#: ../regexp.c:2049 ../regexp_nfa.c:1296
-#, fuzzy, c-format
-msgid "E678: Invalid character after %s%%[dxouU]"
-msgstr "E71: Ký tự không cho phép sau %s%%"
+#, c-format
+msgid "E69: Missing ] after %s%%["
+msgstr "E69: Thiếu ] sau %s%%["
+
+#, c-format
+msgid "E70: Empty %s%%[]"
+msgstr "E70: %s%%[] rỗng"
-#: ../regexp.c:2107
#, c-format
msgid "E71: Invalid character after %s%%"
msgstr "E71: Ký tự không cho phép sau %s%%"
-#: ../regexp.c:3017
#, c-format
msgid "E554: Syntax error in %s{...}"
msgstr "E554: Lỗi cú pháp trong %s{...}"
-#: ../regexp.c:3805
-msgid "External submatches:\n"
-msgstr "Sự tương ứng con ngoài:\n"
-
-#: ../regexp.c:7022
-msgid ""
-"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
-"used "
-msgstr ""
-
-#: ../regexp_nfa.c:239
-msgid "E865: (NFA) Regexp end encountered prematurely"
-msgstr ""
-
-#: ../regexp_nfa.c:240
-#, c-format
-msgid "E866: (NFA regexp) Misplaced %c"
-msgstr ""
-
-#: ../regexp_nfa.c:242
-#, c-format
-msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr ""
-
-#: ../regexp_nfa.c:1261
-#, c-format
-msgid "E867: (NFA) Unknown operator '\\z%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1387
-#, c-format
-msgid "E867: (NFA) Unknown operator '\\%%%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1802
-#, c-format
-msgid "E869: (NFA) Unknown operator '\\@%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1831
-msgid "E870: (NFA regexp) Error reading repetition limits"
-msgstr ""
-
-#. 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 ""
-
-#. Too many `('
-#: ../regexp_nfa.c:2037
-msgid "E872: (NFA regexp) Too many '('"
-msgstr ""
-
-#: ../regexp_nfa.c:2042
-#, fuzzy
-msgid "E879: (NFA regexp) Too many \\z("
-msgstr "E50: Quá nhiều \\z("
-
-#: ../regexp_nfa.c:2066
-msgid "E873: (NFA regexp) proper termination error"
-msgstr ""
+msgid "E361: Crash intercepted; regexp too complex?"
+msgstr "E361: Sự cố được ngăn chặn; biểu thức chính quy quá phức tạp?"
-#: ../regexp_nfa.c:2599
-msgid "E874: (NFA) Could not pop the stack !"
-msgstr ""
-
-#: ../regexp_nfa.c:3298
-msgid ""
-"E875: (NFA regexp) (While converting from postfix to NFA), too many states "
-"left on stack"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E363: Pattern caused out-of-stack error"
+msgstr "E363: sử dụng mẫu (pattern) gây ra lỗi out-of-stack"
-#: ../regexp_nfa.c:3302
-msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr ""
-
-#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869
-msgid ""
-"Could not open temporary log file for writing, displaying on stderr ... "
-msgstr ""
+msgid "External submatches:\n"
+msgstr "Sự tương ứng con ngoài:\n"
-#: ../regexp_nfa.c:4840
#, c-format
-msgid "(NFA) COULD NOT OPEN %s !"
-msgstr ""
-
-#: ../regexp_nfa.c:6049
-#, fuzzy
-msgid "Could not open temporary log file for writing "
-msgstr "E214: Không tìm thấy tập tin tạm thời (temp) để ghi nhớ"
+msgid "+--%3ld lines folded "
+msgstr "+--%3ld dòng được gấp"
-#: ../screen.c:7435
msgid " VREPLACE"
msgstr " THAY THẾ ẢO"
-#: ../screen.c:7437
msgid " REPLACE"
msgstr " THAY THẾ"
-#: ../screen.c:7440
msgid " REVERSE"
msgstr " NGƯỢC LẠI"
-#: ../screen.c:7441
msgid " INSERT"
msgstr " CHÈN"
-#: ../screen.c:7443
msgid " (insert)"
msgstr " (chèn)"
-#: ../screen.c:7445
msgid " (replace)"
msgstr " (thay thế)"
-#: ../screen.c:7447
msgid " (vreplace)"
msgstr " (thay thế ảo)"
-#: ../screen.c:7449
msgid " Hebrew"
msgstr " Do thái"
-#: ../screen.c:7454
msgid " Arabic"
msgstr " Ả rập"
-#: ../screen.c:7456
msgid " (lang)"
msgstr " (ngôn ngữ)"
-#: ../screen.c:7459
msgid " (paste)"
msgstr " (dán)"
-#: ../screen.c:7469
msgid " VISUAL"
msgstr " CHẾ ĐỘ VISUAL"
-#: ../screen.c:7470
msgid " VISUAL LINE"
msgstr " DÒNG VISUAL"
-#: ../screen.c:7471
msgid " VISUAL BLOCK"
msgstr " KHỐI VISUAL"
-#: ../screen.c:7472
msgid " SELECT"
msgstr " LỰA CHỌN"
-#: ../screen.c:7473
msgid " SELECT LINE"
msgstr " LỰA CHỌN DÒNG"
-#: ../screen.c:7474
msgid " SELECT BLOCK"
msgstr " LỰA CHỌN KHỐI"
-#: ../screen.c:7486 ../screen.c:7541
msgid "recording"
msgstr "đang ghi"
-#: ../search.c:487
+msgid "search hit TOP, continuing at BOTTOM"
+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"
+
#, c-format
msgid "E383: Invalid search string: %s"
msgstr "E383: Chuỗi tìm kiếm không đúng: %s"
-#: ../search.c:832
-#, c-format
-msgid "E384: search hit TOP without match for: %s"
+# TODO: Capitalise first word of message?
+msgid "E384: Search hit TOP without match for: %s"
msgstr "E384: tìm kiếm kết thúc ở ĐẦU tập tin; không tìm thấy %s"
-#: ../search.c:835
-#, c-format
-msgid "E385: search hit BOTTOM without match for: %s"
+# TODO: Capitalise first word of message?
+msgid "E385: Search hit BOTTOM without match for: %s"
msgstr "E385: tìm kiếm kết thúc ở CUỐI tập tin; không tìm thấy %s"
-#: ../search.c:1200
msgid "E386: Expected '?' or '/' after ';'"
msgstr "E386: Mong đợi nhập '?' hoặc '/' sau ';'"
-#: ../search.c:4085
msgid " (includes previously listed match)"
msgstr " (gồm cả những tương ứng đã liệt kê trước đây)"
-#. cursor at status line
-#: ../search.c:4104
msgid "--- Included files "
msgstr "--- Tập tin tính đến "
-#: ../search.c:4106
msgid "not found "
msgstr "không tìm thấy "
-#: ../search.c:4107
msgid "in path ---\n"
msgstr "trong đường dẫn ---\n"
-#: ../search.c:4168
msgid " (Already listed)"
msgstr " (Đã liệt kê)"
-#: ../search.c:4170
msgid " NOT FOUND"
msgstr " KHÔNG TÌM THẤY"
-#: ../search.c:4211
#, c-format
msgid "Scanning included file: %s"
msgstr "Quét trong tập tin được tính đến: %s"
-#: ../search.c:4216
-#, fuzzy, c-format
-msgid "Searching included file %s"
-msgstr "Quét trong tập tin được tính đến: %s"
-
-#: ../search.c:4405
msgid "E387: Match is on current line"
msgstr "E387: Tương ứng nằm trên dòng hiện tại"
-#: ../search.c:4517
msgid "All included files were found"
msgstr "Tìm thấy tất cả các tập tin được tính đến"
-#: ../search.c:4519
msgid "No included files"
msgstr "Không có tập tin được tính đến"
-#: ../search.c:4527
msgid "E388: Couldn't find definition"
msgstr "E388: Không tìm thấy định nghĩa"
-#: ../search.c:4529
msgid "E389: Couldn't find pattern"
msgstr "E389: Không tìm thấy mẫu (pattern)"
-#: ../search.c:4668
-#, fuzzy
-msgid "Substitute "
-msgstr "1 thay thế"
-
-#: ../search.c:4681
-#, c-format
-msgid ""
-"\n"
-"# Last %sSearch Pattern:\n"
-"~"
-msgstr ""
-
-#: ../spell.c:951
-#, fuzzy
-msgid "E759: Format error in spell file"
-msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
-
-#: ../spell.c:952
-msgid "E758: Truncated spell file"
-msgstr ""
-
-#: ../spell.c:953
-#, c-format
-msgid "Trailing text in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:954
-#, c-format
-msgid "Affix name too long in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:955
-#, fuzzy
-msgid "E761: Format error in affix file FOL, LOW or UPP"
-msgstr "E431: Lỗi định dạng trong tập tin thẻ ghi \"%s\""
-
-#: ../spell.c:957
-msgid "E762: Character in FOL, LOW or UPP is out of range"
-msgstr ""
-
-#: ../spell.c:958
-msgid "Compressing word tree..."
-msgstr ""
-
-#: ../spell.c:1951
-msgid "E756: Spell checking is not enabled"
-msgstr ""
-
-#: ../spell.c:2249
-#, c-format
-msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr ""
-
-#: ../spell.c:2473
-#, fuzzy, c-format
-msgid "Reading spell file \"%s\""
-msgstr "Đang sử dụng tập tin trao đổi (swap) \"%s\""
-
-#: ../spell.c:2496
-#, fuzzy
-msgid "E757: This does not look like a spell file"
-msgstr "E307: %s không phải là tập tin trao đổi (swap) của Vim"
-
-#: ../spell.c:2501
-msgid "E771: Old spell file, needs to be updated"
-msgstr ""
-
-#: ../spell.c:2504
-msgid "E772: Spell file is for newer version of Vim"
-msgstr ""
-
-#: ../spell.c:2602
-#, fuzzy
-msgid "E770: Unsupported section in spell file"
-msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
-
-#: ../spell.c:3762
-#, fuzzy, c-format
-msgid "Warning: region %s not supported"
-msgstr "E519: Tùy chọn không được hỗ trợ"
-
-#: ../spell.c:4550
-#, fuzzy, c-format
-msgid "Reading affix file %s ..."
-msgstr "Tìm kiếm tập tin thẻ ghi %s"
-
-#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
-#, c-format
-msgid "Conversion failure for word in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4630 ../spell.c:6170
-#, c-format
-msgid "Conversion in %s not supported: from %s to %s"
-msgstr ""
-
-#: ../spell.c:4642
-#, c-format
-msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4655
-#, c-format
-msgid "FLAG after using flags in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4723
-#, c-format
-msgid ""
-"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
-"%d"
-msgstr ""
-
-#: ../spell.c:4731
-#, c-format
-msgid ""
-"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
-"%d"
-msgstr ""
-
-#: ../spell.c:4747
-#, c-format
-msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4771
-#, c-format
-msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4777
-#, c-format
-msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4783
-#, c-format
-msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4795
-#, c-format
-msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4847
-#, c-format
-msgid "Different combining flag in continued affix block in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4850
-#, fuzzy, c-format
-msgid "Duplicate affix in %s line %d: %s"
-msgstr "E154: Thẻ ghi lặp lại \"%s\" trong tập tin %s"
-
-#: ../spell.c:4871
-#, c-format
-msgid ""
-"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
-"line %d: %s"
-msgstr ""
-
-#: ../spell.c:4893
-#, c-format
-msgid "Expected Y or N in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4968
-#, c-format
-msgid "Broken condition in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5091
-#, c-format
-msgid "Expected REP(SAL) count in %s line %d"
-msgstr ""
-
-#: ../spell.c:5120
-#, c-format
-msgid "Expected MAP count in %s line %d"
-msgstr ""
-
-#: ../spell.c:5132
-#, c-format
-msgid "Duplicate character in MAP in %s line %d"
-msgstr ""
-
-#: ../spell.c:5176
-#, c-format
-msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5197
-#, c-format
-msgid "Missing FOL/LOW/UPP line in %s"
-msgstr ""
-
-#: ../spell.c:5220
-msgid "COMPOUNDSYLMAX used without SYLLABLE"
-msgstr ""
-
-#: ../spell.c:5236
-#, fuzzy
-msgid "Too many postponed prefixes"
-msgstr "Có quá nhiều tham số soạn thảo"
-
-#: ../spell.c:5238
-#, fuzzy
-msgid "Too many compound flags"
-msgstr "Có quá nhiều tham số soạn thảo"
-
-#: ../spell.c:5240
-msgid "Too many postponed prefixes and/or compound flags"
-msgstr ""
-
-#: ../spell.c:5250
-#, c-format
-msgid "Missing SOFO%s line in %s"
-msgstr ""
-
-#: ../spell.c:5253
-#, c-format
-msgid "Both SAL and SOFO lines in %s"
-msgstr ""
-
-#: ../spell.c:5331
-#, c-format
-msgid "Flag is not a number in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5334
-#, c-format
-msgid "Illegal flag in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5493 ../spell.c:5501
-#, c-format
-msgid "%s value differs from what is used in another .aff file"
-msgstr ""
-
-#: ../spell.c:5602
-#, fuzzy, c-format
-msgid "Reading dictionary file %s ..."
-msgstr "Quét từ điển: %s"
-
-#: ../spell.c:5611
-#, c-format
-msgid "E760: No word count in %s"
-msgstr ""
-
-#: ../spell.c:5669
-#, c-format
-msgid "line %6d, word %6d - %s"
-msgstr ""
-
-#: ../spell.c:5691
-#, fuzzy, c-format
-msgid "Duplicate word in %s line %d: %s"
-msgstr "Tìm thấy tương ứng trên mọi dòng: %s"
-
-#: ../spell.c:5694
-#, c-format
-msgid "First duplicate word in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5746
-#, c-format
-msgid "%d duplicate word(s) in %s"
-msgstr ""
-
-#: ../spell.c:5748
-#, c-format
-msgid "Ignored %d word(s) with non-ASCII characters in %s"
-msgstr ""
-
-#: ../spell.c:6115
-#, fuzzy, c-format
-msgid "Reading word file %s ..."
-msgstr "Đọc từ đầu vào tiêu chuẩn stdin..."
-
-#: ../spell.c:6155
-#, c-format
-msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6159
-#, c-format
-msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6180
-#, c-format
-msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6185
-#, c-format
-msgid "Too many regions in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6198
-#, c-format
-msgid "/ line ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6224
-#, fuzzy, c-format
-msgid "Invalid region nr in %s line %d: %s"
-msgstr "E573: Sử dụng id máy chủ không đúng: %s"
-
-#: ../spell.c:6230
-#, c-format
-msgid "Unrecognized flags in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6257
-#, c-format
-msgid "Ignored %d words with non-ASCII characters"
-msgstr ""
-
-#: ../spell.c:6656
-#, c-format
-msgid "Compressed %d of %d nodes; %d (%d%%) remaining"
-msgstr ""
-
-#: ../spell.c:7340
-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
-msgid "Performing soundfolding..."
-msgstr ""
-
-#: ../spell.c:7368
-#, c-format
-msgid "Number of words after soundfolding: %<PRId64>"
-msgstr ""
-
-#: ../spell.c:7476
-#, c-format
-msgid "Total number of words: %d"
-msgstr ""
-
-#: ../spell.c:7655
-#, fuzzy, c-format
-msgid "Writing suggestion file %s ..."
-msgstr "Ghi tập tin viminfo \"%s\""
-
-#: ../spell.c:7707 ../spell.c:7927
-#, c-format
-msgid "Estimated runtime memory use: %d bytes"
-msgstr ""
-
-#: ../spell.c:7820
-msgid "E751: Output file name must not have region name"
-msgstr ""
-
-#: ../spell.c:7822
-#, fuzzy
-msgid "E754: Only up to 8 regions supported"
-msgstr "E519: Tùy chọn không được hỗ trợ"
-
-#: ../spell.c:7846
-#, fuzzy, c-format
-msgid "E755: Invalid region in %s"
-msgstr "E15: Biểu thức không cho phép: %s"
-
-#: ../spell.c:7907
-msgid "Warning: both compounding and NOBREAK specified"
-msgstr ""
-
-#: ../spell.c:7920
-#, fuzzy, c-format
-msgid "Writing spell file %s ..."
-msgstr "Ghi tập tin viminfo \"%s\""
-
-#: ../spell.c:7925
-msgid "Done!"
-msgstr ""
-
-#: ../spell.c:8034
-#, c-format
-msgid "E765: 'spellfile' does not have %<PRId64> entries"
-msgstr ""
-
-#: ../spell.c:8074
-#, c-format
-msgid "Word '%.*s' removed from %s"
-msgstr ""
-
-#: ../spell.c:8117
-#, c-format
-msgid "Word '%.*s' added to %s"
-msgstr ""
-
-#: ../spell.c:8381
-msgid "E763: Word characters differ between spell files"
-msgstr ""
-
-#: ../spell.c:8684
-msgid "Sorry, no suggestions"
-msgstr ""
-
-#: ../spell.c:8687
-#, fuzzy, c-format
-msgid "Sorry, only %<PRId64> suggestions"
-msgstr " trên %<PRId64> dòng"
-
-#. for when 'cmdheight' > 1
-#. avoid more prompt
-#: ../spell.c:8704
-#, fuzzy, c-format
-msgid "Change \"%.*s\" to:"
-msgstr "Ghi nhớ thay đổi vào \"%.*s\"?"
-
-#: ../spell.c:8737
-#, c-format
-msgid " < \"%.*s\""
-msgstr ""
-
-#: ../spell.c:8882
-#, fuzzy
-msgid "E752: No previous spell replacement"
-msgstr "E35: Không có biểu thức chính quy trước"
-
-#: ../spell.c:8925
-#, fuzzy, c-format
-msgid "E753: Not found: %s"
-msgstr "E334: Không tìm thấy trình đơn: %s"
-
-#: ../spell.c:9276
-#, fuzzy, c-format
-msgid "E778: This does not look like a .sug file: %s"
-msgstr "E307: %s không phải là tập tin trao đổi (swap) của Vim"
-
-#: ../spell.c:9282
-#, c-format
-msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr ""
-
-#: ../spell.c:9286
-#, c-format
-msgid "E780: .sug file is for newer version of Vim: %s"
-msgstr ""
-
-#: ../spell.c:9295
-#, c-format
-msgid "E781: .sug file doesn't match .spl file: %s"
-msgstr ""
-
-#: ../spell.c:9305
-#, fuzzy, c-format
-msgid "E782: error while reading .sug file: %s"
-msgstr "E47: Lỗi khi đọc tập tin lỗi"
-
-#. This should have been checked when generating the .spl
-#. file.
-#: ../spell.c:11575
-msgid "E783: duplicate char in MAP entry"
-msgstr ""
-
-#: ../syntax.c:266
-msgid "No Syntax items defined for this buffer"
-msgstr "Không có phần tử cú pháp nào được định nghĩa cho bộ đệm này"
-
-#: ../syntax.c:3083 ../syntax.c:3104 ../syntax.c:3127
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Tham số không cho phép: %s"
-#: ../syntax.c:3299
#, c-format
msgid "E391: No such syntax cluster: %s"
msgstr "E391: Không có cụm cú pháp như vậy: %s"
-#: ../syntax.c:3433
+msgid "No Syntax items defined for this buffer"
+msgstr "Không có phần tử cú pháp nào được định nghĩa cho bộ đệm này"
+
msgid "syncing on C-style comments"
msgstr "Đồng bộ hóa theo chú thích kiểu C"
-#: ../syntax.c:3439
msgid "no syncing"
msgstr "không đồng bộ hóa"
-#: ../syntax.c:3441
msgid "syncing starts "
msgstr "đồng bộ hóa bắt đầu "
-#: ../syntax.c:3443 ../syntax.c:3506
msgid " lines before top line"
msgstr " dòng trước dòng đầu tiên"
-#: ../syntax.c:3448
msgid ""
"\n"
"--- Syntax sync items ---"
@@ -5637,7 +4155,6 @@ msgstr ""
"\n"
"--- Phần tử đồng bộ hóa cú pháp ---"
-#: ../syntax.c:3452
msgid ""
"\n"
"syncing on items"
@@ -5645,7 +4162,6 @@ msgstr ""
"\n"
"đồng bộ hóa theo phần tử"
-#: ../syntax.c:3457
msgid ""
"\n"
"--- Syntax items ---"
@@ -5653,274 +4169,197 @@ msgstr ""
"\n"
"--- Phần tử cú pháp ---"
-#: ../syntax.c:3475
#, c-format
msgid "E392: No such syntax cluster: %s"
msgstr "E392: Không có cụm cú pháp như vậy: %s"
-#: ../syntax.c:3497
msgid "minimal "
msgstr "nhỏ nhất "
-#: ../syntax.c:3503
msgid "maximal "
msgstr "lớn nhất "
-#: ../syntax.c:3513
msgid "; match "
msgstr "; tương ứng "
-#: ../syntax.c:3515
msgid " line breaks"
msgstr " chuyển dòng"
-#: ../syntax.c:4076
-msgid "E395: contains argument not accepted here"
-msgstr "E395: không được sử dụng tham số contains ở đây"
-
-#: ../syntax.c:4096
-#, fuzzy
-msgid "E844: invalid cchar value"
-msgstr "E474: Tham số không cho phép"
-
-#: ../syntax.c:4107
msgid "E393: group[t]here not accepted here"
msgstr "E393: không được sử dụng group[t]here ở đây"
-#: ../syntax.c:4126
#, c-format
msgid "E394: Didn't find region item for %s"
msgstr "E394: Phần tử vùng cho %s không tìm thấy"
-#: ../syntax.c:4188
-msgid "E397: Filename required"
-msgstr "E397: Yêu cầu tên tập tin"
+# TODO: Capitalise first word of message?
+msgid "E395: Contains argument not accepted here"
+msgstr "E395: không được sử dụng tham số contains ở đây"
-#: ../syntax.c:4221
-#, fuzzy
-msgid "E847: Too many syntax includes"
-msgstr "E77: Quá nhiều tên tập tin"
+msgid "E396: containedin argument not accepted here"
+msgstr "E396: không được sử dụng tham số containedin ở đây"
-#: ../syntax.c:4303
-#, fuzzy, c-format
-msgid "E789: Missing ']': %s"
-msgstr "E398: Thiếu '=': %s"
+msgid "E397: Filename required"
+msgstr "E397: Yêu cầu tên tập tin"
-#: ../syntax.c:4531
#, c-format
msgid "E398: Missing '=': %s"
msgstr "E398: Thiếu '=': %s"
-#: ../syntax.c:4666
#, c-format
msgid "E399: Not enough arguments: syntax region %s"
msgstr "E399: Không đủ tham số: vùng cú pháp %s"
-#: ../syntax.c:4870
-#, fuzzy
-msgid "E848: Too many syntax clusters"
-msgstr "E391: Không có cụm cú pháp như vậy: %s"
-
-#: ../syntax.c:4954
msgid "E400: No cluster specified"
msgstr "E400: Chưa chỉ ra cụm"
-#. end delimiter not found
-#: ../syntax.c:4986
#, c-format
msgid "E401: Pattern delimiter not found: %s"
msgstr "E401: Không tìm thấy ký tự phân chia mẫu (pattern): %s"
-#: ../syntax.c:5049
#, c-format
msgid "E402: Garbage after pattern: %s"
msgstr "E402: Rác ở sau mẫu (pattern): %s"
-#: ../syntax.c:5120
-msgid "E403: syntax sync: line continuations pattern specified twice"
+# TODO: Capitalise first word of message?
+msgid "E403: syntax sync: Line continuations pattern specified twice"
msgstr "E403: đồng bộ hóa cú pháp: mẫu tiếp tục của dòng chỉ ra hai lần"
-#: ../syntax.c:5169
#, c-format
msgid "E404: Illegal arguments: %s"
msgstr "E404: Tham số không cho phép: %s"
-#: ../syntax.c:5217
#, c-format
msgid "E405: Missing equal sign: %s"
msgstr "E405: Thiếu dấu bằng: %s"
-#: ../syntax.c:5222
#, c-format
msgid "E406: Empty argument: %s"
msgstr "E406: Tham số trống rỗng: %s"
-#: ../syntax.c:5240
#, c-format
msgid "E407: %s not allowed here"
msgstr "E407: %s không được cho phép ở đây"
-#: ../syntax.c:5246
#, c-format
msgid "E408: %s must be first in contains list"
msgstr "E408: %s phải là đầu tiên trong danh sách contains"
-#: ../syntax.c:5304
#, c-format
msgid "E409: Unknown group name: %s"
msgstr "E409: Tên nhóm không biết: %s"
-#: ../syntax.c:5512
#, c-format
msgid "E410: Invalid :syntax subcommand: %s"
msgstr "E410: Câu lệnh con :syntax không đúng: %s"
-#: ../syntax.c:5854
-msgid ""
-" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
-msgstr ""
-
-#: ../syntax.c:6146
-msgid "E679: recursive loop loading syncolor.vim"
-msgstr ""
-
-#: ../syntax.c:6256
-#, c-format
-msgid "E411: highlight group not found: %s"
+# TODO: Capitalise first word of message?
+msgid "E411: Highlight group not found: %s"
msgstr "E411: không tìm thấy nhóm chiếu sáng cú pháp: %s"
-#: ../syntax.c:6278
#, c-format
msgid "E412: Not enough arguments: \":highlight link %s\""
msgstr "E412: Không đủ tham số: \":highlight link %s\""
-#: ../syntax.c:6284
#, c-format
msgid "E413: Too many arguments: \":highlight link %s\""
msgstr "E413: Quá nhiều tham số: \":highlight link %s\""
-#: ../syntax.c:6302
-msgid "E414: group has settings, highlight link ignored"
+# TODO: Capitalise first word of message?
+msgid "E414: Group has settings, highlight link ignored"
msgstr "E414: nhóm có thiết lập riêng, chiếu sáng liên kết bị bỏ qua"
-#: ../syntax.c:6367
-#, c-format
-msgid "E415: unexpected equal sign: %s"
+# TODO: Capitalise first word of message?
+msgid "E415: Unexpected equal sign: %s"
msgstr "E415: dấu bằng không được mong đợi: %s"
-#: ../syntax.c:6395
-#, c-format
-msgid "E416: missing equal sign: %s"
+# TODO: Capitalise first word of message?
+msgid "E416: Missing equal sign: %s"
msgstr "E416: thiếu dấu bằng: %s"
-#: ../syntax.c:6418
-#, c-format
-msgid "E417: missing argument: %s"
+# TODO: Capitalise first word of message?
+msgid "E417: Missing argument: %s"
msgstr "E417: thiếu tham số: %s"
-#: ../syntax.c:6446
#, c-format
msgid "E418: Illegal value: %s"
msgstr "E418: Giá trị không cho phép: %s"
-#: ../syntax.c:6496
msgid "E419: FG color unknown"
msgstr "E419: Không rõ màu văn bản (FG)"
-#: ../syntax.c:6504
msgid "E420: BG color unknown"
msgstr "E420: Không rõ màu nền sau (BG)"
-#: ../syntax.c:6564
#, c-format
msgid "E421: Color name or number not recognized: %s"
msgstr "E421: Tên hoặc số của màu không được nhận ra: %s"
-#: ../syntax.c:6714
-#, c-format
-msgid "E422: terminal code too long: %s"
+# TODO: Capitalise first word of message?
+msgid "E422: Terminal code too long: %s"
msgstr "E422: mã terminal quá dài: %s"
-#: ../syntax.c:6753
#, c-format
msgid "E423: Illegal argument: %s"
msgstr "E423: Tham số không cho phép: %s"
-#: ../syntax.c:6925
msgid "E424: Too many different highlighting attributes in use"
msgstr "E424: Sử dụng quá nhiều thuộc tính chiếu sáng cú pháp"
-#: ../syntax.c:7427
msgid "E669: Unprintable character in group name"
msgstr "E669: Ký tự không thể tin ra trong tên nhóm"
-#: ../highlight_group.c:1756
-msgid "E5248: Invalid character in group name"
-msgstr "E5248: Ký tự không cho phép trong tên nhóm"
+msgid "W18: Invalid character in group name"
+msgstr "W18: Ký tự không cho phép trong tên nhóm"
-#: ../syntax.c:7448
-msgid "E849: Too many highlight and syntax groups"
-msgstr ""
-
-#: ../tag.c:104
-msgid "E555: at bottom of tag stack"
+# TODO: Capitalise first word of message?
+msgid "E555: At bottom of tag stack"
msgstr "E555: ở cuối đống thẻ ghi"
-#: ../tag.c:105
-msgid "E556: at top of tag stack"
+# TODO: Capitalise first word of message?
+msgid "E556: At top of tag stack"
msgstr "E556: ở đầu đống thẻ ghi"
-#: ../tag.c:380
msgid "E425: Cannot go before first matching tag"
msgstr "E425: Không chuyển được tới vị trí ở trước thẻ ghi tương ứng đầu tiên"
-#: ../tag.c:504
-#, c-format
-msgid "E426: tag not found: %s"
+# TODO: Capitalise first word of message?
+msgid "E426: Tag not found: %s"
msgstr "E426: không tìm thấy thẻ ghi: %s"
-#: ../tag.c:528
msgid " # pri kind tag"
msgstr " # pri loại thẻ ghi"
-#: ../tag.c:531
msgid "file\n"
msgstr "tập tin\n"
-#: ../tag.c:829
+msgid "Enter nr of choice (<CR> to abort): "
+msgstr "Hãy chọn số cần thiết (<CR> để dừng):"
+
msgid "E427: There is only one matching tag"
msgstr "E427: Chỉ có một thẻ ghi tương ứng"
-#: ../tag.c:831
msgid "E428: Cannot go beyond last matching tag"
msgstr "E428: Không chuyển được tới vị trí ở sau thẻ ghi tương ứng cuối cùng"
-#: ../tag.c:850
#, c-format
msgid "File \"%s\" does not exist"
msgstr "Tập tin \"%s\" không tồn tại"
-#. Give an indication of the number of matching tags
-#: ../tag.c:859
#, c-format
msgid "tag %d of %d%s"
msgstr "thẻ ghi %d của %d%s"
-#: ../tag.c:862
msgid " or more"
msgstr " và hơn nữa"
-#: ../tag.c:864
msgid " Using tag with different case!"
msgstr " Đang sử dụng thẻ ghi với kiểu chữ khác!"
-#: ../tag.c:909
#, c-format
msgid "E429: File \"%s\" does not exist"
msgstr "E429: Tập tin \"%s\" không tồn tại"
-#. Highlight title
-#: ../tag.c:960
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -5928,79 +4367,58 @@ msgstr ""
"\n"
" # TỚI thẻ ghi TỪ dòng trong tập tin/văn bản"
-#: ../tag.c:1303
#, c-format
msgid "Searching tags file %s"
msgstr "Tìm kiếm tập tin thẻ ghi %s"
-#: ../tag.c:1545
-msgid "Ignoring long line in tags file"
-msgstr ""
+#, c-format
+msgid "E430: Tag file path truncated for %s\n"
+msgstr "E430: Đường dẫn tới tập tin thẻ ghi bị cắt bớt cho %s\n"
-#: ../tag.c:1915
#, c-format
msgid "E431: Format error in tags file \"%s\""
msgstr "E431: Lỗi định dạng trong tập tin thẻ ghi \"%s\""
-#: ../tag.c:1917
#, c-format
-msgid "Before byte %<PRId64>"
-msgstr "Trước byte %<PRId64>"
+msgid "Before byte %ld"
+msgstr "Trước byte %ld"
-#: ../tag.c:1929
#, c-format
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Tập tin thẻ ghi chưa được sắp xếp: %s"
-#. never opened any tags file
-#: ../tag.c:1960
msgid "E433: No tags file"
msgstr "E433: Không có tập tin thẻ ghi"
-#: ../tag.c:2536
msgid "E434: Can't find tag pattern"
msgstr "E434: Không tìm thấy mẫu thẻ ghi"
-#: ../tag.c:2544
msgid "E435: Couldn't find tag, just guessing!"
msgstr "E435: Không tìm thấy thẻ ghi, đang thử đoán!"
-#: ../tag.c:2797
-#, c-format
-msgid "Duplicate field name: %s"
-msgstr ""
-
-#: ../term.c:1442
msgid "' not known. Available builtin terminals are:"
msgstr "' không rõ. Có các terminal gắn sẵn (builtin) sau:"
-#: ../term.c:1463
msgid "defaulting to '"
msgstr "theo mặc định '"
-#: ../term.c:1731
msgid "E557: Cannot open termcap file"
msgstr "E557: Không thể mở tập tin termcap"
-#: ../term.c:1735
msgid "E558: Terminal entry not found in terminfo"
msgstr "E558: Trong terminfo không có bản ghi nào về terminal này"
-#: ../term.c:1737
msgid "E559: Terminal entry not found in termcap"
msgstr "E559: Trong termcap không có bản ghi nào về terminal này"
-#: ../term.c:1878
#, c-format
msgid "E436: No \"%s\" entry in termcap"
msgstr "E436: Trong termcap không có bản ghi \"%s\""
-#: ../term.c:2249
-msgid "E437: terminal capability \"cm\" required"
+# TODO: Capitalise first word of message?
+msgid "E437: Terminal capability \"cm\" required"
msgstr "E437: cần khả năng của terminal \"cm\""
-#. Highlight title
-#: ../term.c:4376
msgid ""
"\n"
"--- Terminal keys ---"
@@ -6008,195 +4426,120 @@ msgstr ""
"\n"
"--- Phím terminal ---"
-#: ../ui.c:481
+msgid "new shell started\n"
+msgstr "đã chạy shell mới\n"
+
msgid "Vim: Error reading input, exiting...\n"
msgstr "Vim: Lỗi đọc dữ liệu nhập, thoát...\n"
-#. This happens when the FileChangedRO autocommand changes the
-#. * file in a way it becomes shorter.
-#: ../undo.c:379
-msgid "E881: Line count changed unexpectedly"
-msgstr ""
+msgid "No undo possible; continue anyway"
+msgstr "Không thể hủy thao tác; tiếp tục thực hiện"
-#: ../undo.c:627
-#, fuzzy, c-format
-msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: Không thể mở tập tin để ghi nhớ"
+# TODO: Capitalise first word of message?
+msgid "E438: u_undo: Line numbers wrong"
+msgstr "E438: u_undo: số thứ tự dòng không đúng"
+
+msgid "1 change"
+msgstr "duy nhất 1 thay đổi"
-#: ../undo.c:717
#, c-format
-msgid "E825: Corrupted undo file (%s): %s"
-msgstr ""
+msgid "%ld changes"
+msgstr "%ld thay đổi"
-#: ../undo.c:1039
-msgid "Cannot write undo file in any directory in 'undodir'"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E439: Undo list corrupt"
+msgstr "E439: danh sách hủy thao tác (undo) bị hỏng"
-#: ../undo.c:1074
-#, c-format
-msgid "Will not overwrite with undo file, cannot read: %s"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E440: Undo line missing"
+msgstr "E440: bị mất dòng hủy thao tác"
-#: ../undo.c:1092
-#, c-format
-msgid "Will not overwrite, this is not an undo file: %s"
+msgid ""
+"\n"
+"MS-Windows 16/32-bit GUI version"
msgstr ""
+"\n"
+"Phiên bản với giao diện đồ họa GUI cho MS-Windows 16/32 bit"
-#: ../undo.c:1108
-msgid "Skipping undo file write, nothing to undo"
+msgid ""
+"\n"
+"MS-Windows 32-bit GUI version"
msgstr ""
+"\n"
+"Phiên bản với giao diện đồ họa GUI cho MS-Windows 32 bit"
-#: ../undo.c:1121
-#, fuzzy, c-format
-msgid "Writing undo file: %s"
-msgstr "Ghi tập tin viminfo \"%s\""
+msgid " in Win32s mode"
+msgstr " trong chế độ Win32"
-#: ../undo.c:1213
-#, fuzzy, c-format
-msgid "E829: write error in undo file: %s"
-msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
+msgid " with OLE support"
+msgstr " với hỗ trợ OLE"
-#: ../undo.c:1280
-#, c-format
-msgid "Not reading undo file, owner differs: %s"
+msgid ""
+"\n"
+"MS-Windows 32-bit console version"
msgstr ""
+"\n"
+"Phiên bản console cho MS-Windows 32 bit"
-#: ../undo.c:1292
-#, fuzzy, c-format
-msgid "Reading undo file: %s"
-msgstr "Đọc tập tin viminfo \"%s\"%s%s%s"
-
-#: ../undo.c:1299
-#, fuzzy, c-format
-msgid "E822: Cannot open undo file for reading: %s"
-msgstr "E195: Không thể mở tập tin viminfo để đọc"
-
-#: ../undo.c:1308
-#, fuzzy, c-format
-msgid "E823: Not an undo file: %s"
-msgstr "E484: Không mở được tập tin %s"
-
-#: ../undo.c:1313
-#, fuzzy, c-format
-msgid "E824: Incompatible undo file: %s"
-msgstr "E484: Không mở được tập tin %s"
-
-#: ../undo.c:1328
-msgid "File contents changed, cannot use undo info"
+msgid ""
+"\n"
+"MS-Windows 16-bit version"
msgstr ""
+"\n"
+"Phiên bản cho MS-Windows 16 bit"
-#: ../undo.c:1497
-#, fuzzy, c-format
-msgid "Finished reading undo file %s"
-msgstr "thực hiện xong %s"
-
-#: ../undo.c:1586 ../undo.c:1812
-msgid "Already at oldest change"
+msgid ""
+"\n"
+"32-bit MS-DOS version"
msgstr ""
+"\n"
+"Phiên bản cho MS-DOS 32 bit"
-#: ../undo.c:1597 ../undo.c:1814
-msgid "Already at newest change"
+msgid ""
+"\n"
+"16-bit MS-DOS version"
msgstr ""
+"\n"
+"Phiên bản cho MS-DOS 16 bit"
-#: ../undo.c:1806
-#, fuzzy, c-format
-msgid "E830: Undo number %<PRId64> not found"
-msgstr "E92: Bộ đệm %<PRId64> không được tìm thấy"
-
-#: ../undo.c:1979
-msgid "E438: u_undo: line numbers wrong"
-msgstr "E438: u_undo: số thứ tự dòng không đúng"
-
-#: ../undo.c:2183
-#, fuzzy
-msgid "more line"
-msgstr "Thêm 1 dòng"
-
-#: ../undo.c:2185
-#, fuzzy
-msgid "more lines"
-msgstr "Thêm 1 dòng"
-
-#: ../undo.c:2187
-#, fuzzy
-msgid "line less"
-msgstr "Bớt 1 dòng"
-
-#: ../undo.c:2189
-#, fuzzy
-msgid "fewer lines"
-msgstr "Bớt %<PRId64> dòng"
-
-#: ../undo.c:2193
-#, fuzzy
-msgid "change"
-msgstr "duy nhất 1 thay đổi"
-
-#: ../undo.c:2195
-#, fuzzy
-msgid "changes"
-msgstr "duy nhất 1 thay đổi"
-
-#: ../undo.c:2225
-#, fuzzy, c-format
-msgid "%<PRId64> %s; %s #%<PRId64> %s"
-msgstr "Trên %<PRId64> dòng %s %d lần"
-
-#: ../undo.c:2228
-msgid "before"
+msgid ""
+"\n"
+"MacOS X (unix) version"
msgstr ""
+"\n"
+"Phiên bản cho MacOS X (unix)"
-#: ../undo.c:2228
-msgid "after"
+msgid ""
+"\n"
+"MacOS X version"
msgstr ""
+"\n"
+"Phiên bản cho MacOS X"
-#: ../undo.c:2325
-#, fuzzy
-msgid "Nothing to undo"
-msgstr "Không tìm thấy ánh xạ"
-
-#: ../undo.c:2330
-msgid "number changes when saved"
+msgid ""
+"\n"
+"MacOS version"
msgstr ""
+"\n"
+"Phiên bản cho MacOS"
-#: ../undo.c:2360
-#, fuzzy, c-format
-msgid "%<PRId64> seconds ago"
-msgstr "%<PRId64> Cột; "
-
-#: ../undo.c:2372
-#, fuzzy
-msgid "E790: undojoin is not allowed after undo"
-msgstr "E407: %s không được cho phép ở đây"
-
-#: ../undo.c:2466
-msgid "E439: undo list corrupt"
-msgstr "E439: danh sách hủy thao tác (undo) bị hỏng"
-
-#: ../undo.c:2495
-msgid "E440: undo line missing"
-msgstr "E440: bị mất dòng hủy thao tác"
-
-#: ../version.c:600
msgid ""
"\n"
-"Included patches: "
+"RISC OS version"
msgstr ""
"\n"
-"Bao gồm các bản vá lỗi: "
+"Phiên bản cho RISC OS"
-#: ../version.c:627
-#, fuzzy
msgid ""
"\n"
-"Extra patches: "
-msgstr "Sự tương ứng con ngoài:\n"
+"Included patches: "
+msgstr ""
+"\n"
+"Bao gồm các bản vá lỗi: "
-#: ../version.c:639 ../version.c:864
msgid "Modified by "
msgstr "Với các thay đổi bởi "
-#: ../version.c:646
msgid ""
"\n"
"Compiled "
@@ -6204,11 +4547,9 @@ msgstr ""
"\n"
"Được biên dịch "
-#: ../version.c:649
msgid "by "
msgstr "bởi "
-#: ../version.c:660
msgid ""
"\n"
"Huge version "
@@ -6216,1521 +4557,574 @@ msgstr ""
"\n"
"Phiên bản khổng lồ "
-#: ../version.c:661
+msgid ""
+"\n"
+"Big version "
+msgstr ""
+"\n"
+"Phiên bản lớn "
+
+msgid ""
+"\n"
+"Normal version "
+msgstr ""
+"\n"
+"Phiên bản thông thường "
+
+msgid ""
+"\n"
+"Small version "
+msgstr ""
+"\n"
+"Phiên bản nhỏ "
+
+msgid ""
+"\n"
+"Tiny version "
+msgstr ""
+"\n"
+"Phiên bản \"tí hon\" "
+
msgid "without GUI."
msgstr "không có giao diện đồ họa GUI."
-#: ../version.c:662
+msgid "with GTK2-GNOME GUI."
+msgstr "với giao diện đồ họa GUI GTK2-GNOME."
+
+msgid "with GTK-GNOME GUI."
+msgstr "với giao diện đồ họa GUI GTK-GNOME."
+
+msgid "with GTK2 GUI."
+msgstr "với giao diện đồ họa GUI GTK2."
+
+msgid "with GTK GUI."
+msgstr "với giao diện đồ họa GUI GTK."
+
+msgid "with X11-Motif GUI."
+msgstr "với giao diện đồ họa GUI X11-Motif."
+
+msgid "with X11-neXtaw GUI."
+msgstr "với giao diện đồ họa GUI X11-neXtaw."
+
+msgid "with X11-Athena GUI."
+msgstr "với giao diện đồ họa GUI X11-Athena."
+
+msgid "with BeOS GUI."
+msgstr "với giao diện đồ họa GUI BeOS."
+
+msgid "with Photon GUI."
+msgstr "với giao diện đồ họa GUI Photon."
+
+msgid "with GUI."
+msgstr "với giao diện đồ họa GUI."
+
+msgid "with Carbon GUI."
+msgstr "với giao diện đồ họa GUI Carbon."
+
+msgid "with Cocoa GUI."
+msgstr "với giao diện đồ họa GUI Cocoa."
+
+msgid "with (classic) GUI."
+msgstr "với giao diện đồ họa (cổ điển) GUI."
+
msgid " Features included (+) or not (-):\n"
msgstr " Tính năng có (+) hoặc không (-):\n"
-#: ../version.c:667
msgid " system vimrc file: \""
msgstr " tập tin vimrc chung cho hệ thống: \""
-#: ../version.c:672
msgid " user vimrc file: \""
msgstr " tập tin vimrc của người dùng: \""
-#: ../version.c:677
msgid " 2nd user vimrc file: \""
msgstr " tập tin vimrc thứ hai của người dùng: \""
-#: ../version.c:682
msgid " 3rd user vimrc file: \""
msgstr " tập tin vimrc thứ ba của người dùng: \""
-#: ../version.c:687
msgid " user exrc file: \""
msgstr " tập tin exrc của người dùng: \""
-#: ../version.c:692
msgid " 2nd user exrc file: \""
msgstr " tập tin exrc thứ hai của người dùng: \""
-#: ../version.c:699
+msgid " system gvimrc file: \""
+msgstr " tập tin gvimrc chung cho hệ thống: \""
+
+msgid " user gvimrc file: \""
+msgstr " tập tin gvimrc của người dùng: \""
+
+msgid "2nd user gvimrc file: \""
+msgstr " tập tin gvimrc thứ hai của người dùng: \""
+
+msgid "3rd user gvimrc file: \""
+msgstr " tập tin gvimrc thứ ba của người dùng: \""
+
+msgid " system menu file: \""
+msgstr " tập tin trình đơn chung cho hệ thống: \""
+
msgid " fall-back for $VIM: \""
msgstr " giá trị $VIM theo mặc định: \""
-#: ../version.c:705
msgid " f-b for $VIMRUNTIME: \""
msgstr " giá trị $VIMRUNTIME theo mặc định: \""
-#: ../version.c:709
msgid "Compilation: "
msgstr "Tham số biên dịch: "
-#: ../version.c:712
+msgid "Compiler: "
+msgstr "Trình biên dịch: "
+
msgid "Linking: "
msgstr "Liên kết: "
-#: ../version.c:717
msgid " DEBUG BUILD"
msgstr " BIÊN DỊCH SỬA LỖI (DEBUG)"
-#: ../version.c:767
msgid "VIM - Vi IMproved"
msgstr "VIM ::: Vi IMproved (Vi cải tiến) ::: Phiên bản tiếng Việt"
-#: ../version.c:769
msgid "version "
msgstr "phiên bản "
-#: ../version.c:770
msgid "by Bram Moolenaar et al."
msgstr "Do Bram Moolenaar và những người khác thực hiện"
-#: ../version.c:774
msgid "Vim is open source and freely distributable"
msgstr "Vim là chương trình mã nguồn mở và phân phối tự do"
-#: ../version.c:776
msgid "Help poor children in Uganda!"
msgstr "Hãy giúp đỡ trẻ em nghèo Uganda!"
-#: ../version.c:777
msgid "type :help iccf<Enter> for information "
msgstr "hãy gõ :help iccf<Enter> để biết thêm thông tin"
-#: ../version.c:779
msgid "type :q<Enter> to exit "
msgstr " hãy gõ :q<Enter> để thoát khỏi chương trình "
-#: ../version.c:780
msgid "type :help<Enter> or <F1> for on-line help"
msgstr " hãy gõ :help<Enter> hoặc <F1> để có được trợ giúp "
-#: ../version.c:781
-msgid "type :help version7<Enter> for version info"
-msgstr "hãy gõ :help version7<Enter> để biết về phiên bản này "
+msgid "type :help version9<Enter> for version info"
+msgstr "hãy gõ :help version9<Enter> để biết về phiên bản này "
-#: ../version.c:784
msgid "Running in Vi compatible mode"
msgstr "Làm việc trong chế độ tương thích với Vi"
-#: ../version.c:785
msgid "type :set nocp<Enter> for Vim defaults"
msgstr "hãy gõ :set nocp<Enter> để chuyển vào chế độ Vim "
-#: ../version.c:786
msgid "type :help cp-default<Enter> for info on this"
msgstr "hãy gõ :help cp-default<Enter> để có thêm thông tin về điều này"
-#: ../version.c:827
+msgid "menu Help->Orphans for information "
+msgstr "trình đơn Trợ giúp->Mồ côi để có thêm thông tin "
+
+msgid "Running modeless, typed text is inserted"
+msgstr "Không chế độ, văn bản nhập vào sẽ được chèn"
+
+msgid "menu Edit->Global Settings->Toggle Insert Mode "
+msgstr "trình đơn Soạn thảo->Thiết lập chung->Chế độ chèn "
+
+msgid " for two modes "
+msgstr " cho hai chế độ "
+
+msgid "menu Edit->Global Settings->Toggle Vi Compatible"
+msgstr ""
+"trình đơn Soạn thảo->Thiết lập chung->Tương thích với Vi "
+
+msgid " for Vim defaults "
+msgstr ""
+" để chuyển vào chế độ Vim mặc định "
+
msgid "Sponsor Vim development!"
msgstr "Hãy giúp đỡ phát triển Vim!"
-#: ../version.c:828
msgid "Become a registered Vim user!"
msgstr "Hãy trở thành người dùng đăng ký của Vim!"
-#: ../version.c:831
msgid "type :help sponsor<Enter> for information "
msgstr "hãy gõ :help sponsor<Enter> để biết thêm thông tin "
-#: ../version.c:832
msgid "type :help register<Enter> for information "
msgstr "hãy gõ :help register<Enter> để biết thêm thông tin "
-#: ../version.c:834
msgid "menu Help->Sponsor/Register for information "
msgstr "trình đơn Trợ giúp->Giúp đỡ/Đăng ký để biết thêm thông tin "
-#: ../window.c:119
-msgid "Already only one window"
-msgstr "Chỉ có một cửa sổ"
+msgid "WARNING: Windows 95/98/ME detected"
+msgstr "CẢNH BÁO: nhận ra Windows 95/98/ME"
+
+msgid "type :help windows95<Enter> for info on this"
+msgstr "hãy gõ :help windows95<Enter> để biết thêm thông tin "
-#: ../window.c:224
msgid "E441: There is no preview window"
msgstr "E441: Không có cửa sổ xem trước"
-#: ../window.c:559
msgid "E442: Can't split topleft and botright at the same time"
msgstr ""
"E442: Cửa sổ không thể đồng thời ở bên trái phía trên và bên phải phía dưới"
-#: ../window.c:1228
msgid "E443: Cannot rotate when another window is split"
msgstr "E443: Không đổi được chỗ khi cửa sổ khác được chia"
-#: ../window.c:1803
msgid "E444: Cannot close last window"
msgstr "E444: Không được đóng cửa sổ cuối cùng"
-#: ../window.c:1810
-#, fuzzy
-msgid "E813: Cannot close autocmd window"
-msgstr "E444: Không được đóng cửa sổ cuối cùng"
-
-#: ../window.c:1814
-#, fuzzy
-msgid "E814: Cannot close window, only autocmd window would remain"
-msgstr "E444: Không được đóng cửa sổ cuối cùng"
+msgid "Already only one window"
+msgstr "Chỉ có một cửa sổ"
-#: ../window.c:2717
msgid "E445: Other window contains changes"
msgstr "E445: Cửa sổ khác có thay đổi chưa được ghi nhớ"
-#: ../window.c:4805
msgid "E446: No file name under cursor"
msgstr "E446: Không có tên tập tin tại vị trí con trỏ"
-#~ msgid "[Error List]"
-#~ msgstr "[Danh sách lỗi]"
-
-#~ msgid "[No File]"
-#~ msgstr "[Không có tập tin]"
-
-#~ msgid "Patch file"
-#~ msgstr "Tập tin vá lỗi (patch)"
-
-#~ msgid "E106: Unknown variable: \"%s\""
-#~ msgstr "E106: Biến không biết: \"%s\""
-
-#~ msgid ""
-#~ "&OK\n"
-#~ "&Cancel"
-#~ msgstr ""
-#~ "&OK\n"
-#~ "&Hủy bỏ"
-
-#~ msgid "E240: No connection to Vim server"
-#~ msgstr "E240: Không có kết nối với máy chủ Vim"
-
-#~ msgid "E277: Unable to read a server reply"
-#~ msgstr "E277: Máy chủ không trả lời"
-
-#~ msgid "E241: Unable to send to %s"
-#~ msgstr "E241: Không thể gửi tin nhắn tới %s"
-
-#~ msgid "E130: Undefined function: %s"
-#~ msgstr "E130: Hàm số %s chưa xác định"
-
-#~ msgid "Save As"
-#~ msgstr "Ghi nhớ như"
-
-#~ msgid "Source Vim script"
-#~ msgstr "Thực hiện script của Vim"
-
-#~ msgid "Edit File"
-#~ msgstr "Soạn thảo tập tin"
-
-#~ msgid " (NOT FOUND)"
-#~ msgstr " (KHÔNG TÌM THẤY)"
-
-#~ msgid "Edit File in new window"
-#~ msgstr "Soạn thảo tập tin trong cửa sổ mới"
-
-#~ msgid "Append File"
-#~ msgstr "Thêm tập tin"
-
-#~ msgid "Window position: X %d, Y %d"
-#~ msgstr "Vị trí cửa sổ: X %d, Y %d"
-
-#~ msgid "Save Redirection"
-#~ msgstr "Chuyển hướng ghi nhớ"
-
-#~ msgid "Save View"
-#~ msgstr "Ghi nhớ vẻ ngoài"
-
-#~ msgid "Save Session"
-#~ msgstr "Ghi nhớ buổi làm việc"
-
-#~ msgid "Save Setup"
-#~ msgstr "Ghi nhớ cấu hình"
-
-#~ msgid "E196: No digraphs in this version"
-#~ msgstr "E196: Trong phiên bản này chữ ghép không được hỗ trợ"
-
-#~ msgid "[NL found]"
-#~ msgstr "[tìm thấy ký tự NL]"
-
-#~ msgid "[crypted]"
-#~ msgstr "[đã mã hóa]"
-
-#~ msgid "[CONVERSION ERROR]"
-#~ msgstr "[LỖI CHUYỂN BẢNG MÃ]"
-
-#~ msgid "NetBeans disallows writes of unmodified buffers"
-#~ msgstr "NetBeans không cho phép ghi nhớ bộ đệm chưa có thay đổi nào"
-
-#~ msgid "Partial writes disallowed for NetBeans buffers"
-#~ msgstr "Ghi nhớ một phần bộ đệm NetBeans không được cho phép"
-
-#~ msgid "E460: The resource fork would be lost (add ! to override)"
-#~ msgstr ""
-#~ "E460: Nhánh tài nguyên sẽ bị mất (thêm ! để bỏ qua việc kiểm tra lại)"
-
-#~ msgid "<cannot open> "
-#~ msgstr "<không thể mở> "
-
-#~ msgid "E616: vim_SelFile: can't get font %s"
-#~ msgstr "E616: vim_SelFile: không tìm thấy phông chữ %s"
-
-#~ msgid "E614: vim_SelFile: can't return to current directory"
-#~ msgstr "E614: vim_SelFile: không trở lại được thư mục hiện thời"
-
-#~ msgid "Pathname:"
-#~ msgstr "Đường dẫn tới tập tin:"
-
-#~ msgid "E615: vim_SelFile: can't get current directory"
-#~ msgstr "E615: vim_SelFile: không tìm thấy thư mục hiện thời"
-
-#~ msgid "OK"
-#~ msgstr "Đồng ý"
-
-#~ msgid "Cancel"
-#~ msgstr "Hủy bỏ"
-
-#~ msgid "Vim dialog"
-#~ msgstr "Hộp thoại Vim"
-
-#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
-#~ msgstr "Thanh cuộn: Không thể xác định hình học của thanh cuộn."
-
-#~ msgid "E232: Cannot create BalloonEval with both message and callback"
-#~ msgstr ""
-#~ "E232: Không tạo được BalloonEval với cả thông báo và lời gọi ngược lại"
-
-#~ msgid "E229: Cannot start the GUI"
-#~ msgstr "E229: Không chạy được giao diện đồ họa GUI"
-
-#~ msgid "E230: Cannot read from \"%s\""
-#~ msgstr "E230: Không đọc được từ \"%s\""
-
-#~ msgid "E665: Cannot start GUI, no valid font found"
-#~ msgstr ""
-#~ "E665: Không chạy được giao diện đồ họa GUI, đưa ra phông chữ không đúng"
-
-#~ msgid "E231: 'guifontwide' invalid"
-#~ msgstr "E231: 'guifontwide' có giá trị không đúng"
-
-#~ msgid "E599: Value of 'imactivatekey' is invalid"
-#~ msgstr "E599: Giá trị của 'imactivatekey' không đúng"
-
-#~ msgid "E254: Cannot allocate color %s"
-#~ msgstr "E254: Không chỉ định được màu %s"
-
-#~ msgid "Vim dialog..."
-#~ msgstr "Hộp thoại Vim..."
-
-#~ msgid "Input _Methods"
-#~ msgstr "Phương pháp _nhập liệu"
-
-#~ msgid "VIM - Search and Replace..."
-#~ msgstr "VIM - Tìm kiếm và thay thế..."
-
-#~ msgid "VIM - Search..."
-#~ msgstr "VIM - Tìm kiếm..."
-
-#~ msgid "Find what:"
-#~ msgstr "Tìm kiếm gì:"
-
-#~ msgid "Replace with:"
-#~ msgstr "Thay thế bởi:"
-
-#~ msgid "Match whole word only"
-#~ msgstr "Chỉ tìm tương ứng hoàn toàn với từ"
-
-#~ msgid "Match case"
-#~ msgstr "Có tính kiểu chữ"
-
-#~ msgid "Direction"
-#~ msgstr "Hướng"
-
-#~ msgid "Up"
-#~ msgstr "Lên"
-
-#~ msgid "Down"
-#~ msgstr "Xuống"
-
-#~ msgid "Find Next"
-#~ msgstr "Tìm tiếp"
-
-#~ msgid "Replace"
-#~ msgstr "Thay thế"
-
-#~ msgid "Replace All"
-#~ msgstr "Thay thế tất cả"
-
-#~ msgid "Vim: Received \"die\" request from session manager\n"
-#~ msgstr "Vim: Nhận được yêu cầu \"chết\" (dừng) từ trình quản lý màn hình\n"
-
-#~ msgid "Vim: Main window unexpectedly destroyed\n"
-#~ msgstr "Vim: Cửa sổ chính đã bị đóng đột ngột\n"
-
-#~ msgid "Font Selection"
-#~ msgstr "Chọn phông chữ"
-
-#~ msgid "Used CUT_BUFFER0 instead of empty selection"
-#~ msgstr "Sử dụng CUT_BUFFER0 thay cho lựa chọn trống rỗng"
-
-#~ msgid "Filter"
-#~ msgstr "Đầu lọc"
-
-#~ msgid "Directories"
-#~ msgstr "Thư mục"
-
-#~ msgid "Help"
-#~ msgstr "Trợ giúp"
-
-#~ msgid "Files"
-#~ msgstr "Tập tin"
-
-#~ msgid "Selection"
-#~ msgstr "Lựa chọn"
-
-#~ msgid "Undo"
-#~ msgstr "Hủy thao tác"
-
-#~ msgid "E671: Cannot find window title \"%s\""
-#~ msgstr "E671: Không tìm được tiêu đề cửa sổ \"%s\""
-
-#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
-#~ msgstr ""
-#~ "E243: Tham số không được hỗ trợ: \"-%s\"; Hãy sử dụng phiên bản OLE."
-
-#~ msgid "E672: Unable to open window inside MDI application"
-#~ msgstr "E672: Không mở được cửa sổ bên trong ứng dụng MDI"
-
-#~ msgid "Find string (use '\\\\' to find a '\\')"
-#~ msgstr "Tìm kiếm chuỗi (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-
-#~ msgid "Find & Replace (use '\\\\' to find a '\\')"
-#~ msgstr "Tìm kiếm và Thay thế (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-
-#~ msgid ""
-#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
-#~ msgstr ""
-#~ "Vim E458: Không chỉ định được bản ghi trong bảng màu, một vài màu có thể "
-#~ "hiển thị không chính xác"
-
-#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:"
-#~ msgstr "E250: Trong bộ phông chữ %s thiếu phông cho các bảng mã sau:"
-
-#~ msgid "E252: Fontset name: %s"
-#~ msgstr "E252: Bộ phông chữ: %s"
-
-#~ msgid "Font '%s' is not fixed-width"
-#~ msgstr "Phông chữ '%s' không phải là phông có độ rộng cố định (fixed-width)"
-
-#~ msgid "E253: Fontset name: %s\n"
-#~ msgstr "E253: Bộ phông chữ: %s\n"
-
-#~ msgid "Font0: %s\n"
-#~ msgstr "Font0: %s\n"
-
-#~ msgid "Font1: %s\n"
-#~ msgstr "Font1: %s\n"
-
-#~ msgid "Font%<PRId64> width is not twice that of font0\n"
-#~ msgstr ""
-#~ "Chiều rộng phông chữ font%<PRId64> phải lớn hơn hai lần so với chiều rộng "
-#~ "font0\n"
-
-#~ msgid "Font0 width: %<PRId64>\n"
-#~ msgstr "Chiều rộng font0: %<PRId64>\n"
-
-#~ msgid ""
-#~ "Font1 width: %<PRId64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "Chiều rộng font1: %<PRId64>\n"
-#~ "\n"
-
-#~ msgid "E256: Hangul automata ERROR"
-#~ msgstr "E256: LỖI máy tự động Hangual (tiếng Hàn)"
-
-#~ msgid "E563: stat error"
-#~ msgstr "E563: lỗi stat"
-
-#~ msgid "E625: cannot open cscope database: %s"
-#~ msgstr "E625: không mở được cơ sở dữ liệu cscope: %s"
-
-#~ msgid "E626: cannot get cscope database information"
-#~ msgstr "E626: không lấy được thông tin về cơ sở dữ liệu cscope"
-
-#~ msgid "E569: maximum number of cscope connections reached"
-#~ msgstr "E569: đã đạt tới số kết nối lớn nhất cho phép với cscope"
-
-#~ msgid ""
-#~ "E263: Sorry, this command is disabled, the Python library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E263: Rất tiếc câu lệnh này không làm việc, vì thư viện Python chưa được "
-#~ "nạp."
-
-#~ msgid "E659: Cannot invoke Python recursively"
-#~ msgstr "E659: Không thể gọi Python một cách đệ quy"
-
-#~ msgid "can't delete OutputObject attributes"
-#~ msgstr "Không xóa được thuộc tính OutputObject"
-
-#~ msgid "softspace must be an integer"
-#~ msgstr "giá trị softspace phải là một số nguyên"
-
-#~ msgid "invalid attribute"
-#~ msgstr "thuộc tính không đúng"
-
-#~ msgid "writelines() requires list of strings"
-#~ msgstr "writelines() yêu cầu một danh sách các chuỗi"
-
-#~ msgid "E264: Python: Error initialising I/O objects"
-#~ msgstr "E264: Python: Lỗi khi bắt đầu sử dụng vật thể I/O"
-
-#~ msgid "invalid expression"
-#~ msgstr "biểu thức không đúng"
-
-#~ msgid "expressions disabled at compile time"
-#~ msgstr "biểu thức bị tắt khi biên dịch"
-
-#~ msgid "attempt to refer to deleted buffer"
-#~ msgstr "cố chỉ đến bộ đệm đã bị xóa"
-
-#~ msgid "line number out of range"
-#~ msgstr "số thứ tự của dòng vượt quá giới hạn"
-
-#~ msgid "<buffer object (deleted) at %8lX>"
-#~ msgstr "<vật thể của bộ đệm (bị xóa) tại %8lX>"
-
-#~ msgid "invalid mark name"
-#~ msgstr "tên dấu hiệu không đúng"
-
-#~ msgid "no such buffer"
-#~ msgstr "không có bộ đệm như vậy"
-
-#~ msgid "attempt to refer to deleted window"
-#~ msgstr "cố chỉ đến cửa sổ đã bị đóng"
-
-#~ msgid "readonly attribute"
-#~ msgstr "thuộc tính chỉ đọc"
-
-#~ msgid "cursor position outside buffer"
-#~ msgstr "vị trí con trỏ nằm ngoài bộ đệm"
-
-#~ msgid "<window object (deleted) at %.8lX>"
-#~ msgstr "<vật thể của cửa sổ (bị xóa) tại %.8lX>"
-
-#~ msgid "<window object (unknown) at %.8lX>"
-#~ msgstr "<vật thể của cửa sổ (không rõ) tại %.8lX>"
-
-#~ msgid "<window %d>"
-#~ msgstr "<cửa sổ %d>"
-
-#~ msgid "no such window"
-#~ msgstr "không có cửa sổ như vậy"
-
-#~ msgid "cannot save undo information"
-#~ msgstr "không ghi được thông tin về việc hủy thao tác"
-
-#~ msgid "cannot delete line"
-#~ msgstr "không xóa được dòng"
-
-#~ msgid "cannot replace line"
-#~ msgstr "không thay thế được dòng"
-
-#~ msgid "cannot insert line"
-#~ msgstr "không chèn được dòng"
-
-#~ msgid "string cannot contain newlines"
-#~ msgstr "chuỗi không thể chứa ký tự dòng mới"
-
-#~ msgid ""
-#~ "E266: Sorry, this command is disabled, the Ruby library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E266: Rất tiếc câu lệnh này không làm việc, vì thư viện Ruby chưa đượcnạp."
-
-#~ msgid "E273: unknown longjmp status %d"
-#~ msgstr "E273: không rõ trạng thái của longjmp %d"
-
-#~ msgid "Toggle implementation/definition"
-#~ msgstr "Bật tắt giữa thi hành/định nghĩa"
-
-#~ msgid "Show base class of"
-#~ msgstr "Hiển thị hạng cơ bản của"
-
-#~ msgid "Show overridden member function"
-#~ msgstr "Hiển thị hàm số bị nạp đè lên"
-
-#~ msgid "Retrieve from file"
-#~ msgstr "Nhận từ tập tin"
-
-#~ msgid "Retrieve from project"
-#~ msgstr "Nhận từ dự án"
-
-#~ msgid "Retrieve from all projects"
-#~ msgstr "Nhận từ tất cả các dự án"
-
-#~ msgid "Retrieve"
-#~ msgstr "Nhận"
-
-#~ msgid "Show source of"
-#~ msgstr "Hiển thị mã nguồn"
-
-#~ msgid "Find symbol"
-#~ msgstr "Tìm ký hiệu"
-
-#~ msgid "Browse class"
-#~ msgstr "Duyệt hạng"
-
-#~ msgid "Show class in hierarchy"
-#~ msgstr "Hiển thị hạng trong hệ thống cấp bậc"
-
-#~ msgid "Show class in restricted hierarchy"
-#~ msgstr "Hiển thị hạng trong hệ thống cấp bậc giới hạn"
-
-#~ msgid "Xref refers to"
-#~ msgstr "Xref chỉ đến"
-
-#~ msgid "Xref referred by"
-#~ msgstr "Liên kết đến xref từ"
-
-#~ msgid "Xref has a"
-#~ msgstr "Xref có một"
-
-#~ msgid "Xref used by"
-#~ msgstr "Xref được sử dụng bởi"
-
-#~ msgid "Show docu of"
-#~ msgstr "Hiển thị docu của"
-
-#~ msgid "Generate docu for"
-#~ msgstr "Tạo docu cho"
-
-#~ msgid ""
-#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in "
-#~ "$PATH).\n"
-#~ msgstr ""
-#~ "Không kết nối được tới SNiFF+. Hãy kiểm tra cấu hình môi trường."
-#~ "(sniffemacs phải được chỉ ra trong biến $PATH).\n"
-
-#~ msgid "E274: Sniff: Error during read. Disconnected"
-#~ msgstr "E274: Sniff: Lỗi trong thời gian đọc. Ngắt kết nối"
-
-#~ msgid "SNiFF+ is currently "
-#~ msgstr "Trong thời điểm hiện nay SNiFF+ "
-
-#~ msgid "not "
-#~ msgstr "không "
-
-#~ msgid "connected"
-#~ msgstr "được kết nối"
-
-#~ msgid "E275: Unknown SNiFF+ request: %s"
-#~ msgstr "E275: không rõ yêu cầu của SNiFF+: %s"
-
-#~ msgid "E276: Error connecting to SNiFF+"
-#~ msgstr "E276: Lỗi kết nối với SNiFF+"
-
-#~ msgid "E278: SNiFF+ not connected"
-#~ msgstr "E278: SNiFF+ chưa được kết nối"
-
-#~ msgid "Sniff: Error during write. Disconnected"
-#~ msgstr "Sniff: Lỗi trong thời gian ghi nhớ. Ngắt kết nối"
-
-#~ msgid "not implemented yet"
-#~ msgstr "tạm thời chưa được thực thi"
-
-#~ msgid "unknown option"
-#~ msgstr "tùy chọn không rõ"
-
-#~ msgid "cannot set line(s)"
-#~ msgstr "không thể đặt (các) dòng"
-
-#~ msgid "mark not set"
-#~ msgstr "dấu hiệu chưa được đặt"
-
-#~ msgid "row %d column %d"
-#~ msgstr "hàng %d cột %d"
-
-#~ msgid "cannot insert/append line"
-#~ msgstr "không thể chèn hoặc thêm dòng"
-
-#~ msgid "unknown flag: "
-#~ msgstr "cờ không biết: "
-
-#~ msgid "unknown vimOption"
-#~ msgstr "không rõ tùy chọn vimOption"
-
-#~ msgid "keyboard interrupt"
-#~ msgstr "sự gián đoạn của bàn phím"
-
-#~ msgid "vim error"
-#~ msgstr "lỗi của vim"
-
-#~ msgid "cannot create buffer/window command: object is being deleted"
-#~ msgstr ""
-#~ "không tạo được câu lệnh của bộ đệm hay của cửa sổ: vật thể đang bị xóa"
-
-#~ msgid ""
-#~ "cannot register callback command: buffer/window is already being deleted"
-#~ msgstr ""
-#~ "không đăng ký được câu lệnh gọi ngược: bộ đệm hoặc cửa sổ đang bị xóa"
-
-#~ msgid ""
-#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-"
-#~ "dev@vim.org"
-#~ msgstr ""
-#~ "E280: LỖI NẶNG CỦA TCL: bị hỏng danh sách liên kết!? Hãy thông báo việc "
-#~ "nàyđến danh sách thư (mailing list) vim-dev@vim.org"
-
-#~ msgid "cannot register callback command: buffer/window reference not found"
-#~ msgstr ""
-#~ "không đăng ký được câu lệnh gọi ngược: không tìm thấy liên kết đến bộ đệm "
-#~ "hoặc cửa sổ"
-
-#~ msgid ""
-#~ "E571: Sorry, this command is disabled: the Tcl library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E571: Rất tiếc là câu lệnh này không làm việc, vì thư viện Tcl chưa được "
-#~ "nạp"
-
-#~ msgid ""
-#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim."
-#~ "org"
-#~ msgstr ""
-#~ "E281: LỖI TCL: mã thoát ra không phải là một số nguyên!? Hãy thông báo "
-#~ "điều này đến danh sách thư (mailing list) vim-dev@vim.org"
-
-#~ msgid "cannot get line"
-#~ msgstr "không nhận được dòng"
-
-#~ msgid "Unable to register a command server name"
-#~ msgstr "Không đăng ký được một tên cho máy chủ câu lệnh"
-
-#~ msgid "E248: Failed to send command to the destination program"
-#~ msgstr "E248: Gửi câu lệnh vào chương trình khác không thành công"
-
-#~ msgid "E251: VIM instance registry property is badly formed. Deleted!"
-#~ msgstr "E251: Thuộc tính đăng ký của Vim được định dạng không đúng. Xóa!"
-
-#~ msgid "This Vim was not compiled with the diff feature."
-#~ msgstr "Vim không được biên dịch với tính năng hỗ trợ xem khác biệt (diff)."
-
-#~ msgid "-register\t\tRegister this gvim for OLE"
-#~ msgstr "-register\t\tĐăng ký gvim này cho OLE"
-
-#~ msgid "-unregister\t\tUnregister gvim for OLE"
-#~ msgstr "-unregister\t\tBỏ đăng ký gvim này cho OLE"
-
-#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")"
-#~ msgstr "-g\t\t\tSử dụng giao diện đồ họa GUI (giống \"gvim\")"
-
-#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
-#~ msgstr ""
-#~ "-f hoặc --nofork\tTrong chương trình hoạt động: Không thực hiện fork "
-#~ "khi chạy GUI"
-
-#~ msgid "-V[N]\t\tVerbose level"
-#~ msgstr "-V[N]\t\tMức độ chi tiết của thông báo"
-
-#~ msgid "-f\t\t\tDon't use newcli to open window"
-#~ msgstr "-f\t\t\tKhông sử dụng newcli để mở cửa sổ"
-
-#~ msgid "-dev <device>\t\tUse <device> for I/O"
-#~ msgstr "-dev <thiết bị>\t\tSử dụng <thiết bị> cho I/O"
-
-#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
-#~ msgstr "-U <gvimrc>\t\tSử dụng <gvimrc> thay thế cho mọi .gvimrc"
-
-#~ msgid "-x\t\t\tEdit encrypted files"
-#~ msgstr "-x\t\t\tSoạn thảo tập tin đã mã hóa"
-
-#~ msgid "-display <display>\tConnect vim to this particular X-server"
-#~ msgstr "-display <màn hình>\tKết nối vim tới máy chủ X đã chỉ ra"
-
-#~ msgid "-X\t\t\tDo not connect to X server"
-#~ msgstr "-X\t\t\tKhông thực hiện việc kết nối tới máy chủ X"
-
-#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible"
-#~ msgstr "--remote <tập tin>\tSoạn thảo <tập tin> trên máy chủ Vim nếu có thể"
-
-#~ msgid "--remote-silent <files> Same, don't complain if there is no server"
-#~ msgstr ""
-#~ "--remote-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có máy "
-#~ "chủ"
-
-#~ msgid ""
-#~ "--remote-wait <files> As --remote but wait for files to have been edited"
-#~ msgstr "--remote-wait <tập tin> Cũng như --remote, nhưng chờ sự kết thúc"
-
-#~ msgid ""
-#~ "--remote-wait-silent <files> Same, don't complain if there is no server"
-#~ msgstr ""
-#~ "--remote-wait-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có "
-#~ "máy chủ"
-
-#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
-#~ msgstr "--remote-send <phím>\tGửi <phím> lên máy chủ Vim và thoát"
-
-#~ msgid ""
-#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
-#~ msgstr ""
-#~ "--remote-expr <biểu thức>\tTính <biểu thức> trên máy chủ Vim và in ra kết "
-#~ "quả"
-
-#~ msgid "--serverlist\t\tList available Vim server names and exit"
-#~ msgstr "--serverlist\t\tHiển thị danh sách máy chủ Vim và thoát"
-
-#~ msgid "--servername <name>\tSend to/become the Vim server <name>"
-#~ msgstr "--servername <tên>\tGửi lên (hoặc trở thành) máy chủ Vim với <tên>"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (Motif version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản Motif):\n"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (neXtaw version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản neXtaw):\n"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (Athena version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản Athena):\n"
-
-#~ msgid "-display <display>\tRun vim on <display>"
-#~ msgstr "-display <màn hình>\tChạy vim trong <màn hình> đã chỉ ra"
-
-#~ msgid "-iconic\t\tStart vim iconified"
-#~ msgstr "-iconic\t\tChạy vim ở dạng thu nhỏ"
-
-#~ msgid "-name <name>\t\tUse resource as if vim was <name>"
-#~ msgstr "-name <tên>\t\tSử dụng tài nguyên giống như khi vim có <tên>"
-
-#~ msgid "\t\t\t (Unimplemented)\n"
-#~ msgstr "\t\t\t (Chưa được thực thi)\n"
-
-#~ msgid "-background <color>\tUse <color> for the background (also: -bg)"
-#~ msgstr "-background <màu>\tSử dụng <màu> chỉ ra cho nền (cũng như: -bg)"
-
-#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
-#~ msgstr ""
-#~ "-foreground <màu>\tSử dụng <màu> cho văn bản thông thường (cũng như: -fg)"
-
-#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
-#~ msgstr ""
-#~ "-font <phông>\t\tSử dụng <phông> chữ cho văn bản thông thường (cũng như: -"
-#~ "fn)"
-
-#~ msgid "-boldfont <font>\tUse <font> for bold text"
-#~ msgstr "-boldfont <phông>\tSử dụng <phông> chữ cho văn bản in đậm"
-
-#~ msgid "-italicfont <font>\tUse <font> for italic text"
-#~ msgstr "-italicfont <phông>\tSử dụng <phông> chữ cho văn bản in nghiêng"
-
-#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
-#~ msgstr ""
-#~ "-geometry <kích thước>\tSử dụng <kích thước> ban đầu (cũng như: -geom)"
-
-#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
-#~ msgstr ""
-#~ "-borderwidth <rộng>\tSử dụng đường viền có chiều <rộng> (cũng như: -bw)"
-
-#~ msgid ""
-#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
-#~ msgstr ""
-#~ "-scrollbarwidth <rộng> Sử dụng thanh cuộn với chiều <rộng> (cũng như: -sw)"
-
-#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
-#~ msgstr ""
-#~ "-menuheight <cao>\tSử dụng thanh trình đơn với chiều <cao> (cũng như: -mh)"
-
-#~ msgid "-reverse\t\tUse reverse video (also: -rv)"
-#~ msgstr "-reverse\t\tSử dụng chế độ video đảo ngược (cũng như: -rv)"
-
-#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)"
-#~ msgstr "+reverse\t\tKhông sử dụng chế độ video đảo ngược (cũng như: +rv)"
-
-#~ msgid "-xrm <resource>\tSet the specified resource"
-#~ msgstr "-xrm <tài nguyên>\tĐặt <tài nguyên> chỉ ra"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (RISC OS version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản RISC OS):\n"
-
-#~ msgid "--columns <number>\tInitial width of window in columns"
-#~ msgstr "--columns <số>\tChiều rộng ban đầu của cửa sổ tính theo số cột"
-
-#~ msgid "--rows <number>\tInitial height of window in rows"
-#~ msgstr "--rows <số>\tChiều cao ban đầu của cửa sổ tính theo số dòng"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (GTK+ version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản GTK+):\n"
-
-#~ msgid "-display <display>\tRun vim on <display> (also: --display)"
-#~ msgstr ""
-#~ "-display <màn hình>\tChạy vim trên <màn hình> chỉ ra (cũng như: --display)"
-
-#~ msgid "--role <role>\tSet a unique role to identify the main window"
-#~ msgstr "--role <vai trò>\tĐặt <vai trò> duy nhất để nhận diện cửa sổ chính"
-
-#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
-#~ msgstr "--socketid <xid>\tMở Vim bên trong thành phần GTK khác"
-
-#~ msgid "-P <parent title>\tOpen Vim inside parent application"
-#~ msgstr "-P <tiêu đề của mẹ>\tMở Vim bên trong ứng dụng mẹ"
-
-#~ msgid "No display"
-#~ msgstr "Không có màn hình"
-
-#~ msgid ": Send failed.\n"
-#~ msgstr ": Gửi không thành công.\n"
-
-#~ msgid ": Send failed. Trying to execute locally\n"
-#~ msgstr ": Gửi không thành công. Thử thực hiện nội bộ\n"
-
-#~ msgid "%d of %d edited"
-#~ msgstr "đã soạn thảo %d từ %d"
-
-#~ msgid "No display: Send expression failed.\n"
-#~ msgstr "Không có màn hình: gửi biểu thức không thành công.\n"
-
-#~ msgid ": Send expression failed.\n"
-#~ msgstr ": Gửi biểu thức không thành công.\n"
-
-#~ msgid "E543: Not a valid codepage"
-#~ msgstr "E543: Bảng mã không cho phép"
-
-#~ msgid "E285: Failed to create input context"
-#~ msgstr "E285: Không tạo được nội dung nhập vào"
-
-#~ msgid "E286: Failed to open input method"
-#~ msgstr "E286: Việc thử mở phương pháp nhập không thành công"
-
-#~ msgid "E287: Warning: Could not set destroy callback to IM"
-#~ msgstr ""
-#~ "E287: Cảnh báo: không đặt được sự gọi ngược hủy diệt thành phương pháp "
-#~ "nhập"
-
-#~ msgid "E288: input method doesn't support any style"
-#~ msgstr "E288: phương pháp nhập không hỗ trợ bất kỳ phong cách (style) nào"
-
-#~ msgid "E289: input method doesn't support my preedit type"
-#~ msgstr "E289: phương pháp nhập không hỗ trợ loại soạn thảo trước của Vim"
-
-#~ msgid "E290: over-the-spot style requires fontset"
-#~ msgstr "E290: phong cách over-the-spot yêu cầu một bộ phông chữ"
-
-#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
-#~ msgstr "E291: GTK+ cũ hơn 1.2.3. Vùng chỉ trạng thái không làm việc"
-
-#~ msgid "E292: Input Method Server is not running"
-#~ msgstr "E292: Máy chủ phương pháp nhập liệu chưa được chạy"
-
-#~ msgid ""
-#~ "\n"
-#~ " [not usable with this version of Vim]"
-#~ msgstr ""
-#~ "\n"
-#~ " [không sử dụng được với phiên bản này của Vim]"
-
-#~ msgid ""
-#~ "&Open Read-Only\n"
-#~ "&Edit anyway\n"
-#~ "&Recover\n"
-#~ "&Quit\n"
-#~ "&Abort\n"
-#~ "&Delete it"
-#~ msgstr ""
-#~ "&O Mở chỉ để đọc\n"
-#~ "&E Vẫn soạn thảo\n"
-#~ "&R Phục hồi\n"
-#~ "&Q Thoát\n"
-#~ "&A Gián đoạn&D Xóa nó"
-
-#~ msgid "Tear off this menu"
-#~ msgstr "Chia cắt trình đơn này"
-
-#~ msgid "[string too long]"
-#~ msgstr "[chuỗi quá dài]"
-
-#~ msgid "Hit ENTER to continue"
-#~ msgstr "Nhấn phím ENTER để tiếp tục"
-
-#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
-#~ msgstr " (RET/BS: dòng, SPACE/b: trang, d/u: nửa trang, q: thoát)"
-
-#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)"
-#~ msgstr " (RET: dòng, SPACE: trang, d: nửa trang, q: thoát)"
-
-#~ msgid "Save File dialog"
-#~ msgstr "Ghi nhớ tập tin"
-
-#~ msgid "Open File dialog"
-#~ msgstr "Mở tập tin"
-
-#~ msgid "E338: Sorry, no file browser in console mode"
-#~ msgstr ""
-#~ "E338: Xin lỗi nhưng không có trình duyệt tập tin trong chế độ kênh giao "
-#~ "tác (console)"
-
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim: ghi nhớ các tập tin...\n"
-
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim: Đã xong.\n"
-
-#~ msgid "ERROR: "
-#~ msgstr "LỖI: "
-
-#~ msgid ""
-#~ "\n"
-#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use "
-#~ "%<PRIu64>\n"
-#~ msgstr ""
-#~ "\n"
-#~ "[byte] tổng phân phối-còn trống %<PRIu64>-%<PRIu64>, sử dụng %<PRIu64>, "
-#~ "píc sử dụng %<PRIu64>\n"
-
-#~ msgid ""
-#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "[gọi] tổng re/malloc() %<PRIu64>, tổng free() %<PRIu64>\n"
-#~ "\n"
-
-#~ msgid "E340: Line is becoming too long"
-#~ msgstr "E340: Dòng đang trở thành quá dài"
-
-#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
-#~ msgstr "E341: Lỗi nội bộ: lalloc(%<PRId64>, )"
+#, c-format
+msgid "E447: Can't find file \"%s\" in path"
+msgstr "E447: Không tìm thấy tập tin \"%s\" trong đường dẫn"
-#~ msgid "E547: Illegal mouseshape"
-#~ msgstr "E547: Dạng trỏ chuột không cho phép"
+#, c-format
+msgid "E370: Could not load library %s"
+msgstr "E370: Không nạp được thư viện %s"
-#~ msgid "Enter encryption key: "
-#~ msgstr "Nhập mật khẩu để mã hóa: "
+msgid "Sorry, this command is disabled: the Perl library could not be loaded."
+msgstr "Xin lỗi, câu lệnh này bị tắt: không nạp được thư viện Perl."
-#~ msgid "Enter same key again: "
-#~ msgstr " Nhập lại mật khẩu:"
+msgid "E299: Perl evaluation forbidden in sandbox without the Safe module"
+msgstr ""
+"E299: Không cho phép sự tính toán Perl trong hộp cát mà không có môđun An "
+"toàn"
-#~ msgid "Keys don't match!"
-#~ msgstr "Hai mật khẩu không trùng nhau!"
+msgid "Edit with &multiple Vims"
+msgstr "Soạn thảo trong nhiều Vi&m"
-#~ msgid "Cannot connect to Netbeans #2"
-#~ msgstr "Không kết nối được với Netbeans #2"
+msgid "Edit with single &Vim"
+msgstr "Soạn thảo trong một &Vim"
-#~ msgid "Cannot connect to Netbeans"
-#~ msgstr "Không kết nối được với NetBeans"
+msgid "&Diff with Vim"
+msgstr "&So sánh (diff) qua Vim"
-#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
-#~ msgstr ""
-#~ "E668: Chế độ truy cập thông tin về liên kết với NetBeans không đúng: \"%s"
-#~ "\""
+msgid "Edit with &Vim"
+msgstr "Soạn thảo trong &Vim"
-#~ msgid "read from Netbeans socket"
-#~ msgstr "đọc từ socket NetBeans"
+msgid "Edit with existing Vim - &"
+msgstr "Soạn thảo trong Vim đã chạy - &"
-#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>"
-#~ msgstr "E658: Bị mất liên kết với NetBeans cho bộ đệm %<PRId64>"
+msgid "Edits the selected file(s) with Vim"
+msgstr "Soạn thảo (các) tập tin đã chọn trong Vim"
-#~ msgid "freeing %<PRId64> lines"
-#~ msgstr "đã làm sạch %<PRId64> dòng"
+msgid "Error creating process: Check if gvim is in your path!"
+msgstr "Lỗi tạo tiến trình: Hãy kiểm tra xem gvim có trong đường dẫn không!"
-#~ msgid "E530: Cannot change term in GUI"
-#~ msgstr "E530: Không thể thay đổi terminal trong giao diện đồ họa GUI"
+msgid "gvimext.dll error"
+msgstr "lỗi gvimext.dll"
-#~ msgid "E531: Use \":gui\" to start the GUI"
-#~ msgstr "E531: Hãy sử dụng \":gui\" để chạy giao diện đồ họa GUI"
+msgid "Path length too long!"
+msgstr "Đường dẫn quá dài!"
-#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI"
-#~ msgstr "E617: Không thể thay đổi trong giao diện đồ họa GTK+ 2"
+msgid "--No lines in buffer--"
+msgstr "-- Không có dòng nào trong bộ đệm --"
-#~ msgid "E597: can't select fontset"
-#~ msgstr "E597: không chọn được bộ phông chữ"
+msgid "E470: Command aborted"
+msgstr "E470: Câu lệnh bị dừng"
-#~ msgid "E598: Invalid fontset"
-#~ msgstr "E598: Bộ phông chữ không đúng"
+msgid "E471: Argument required"
+msgstr "E471: Cần chỉ ra tham số"
-#~ msgid "E533: can't select wide font"
-#~ msgstr "E533: không chọn được phông chữ với các ký tự có chiều rộng gấp đôi"
+msgid "E10: \\ should be followed by /, ? or &"
+msgstr "E10: Sau \\ phải là các ký tự /, ? hoặc &"
-#~ msgid "E534: Invalid wide font"
-#~ msgstr "E534: Phông chữ, với ký tự có chiều rộng gấp đôi, không đúng"
+msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits"
+msgstr "E11: Lỗi trong cửa sổ dòng lệnh; <CR> thực hiện, CTRL-C thoát"
-#~ msgid "E538: No mouse support"
-#~ msgstr "E538: Chuột không được hỗ trợ"
+msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search"
+msgstr ""
+"E12: Câu lệnh không cho phép từ exrc/vimrc trong thư mục hiện thời hoặc "
+"trong tìm kiếm thẻ ghi"
-#~ msgid "cannot open "
-#~ msgstr "không mở được "
+msgid "E171: Missing :endif"
+msgstr "E171: Thiếu câu lệnh :endif"
-#~ msgid "VIM: Can't open window!\n"
-#~ msgstr "VIM: Không mở được cửa sổ!\n"
+msgid "E600: Missing :endtry"
+msgstr "E600: Thiếu câu lệnh :endtry"
-#~ msgid "Need Amigados version 2.04 or later\n"
-#~ msgstr "Cần Amigados phiên bản 2.04 hoặc mới hơn\n"
+msgid "E170: Missing :endwhile"
+msgstr "E170: Thiếu câu lệnh :endwhile"
-#~ msgid "Need %s version %<PRId64>\n"
-#~ msgstr "Cần %s phiên bản %<PRId64>\n"
+msgid "E588: :endwhile without :while"
+msgstr "E588: Câu lệnh :endwhile không có lệnh :while (1 cặp)"
-#~ msgid "Cannot open NIL:\n"
-#~ msgstr "Không mở được NIL:\n"
+msgid "E13: File exists (add ! to override)"
+msgstr "E13: Tập tin đã tồn tại (thêm ! để ghi chèn)"
-#~ msgid "Cannot create "
-#~ msgstr "Không tạo được "
+msgid "E472: Command failed"
+msgstr "E472: Không thực hiện thành công câu lệnh"
-#~ msgid "Vim exiting with %d\n"
-#~ msgstr "Thoát Vim với mã %d\n"
+#, c-format
+msgid "E234: Unknown fontset: %s"
+msgstr "E234: Không rõ bộ phông chữ: %s"
-#~ msgid "cannot change console mode ?!\n"
-#~ msgstr "không thay đổi được chế độ kênh giao tác (console)?!\n"
+#, c-format
+msgid "E235: Unknown font: %s"
+msgstr "E235: Không rõ phông chữ: %s"
-#~ msgid "mch_get_shellsize: not a console??\n"
-#~ msgstr "mch_get_shellsize: không phải là kênh giao tác (console)??\n"
+#, c-format
+msgid "E236: Font \"%s\" is not fixed-width"
+msgstr "E236: Phông chữ \"%s\" không có độ rộng cố định (fixed-width)"
-#~ msgid "Cannot execute "
-#~ msgstr "Không chạy được "
+msgid "E473: Internal error"
+msgstr "E473: Lỗi nội bộ"
-#~ msgid "shell "
-#~ msgstr "shell "
+msgid "Interrupted"
+msgstr "Bị gián đoạn"
-#~ msgid " returned\n"
-#~ msgstr " thoát\n"
+msgid "E14: Invalid address"
+msgstr "E14: Địa chỉ không cho phép"
-#~ msgid "ANCHOR_BUF_SIZE too small."
-#~ msgstr "Giá trị ANCHOR_BUF_SIZE quá nhỏ."
+msgid "E474: Invalid argument"
+msgstr "E474: Tham số không cho phép"
-#~ msgid "I/O ERROR"
-#~ msgstr "LỖI I/O (NHẬP/XUẤT)"
+#, c-format
+msgid "E475: Invalid argument: %s"
+msgstr "E475: Tham số không cho phép: %s"
-#~ msgid "...(truncated)"
-#~ msgstr "...(bị cắt bớt)"
+#, c-format
+msgid "E15: Invalid expression: %s"
+msgstr "E15: Biểu thức không cho phép: %s"
-#~ msgid "'columns' is not 80, cannot execute external commands"
-#~ msgstr ""
-#~ "Tùy chọn 'columns' khác 80, chương trình ngoại trú không thể thực hiện"
+msgid "E16: Invalid range"
+msgstr "E16: Vùng không cho phép"
-#~ msgid "to %s on %s"
-#~ msgstr "tới %s trên %s"
+msgid "E476: Invalid command"
+msgstr "E476: Câu lệnh không cho phép"
-#~ msgid "E613: Unknown printer font: %s"
-#~ msgstr "E613: Không rõ phông chữ của máy in: %s"
+#, c-format
+msgid "E17: \"%s\" is a directory"
+msgstr "E17: \"%s\" là mộ thư mục"
-#~ msgid "E238: Print error: %s"
-#~ msgstr "E238: Lỗi in: %s"
+msgid "E18: Unexpected characters before '='"
+msgstr "E18: Ở trước '=' có các ký tự không mong đợi"
-#~ msgid "Printing '%s'"
-#~ msgstr "Đang in '%s'"
+#, c-format
+msgid "E364: Library call failed for \"%s()\""
+msgstr "E364: Gọi hàm số \"%s()\" của thư viện không thành công"
-#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
-#~ msgstr "E244: Tên bảng mã không cho phép \"%s\" trong tên phông chữ \"%s\""
+#, c-format
+msgid "E448: Could not load library function %s"
+msgstr "E448: Nạp hàm số %s của thư viện không thành công"
-#~ msgid "E245: Illegal char '%c' in font name \"%s\""
-#~ msgstr "E245: Ký tự không cho phép '%c' trong tên phông chữ \"%s\""
+msgid "E19: Mark has invalid line number"
+msgstr "E19: Dấu hiệu chỉ đến một số thứ tự dòng không đúng"
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim: Tín hiệu đôi, thoát\n"
+msgid "E20: Mark not set"
+msgstr "E20: Dấu hiệu không được xác định"
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim: Nhận được tín hiệu chết %s\n"
+msgid "E21: Cannot make changes, 'modifiable' is off"
+msgstr "E21: Không thể thay đổi, vì tùy chọn 'modifiable' bị tắt"
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim: Nhận được tín hiệu chết\n"
+msgid "E22: Scripts nested too deep"
+msgstr "E22: Các script lồng vào nhau quá sâu"
-#~ msgid "Opening the X display took %<PRId64> msec"
-#~ msgstr "Mở màn hình X mất %<PRId64> mili giây"
+msgid "E23: No alternate file"
+msgstr "E23: Không có tập tin xen kẽ"
-#~ msgid ""
-#~ "\n"
-#~ "Vim: Got X error\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Vim: Lỗi X\n"
+msgid "E24: No such abbreviation"
+msgstr "E24: Không có chữ viết tắt như vậy"
-#~ msgid "Testing the X display failed"
-#~ msgstr "Kiểm tra màn hình X không thành công"
+msgid "E477: No ! allowed"
+msgstr "E477: Không cho phép !"
-#~ msgid "Opening the X display timed out"
-#~ msgstr "Không mở được màn hình X trong thời gian cho phép (time out)"
+msgid "E25: GUI cannot be used: Not enabled at compile time"
+msgstr "E25: Không sử dụng được giao diện đồ họa vì không chọn khi biên dịch"
-#~ msgid ""
-#~ "\n"
-#~ "Cannot execute shell sh\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Không chạy được shell sh\n"
+msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
+msgstr "E26: Tiếng Do thái không được chọn khi biên dịch\n"
-#~ msgid ""
-#~ "\n"
-#~ "Cannot create pipes\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Không tạo được đường ống (pipe)\n"
+msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
+msgstr "E27: Tiếng Farsi không được chọn khi biên dịch\n"
-#~ msgid ""
-#~ "\n"
-#~ "Cannot fork\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Không thực hiện được fork()\n"
+msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
+msgstr "E800: Tiếng Ả Rập không được chọn khi biên dịch\n"
-#~ msgid ""
-#~ "\n"
-#~ "Command terminated\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Câu lệnh bị gián đoạn\n"
+#, c-format
+msgid "E28: No such highlight group name: %s"
+msgstr "E28: Nhóm chiếu sáng cú pháp %s không tồn tại"
-#~ msgid "XSMP lost ICE connection"
-#~ msgstr "XSMP mất kết nối ICE"
+msgid "E29: No inserted text yet"
+msgstr "E29: Tạm thời chưa có văn bản được chèn"
-#~ msgid "Opening the X display failed"
-#~ msgstr "Mở màn hình X không thành công"
+msgid "E30: No previous command line"
+msgstr "E30: Không có dòng lệnh trước"
-#~ msgid "XSMP handling save-yourself request"
-#~ msgstr "XSMP xử lý yêu cầu tự động ghi nhớ"
+msgid "E31: No such mapping"
+msgstr "E31: Không có ánh xạ (mapping) như vậy"
-#~ msgid "XSMP opening connection"
-#~ msgstr "XSMP mở kết nối"
+msgid "E479: No match"
+msgstr "E479: Không có tương ứng"
-#~ msgid "XSMP ICE connection watch failed"
-#~ msgstr "XSMP mất theo dõi kết nối ICE"
+#, c-format
+msgid "E480: No match: %s"
+msgstr "E480: Không có tương ứng: %s"
-#~ msgid "XSMP SmcOpenConnection failed: %s"
-#~ msgstr "XSMP thực hiện SmcOpenConnection không thành công: %s"
+msgid "E32: No file name"
+msgstr "E32: Không có tên tập tin"
-#~ msgid "At line"
-#~ msgstr "Tại dòng"
+msgid "E33: No previous substitute regular expression"
+msgstr "E33: Không có biểu thức chính quy trước để thay thế"
-#~ msgid "Could not allocate memory for command line."
-#~ msgstr "Không phân chia được bộ nhớ cho dòng lệnh."
+msgid "E34: No previous command"
+msgstr "E34: Không có câu lệnh trước"
-#~ msgid "VIM Error"
-#~ msgstr "Lỗi VIM"
+msgid "E35: No previous regular expression"
+msgstr "E35: Không có biểu thức chính quy trước"
-#~ msgid "Could not load vim32.dll!"
-#~ msgstr "Không nạp được vim32.dll!"
+msgid "E481: No range allowed"
+msgstr "E481: Không cho phép sử dụng phạm vi"
-#~ msgid "Could not fix up function pointers to the DLL!"
-#~ msgstr "Không sửa được cái chỉ (pointer) hàm số tới DLL!"
+msgid "E36: Not enough room"
+msgstr "E36: Không đủ chỗ trống"
-#~ msgid "shell returned %d"
-#~ msgstr "thoát shell với mã %d"
+# TODO: Capitalise first word of message?
+msgid "E247: No registered server named \"%s\""
+msgstr "E247: máy chủ \"%s\" chưa đăng ký"
-#~ msgid "Vim: Caught %s event\n"
-#~ msgstr "Vim: Nhận được sự kiện %s\n"
+#, c-format
+msgid "E482: Can't create file %s"
+msgstr "E482: Không tạo được tập tin %s"
-#~ msgid "close"
-#~ msgstr "đóng"
+msgid "E483: Can't get temp file name"
+msgstr "E483: Không nhận được tên tập tin tạm thời (temp)"
-#~ msgid "logoff"
-#~ msgstr "thoát"
-
-#~ msgid "shutdown"
-#~ msgstr "tắt máy"
-
-#~ msgid "E371: Command not found"
-#~ msgstr "E371: Câu lệnh không tìm thấy"
-
-#~ 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 ""
-#~ "Không tìm thấy VIMRUN.EXE trong $PATH.\n"
-#~ "Lệnh ngoại trú sẽ không dừng lại sau khi hoàn thành.\n"
-#~ "Thông tin chi tiết xem trong :help win32-vimrun"
-
-#~ msgid "Vim Warning"
-#~ msgstr "Cảnh báo Vim"
-
-#~ msgid "E56: %s* operand could be empty"
-#~ msgstr "E56: operand %s* không thể rỗng"
-
-#~ msgid "E57: %s+ operand could be empty"
-#~ msgstr "E57: operand %s+ không thể rỗng"
-
-#~ msgid "E58: %s{ operand could be empty"
-#~ msgstr "E58: operand %s{ không thể rỗng"
-
-#~ msgid "E361: Crash intercepted; regexp too complex?"
-#~ msgstr "E361: Sự cố được ngăn chặn; biểu thức chính quy quá phức tạp?"
-
-#~ msgid "E363: pattern caused out-of-stack error"
-#~ msgstr "E363: sử dụng mẫu (pattern) gây ra lỗi out-of-stack"
-
-#~ msgid "E396: containedin argument not accepted here"
-#~ msgstr "E396: không được sử dụng tham số containedin ở đây"
-
-#~ msgid "Enter nr of choice (<CR> to abort): "
-#~ msgstr "Hãy chọn số cần thiết (<CR> để dừng):"
-
-#~ msgid "E430: Tag file path truncated for %s\n"
-#~ msgstr "E430: Đường dẫn tới tập tin thẻ ghi bị cắt bớt cho %s\n"
-
-#~ msgid "new shell started\n"
-#~ msgstr "đã chạy shell mới\n"
-
-#~ msgid "No undo possible; continue anyway"
-#~ msgstr "Không thể hủy thao tác; tiếp tục thực hiện"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 16/32-bit GUI version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản với giao diện đồ họa GUI cho MS-Windows 16/32 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 32-bit GUI version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản với giao diện đồ họa GUI cho MS-Windows 32 bit"
-
-#~ msgid " in Win32s mode"
-#~ msgstr " trong chế độ Win32"
-
-#~ msgid " with OLE support"
-#~ msgstr " với hỗ trợ OLE"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 32-bit console version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản console cho MS-Windows 32 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 16-bit version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MS-Windows 16 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "32-bit MS-DOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MS-DOS 32 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "16-bit MS-DOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MS-DOS 16 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "MacOS X (unix) version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MacOS X (unix)"
-
-#~ msgid ""
-#~ "\n"
-#~ "MacOS X version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MacOS X"
-
-#~ msgid ""
-#~ "\n"
-#~ "MacOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MacOS"
-
-#~ msgid ""
-#~ "\n"
-#~ "RISC OS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho RISC OS"
-
-#~ msgid ""
-#~ "\n"
-#~ "Big version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản lớn "
-
-#~ msgid ""
-#~ "\n"
-#~ "Normal version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản thông thường "
-
-#~ msgid ""
-#~ "\n"
-#~ "Small version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản nhỏ "
-
-#~ msgid ""
-#~ "\n"
-#~ "Tiny version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản \"tí hon\" "
-
-#~ msgid "with GTK2-GNOME GUI."
-#~ msgstr "với giao diện đồ họa GUI GTK2-GNOME."
-
-#~ msgid "with GTK-GNOME GUI."
-#~ msgstr "với giao diện đồ họa GUI GTK-GNOME."
-
-#~ msgid "with GTK2 GUI."
-#~ msgstr "với giao diện đồ họa GUI GTK2."
-
-#~ msgid "with GTK GUI."
-#~ msgstr "với giao diện đồ họa GUI GTK."
-
-#~ msgid "with X11-Motif GUI."
-#~ msgstr "với giao diện đồ họa GUI X11-Motif."
-
-#~ msgid "with X11-neXtaw GUI."
-#~ msgstr "với giao diện đồ họa GUI X11-neXtaw."
-
-#~ msgid "with X11-Athena GUI."
-#~ msgstr "với giao diện đồ họa GUI X11-Athena."
+#, c-format
+msgid "E484: Can't open file %s"
+msgstr "E484: Không mở được tập tin %s"
-#~ msgid "with BeOS GUI."
-#~ msgstr "với giao diện đồ họa GUI BeOS."
+#, c-format
+msgid "E485: Can't read file %s"
+msgstr "E485: Không đọc được tập tin %s"
-#~ msgid "with Photon GUI."
-#~ msgstr "với giao diện đồ họa GUI Photon."
+msgid "E37: No write since last change (add ! to override)"
+msgstr "E37: Thay đổi chưa được ghi nhớ (thêm ! để bỏ qua ghi nhớ)"
-#~ msgid "with GUI."
-#~ msgstr "với giao diện đồ họa GUI."
+msgid "E38: Null argument"
+msgstr "E38: Tham sô bằng 0"
-#~ msgid "with Carbon GUI."
-#~ msgstr "với giao diện đồ họa GUI Carbon."
+msgid "E39: Number expected"
+msgstr "E39: Yêu cầu một số"
-#~ msgid "with Cocoa GUI."
-#~ msgstr "với giao diện đồ họa GUI Cocoa."
+#, c-format
+msgid "E40: Can't open errorfile %s"
+msgstr "E40: Không mở được tập tin lỗi %s"
-#~ msgid "with (classic) GUI."
-#~ msgstr "với giao diện đồ họa (cổ điển) GUI."
+# TODO: Capitalise first word of message?
+msgid "E233: Cannot open display"
+msgstr "E233: không mở được màn hình"
-#~ msgid " system gvimrc file: \""
-#~ msgstr " tập tin gvimrc chung cho hệ thống: \""
+msgid "E41: Out of memory!"
+msgstr "E41: Không đủ bộ nhớ!"
-#~ msgid " user gvimrc file: \""
-#~ msgstr " tập tin gvimrc của người dùng: \""
+msgid "Pattern not found"
+msgstr "Không tìm thấy mẫu (pattern)"
-#~ msgid "2nd user gvimrc file: \""
-#~ msgstr " tập tin gvimrc thứ hai của người dùng: \""
+#, c-format
+msgid "E486: Pattern not found: %s"
+msgstr "E486: Không tìm thấy mẫu (pattern): %s"
-#~ msgid "3rd user gvimrc file: \""
-#~ msgstr " tập tin gvimrc thứ ba của người dùng: \""
+msgid "E487: Argument must be positive"
+msgstr "E487: Tham số phải là một số dương"
-#~ msgid " system menu file: \""
-#~ msgstr " tập tin trình đơn chung cho hệ thống: \""
+msgid "E459: Cannot go back to previous directory"
+msgstr "E459: Không quay lại được thư mục trước đó"
-#~ msgid "Compiler: "
-#~ msgstr "Trình biên dịch: "
+msgid "E42: No Errors"
+msgstr "E42: Không có lỗi"
-#~ msgid "menu Help->Orphans for information "
-#~ msgstr "trình đơn Trợ giúp->Mồ côi để có thêm thông tin "
+msgid "E43: Damaged match string"
+msgstr "E43: Chuỗi tương ứng bị hỏng"
-#~ msgid "Running modeless, typed text is inserted"
-#~ msgstr "Không chế độ, văn bản nhập vào sẽ được chèn"
+msgid "E44: Corrupted regexp program"
+msgstr "E44: Chương trình xử lý biểu thức chính quy bị hỏng"
-#~ msgid "menu Edit->Global Settings->Toggle Insert Mode "
-#~ msgstr ""
-#~ "trình đơn Soạn thảo->Thiết lập chung->Chế độ chèn "
+msgid "E45: 'readonly' option is set (add ! to override)"
+msgstr "E45: Tùy chọn 'readonly' được bật (Hãy thêm ! để lờ đi)"
-#~ msgid " for two modes "
-#~ msgstr " cho hai chế độ "
+#, c-format
+msgid "E46: Cannot set read-only variable \"%s\""
+msgstr "E46: Không thay đổi được biến chỉ đọc \"%s\""
-#~ msgid "menu Edit->Global Settings->Toggle Vi Compatible"
-#~ msgstr ""
-#~ "trình đơn Soạn thảo->Thiết lập chung->Tương thích với Vi "
+msgid "E47: Error while reading errorfile"
+msgstr "E47: Lỗi khi đọc tập tin lỗi"
-#~ msgid " for Vim defaults "
-#~ msgstr ""
-#~ " để chuyển vào chế độ Vim mặc định "
+msgid "E48: Not allowed in sandbox"
+msgstr "E48: Không cho phép trong hộp cát (sandbox)"
-#~ msgid "WARNING: Windows 95/98/ME detected"
-#~ msgstr "CẢNH BÁO: nhận ra Windows 95/98/ME"
+msgid "E523: Not allowed here"
+msgstr "E523: Không cho phép ở đây"
-#~ msgid "type :help windows95<Enter> for info on this"
-#~ msgstr "hãy gõ :help windows95<Enter> để biết thêm thông tin "
+msgid "E359: Screen mode setting not supported"
+msgstr "E359: Chế độ màn hình không được hỗ trợ"
-#~ msgid "E370: Could not load library %s"
-#~ msgstr "E370: Không nạp được thư viện %s"
+msgid "E49: Invalid scroll size"
+msgstr "E49: Kích thước thanh cuộn không cho phép"
-#~ msgid ""
-#~ "Sorry, this command is disabled: the Perl library could not be loaded."
-#~ msgstr "Xin lỗi, câu lệnh này bị tắt: không nạp được thư viện Perl."
+msgid "E91: 'shell' option is empty"
+msgstr "E91: Tùy chọn 'shell' là một chuỗi rỗng"
-#~ msgid "E299: Perl evaluation forbidden in sandbox without the Safe module"
-#~ msgstr ""
-#~ "E299: Không cho phép sự tính toán Perl trong hộp cát mà không có môđun An "
-#~ "toàn"
+msgid "E255: Couldn't read in sign data!"
+msgstr "E255: Không đọc được dữ liệu về ký tự!"
-#~ msgid "Edit with &multiple Vims"
-#~ msgstr "Soạn thảo trong nhiều Vi&m"
+msgid "E72: Close error on swap file"
+msgstr "E72: Lỗi đóng tập tin trao đổi (swap)"
-#~ msgid "Edit with single &Vim"
-#~ msgstr "Soạn thảo trong một &Vim"
+# TODO: Capitalise first word of message?
+msgid "E73: Tag stack empty"
+msgstr "E73: đống thẻ ghi rỗng"
-#~ msgid "&Diff with Vim"
-#~ msgstr "&So sánh (diff) qua Vim"
+msgid "E74: Command too complex"
+msgstr "E74: Câu lệnh quá phức tạp"
-#~ msgid "Edit with &Vim"
-#~ msgstr "Soạn thảo trong &Vim"
+msgid "E75: Name too long"
+msgstr "E75: Tên quá dài"
-#~ msgid "Edit with existing Vim - &"
-#~ msgstr "Soạn thảo trong Vim đã chạy - &"
+msgid "E76: Too many ["
+msgstr "E76: Quá nhiều ký tự ["
-#~ msgid "Edits the selected file(s) with Vim"
-#~ msgstr "Soạn thảo (các) tập tin đã chọn trong Vim"
+msgid "E77: Too many file names"
+msgstr "E77: Quá nhiều tên tập tin"
-#~ msgid "Error creating process: Check if gvim is in your path!"
-#~ msgstr "Lỗi tạo tiến trình: Hãy kiểm tra xem gvim có trong đường dẫn không!"
+msgid "E488: Trailing characters"
+msgstr "E488: Ký tự thừa ở đuôi"
-#~ msgid "gvimext.dll error"
-#~ msgstr "lỗi gvimext.dll"
+msgid "E78: Unknown mark"
+msgstr "E78: Dấu hiệu không biết"
-#~ msgid "Path length too long!"
-#~ msgstr "Đường dẫn quá dài!"
+msgid "E79: Cannot expand wildcards"
+msgstr "E79: Không thực hiện được phép thế theo wildcard"
-#~ msgid "E234: Unknown fontset: %s"
-#~ msgstr "E234: Không rõ bộ phông chữ: %s"
+msgid "E591: 'winheight' cannot be smaller than 'winminheight'"
+msgstr "E591: giá trị của 'winheight' không thể nhỏ hơn 'winminheight'"
-#~ msgid "E235: Unknown font: %s"
-#~ msgstr "E235: Không rõ phông chữ: %s"
+msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'"
+msgstr "E592: giá trị của 'winwidth' không thể nhỏ hơn 'winminwidth'"
-#~ msgid "E448: Could not load library function %s"
-#~ msgstr "E448: Nạp hàm số %s của thư viện không thành công"
+msgid "E80: Error while writing"
+msgstr "E80: Lỗi khi ghi nhớ"
-#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
-#~ msgstr "E26: Tiếng Do thái không được chọn khi biên dịch\n"
+msgid "Zero count"
+msgstr "Giá trị của bộ đếm bằng 0"
-#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
-#~ msgstr "E27: Tiếng Farsi không được chọn khi biên dịch\n"
+msgid "E81: Using <SID> not in a script context"
+msgstr "E81: Sử dụng <SID> ngoài phạm vi script"
-#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
-#~ msgstr "E800: Tiếng Ả Rập không được chọn khi biên dịch\n"
+msgid "E449: Invalid expression received"
+msgstr "E449: Nhận được một biểu thức không cho phép"
-#~ msgid "E247: no registered server named \"%s\""
-#~ msgstr "E247: máy chủ \"%s\" chưa đăng ký"
+msgid "E463: Region is guarded, cannot modify"
+msgstr "E463: Không thể thay đổi vùng đã được bảo vệ"
-#~ msgid "E233: cannot open display"
-#~ msgstr "E233: không mở được màn hình"
+msgid "No tutorial with that name found"
+msgstr "Không tìm thấy hướng dẫn (tutorial) có tên đó"
-#~ msgid "E463: Region is guarded, cannot modify"
-#~ msgstr "E463: Không thể thay đổi vùng đã được bảo vệ"
+msgid "Only one argument accepted (check spaces)"
+msgstr "Chỉ chấp nhận một tham số (vui lòng kiểm tra dấu cách)"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index e30fc47806..3593175572 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -1991,8 +1991,8 @@ msgstr "E146: 正则表达式不能用字母作分界"
#. 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)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "替换为 %s?(y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4462
msgid "(Interrupted) "
diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po
index cbbc4ad6eb..04e11a9193 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -1267,8 +1267,8 @@ msgstr "E146: Regular expression 無法用字母分隔 (?)"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "取代為 %s (y/n/a/q/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "取代為 %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 7df6a1a5d7..d1c6f647fd 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -4,22 +4,19 @@
#include <assert.h>
#include <stdbool.h>
+#include <stdint.h>
#include <string.h>
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii_defs.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/cmdexpand.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
@@ -27,6 +24,7 @@
#include "nvim/extmark.h"
#include "nvim/extmark_defs.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
@@ -35,6 +33,7 @@
#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
+#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -46,7 +45,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
@@ -234,12 +232,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
// Figure out the size and position of the pum.
- if (size < PUM_DEF_HEIGHT) {
- pum_height = size;
- } else {
- pum_height = PUM_DEF_HEIGHT;
- }
-
+ pum_height = MIN(size, PUM_DEF_HEIGHT);
if (p_ph > 0 && pum_height > p_ph) {
pum_height = (int)p_ph;
}
@@ -256,11 +249,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
context_lines = 0;
} else {
// Leave two lines of context if possible
- if (curwin->w_wrow - curwin->w_cline_row >= 2) {
- context_lines = 2;
- } else {
- context_lines = curwin->w_wrow - curwin->w_cline_row;
- }
+ context_lines = MIN(2, curwin->w_wrow - curwin->w_cline_row);
}
if (pum_win_row - min_row >= size + context_lines) {
@@ -285,20 +274,13 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
} else {
// Leave two lines of context if possible
validate_cheight(curwin);
- if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) {
- context_lines = 3;
- } else {
- context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow;
- }
+ int cline_visible_offset = curwin->w_cline_row +
+ curwin->w_cline_height - curwin->w_wrow;
+ context_lines = MIN(3, cline_visible_offset);
}
pum_row = pum_win_row + context_lines;
- if (size > below_row - pum_row) {
- pum_height = below_row - pum_row;
- } else {
- pum_height = size;
- }
-
+ pum_height = MIN(below_row - pum_row, size);
if (p_ph > 0 && pum_height > p_ph) {
pum_height = (int)p_ph;
}
@@ -353,15 +335,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_width = max_col - pum_col - pum_scrollbar;
}
- if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
- && pum_width > p_pw) {
- // the width is more than needed for the items, make it
- // narrower
- pum_width = max_width + pum_kind_width + pum_extra_width + 1;
-
- if (pum_width < p_pw) {
- pum_width = (int)p_pw;
- }
+ int content_width = max_width + pum_kind_width + pum_extra_width + 1;
+ if (pum_width > content_width && pum_width > p_pw) {
+ // Reduce width to fit item
+ pum_width = MAX(content_width, (int)p_pw);
} else if (((cursor_col - min_col > p_pw
|| cursor_col - min_col > max_width) && !pum_rl)
|| (pum_rl && (cursor_col < max_col - p_pw
@@ -373,13 +350,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_col = max_col - 1;
}
} else if (!pum_rl) {
- if (win_start_col > max_col - max_width - pum_scrollbar
- && max_width <= p_pw) {
+ int right_edge_col = max_col - max_width - pum_scrollbar;
+ if (win_start_col > right_edge_col && max_width <= p_pw) {
// use full width to end of the screen
- pum_col = max_col - max_width - pum_scrollbar;
- if (pum_col < min_col) {
- pum_col = min_col;
- }
+ pum_col = MAX(min_col, right_edge_col);
}
}
@@ -400,12 +374,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_width = max_col - pum_col - 1;
}
}
- } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
- && pum_width > p_pw) {
- pum_width = max_width + pum_kind_width + pum_extra_width + 1;
- if (pum_width < p_pw) {
- pum_width = (int)p_pw;
- }
+ } else if (pum_width > content_width && pum_width > p_pw) {
+ pum_width = MAX(content_width, (int)p_pw);
}
}
} else if (max_col - min_col < def_width) {
@@ -442,7 +412,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
/// Returns attributes for every cell, or NULL if all attributes are the same.
static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
{
- if ((hlf != HLF_PSI && hlf != HLF_PNI)
+ if (*text == NUL || (hlf != HLF_PSI && hlf != HLF_PNI)
|| (win_hl_attr(curwin, HLF_PMSI) == win_hl_attr(curwin, HLF_PSI)
&& win_hl_attr(curwin, HLF_PMNI) == win_hl_attr(curwin, HLF_PNI))) {
return NULL;
@@ -456,7 +426,7 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
int *attrs = xmalloc(sizeof(int) * (size_t)vim_strsize(text));
bool in_fuzzy = State == MODE_CMDLINE ? cmdline_compl_is_fuzzy()
- : (get_cot_flags() & COT_FUZZY) != 0;
+ : (get_cot_flags() & kOptCotFlagFuzzy) != 0;
size_t leader_len = strlen(leader);
garray_T *ga = NULL;
@@ -471,6 +441,7 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
const char *ptr = text;
int cell_idx = 0;
uint32_t char_pos = 0;
+ bool is_select = hlf == HLF_PSI;
while (*ptr != NUL) {
int new_attr = win_hl_attr(curwin, (int)hlf);
@@ -479,14 +450,14 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
// Handle fuzzy matching
for (int i = 0; i < ga->ga_len; i++) {
if (char_pos == ((uint32_t *)ga->ga_data)[i]) {
- new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI);
+ new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI);
new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
break;
}
}
} else if (matched_start && ptr < text + leader_len) {
- new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI);
+ new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI);
new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
}
@@ -552,6 +523,15 @@ static inline char *pum_get_item(int index, int type)
return NULL;
}
+static inline int pum_user_attr_combine(int idx, int type, int attr)
+{
+ int user_attr[] = {
+ pum_array[idx].pum_user_abbr_hlattr,
+ pum_array[idx].pum_user_kind_hlattr,
+ };
+ return user_attr[type] > 0 ? hl_combine_attr(attr, user_attr[type]) : attr;
+}
+
/// Redraw the popup menu, using "pum_first" and "pum_selected".
void pum_redraw(void)
{
@@ -615,19 +595,16 @@ void pum_redraw(void)
pum_row - row_off, pum_left_col, false, pum_grid.zindex);
}
+ int scroll_range = pum_size - pum_height;
// Never display more than we have
- if (pum_first > pum_size - pum_height) {
- pum_first = pum_size - pum_height;
- }
+ pum_first = MIN(pum_first, scroll_range);
if (pum_scrollbar) {
thumb_height = pum_height * pum_height / pum_size;
if (thumb_height == 0) {
thumb_height = 1;
}
- thumb_pos = (pum_first * (pum_height - thumb_height)
- + (pum_size - pum_height) / 2)
- / (pum_size - pum_height);
+ thumb_pos = (pum_first * (pum_height - thumb_height) + scroll_range / 2) / scroll_range;
}
for (int i = 0; i < pum_height; i++) {
@@ -665,13 +642,8 @@ void pum_redraw(void)
attr = win_hl_attr(curwin, (int)hlf);
attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr);
orig_attr = attr;
- int user_abbr_hlattr = pum_array[idx].pum_user_abbr_hlattr;
- int user_kind_hlattr = pum_array[idx].pum_user_kind_hlattr;
- if (item_type == CPT_ABBR && user_abbr_hlattr > 0) {
- attr = hl_combine_attr(attr, user_abbr_hlattr);
- }
- if (item_type == CPT_KIND && user_kind_hlattr > 0) {
- attr = hl_combine_attr(attr, user_kind_hlattr);
+ if (item_type < 2) { // try combine attr with user custom
+ attr = pum_user_attr_combine(idx, item_type, attr);
}
int width = 0;
char *s = NULL;
@@ -682,89 +654,87 @@ void pum_redraw(void)
s = p;
}
int w = ptr2cells(p);
+ if (*p != NUL && *p != TAB && totwidth + w <= pum_width) {
+ width += w;
+ continue;
+ }
- 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 *st;
- char saved = *p;
-
- if (saved != NUL) {
- *p = NUL;
- }
- st = transstr(s, true);
- if (saved != NUL) {
- *p = saved;
- }
+ // Display the text that fits or comes before a Tab.
+ // First convert it to printable characters.
+ char saved = *p;
- int *attrs = NULL;
- if (item_type == CPT_ABBR) {
- attrs = pum_compute_text_attrs(st, hlf, user_abbr_hlattr);
- }
+ if (saved != NUL) {
+ *p = NUL;
+ }
+ char *st = transstr(s, true);
+ if (saved != NUL) {
+ *p = saved;
+ }
- if (pum_rl) {
- char *rt = reverse_text(st);
- char *rt_start = rt;
- int cells = vim_strsize(rt);
-
- if (cells > pum_width) {
- do {
- cells -= utf_ptr2cells(rt);
- MB_PTR_ADV(rt);
- } while (cells > pum_width);
-
- if (cells < pum_width) {
- // Most left character requires 2-cells but only 1 cell
- // is available on screen. Put a '<' on the left of the
- // pum item
- *(--rt) = '<';
- cells++;
- }
- }
+ int *attrs = NULL;
+ if (item_type == CPT_ABBR) {
+ attrs = pum_compute_text_attrs(st, hlf,
+ pum_array[idx].pum_user_abbr_hlattr);
+ }
- if (attrs == NULL) {
- grid_line_puts(grid_col - cells + 1, rt, -1, attr);
- } else {
- pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs);
+ if (pum_rl) {
+ char *rt = reverse_text(st);
+ char *rt_start = rt;
+ int cells = vim_strsize(rt);
+
+ if (cells > pum_width) {
+ do {
+ cells -= utf_ptr2cells(rt);
+ MB_PTR_ADV(rt);
+ } while (cells > pum_width);
+
+ if (cells < pum_width) {
+ // Most left character requires 2-cells but only 1 cell is available on
+ // screen. Put a '<' on the left of the pum item.
+ *(--rt) = '<';
+ cells++;
}
+ }
- xfree(rt_start);
- xfree(st);
- grid_col -= width;
+ if (attrs == NULL) {
+ grid_line_puts(grid_col - cells + 1, rt, -1, attr);
} else {
- if (attrs == NULL) {
- grid_line_puts(grid_col, st, -1, attr);
- } else {
- pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs);
- }
-
- xfree(st);
- grid_col += width;
+ pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs);
}
- if (attrs != NULL) {
- XFREE_CLEAR(attrs);
+ xfree(rt_start);
+ xfree(st);
+ grid_col -= width;
+ } else {
+ if (attrs == NULL) {
+ grid_line_puts(grid_col, st, -1, attr);
+ } else {
+ pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs);
}
- if (*p != TAB) {
- break;
- }
+ xfree(st);
+ grid_col += width;
+ }
- // Display two spaces for a Tab.
- if (pum_rl) {
- grid_line_puts(grid_col - 1, " ", 2, attr);
- grid_col -= 2;
- } else {
- grid_line_puts(grid_col, " ", 2, attr);
- grid_col += 2;
- }
- totwidth += 2;
- // start text at next char
- s = NULL;
- width = 0;
+ if (attrs != NULL) {
+ XFREE_CLEAR(attrs);
+ }
+
+ if (*p != TAB) {
+ break;
+ }
+
+ // Display two spaces for a Tab.
+ if (pum_rl) {
+ grid_line_puts(grid_col - 1, " ", 2, attr);
+ grid_col -= 2;
} else {
- width += w;
+ grid_line_puts(grid_col, " ", 2, attr);
+ grid_col += 2;
}
+ totwidth += 2;
+ s = NULL; // start text at next char
+ width = 0;
}
}
@@ -817,36 +787,41 @@ void pum_redraw(void)
}
}
-/// set info text to preview buffer.
+/// Set the informational text in the preview buffer when the completion
+/// item does not include a dedicated preview or popup window.
+///
+/// @param[in] buf Buffer where the text will be set.
+/// @param[in] info Informational text to display in the preview buffer.
+/// @param[in] lnum Where to start the text. Incremented for each added line.
+/// @param[out] max_width Maximum width of the displayed text.
static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width)
{
- bcount_t inserted_bytes = 0;
- for (char *p = info; *p != NUL;) {
- int text_width = 0;
- char *e = vim_strchr(p, '\n');
- if (e == NULL) {
- ml_append_buf(buf, (*lnum)++, p, 0, false);
- text_width = (int)mb_string2cells(p);
- if (text_width > *max_width) {
- *max_width = text_width;
- }
- break;
- }
- *e = NUL;
- ml_append_buf(buf, (*lnum)++, p, (int)(e - p + 1), false);
- inserted_bytes += (bcount_t)strlen(p) + 1;
- text_width = (int)mb_string2cells(p);
- if (text_width > *max_width) {
- *max_width = text_width;
- }
- *e = '\n';
- p = e + 1;
+ Error err = ERROR_INIT;
+ Arena arena = ARENA_EMPTY;
+ Array replacement = ARRAY_DICT_INIT;
+ char *token = NULL;
+ char *line = os_strtok(info, "\n", &token);
+ buf->b_p_ma = true;
+ while (line != NULL) {
+ ADD(replacement, STRING_OBJ(cstr_to_string(line)));
+ (*lnum)++;
+ (*max_width) = MAX(*max_width, (int)mb_string2cells(line));
+ line = os_strtok(NULL, "\n", &token);
}
- // delete the empty last line
- ml_delete_buf(buf, buf->b_ml.ml_line_count, false);
- if (get_cot_flags() & COT_POPUP) {
- extmark_splice(buf, 1, 0, 1, 0, 0, buf->b_ml.ml_line_count, 0, inserted_bytes, kExtmarkNoUndo);
+
+ int original_textlock = textlock;
+ if (textlock > 0) {
+ textlock = 0;
}
+ nvim_buf_set_lines(0, buf->handle, 0, -1, false, replacement, &arena, &err);
+ textlock = original_textlock;
+ if (ERROR_SET(&err)) {
+ emsg(err.msg);
+ api_clear_error(&err);
+ }
+ arena_mem_free(arena_finish(&arena));
+ api_free_array(replacement);
+ buf->b_p_ma = false;
}
/// adjust floating info preview window position
@@ -880,7 +855,7 @@ static void pum_adjust_info_position(win_T *wp, int height, int width)
/// Used for nvim__complete_set
///
/// @param selected the selected compl item.
-/// @parma info Info string.
+/// @param info Info string.
/// @return a win_T pointer.
win_T *pum_set_info(int selected, char *info)
{
@@ -896,14 +871,6 @@ win_T *pum_set_info(int selected, char *info)
if (!wp) {
return NULL;
}
- } else {
- // clean exist buffer
- linenr_T count = wp->w_buffer->b_ml.ml_line_count;
- while (!buf_is_empty(wp->w_buffer)) {
- ml_delete_buf(wp->w_buffer, 1, false);
- }
- bcount_t deleted_bytes = get_region_bytecount(wp->w_buffer, 1, count, 0, 0);
- extmark_splice(wp->w_buffer, 1, 0, count, 0, deleted_bytes, 1, 0, 0, kExtmarkNoUndo);
}
linenr_T lnum = 0;
int max_info_width = 0;
@@ -939,13 +906,17 @@ static bool pum_set_selected(int n, int repeat)
int prev_selected = pum_selected;
pum_selected = n;
+ int scroll_offset = pum_selected - pum_height;
unsigned cur_cot_flags = get_cot_flags();
- bool use_float = (cur_cot_flags & COT_POPUP) != 0;
- // when new leader add and info window is shown and no selected we still
- // need use the first index item to update the info float window position.
- bool force_select = use_float && pum_selected < 0 && win_float_find_preview();
- if (force_select) {
- pum_selected = 0;
+ bool use_float = (cur_cot_flags & kOptCotFlagPopup) != 0;
+
+ // Close the floating preview window if 'selected' is -1, indicating a return to the original
+ // state. It is also closed when the selected item has no corresponding info item.
+ if (use_float && (pum_selected < 0 || pum_array[pum_selected].pum_info == NULL)) {
+ win_T *wp = win_float_find_preview();
+ if (wp) {
+ win_close(wp, true, true);
+ }
}
if ((pum_selected >= 0) && (pum_selected < pum_size)) {
@@ -962,41 +933,28 @@ static bool pum_set_selected(int n, int repeat)
} else {
pum_first = pum_selected;
}
- } else if (pum_first < pum_selected - pum_height + 5) {
+ } else if (pum_first < scroll_offset + 5) {
// scroll up; when we did a jump it's probably a PageDown then
// scroll a whole page
- if (pum_first < pum_selected - pum_height + 1 + 2) {
- pum_first += pum_height - 2;
- if (pum_first < pum_selected - pum_height + 1) {
- pum_first = pum_selected - pum_height + 1;
- }
+ if (pum_first < scroll_offset + 3) {
+ pum_first = MAX(pum_first + pum_height - 2, scroll_offset + 1);
} else {
- pum_first = pum_selected - pum_height + 1;
+ pum_first = scroll_offset + 1;
}
}
// Give a few lines of context when possible.
- if (context > 3) {
- context = 3;
- }
+ context = MIN(context, 3);
if (pum_height > 2) {
if (pum_first > pum_selected - context) {
- // scroll down
- pum_first = pum_selected - context;
-
- if (pum_first < 0) {
- pum_first = 0;
- }
+ pum_first = MAX(pum_selected - context, 0); // scroll down
} else if (pum_first < pum_selected + context - pum_height + 1) {
- // scroll up
- pum_first = pum_selected + context - pum_height + 1;
+ pum_first = pum_selected + context - pum_height + 1; // up
}
}
// adjust for the number of lines displayed
- if (pum_first > pum_size - pum_height) {
- pum_first = pum_size - pum_height;
- }
+ pum_first = MIN(pum_first, pum_size - pum_height);
// Show extra info in the preview window if there is something and
// 'completeopt' contains "preview".
@@ -1006,7 +964,7 @@ static bool pum_set_selected(int n, int repeat)
if ((pum_array[pum_selected].pum_info != NULL)
&& (Rows > 10)
&& (repeat <= 1)
- && (cur_cot_flags & COT_ANY_PREVIEW)) {
+ && (cur_cot_flags & (kOptCotFlagPreview | kOptCotFlagPopup))) {
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
@@ -1050,7 +1008,8 @@ static bool pum_set_selected(int n, int repeat)
&& (curbuf->b_nwindows == 1)
&& (curbuf->b_fname == NULL)
&& bt_nofile(curbuf)
- && (curbuf->b_p_bh[0] == 'w')) {
+ && (curbuf->b_p_bh[0] == 'w')
+ && !use_float) {
// Already a "wipeout" buffer, make it empty.
while (!buf_is_empty(curbuf)) {
ml_delete(1, false);
@@ -1079,9 +1038,7 @@ static bool pum_set_selected(int n, int repeat)
// Increase the height of the preview window to show the
// text, but no more than 'previewheight' lines.
if (repeat == 0 && !use_float) {
- if (lnum > p_pvh) {
- lnum = (linenr_T)p_pvh;
- }
+ lnum = MIN(lnum, (linenr_T)p_pvh);
if (curwin->w_height < lnum) {
win_setheight((int)lnum);
@@ -1164,11 +1121,6 @@ static bool pum_set_selected(int n, int repeat)
}
}
- // restore before selected value
- if (force_select) {
- pum_selected = n;
- }
-
return resized;
}
@@ -1349,9 +1301,7 @@ static void pum_position_at_mouse(int min_width)
pum_width = max_col - pum_col;
}
- if (pum_width > pum_base_width + 1) {
- pum_width = pum_base_width + 1;
- }
+ pum_width = MIN(pum_width, pum_base_width + 1);
}
/// Select the pum entry at the mouse position.
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index 1b4f4a2029..65d341cac3 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <uv.h>
#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 6526b0d0bf..44b66c4f73 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -38,7 +38,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
@@ -2697,7 +2696,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum)
// Didn't find it, go to the window before the quickfix
// window, unless 'switchbuf' contains 'uselast': in this case we
// try to jump to the previously used window first.
- if ((swb_flags & SWB_USELAST) && win_valid(prevwin)
+ if ((swb_flags & kOptSwbFlagUselast) && win_valid(prevwin)
&& !prevwin->w_p_wfb) {
win = prevwin;
} else if (altwin != NULL) {
@@ -2754,7 +2753,7 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, bool *opened_windo
// If no usable window is found and 'switchbuf' contains "usetab"
// then search in other tabs.
- if (!usable_win && (swb_flags & SWB_USETAB)) {
+ if (!usable_win && (swb_flags & kOptSwbFlagUsetab)) {
usable_win = qf_goto_tabwin_with_file(qf_fnum);
}
@@ -2937,7 +2936,7 @@ 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_hl_keep(gap->ga_data, 0, true, false);
+ msg_keep(gap->ga_data, 0, true, false);
msg_scroll = (int)i;
qfga_clear();
@@ -3032,7 +3031,7 @@ static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int
qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol, qf_ptr->qf_pattern);
- if ((fdo_flags & FDO_QUICKFIX) && openfold) {
+ if ((fdo_flags & kOptFdoFlagQuickfix) && openfold) {
foldOpenCursor();
}
if (print_message) {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index c91c112c3c..de9a7e580f 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -12,6 +12,7 @@
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <uv.h>
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 3f00b74e61..cdedf86977 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -43,6 +43,7 @@
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
@@ -159,10 +160,7 @@ char *estack_sfile(estack_arg_T which)
{
const estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC) {
- if (entry->es_name == NULL) {
- return NULL;
- }
- return xstrdup(entry->es_name);
+ return entry->es_name != NULL ? xstrdup(entry->es_name) : NULL;
}
// If evaluated in a function or autocommand, return the path of the script
@@ -230,6 +228,72 @@ char *estack_sfile(estack_arg_T which)
return (char *)ga.ga_data;
}
+static void stacktrace_push_item(list_T *const l, ufunc_T *const fp, const char *const event,
+ const linenr_T lnum, char *const filepath,
+ const bool filepath_alloced)
+{
+ dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
+ typval_T tv = {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_LOCKED,
+ .vval.v_dict = d,
+ };
+
+ if (fp != NULL) {
+ tv_dict_add_func(d, S_LEN("funcref"), fp);
+ }
+ if (event != NULL) {
+ tv_dict_add_str(d, S_LEN("event"), event);
+ }
+ tv_dict_add_nr(d, S_LEN("lnum"), lnum);
+ if (filepath_alloced) {
+ tv_dict_add_allocated_str(d, S_LEN("filepath"), filepath);
+ } else {
+ tv_dict_add_str(d, S_LEN("filepath"), filepath);
+ }
+
+ tv_list_append_tv(l, &tv);
+}
+
+/// Create the stacktrace from exestack.
+list_T *stacktrace_create(void)
+{
+ list_T *const l = tv_list_alloc(exestack.ga_len);
+
+ for (int i = 0; i < exestack.ga_len; i++) {
+ estack_T *const entry = &((estack_T *)exestack.ga_data)[i];
+ linenr_T lnum = entry->es_lnum;
+
+ if (entry->es_type == ETYPE_SCRIPT) {
+ stacktrace_push_item(l, NULL, NULL, lnum, entry->es_name, false);
+ } else if (entry->es_type == ETYPE_UFUNC) {
+ ufunc_T *const fp = entry->es_info.ufunc;
+ const sctx_T sctx = fp->uf_script_ctx;
+ bool filepath_alloced = false;
+ char *filepath = sctx.sc_sid > 0
+ ? get_scriptname((LastSet){ .script_ctx = sctx },
+ &filepath_alloced) : "";
+ lnum += sctx.sc_lnum;
+ stacktrace_push_item(l, fp, NULL, lnum, filepath, filepath_alloced);
+ } else if (entry->es_type == ETYPE_AUCMD) {
+ const sctx_T sctx = entry->es_info.aucmd->script_ctx;
+ bool filepath_alloced = false;
+ char *filepath = sctx.sc_sid > 0
+ ? get_scriptname((LastSet){ .script_ctx = sctx },
+ &filepath_alloced) : "";
+ lnum += sctx.sc_lnum;
+ stacktrace_push_item(l, NULL, entry->es_name, lnum, filepath, filepath_alloced);
+ }
+ }
+ return l;
+}
+
+/// getstacktrace() function
+void f_getstacktrace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_set_ret(rettv, stacktrace_create());
+}
+
static bool runtime_search_path_valid = false;
static int *runtime_search_path_ref = NULL;
static RuntimeSearchPath runtime_search_path;
@@ -499,7 +563,6 @@ static void runtime_search_path_unref(RuntimeSearchPath path, const int *ref)
/// return FAIL when no file could be sourced, OK otherwise.
static int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char *tail;
bool did_one = false;
char buf[MAXPATHL];
@@ -533,7 +596,7 @@ static int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback,
} else if (buflen + strlen(name) + 2 < MAXPATHL) {
STRCPY(buf, item.path);
add_pathsep(buf);
- tail = buf + strlen(buf);
+ char *tail = buf + strlen(buf);
// Loop over all patterns in "name"
char *np = name;
@@ -935,7 +998,6 @@ static int gen_expand_wildcards_and_cb(int num_pat, char **pats, int flags, bool
/// @param is_pack whether the added dir is a "pack/*/start/*/" style package
static int add_pack_dir_to_rtp(char *fname, bool is_pack)
{
- char *p;
char *afterdir = NULL;
int retval = FAIL;
@@ -943,7 +1005,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
char *p2 = p1;
char *p3 = p1;
char *p4 = p1;
- for (p = p1; *p; MB_PTR_ADV(p)) {
+ for (char *p = p1; *p; MB_PTR_ADV(p)) {
if (vim_ispathsep_nocolon(*p)) {
p4 = p3;
p3 = p2;
@@ -970,21 +1032,21 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
// Find "ffname" in "p_rtp", ignoring '/' vs '\' differences
// Also stop at the first "after" directory
size_t fname_len = strlen(ffname);
- char *buf = try_malloc(MAXPATHL);
- if (buf == NULL) {
- goto theend;
- }
+ char buf[MAXPATHL];
const char *insp = NULL;
const char *after_insp = NULL;
- for (const char *entry = p_rtp; *entry != NUL;) {
+ const char *entry = p_rtp;
+ while (*entry != NUL) {
const char *cur_entry = entry;
-
copy_option_part((char **)&entry, buf, MAXPATHL, ",");
- if ((p = strstr(buf, "after")) != NULL
- && p > buf
- && vim_ispathsep(p[-1])
- && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) {
+ char *p = strstr(buf, "after");
+ bool is_after = p != NULL
+ && p > buf
+ && vim_ispathsep(p[-1])
+ && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',');
+
+ if (is_after) {
if (insp == NULL) {
// Did not find "ffname" before the first "after" directory,
// insert it before this entry.
@@ -1000,12 +1062,11 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
if (rtp_ffname == NULL) {
goto theend;
}
- bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
- xfree(rtp_ffname);
- if (match) {
+ if (path_fnamencmp(rtp_ffname, ffname, fname_len) == 0) {
// Insert "ffname" after this entry (and comma).
insp = entry;
}
+ xfree(rtp_ffname);
}
}
@@ -1075,7 +1136,6 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
retval = OK;
theend:
- xfree(buf);
xfree(ffname);
xfree(afterdir);
return retval;
@@ -1123,7 +1183,7 @@ static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all,
bool did_one = false;
if (cookie != &APP_LOAD) {
- char *buf = xmalloc(MAXPATHL);
+ char buf[MAXPATHL];
for (int i = 0; i < num_fnames; i++) {
bool found = false;
@@ -1138,7 +1198,6 @@ static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all,
if (!found) {
// directory is not yet in 'runtimepath', add it
if (add_pack_dir_to_rtp(fnames[i], false) == FAIL) {
- xfree(buf);
return;
}
}
@@ -1147,7 +1206,6 @@ static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all,
break;
}
}
- xfree(buf);
}
if (!all && did_one) {
@@ -1276,25 +1334,23 @@ void ex_packadd(exarg_T *eap)
static const char plugpat[] = "pack/*/%s/%s"; // NOLINT
int res = OK;
- // Round 1: use "start", round 2: use "opt".
- for (int round = 1; round <= 2; round++) {
- // Only look under "start" when loading packages wasn't done yet.
- if (round == 1 && did_source_packages) {
- continue;
- }
+ const size_t len = sizeof(plugpat) + strlen(eap->arg) + 5;
+ char *pat = xmallocz(len);
+ void *cookie = eap->forceit ? &APP_ADD_DIR : &APP_BOTH;
- const size_t len = sizeof(plugpat) + strlen(eap->arg) + 5;
- char *pat = xmallocz(len);
- vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
- // The first round don't give a "not found" error, in the second round
- // only when nothing was found in the first round.
- res =
- do_in_path(p_pp, "", pat,
- DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
- round == 1 ? add_start_pack_plugins : add_opt_pack_plugins,
- eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
- xfree(pat);
+ // Only look under "start" when loading packages wasn't done yet.
+ if (!did_source_packages) {
+ vim_snprintf(pat, len, plugpat, "start", eap->arg);
+ res = do_in_path(p_pp, "", pat, DIP_ALL + DIP_DIR,
+ add_start_pack_plugins, cookie);
}
+
+ // Give a "not found" error if nothing was found in 'start' or 'opt'.
+ vim_snprintf(pat, len, plugpat, "opt", eap->arg);
+ do_in_path(p_pp, "", pat, DIP_ALL + DIP_DIR + (res == FAIL ? DIP_ERR : 0),
+ add_opt_pack_plugins, cookie);
+
+ xfree(pat);
}
static void ExpandRTDir_int(char *pat, size_t pat_len, int flags, bool keep_ext, garray_T *gap,
@@ -2726,22 +2782,15 @@ retry:
/// Without the multi-byte feature it's simply ignored.
void ex_scriptencoding(exarg_T *eap)
{
- source_cookie_T *sp;
- char *name;
-
if (!getline_equal(eap->ea_getline, eap->cookie, getsourceline)) {
emsg(_("E167: :scriptencoding used outside of a sourced file"));
return;
}
- if (*eap->arg != NUL) {
- name = enc_canonize(eap->arg);
- } else {
- name = eap->arg;
- }
+ char *name = (*eap->arg != NUL) ? enc_canonize(eap->arg) : eap->arg;
// Setup for conversion from the specified encoding to 'encoding'.
- sp = (source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie);
+ source_cookie_T *sp = (source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie);
convert_setup(&sp->conv, name, p_enc);
if (name != eap->arg) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index debc5697d1..9f8ceae2a0 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -30,10 +30,10 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
@@ -41,7 +41,6 @@
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
-#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -1203,6 +1202,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen
// Compute msg_row early.
msg_start();
+ msg_ext_set_kind("search_cmd");
// Get the offset, so we know how long it is.
if (!cmd_silent
@@ -1422,7 +1422,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen
cmdline_search_stat(dirc, &pos, &curwin->w_cursor,
show_top_bot_msg, msgbuf, msgbuflen,
(count != 1 || has_offset
- || (!(fdo_flags & FDO_SEARCH)
+ || (!(fdo_flags & kOptFdoFlagSearch)
&& hasFolding(curwin, curwin->w_cursor.lnum, NULL,
NULL))),
SEARCH_STAT_DEF_MAX_COUNT,
@@ -2349,7 +2349,7 @@ void showmatch(int c)
}
if ((lpos = findmatch(NULL, NUL)) == NULL) { // no match, so beep
- vim_beep(BO_MATCH);
+ vim_beep(kOptBoFlagShowmatch);
return;
}
@@ -2534,7 +2534,7 @@ int current_search(int count, bool forward)
}
}
- if (fdo_flags & FDO_SEARCH && KeyTyped) {
+ if (fdo_flags & kOptFdoFlagSearch && KeyTyped) {
foldOpenCursor();
}
@@ -2973,6 +2973,10 @@ typedef struct {
#define CAMEL_BONUS 30
/// bonus if the first letter is matched
#define FIRST_LETTER_BONUS 15
+/// bonus if exact match
+#define EXACT_MATCH_BONUS 100
+/// bonus if case match when no ignorecase
+#define CASE_MATCH_BONUS 25
/// penalty applied for every letter in str before the first match
#define LEADING_LETTER_PENALTY (-5)
/// maximum penalty for leading letters
@@ -2988,11 +2992,21 @@ 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 *const str, const int strSz,
- const uint32_t *const matches, const int numMatches)
+static int fuzzy_match_compute_score(const char *const fuzpat, 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
{
assert(numMatches > 0); // suppress clang "result of operation is garbage"
+ const char *p = str;
+ uint32_t sidx = 0;
+ bool is_exact_match = true;
+ const char *const orig_fuzpat = fuzpat - numMatches;
+ const char *curpat = orig_fuzpat;
+ int pat_idx = 0;
+ // Track consecutive camel case matches
+ int consecutive_camel = 0;
+
// Initialize score
int score = 100;
@@ -3010,6 +3024,7 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
// Apply ordering bonuses
for (int i = 0; i < numMatches; i++) {
const uint32_t currIdx = matches[i];
+ bool is_camel = false;
if (i > 0) {
const uint32_t prevIdx = matches[i - 1];
@@ -3019,23 +3034,35 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
score += SEQUENTIAL_BONUS;
} else {
score += GAP_PENALTY * (int)(currIdx - prevIdx);
+ // Reset consecutive camel count on gap
+ consecutive_camel = 0;
}
}
+ int curr;
// Check for bonuses based on neighbor character value
if (currIdx > 0) {
// Camel case
- const char *p = str;
int neighbor = ' ';
- for (uint32_t sidx = 0; sidx < currIdx; sidx++) {
+ while (sidx < currIdx) {
neighbor = utf_ptr2char(p);
MB_PTR_ADV(p);
+ sidx++;
}
- const int curr = utf_ptr2char(p);
+ curr = utf_ptr2char(p);
+ // Enhanced camel case scoring
if (mb_islower(neighbor) && mb_isupper(curr)) {
- score += CAMEL_BONUS;
+ score += CAMEL_BONUS * 2; // Double the camel case bonus
+ is_camel = true;
+ consecutive_camel++;
+ // Additional bonus for consecutive camel
+ if (consecutive_camel > 1) {
+ score += CAMEL_BONUS;
+ }
+ } else {
+ consecutive_camel = 0;
}
// Bonus if the match follows a separator character
@@ -3047,8 +3074,36 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
} else {
// First letter
score += FIRST_LETTER_BONUS;
+ curr = utf_ptr2char(p);
+ }
+
+ // Case matching bonus
+ if (mb_isalpha(curr)) {
+ while (pat_idx < i && *curpat) {
+ MB_PTR_ADV(curpat);
+ pat_idx++;
+ }
+
+ if (curr == utf_ptr2char(curpat)) {
+ score += CASE_MATCH_BONUS;
+ // Extra bonus for exact case match in camel
+ if (is_camel) {
+ score += CASE_MATCH_BONUS / 2;
+ }
+ }
+ }
+
+ // Check exact match condition
+ if (currIdx != (uint32_t)i) {
+ is_exact_match = false;
}
}
+
+ // Boost score for exact matches
+ if (is_exact_match && numMatches == strSz) {
+ score += EXACT_MATCH_BONUS;
+ }
+
return score;
}
@@ -3127,7 +3182,7 @@ static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t s
// Calculate score
if (matched) {
- *outScore = fuzzy_match_compute_score(strBegin, strLen, matches, nextMatch);
+ *outScore = fuzzy_match_compute_score(fuzpat, strBegin, strLen, matches, nextMatch);
}
// Return best result
diff --git a/src/nvim/search.h b/src/nvim/search.h
index 1b6c1a6375..d92f3c9002 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -1,9 +1,10 @@
#pragma once
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/normal_defs.h" // IWYU pragma: keep
#include "nvim/os/time_defs.h"
#include "nvim/pos_defs.h"
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 2961b35a29..a5e98af7ac 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2,21 +2,22 @@
#include <inttypes.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/kvec.h"
+#include "mpack/mpack_core.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/cmdhist.h"
-#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
@@ -26,8 +27,6 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
-#include "nvim/garray.h"
-#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
@@ -38,8 +37,10 @@
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/normal_defs.h"
#include "nvim/ops.h"
diff --git a/src/nvim/shada.h b/src/nvim/shada.h
index 7d736dadc7..558e020180 100644
--- a/src/nvim/shada.h
+++ b/src/nvim/shada.h
@@ -1,6 +1,6 @@
#pragma once
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
/// Flags for shada_read_file and children
typedef enum {
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index f8e7eeaca4..a6e72b8855 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -32,8 +32,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
@@ -43,7 +41,6 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/move.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
#include "nvim/sign_defs.h"
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index a0fdd46b04..7437a7a32f 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -245,7 +245,7 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount
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_flags & SPO_CAMEL) != 0;
+ bool use_camel_case = (wp->w_s->b_p_spo_flags & kOptSpoFlagCamel) != 0;
bool is_camel_case = false;
matchinf_T mi; // Most things are put in "mi" so that it can be passed to functions quickly.
@@ -1407,7 +1407,7 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a
: p - buf) > wp->w_cursor.col)) {
colnr_T col = (colnr_T)(p - buf);
- bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
+ bool no_plain_buffer = (wp->w_s->b_p_spo_flags & kOptSpoFlagNoplainbuffer) != 0;
bool can_spell = !no_plain_buffer;
switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col)) {
case kTrue:
@@ -3141,7 +3141,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res)
c = *ws;
}
if (strstr(s, "^^") != NULL) {
- if (c != NUL) {
+ if (c != NUL && reslen < MAXWLEN) {
wres[reslen++] = c;
}
memmove(word, word + i + 1, sizeof(int) * (size_t)(wordlen - (i + 1) + 1));
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index b37f01e769..21bfa367bf 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -402,7 +402,7 @@ int spell_check_sps(void)
if (*s != NUL && !ascii_isdigit(*s)) {
f = -1;
}
- // Note: Keep this in sync with p_sps_values.
+ // Note: Keep this in sync with opt_sps_values.
} else if (strcmp(buf, "best") == 0) {
f = SPS_BEST;
} else if (strcmp(buf, "fast") == 0) {
@@ -444,7 +444,7 @@ void spell_suggest(int count)
char wcopy[MAXWLEN + 2];
suginfo_T sug;
suggest_T *stp;
- bool mouse_used;
+ bool mouse_used = false;
int selected = count;
int badlen = 0;
int msg_scroll_save = msg_scroll;
@@ -464,7 +464,7 @@ void spell_suggest(int count)
// Use the Visually selected text as the bad word. But reject
// a multi-line selection.
if (curwin->w_cursor.lnum != VIsual.lnum) {
- vim_beep(BO_SPELL);
+ vim_beep(kOptBoFlagSpell);
return;
}
badlen = (int)curwin->w_cursor.col - (int)VIsual.col;
@@ -516,6 +516,7 @@ void spell_suggest(int count)
spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
true, need_cap, true);
+ msg_ext_set_kind("list_cmd");
if (GA_EMPTY(&sug.su_ga)) {
msg(_("Sorry, no suggestions"), 0);
} else if (count > 0) {
@@ -593,15 +594,11 @@ void spell_suggest(int count)
cmdmsg_rl = false;
msg_col = 0;
// Ask for choice.
- selected = prompt_for_number(&mouse_used);
-
- if (ui_has(kUIMessages)) {
- ui_call_msg_clear();
- }
-
+ selected = prompt_for_input(NULL, 0, false, &mouse_used);
if (mouse_used) {
- selected -= lines_left;
+ selected = sug.su_ga.ga_len + 1 - (cmdline_row - mouse_row);
}
+
lines_left = Rows; // avoid more prompt
// don't delay for 'smd' in normal_cmd()
msg_scroll = msg_scroll_save;
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 908f724792..c4041dda07 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -138,17 +138,23 @@ void state_handle_k_event(void)
/// Return true if in the current mode we need to use virtual.
bool virtual_active(win_T *wp)
{
- unsigned cur_ve_flags = get_ve_flags(wp);
-
// While an operator is being executed we return "virtual_op", because
// VIsual_active has already been reset, thus we can't check for "block"
// being used.
if (virtual_op != kNone) {
return virtual_op;
}
- return cur_ve_flags == VE_ALL
- || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
- || ((cur_ve_flags & VE_INSERT) && (State & MODE_INSERT));
+
+ // In Terminal mode the cursor can be positioned anywhere by the application
+ if (State & MODE_TERMINAL) {
+ return true;
+ }
+
+ unsigned cur_ve_flags = get_ve_flags(wp);
+
+ return cur_ve_flags == kOptVeFlagAll
+ || ((cur_ve_flags & kOptVeFlagBlock) && VIsual_active && VIsual_mode == Ctrl_V)
+ || ((cur_ve_flags & kOptVeFlagInsert) && (State & MODE_INSERT));
}
/// MODE_VISUAL, MODE_SELECT and MODE_OP_PENDING State are never set, they are
diff --git a/src/nvim/state.h b/src/nvim/state.h
index 8220d90a67..2cc493de46 100644
--- a/src/nvim/state.h
+++ b/src/nvim/state.h
@@ -1,7 +1,7 @@
#pragma once
#include "nvim/state_defs.h" // IWYU pragma: keep
-#include "nvim/types_defs.h"
+#include "nvim/types_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "state.h.generated.h"
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 4e78067d46..ddae023ad5 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -35,7 +35,6 @@
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/path.h"
@@ -97,53 +96,52 @@ void win_redr_status(win_T *wp)
get_trans_bufname(wp->w_buffer);
char *p = NameBuff;
- int len = (int)strlen(p);
+ int plen = (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++) = ' ';
+ && plen < MAXPATHL - 1) {
+ *(p + plen++) = ' '; // replace NUL with space
+ *(p + plen) = NUL; // NUL terminate the string
}
if (bt_help(wp->w_buffer)) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[Help]"));
}
if (wp->w_p_pvw) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[Preview]"));
}
if (bufIsChanged(wp->w_buffer)) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", "[+]");
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", "[+]");
}
if (wp->w_buffer->b_p_ro) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
- // len += (int)strlen(p + len); // dead assignment
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[RO]"));
}
+ (void)plen;
- int this_ru_col = MAX(ru_col - (Columns - stl_width), (stl_width + 1) / 2);
+ int n = (stl_width + 1) / 2;
+ int this_ru_col = ru_col - (Columns - stl_width);
+ this_ru_col = MAX(this_ru_col, n);
if (this_ru_col <= 1) {
p = "<"; // No room for file name!
- len = 1;
+ plen = 1;
} else {
int i;
// Count total number of display cells.
- int clen = (int)mb_string2cells(p);
+ plen = (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;
+ for (i = 0; p[i] != NUL && plen >= this_ru_col - 1;
i += utfc_ptr2len(p + i)) {
- clen -= utf_ptr2cells(p + i);
+ plen -= utf_ptr2cells(p + i);
}
- len = clen;
if (i > 0) {
p = p + i - 1;
*p = '<';
- len++;
+ plen++;
}
}
@@ -153,16 +151,17 @@ void win_redr_status(win_T *wp)
int width = grid_line_puts(off, p, -1, attr);
grid_line_fill(off + width, off + this_ru_col, fillchar, attr);
- if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL)
- && this_ru_col - len > (int)strlen(NameBuff) + 1) {
- grid_line_puts(off + this_ru_col - (int)strlen(NameBuff) - 1, NameBuff, -1, attr);
+ int NameBufflen = get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL);
+ if (NameBufflen > 0 && this_ru_col - plen > NameBufflen + 1) {
+ grid_line_puts(off + this_ru_col - NameBufflen - 1, NameBuff, -1, attr);
}
win_redr_ruler(wp);
// Draw the 'showcmd' information if 'showcmdloc' == "statusline".
if (p_sc && *p_sloc == 's') {
- const int sc_width = MIN(10, this_ru_col - len - 2);
+ n = this_ru_col - plen - 2; // perform the calculation here so we only do it once
+ const int sc_width = MIN(10, n);
if (sc_width > 0) {
grid_line_puts(off + this_ru_col - sc_width - 1, showcmd_buf, sc_width, attr);
@@ -549,36 +548,40 @@ void win_redr_ruler(win_T *wp)
#define RULER_BUF_LEN 70
char buffer[RULER_BUF_LEN];
- // Some sprintfs return the length, some return a pointer.
- // To avoid portability problems we use strlen() here.
- vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
- (wp->w_buffer->b_ml.ml_flags &
- ML_EMPTY) ? 0 : (int64_t)wp->w_cursor.lnum);
- 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);
+ int bufferlen = vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
+ (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
+ ? 0
+ : (int64_t)wp->w_cursor.lnum);
+ bufferlen += col_print(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen,
+ empty_line ? 0 : (int)wp->w_cursor.col + 1,
+ (int)virtcol + 1);
// 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);
- get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
- int o = i + vim_strsize(buffer + i + 1);
+ char rel_pos[RULER_BUF_LEN];
+ int rel_poslen = get_rel_pos(wp, rel_pos, RULER_BUF_LEN);
+ int n1 = bufferlen + vim_strsize(rel_pos);
if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
- o++;
+ n1++;
}
+
+ int this_ru_col = ru_col - (Columns - width);
// Never use more than half the window/screen width, leave the other half
// for the filename.
- int this_ru_col = MAX(ru_col - (Columns - width), (width + 1) / 2);
- if (this_ru_col + o < width) {
- // Need at least 3 chars left for get_rel_pos() + NUL.
- while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) {
- i += (int)schar_get(buffer + i, fillchar);
- o++;
- }
- get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
+ int n2 = (width + 1) / 2;
+ this_ru_col = MAX(this_ru_col, n2);
+ if (this_ru_col + n1 < width) {
+ // need at least space for rel_pos + NUL
+ while (this_ru_col + n1 < width
+ && RULER_BUF_LEN > bufferlen + rel_poslen + 1) { // +1 for NUL
+ bufferlen += (int)schar_get(buffer + bufferlen, fillchar);
+ n1++;
+ }
+ bufferlen += vim_snprintf(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen,
+ "%s", rel_pos);
}
+ (void)bufferlen;
if (ui_has(kUIMessages) && !part_of_status) {
MAXSIZE_TEMP_ARRAY(content, 1);
@@ -596,11 +599,11 @@ void win_redr_ruler(win_T *wp)
did_show_ext_ruler = false;
}
// Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
+ for (n1 = 0, n2 = 0; buffer[n1] != NUL; n1 += utfc_ptr2len(buffer + n1)) {
+ n2 += utf_ptr2cells(buffer + n1);
+ if (this_ru_col + n2 > width) {
+ bufferlen = n1;
+ buffer[bufferlen] = NUL;
break;
}
}
@@ -769,8 +772,7 @@ void draw_tabline(void)
if (modified || wincount > 1) {
if (wincount > 1) {
- vim_snprintf(NameBuff, MAXPATHL, "%d", wincount);
- int len = (int)strlen(NameBuff);
+ int len = vim_snprintf(NameBuff, MAXPATHL, "%d", wincount);
if (col + len >= Columns - 3) {
break;
}
@@ -795,7 +797,8 @@ void draw_tabline(void)
len -= ptr2cells(p);
MB_PTR_ADV(p);
}
- len = MIN(len, Columns - col - 1);
+ int n = Columns - col - 1;
+ len = MIN(len, n);
grid_line_puts(col, p, -1, attr);
col += len;
@@ -829,7 +832,8 @@ void draw_tabline(void)
// 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);
+ int n = Columns - col - (tabcount > 1) * 3;
+ const int sc_width = MIN(10, n);
if (sc_width > 0) {
grid_line_puts(Columns - sc_width - (tabcount > 1) * 2,
@@ -923,6 +927,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
static stl_hlrec_t *stl_hltab = NULL;
static StlClickRecord *stl_tabtab = NULL;
static int *stl_separator_locations = NULL;
+ static int curitem = 0;
#define TMPLEN 70
char buf_tmp[TMPLEN];
@@ -1009,7 +1014,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
int groupdepth = 0;
int evaldepth = 0;
- int curitem = 0;
+ // nvim_eval_statusline() can be called from inside a {-expression item so
+ // this may be a recursive call. Keep track of the start index into "stl_items".
+ // During post-processing only treat items filled in a certain recursion level.
+ int evalstart = curitem;
+
bool prevchar_isflag = true;
bool prevchar_isitem = false;
@@ -1153,9 +1162,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
}
}
- // If the group is longer than it is allowed to be
- // truncate by removing bytes from the start of the group text.
- if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) {
+ // If the group is longer than it is allowed to be truncate by removing
+ // bytes from the start of the group text. Don't truncate when item is a
+ // 'statuscolumn' fold item to ensure correctness of the mouse clicks.
+ if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid
+ && stl_items[stl_groupitems[groupdepth]].type != HighlightFold) {
// { Determine the number of bytes to remove
// Find the first character that should be included.
@@ -1537,7 +1548,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// Store the position percentage in our temporary buffer.
// Note: We cannot store the value in `num` because
// `get_rel_pos` can return a named position. Ex: "Top"
- get_rel_pos(wp, buf_tmp, TMPLEN);
+ (void)get_rel_pos(wp, buf_tmp, TMPLEN);
str = buf_tmp;
break;
@@ -1565,7 +1576,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
+ if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN) > 0) {
str = buf_tmp;
}
break;
@@ -1629,12 +1640,12 @@ stcsign:
break;
}
foldsignitem = curitem;
+ lnum = (linenr_T)get_vim_var_nr(VV_LNUM);
if (fdc > 0) {
schar_T fold_buf[9];
- fill_foldcolumn(wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM),
- 0, fdc, NULL, fold_buf);
- stl_items[curitem].minwid = -(stcp->use_cul ? HLF_CLF : HLF_FC);
+ fill_foldcolumn(wp, stcp->foldinfo, lnum, 0, fdc, NULL, stcp->fold_vcol, fold_buf);
+ stl_items[curitem].minwid = -(use_cursor_line_highlight(wp, lnum) ? HLF_CLF : HLF_FC);
size_t buflen = 0;
// TODO(bfredl): this is very backwards. we must support schar_T
// being used directly in 'statuscolumn'
@@ -1647,18 +1658,18 @@ stcsign:
for (int i = 0; i < width; i++) {
stl_items[curitem].start = out_p + signlen;
if (fdc == 0) {
- if (stcp->sattrs[i].text[0] && get_vim_var_nr(VV_VIRTNUM) == 0) {
- SignTextAttrs sattrs = stcp->sattrs[i];
- signlen += describe_sign_text(buf_tmp + signlen, sattrs.text);
- stl_items[curitem].minwid = -(stcp->sign_cul_id ? stcp->sign_cul_id : sattrs.hl_id);
+ SignTextAttrs sattr = stcp->sattrs[i];
+ if (sattr.text[0] && get_vim_var_nr(VV_VIRTNUM) == 0) {
+ signlen += describe_sign_text(buf_tmp + signlen, sattr.text);
+ stl_items[curitem].minwid = -(stcp->sign_cul_id ? stcp->sign_cul_id : sattr.hl_id);
} else {
buf_tmp[signlen++] = ' ';
buf_tmp[signlen++] = ' ';
buf_tmp[signlen] = NUL;
- stl_items[curitem].minwid = -(stcp->use_cul ? HLF_CLS : HLF_SC);
+ stl_items[curitem].minwid = 0;
}
}
- stl_items[curitem++].type = Highlight;
+ stl_items[curitem++].type = fdc > 0 ? HighlightFold : HighlightSign;
}
str = buf_tmp;
break;
@@ -1943,7 +1954,9 @@ stcsign:
}
*out_p = NUL;
- int itemcnt = curitem;
+ // Subtract offset from `itemcnt` and restore `curitem` to previous recursion level.
+ int itemcnt = curitem - evalstart;
+ curitem = evalstart;
// Free the format buffer if we allocated it internally
if (usefmt != fmt) {
@@ -1969,7 +1982,7 @@ stcsign:
trunc_p = stl_items[0].start;
item_idx = 0;
- for (int i = 0; i < itemcnt; i++) {
+ for (int i = evalstart; i < itemcnt + evalstart; i++) {
if (stl_items[i].type == Trunc) {
// Truncate at %< stl_items.
trunc_p = stl_items[i].start;
@@ -1999,9 +2012,9 @@ stcsign:
// Ignore any items in the statusline that occur after
// the truncation point
- for (int i = 0; i < itemcnt; i++) {
+ for (int i = evalstart; i < itemcnt + evalstart; i++) {
if (stl_items[i].start > trunc_p) {
- for (int j = i; j < itemcnt; j++) {
+ for (int j = i; j < itemcnt + evalstart; j++) {
if (stl_items[j].type == ClickFunc) {
XFREE_CLEAR(stl_items[j].cmd);
}
@@ -2040,7 +2053,7 @@ stcsign:
// the truncation marker `<` is not counted.
int item_offset = trunc_len - 1;
- for (int i = item_idx; i < itemcnt; i++) {
+ for (int i = item_idx; i < itemcnt + evalstart; i++) {
// Items starting at or after the end of the truncated section need
// to be moved backwards.
if (stl_items[i].start >= trunc_end_p) {
@@ -2073,7 +2086,7 @@ stcsign:
// Find how many separators there are, which we will use when
// figuring out how many groups there are.
int num_separators = 0;
- for (int i = 0; i < itemcnt; i++) {
+ for (int i = evalstart; i < itemcnt + evalstart; i++) {
if (stl_items[i].type == Separate) {
// Create an array of the start location for each separator mark.
stl_separator_locations[num_separators] = i;
@@ -2098,7 +2111,7 @@ stcsign:
}
for (int item_idx = stl_separator_locations[l] + 1;
- item_idx < itemcnt;
+ item_idx < itemcnt + evalstart;
item_idx++) {
stl_items[item_idx].start += dislocation;
}
@@ -2112,10 +2125,13 @@ stcsign:
if (hltab != NULL) {
*hltab = stl_hltab;
stl_hlrec_t *sp = stl_hltab;
- for (int l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == Highlight) {
+ for (int l = evalstart; l < itemcnt + evalstart; l++) {
+ if (stl_items[l].type == Highlight
+ || stl_items[l].type == HighlightFold || stl_items[l].type == HighlightSign) {
sp->start = stl_items[l].start;
sp->userhl = stl_items[l].minwid;
+ unsigned type = stl_items[l].type;
+ sp->item = type == HighlightSign ? STL_SIGNCOL : type == HighlightFold ? STL_FOLDCOL : 0;
sp++;
}
}
@@ -2130,7 +2146,7 @@ stcsign:
if (tabtab != NULL) {
*tabtab = stl_tabtab;
StlClickRecord *cur_tab_rec = stl_tabtab;
- for (int l = 0; l < itemcnt; l++) {
+ for (int l = evalstart; l < itemcnt + evalstart; l++) {
if (stl_items[l].type == TabPage) {
cur_tab_rec->start = stl_items[l].start;
if (stl_items[l].minwid == 0) {
diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h
index 118f4a257b..83dda1e035 100644
--- a/src/nvim/statusline_defs.h
+++ b/src/nvim/statusline_defs.h
@@ -5,6 +5,50 @@
#include "nvim/fold_defs.h"
#include "nvim/sign_defs.h"
+/// 'statusline' item flags
+typedef enum {
+ STL_FILEPATH = 'f', ///< Path of file in buffer.
+ STL_FULLPATH = 'F', ///< Full path of file in buffer.
+ STL_FILENAME = 't', ///< Last part (tail) of file path.
+ STL_COLUMN = 'c', ///< Column og cursor.
+ STL_VIRTCOL = 'v', ///< Virtual column.
+ STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display.
+ STL_LINE = 'l', ///< Line number of cursor.
+ STL_NUMLINES = 'L', ///< Number of lines in buffer.
+ STL_BUFNO = 'n', ///< Current buffer number.
+ STL_KEYMAP = 'k', ///< 'keymap' when active.
+ STL_OFFSET = 'o', ///< Offset of character under cursor.
+ STL_OFFSET_X = 'O', ///< - in hexadecimal.
+ STL_BYTEVAL = 'b', ///< Byte value of character.
+ STL_BYTEVAL_X = 'B', ///< - in hexadecimal.
+ STL_ROFLAG = 'r', ///< Readonly flag.
+ STL_ROFLAG_ALT = 'R', ///< - other display.
+ STL_HELPFLAG = 'h', ///< Window is showing a help file.
+ STL_HELPFLAG_ALT = 'H', ///< - other display.
+ STL_FILETYPE = 'y', ///< 'filetype'.
+ STL_FILETYPE_ALT = 'Y', ///< - other display.
+ STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf.
+ STL_PREVIEWFLAG_ALT = 'W', ///< - other display.
+ STL_MODIFIED = 'm', ///< Modified flag.
+ STL_MODIFIED_ALT = 'M', ///< - other display.
+ STL_QUICKFIX = 'q', ///< Quickfix window description.
+ STL_PERCENTAGE = 'p', ///< Percentage through file.
+ 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.
+ STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0.
+ STL_HIGHLIGHT = '#', ///< Highlight name.
+ STL_TABPAGENR = 'T', ///< Tab page label nr.
+ STL_TABCLOSENR = 'X', ///< Tab page close nr.
+ STL_CLICK_FUNC = '@', ///< Click region start.
+} StlFlag;
+
/// Status line click definition
typedef struct {
enum {
@@ -26,27 +70,26 @@ typedef struct {
/// Used for highlighting in the status line.
typedef struct stl_hlrec stl_hlrec_t;
struct stl_hlrec {
- char *start;
- int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID
+ char *start; ///< Where the item starts in the status line output buffer
+ int userhl; ///< 0: no HL, 1-9: User HL, < 0 for syn ID
+ StlFlag item; ///< Item flag belonging to highlight (used for 'statuscolumn')
};
/// Used for building the status line.
typedef struct stl_item stl_item_t;
struct stl_item {
- // Where the item starts in the status line output buffer
- char *start;
- // Function to run for ClickFunc items.
- char *cmd;
- // The minimum width of the item
- int minwid;
- // The maximum width of the item
- int maxwid;
+ char *start; ///< Where the item starts in the status line output buffer
+ char *cmd; ///< Function to run for ClickFunc items
+ int minwid; ///< The minimum width of the item
+ int maxwid; ///< The maximum width of the item
enum {
Normal,
Empty,
Group,
Separate,
Highlight,
+ HighlightSign,
+ HighlightFold,
TabPage,
ClickFunc,
Trunc,
@@ -56,11 +99,10 @@ struct stl_item {
/// Struct to hold info for 'statuscolumn'
typedef struct {
int width; ///< width of the status column
- int num_attr; ///< default highlight attr
int sign_cul_id; ///< cursorline sign highlight id
bool draw; ///< whether to draw the statuscolumn
- bool use_cul; ///< whether to use cursorline attrs
stl_hlrec_t *hlrec; ///< highlight groups
foldinfo_T foldinfo; ///< fold information
+ colnr_T fold_vcol[9]; ///< vcol array filled for fold item
SignTextAttrs *sattrs; ///< sign attributes
} statuscol_T;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 2de65391cc..818e67b32d 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -9,6 +9,8 @@
#include <string.h>
#include "auto/config.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
#include "nvim/charset.h"
@@ -20,12 +22,12 @@
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/macros_defs.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/plines.h"
@@ -496,20 +498,6 @@ char *vim_strchr(const char *const string, const int c)
}
}
-// Sized version of strchr that can handle embedded NULs.
-// Adjusts n to the new size.
-char *strnchr(const char *p, size_t *n, int c)
-{
- while (*n > 0) {
- if (*p == c) {
- return (char *)p;
- }
- p++;
- (*n)--;
- }
- return NULL;
-}
-
// Sort an array of strings.
static int sort_compare(const void *s1, const void *s2)
diff --git a/src/nvim/strings.h b/src/nvim/strings.h
index c2d078615d..e33be2e076 100644
--- a/src/nvim/strings.h
+++ b/src/nvim/strings.h
@@ -5,7 +5,7 @@
#include "auto/config.h"
#include "klib/kvec.h"
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/os/os_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 03f20047a5..7160c757bb 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -28,7 +28,6 @@
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/hashtab_defs.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent_c.h"
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 3e0bb32391..c676b00986 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -34,7 +34,6 @@
#include "nvim/hashtab.h"
#include "nvim/hashtab_defs.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
@@ -451,7 +450,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
curwin->w_cursor.col = saved_fmark.mark.col;
curwin->w_set_curswant = true;
check_cursor(curwin);
- if ((fdo_flags & FDO_TAG) && old_KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) {
foldOpenCursor();
}
@@ -669,7 +668,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
if (ask_for_selection) {
// Ask to select a tag from the list.
- int i = prompt_for_number(NULL);
+ int i = prompt_for_input(NULL, 0, false, NULL);
if (i <= 0 || i > num_matches || got_int) {
// no valid choice: don't change anything
if (use_tagstack) {
@@ -2294,17 +2293,17 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
// Change the value of 'ignorecase' according to 'tagcase' for the
// duration of this function.
switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) {
- case TC_FOLLOWIC: break;
- case TC_IGNORE:
+ case kOptTcFlagFollowic: break;
+ case kOptTcFlagIgnore:
p_ic = true;
break;
- case TC_MATCH:
+ case kOptTcFlagMatch:
p_ic = false;
break;
- case TC_FOLLOWSCS:
+ case kOptTcFlagFollowscs:
p_ic = ignorecase(pat);
break;
- case TC_SMART:
+ case kOptTcFlagSmart:
p_ic = ignorecase_opt(pat, true, true);
break;
default:
@@ -2846,7 +2845,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool 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))) {
+ if (postponed_split && (swb_flags & (kOptSwbFlagUseopen | kOptSwbFlagUsetab))) {
buf_T *const existing_buf = buflist_findname_exp(fname);
if (existing_buf != NULL) {
@@ -3015,7 +3014,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
if (curbuf->b_help) {
set_topline(curwin, curwin->w_cursor.lnum);
}
- if ((fdo_flags & FDO_TAG) && old_KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) {
foldOpenCursor();
}
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 5ff7f721ba..897c393488 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -52,6 +52,7 @@
#include "nvim/channel.h"
#include "nvim/channel_defs.h"
#include "nvim/cursor.h"
+#include "nvim/cursor_shape.h"
#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
@@ -64,6 +65,7 @@
#include "nvim/ex_docmd.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"
@@ -91,9 +93,15 @@
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/vim_defs.h"
+#include "nvim/vterm/keyboard.h"
+#include "nvim/vterm/mouse.h"
+#include "nvim/vterm/parser.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
#include "nvim/window.h"
-#include "vterm/vterm.h"
-#include "vterm/vterm_keycodes.h"
typedef struct {
VimState state;
@@ -160,14 +168,20 @@ struct terminal {
int invalid_start, invalid_end; // invalid rows in libvterm screen
struct {
int row, col;
+ int shape;
bool visible;
+ bool blink;
} cursor;
- bool pending_resize; // pending width/height
- bool color_set[16];
+ struct {
+ bool resize; ///< pending width/height
+ bool cursor; ///< pending cursor shape or blink change
+ StringBuilder *send; ///< When there is a pending TermRequest autocommand, block and store input.
+ } pending;
+
+ bool theme_updates; ///< Send a theme update notification when 'bg' changes
- // When there is a pending TermRequest autocommand, block and store input.
- StringBuilder *pending_send;
+ bool color_set[16];
char *selection_buffer; /// libvterm selection buffer
StringBuilder selection; /// Growable array containing full selection data
@@ -181,6 +195,7 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
.movecursor = term_movecursor,
.settermprop = term_settermprop,
.bell = term_bell,
+ .theme = term_theme,
.sb_pushline = term_sb_push, // Called before a line goes offscreen.
.sb_popline = term_sb_pop,
};
@@ -207,24 +222,24 @@ static void emit_termrequest(void **argv)
apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data);
xfree(payload);
- StringBuilder *term_pending_send = term->pending_send;
- term->pending_send = NULL;
+ StringBuilder *term_pending_send = term->pending.send;
+ term->pending.send = NULL;
if (kv_size(*pending_send)) {
terminal_send(term, pending_send->items, pending_send->size);
kv_destroy(*pending_send);
}
if (term_pending_send != pending_send) {
- term->pending_send = term_pending_send;
+ term->pending.send = term_pending_send;
}
xfree(pending_send);
}
static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length)
{
- term->pending_send = xmalloc(sizeof(StringBuilder));
- kv_init(*term->pending_send);
+ term->pending.send = xmalloc(sizeof(StringBuilder));
+ kv_init(*term->pending.send);
multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length,
- term->pending_send);
+ term->pending.send);
}
static int parse_osc8(VTermStringFragment frag, int *attr)
@@ -352,7 +367,7 @@ static void term_output_callback(const char *s, size_t len, void *user_data)
/// Initializes terminal properties, and triggers TermOpen.
///
-/// The PTY process (TerminalOptions.data) was already started by termopen(),
+/// The PTY process (TerminalOptions.data) was already started by jobstart(),
/// via ex_terminal() or the term:// BufReadCmd.
///
/// @param buf Buffer used for presentation of the terminal.
@@ -363,7 +378,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
// Create a new terminal instance and configure it
Terminal *term = *termpp = xcalloc(1, sizeof(Terminal));
term->opts = opts;
- term->cursor.visible = true;
+
// Associate the terminal instance with the new buffer
term->buf_handle = buf->handle;
buf->terminal = term;
@@ -387,6 +402,28 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
vterm_state_set_selection_callbacks(state, &vterm_selection_callbacks, term,
term->selection_buffer, SELECTIONBUF_SIZE);
+ VTermValue cursor_shape;
+ switch (shape_table[SHAPE_IDX_TERM].shape) {
+ case SHAPE_BLOCK:
+ cursor_shape.number = VTERM_PROP_CURSORSHAPE_BLOCK;
+ break;
+ case SHAPE_HOR:
+ cursor_shape.number = VTERM_PROP_CURSORSHAPE_UNDERLINE;
+ break;
+ case SHAPE_VER:
+ cursor_shape.number = VTERM_PROP_CURSORSHAPE_BAR_LEFT;
+ break;
+ }
+ vterm_state_set_termprop(state, VTERM_PROP_CURSORSHAPE, &cursor_shape);
+
+ VTermValue cursor_blink;
+ if (shape_table[SHAPE_IDX_TERM].blinkon != 0 && shape_table[SHAPE_IDX_TERM].blinkoff != 0) {
+ cursor_blink.boolean = true;
+ } else {
+ cursor_blink.boolean = false;
+ }
+ vterm_state_set_termprop(state, VTERM_PROP_CURSORBLINK, &cursor_blink);
+
// force a initial refresh of the screen to ensure the buffer will always
// have as many lines as screen rows when refresh_scrollback is called
term->invalid_start = 0;
@@ -565,7 +602,7 @@ void terminal_check_size(Terminal *term)
vterm_set_size(term->vt, height, width);
vterm_screen_flush_damage(term->vts);
- term->pending_resize = true;
+ term->pending.resize = true;
invalidate_terminal(term, -1, -1);
}
@@ -598,12 +635,12 @@ bool terminal_enter(void)
int save_w_p_cuc = curwin->w_p_cuc;
OptInt save_w_p_so = curwin->w_p_so;
OptInt save_w_p_siso = curwin->w_p_siso;
- if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) {
+ if (curwin->w_p_cul && curwin->w_p_culopt_flags & kOptCuloptFlagNumber) {
if (strcmp(curwin->w_p_culopt, "number") != 0) {
save_w_p_culopt = curwin->w_p_culopt;
curwin->w_p_culopt = xstrdup("number");
}
- curwin->w_p_culopt_flags = CULOPT_NBR;
+ curwin->w_p_culopt_flags = kOptCuloptFlagNumber;
} else {
curwin->w_p_cul = false;
}
@@ -614,15 +651,25 @@ bool terminal_enter(void)
curwin->w_p_so = 0;
curwin->w_p_siso = 0;
+ // Update the cursor shape table and flush changes to the UI
+ s->term->pending.cursor = true;
+ refresh_cursor(s->term);
+
adjust_topline(s->term, buf, 0); // scroll to end
- // erase the unfocused cursor
- invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
showmode();
curwin->w_redr_status = true; // For mode() in statusline. #8323
- ui_busy_start();
+ redraw_custom_title_later();
+ if (!s->term->cursor.visible) {
+ // Hide cursor if it should be hidden
+ ui_busy_start();
+ }
+ ui_cursor_shape();
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
may_trigger_modechanged();
+ // Tell the terminal it has focus
+ terminal_focus(s->term, true);
+
s->state.execute = terminal_execute;
s->state.check = terminal_check;
state_enter(&s->state);
@@ -634,6 +681,9 @@ bool terminal_enter(void)
RedrawingDisabled = s->save_rd;
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
+ // Restore the terminal cursor to what is set in 'guicursor'
+ (void)parse_shape_opt(SHAPE_CURSOR);
+
if (save_curwin == curwin->handle) { // Else: window was closed.
curwin->w_p_cul = save_w_p_cul;
if (save_w_p_culopt) {
@@ -648,8 +698,9 @@ bool terminal_enter(void)
free_string_option(save_w_p_culopt);
}
- // draw the unfocused cursor
- invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
+ // Tell the terminal it lost focus
+ terminal_focus(s->term, false);
+
if (curbuf->terminal == s->term && !s->close) {
terminal_check_cursor();
}
@@ -658,7 +709,11 @@ bool terminal_enter(void)
} else {
unshowmode(true);
}
- ui_busy_stop();
+ if (!s->term->cursor.visible) {
+ // If cursor was hidden, show it again
+ ui_busy_stop();
+ }
+ ui_cursor_shape();
if (s->close) {
bool wipe = s->term->buf_handle != 0;
s->term->destroy = true;
@@ -728,21 +783,32 @@ static int terminal_execute(VimState *state, int key)
{
TerminalState *s = (TerminalState *)state;
- switch (key) {
+ // Check for certain control keys like Ctrl-C and Ctrl-\. We still send the
+ // unmerged key and modifiers to the terminal.
+ int tmp_mod_mask = mod_mask;
+ int mod_key = merge_modifiers(key, &tmp_mod_mask);
+
+ switch (mod_key) {
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
- case K_MOUSEMOVE:
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
+ case K_X1MOUSE:
+ case K_X1DRAG:
+ case K_X1RELEASE:
+ case K_X2MOUSE:
+ case K_X2DRAG:
+ case K_X2RELEASE:
case K_MOUSEDOWN:
case K_MOUSEUP:
case K_MOUSELEFT:
case K_MOUSERIGHT:
+ case K_MOUSEMOVE:
if (send_mouse_event(s->term, key)) {
return 0;
}
@@ -786,13 +852,13 @@ static int terminal_execute(VimState *state, int key)
FALLTHROUGH;
default:
- if (key == Ctrl_C) {
+ if (mod_key == Ctrl_C) {
// terminal_enter() always sets `mapped_ctrl_c` to avoid `got_int`. 8eeda7169aa4
// But `got_int` may be set elsewhere, e.g. by interrupt() or an autocommand,
// so ensure that it is cleared.
got_int = false;
}
- if (key == Ctrl_BSL && !s->got_bsl) {
+ if (mod_key == Ctrl_BSL && !s->got_bsl) {
s->got_bsl = true;
break;
}
@@ -809,6 +875,19 @@ static int terminal_execute(VimState *state, int key)
return 0;
}
if (s->term != curbuf->terminal) {
+ // Active terminal buffer changed, flush terminal's cursor state to the UI
+ curbuf->terminal->pending.cursor = true;
+
+ if (!s->term->cursor.visible) {
+ // If cursor was hidden, show it again
+ ui_busy_stop();
+ }
+
+ if (!curbuf->terminal->cursor.visible) {
+ // Hide cursor if it should be hidden
+ ui_busy_start();
+ }
+
invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
invalidate_terminal(curbuf->terminal,
curbuf->terminal->cursor.row,
@@ -856,8 +935,8 @@ static void terminal_send(Terminal *term, const char *data, size_t size)
if (term->closed) {
return;
}
- if (term->pending_send) {
- kv_concat_len(*term->pending_send, data, size);
+ if (term->pending.send) {
+ kv_concat_len(*term->pending.send, data, size);
return;
}
term->opts.write_cb(data, size, term->opts.data);
@@ -868,28 +947,28 @@ static bool is_filter_char(int c)
unsigned flag = 0;
switch (c) {
case 0x08:
- flag = TPF_BS;
+ flag = kOptTpfFlagBS;
break;
case 0x09:
- flag = TPF_HT;
+ flag = kOptTpfFlagHT;
break;
case 0x0A:
case 0x0D:
break;
case 0x0C:
- flag = TPF_FF;
+ flag = kOptTpfFlagFF;
break;
case 0x1b:
- flag = TPF_ESC;
+ flag = kOptTpfFlagESC;
break;
case 0x7F:
- flag = TPF_DEL;
+ flag = kOptTpfFlagDEL;
break;
default:
if (c < ' ') {
- flag = TPF_C0;
+ flag = kOptTpfFlagC0;
} else if (c >= 0x80 && c <= 0x9F) {
- flag = TPF_C1;
+ flag = kOptTpfFlagC1;
}
}
return !!(tpf_flags & flag);
@@ -946,9 +1025,9 @@ static void terminal_send_key(Terminal *term, int c)
c = Ctrl_AT;
}
- VTermKey key = convert_key(c, &mod);
+ VTermKey key = convert_key(&c, &mod);
- if (key) {
+ if (key != VTERM_KEY_NONE) {
vterm_keyboard_key(term->vt, key, mod);
} else if (!IS_SPECIAL(c)) {
vterm_keyboard_unichar(term->vt, (uint32_t)c, mod);
@@ -1062,14 +1141,6 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
attr_id = hl_combine_attr(attr_id, cell.uri);
}
- if (term->cursor.visible && term->cursor.row == row
- && term->cursor.col == col) {
- attr_id = hl_combine_attr(attr_id,
- is_focused(term) && wp == curwin
- ? win_hl_attr(wp, HLF_TERM)
- : win_hl_attr(wp, HLF_TERMNC));
- }
-
term_attrs[col] = attr_id;
}
}
@@ -1084,6 +1155,31 @@ bool terminal_running(const Terminal *term)
return !term->closed;
}
+void terminal_notify_theme(Terminal *term, bool dark)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!term->theme_updates) {
+ return;
+ }
+
+ char buf[10];
+ ssize_t ret = snprintf(buf, sizeof(buf), "\x1b[997;%cn", dark ? '1' : '2');
+ assert(ret > 0);
+ assert((size_t)ret <= sizeof(buf));
+ terminal_send(term, buf, (size_t)ret);
+}
+
+static void terminal_focus(const Terminal *term, bool focus)
+ FUNC_ATTR_NONNULL_ALL
+{
+ VTermState *state = vterm_obtain_state(term->vt);
+ if (focus) {
+ vterm_state_focus_in(state);
+ } else {
+ vterm_state_focus_out(state);
+ }
+}
+
// }}}
// libvterm callbacks {{{
@@ -1105,8 +1201,7 @@ static int term_movecursor(VTermPos new_pos, VTermPos old_pos, int visible, void
Terminal *term = data;
term->cursor.row = new_pos.row;
term->cursor.col = new_pos.col;
- invalidate_terminal(term, old_pos.row, old_pos.row + 1);
- invalidate_terminal(term, new_pos.row, new_pos.row + 1);
+ invalidate_terminal(term, -1, -1);
return 1;
}
@@ -1134,8 +1229,17 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
break;
case VTERM_PROP_CURSORVISIBLE:
+ if (is_focused(term)) {
+ if (!val->boolean && term->cursor.visible) {
+ // Hide the cursor
+ ui_busy_start();
+ } else if (val->boolean && !term->cursor.visible) {
+ // Unhide the cursor
+ ui_busy_stop();
+ }
+ invalidate_terminal(term, -1, -1);
+ }
term->cursor.visible = val->boolean;
- invalidate_terminal(term, term->cursor.row, term->cursor.row + 1);
break;
case VTERM_PROP_TITLE: {
@@ -1171,6 +1275,22 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
term->forward_mouse = (bool)val->number;
break;
+ case VTERM_PROP_CURSORBLINK:
+ term->cursor.blink = val->boolean;
+ term->pending.cursor = true;
+ invalidate_terminal(term, -1, -1);
+ break;
+
+ case VTERM_PROP_CURSORSHAPE:
+ term->cursor.shape = val->number;
+ term->pending.cursor = true;
+ invalidate_terminal(term, -1, -1);
+ break;
+
+ case VTERM_PROP_THEMEUPDATES:
+ term->theme_updates = val->boolean;
+ break;
+
default:
return 0;
}
@@ -1181,7 +1301,15 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
/// Called when the terminal wants to ring the system bell.
static int term_bell(void *data)
{
- vim_beep(BO_TERM);
+ vim_beep(kOptBoFlagTerm);
+ return 1;
+}
+
+/// Called when the terminal wants to query the system theme.
+static int term_theme(bool *dark, void *data)
+ FUNC_ATTR_NONNULL_ALL
+{
+ *dark = (*p_bg == 'd');
return 1;
}
@@ -1266,7 +1394,7 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data)
// copy to vterm state
memcpy(cells, sbrow->cells, sizeof(cells[0]) * cols_to_copy);
for (size_t col = cols_to_copy; col < (size_t)cols; col++) {
- cells[col].chars[0] = 0;
+ cells[col].schar = 0;
cells[col].width = 1;
}
@@ -1327,19 +1455,23 @@ static int term_selection_set(VTermSelectionMask mask, VTermStringFragment frag,
// }}}
// input handling {{{
-static void convert_modifiers(int key, VTermModifier *statep)
+static void convert_modifiers(int *key, VTermModifier *statep)
{
if (mod_mask & MOD_MASK_SHIFT) {
*statep |= VTERM_MOD_SHIFT;
}
if (mod_mask & MOD_MASK_CTRL) {
*statep |= VTERM_MOD_CTRL;
+ if (!(mod_mask & MOD_MASK_SHIFT) && *key >= 'A' && *key <= 'Z') {
+ // vterm interprets CTRL+A as SHIFT+CTRL, change to CTRL+a
+ *key += ('a' - 'A');
+ }
}
if (mod_mask & MOD_MASK_ALT) {
*statep |= VTERM_MOD_ALT;
}
- switch (key) {
+ switch (*key) {
case K_S_TAB:
case K_S_UP:
case K_S_DOWN:
@@ -1371,11 +1503,11 @@ static void convert_modifiers(int key, VTermModifier *statep)
}
}
-static VTermKey convert_key(int key, VTermModifier *statep)
+static VTermKey convert_key(int *key, VTermModifier *statep)
{
convert_modifiers(key, statep);
- switch (key) {
+ switch (*key) {
case K_BS:
return VTERM_KEY_BACKSPACE;
case K_S_TAB:
@@ -1678,8 +1810,6 @@ static bool send_mouse_event(Terminal *term, int c)
pressed = true; FALLTHROUGH;
case K_LEFTRELEASE:
button = 1; break;
- case K_MOUSEMOVE:
- button = 0; break;
case K_MIDDLEDRAG:
case K_MIDDLEMOUSE:
pressed = true; FALLTHROUGH;
@@ -1690,6 +1820,16 @@ static bool send_mouse_event(Terminal *term, int c)
pressed = true; FALLTHROUGH;
case K_RIGHTRELEASE:
button = 3; break;
+ case K_X1DRAG:
+ case K_X1MOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_X1RELEASE:
+ button = 8; break;
+ case K_X2DRAG:
+ case K_X2MOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_X2RELEASE:
+ button = 9; break;
case K_MOUSEDOWN:
pressed = true; button = 4; break;
case K_MOUSEUP:
@@ -1698,12 +1838,14 @@ static bool send_mouse_event(Terminal *term, int c)
pressed = true; button = 7; break;
case K_MOUSERIGHT:
pressed = true; button = 6; break;
+ case K_MOUSEMOVE:
+ button = 0; break;
default:
return false;
}
VTermModifier mod = VTERM_MOD_NONE;
- convert_modifiers(c, &mod);
+ convert_modifiers(&c, &mod);
mouse_action(term, button, row, col - offset, pressed, mod);
return false;
}
@@ -1776,12 +1918,8 @@ static void fetch_row(Terminal *term, int row, int end_col)
while (col < end_col) {
VTermScreenCell cell;
fetch_cell(term, row, col, &cell);
- if (cell.chars[0]) {
- int cell_len = 0;
- for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) {
- cell_len += utf_char2bytes((int)cell.chars[i], ptr + cell_len);
- }
- ptr += cell_len;
+ if (cell.schar) {
+ schar_get_adv(&ptr, cell.schar);
line_len = (size_t)(ptr - term->textbuf);
} else {
*ptr++ = ' ';
@@ -1802,7 +1940,7 @@ static bool fetch_cell(Terminal *term, int row, int col, VTermScreenCell *cell)
} else {
// fill the pointer with an empty cell
*cell = (VTermScreenCell) {
- .chars = { 0 },
+ .schar = 0,
.width = 1,
};
return false;
@@ -1848,12 +1986,49 @@ static void refresh_terminal(Terminal *term)
refresh_size(term, buf);
refresh_scrollback(term, buf);
refresh_screen(term, buf);
+ refresh_cursor(term);
aucmd_restbuf(&aco);
int ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
}
+static void refresh_cursor(Terminal *term)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!is_focused(term) || !term->pending.cursor) {
+ return;
+ }
+ term->pending.cursor = false;
+
+ if (term->cursor.blink) {
+ // For the TUI, this value doesn't actually matter, as long as it's non-zero. The terminal
+ // emulator dictates the blink frequency, not the application.
+ // For GUIs we just pick an arbitrary value, for now.
+ shape_table[SHAPE_IDX_TERM].blinkon = 500;
+ shape_table[SHAPE_IDX_TERM].blinkoff = 500;
+ } else {
+ shape_table[SHAPE_IDX_TERM].blinkon = 0;
+ shape_table[SHAPE_IDX_TERM].blinkoff = 0;
+ }
+
+ switch (term->cursor.shape) {
+ case VTERM_PROP_CURSORSHAPE_BLOCK:
+ shape_table[SHAPE_IDX_TERM].shape = SHAPE_BLOCK;
+ break;
+ case VTERM_PROP_CURSORSHAPE_UNDERLINE:
+ shape_table[SHAPE_IDX_TERM].shape = SHAPE_HOR;
+ shape_table[SHAPE_IDX_TERM].percentage = 20;
+ break;
+ case VTERM_PROP_CURSORSHAPE_BAR_LEFT:
+ shape_table[SHAPE_IDX_TERM].shape = SHAPE_VER;
+ shape_table[SHAPE_IDX_TERM].percentage = 25;
+ break;
+ }
+
+ ui_mode_info_set();
+}
+
/// Calls refresh_terminal() on all invalidated_terminals.
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
@@ -1874,11 +2049,11 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
static void refresh_size(Terminal *term, buf_T *buf)
{
- if (!term->pending_resize || term->closed) {
+ if (!term->pending.resize || term->closed) {
return;
}
- term->pending_resize = false;
+ term->pending.resize = false;
int width, height;
vterm_get_size(term->vt, &height, &width);
term->invalid_start = 0;
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 06b3aa0411..ca2829fecb 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -18,6 +18,7 @@
#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -35,6 +36,7 @@
#include "nvim/strings.h"
#include "nvim/textformat.h"
#include "nvim/textobject.h"
+#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -1049,12 +1051,18 @@ void format_lines(linenr_T line_count, bool avoid_fex)
State = MODE_INSERT; // for open_line()
smd_save = p_smd;
p_smd = false;
+
insertchar(NUL, INSCHAR_FORMAT
+ (do_comments ? INSCHAR_DO_COM : 0)
+ (do_comments && do_comments_list ? INSCHAR_COM_LIST : 0)
+ (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
+
State = old_State;
p_smd = smd_save;
+ // Cursor shape may have been updated (e.g. by :normal) in insertchar(),
+ // so it needs to be updated here.
+ ui_cursor_shape();
+
second_indent = -1;
// at end of par.: need to set indent of next par.
need_set_indent = is_end_par;
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
index 45978765bf..e3b3bba7c1 100644
--- a/src/nvim/textobject.c
+++ b/src/nvim/textobject.c
@@ -3,7 +3,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
-#include <string.h>
#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 98dd7b4b45..1f73f2d135 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -8,7 +8,6 @@
#include "nvim/api/private/helpers.h"
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
-#include "nvim/event/stream.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
@@ -160,12 +159,16 @@ void tinput_init(TermInput *input, Loop *loop)
// initialize a timer handle for handling ESC with libtermkey
uv_timer_init(&loop->uv, &input->timer_handle);
input->timer_handle.data = input;
+
+ uv_timer_init(&loop->uv, &input->bg_query_timer);
+ input->bg_query_timer.data = input;
}
void tinput_destroy(TermInput *input)
{
map_destroy(int, &kitty_key_map);
uv_close((uv_handle_t *)&input->timer_handle, NULL);
+ uv_close((uv_handle_t *)&input->bg_query_timer, NULL);
rstream_may_close(&input->read_stream);
termkey_destroy(input->tk);
}
@@ -179,6 +182,7 @@ void tinput_stop(TermInput *input)
{
rstream_stop(&input->read_stream);
uv_timer_stop(&input->timer_handle);
+ uv_timer_stop(&input->bg_query_timer);
}
static void tinput_done_event(void **argv)
@@ -383,6 +387,10 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Middle");
} else if (button == 3) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right");
+ } else if (button == 8) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "X1");
+ } else if (button == 9) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "X2");
}
switch (ev) {
@@ -427,6 +435,15 @@ static void tk_getkeys(TermInput *input, bool force)
TermKeyResult result;
while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) {
+ // Only press and repeat events are handled for now
+ switch (key.event) {
+ case TERMKEY_EVENT_PRESS:
+ case TERMKEY_EVENT_REPEAT:
+ break;
+ default:
+ continue;
+ }
+
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
forward_simple_utf8(input, &key);
} else if (key.type == TERMKEY_TYPE_UNICODE
@@ -474,6 +491,13 @@ static void tinput_timer_cb(uv_timer_t *handle)
tinput_flush(input);
}
+static void bg_query_timer_cb(uv_timer_t *handle)
+ FUNC_ATTR_NONNULL_ALL
+{
+ TermInput *input = handle->data;
+ tui_query_bg_color(input->tui_data);
+}
+
/// Handle focus events.
///
/// If the upcoming sequence of bytes in the input stream matches the termcode
@@ -660,6 +684,33 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key)
}
}
break;
+ case 'n':
+ // Device Status Report (DSR)
+ if (nparams == 2) {
+ int args[2];
+ for (size_t i = 0; i < ARRAY_SIZE(args); i++) {
+ if (termkey_interpret_csi_param(params[i], &args[i], NULL, NULL) != TERMKEY_RES_KEY) {
+ return;
+ }
+ }
+
+ if (args[0] == 997) {
+ // Theme update notification
+ // https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md
+ // The second argument tells us whether the OS theme is set to light
+ // mode or dark mode, but all we care about is the background color of
+ // the terminal emulator. We query for that with OSC 11 and the response
+ // is handled by the autocommand created in _defaults.lua. The terminal
+ // may send us multiple notifications all at once so we use a timer to
+ // coalesce the queries.
+ if (uv_timer_get_due_in(&input->bg_query_timer) > 0) {
+ return;
+ }
+
+ uv_timer_start(&input->bg_query_timer, bg_query_timer_cb, 100, 0);
+ }
+ }
+ break;
default:
break;
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 4c2baf908e..e48982f9a4 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -1,6 +1,7 @@
#pragma once
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <uv.h>
@@ -32,6 +33,7 @@ typedef struct {
TermKey *tk;
TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook
uv_timer_t timer_handle;
+ uv_timer_t bg_query_timer; ///< timer used to batch background color queries
Loop *loop;
RStream read_stream;
TUIData *tui_data;
diff --git a/src/nvim/tui/termkey/driver-csi.c b/src/nvim/tui/termkey/driver-csi.c
index 28c7eaccfd..d427be50ff 100644
--- a/src/nvim/tui/termkey/driver-csi.c
+++ b/src/nvim/tui/termkey/driver-csi.c
@@ -1,11 +1,10 @@
#include <assert.h>
-#include <stdio.h>
+#include <stdint.h>
#include <string.h>
#include "nvim/memory.h"
#include "nvim/tui/termkey/driver-csi.h"
#include "nvim/tui/termkey/termkey-internal.h"
-#include "nvim/tui/termkey/termkey.h"
#include "nvim/tui/termkey/termkey_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -32,11 +31,20 @@ static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd,
if (nparams > 1 && params[1].param != NULL) {
int arg = 0;
- result = termkey_interpret_csi_param(params[1], &arg, NULL, NULL);
+ int subparam = 0;
+ size_t nsubparams = 1;
+ result = termkey_interpret_csi_param(params[1], &arg, &subparam, &nsubparams);
if (result != TERMKEY_RES_KEY) {
return result;
}
+ if (nsubparams > 0) {
+ key->event = parse_key_event(subparam);
+ if (key->event == TERMKEY_EVENT_UNKNOWN) {
+ return TERMKEY_RES_NONE;
+ }
+ }
+
key->modifiers = arg - 1;
} else {
key->modifiers = 0;
@@ -104,11 +112,20 @@ static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, TermK
int args[3];
if (nparams > 1 && params[1].param != NULL) {
- result = termkey_interpret_csi_param(params[1], &args[1], NULL, NULL);
+ int subparam = 0;
+ size_t nsubparams = 1;
+ result = termkey_interpret_csi_param(params[1], &args[1], &subparam, &nsubparams);
if (result != TERMKEY_RES_KEY) {
return result;
}
+ if (nsubparams > 0) {
+ key->event = parse_key_event(subparam);
+ if (key->event == TERMKEY_EVENT_UNKNOWN) {
+ return TERMKEY_RES_NONE;
+ }
+ }
+
key->modifiers = args[1] - 1;
} else {
key->modifiers = 0;
@@ -178,9 +195,11 @@ static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, TermKey
return TERMKEY_RES_ERROR;
}
- if (nsubparams > 0 && subparam != 1) {
- // Not a press event. Ignore for now
- return TERMKEY_RES_NONE;
+ if (nsubparams > 0) {
+ key->event = parse_key_event(subparam);
+ if (key->event == TERMKEY_EVENT_UNKNOWN) {
+ return TERMKEY_RES_NONE;
+ }
}
key->modifiers = args[1] - 1;
@@ -308,6 +327,12 @@ TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKe
btn = code + 4 - 64;
break;
+ case 128:
+ case 129:
+ *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
+ btn = code + 8 - 128;
+ break;
+
default:
*event = TERMKEY_MOUSE_UNKNOWN;
}
@@ -419,6 +444,20 @@ TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, i
#define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
+static TermKeyEvent parse_key_event(int n)
+{
+ switch (n) {
+ case 1:
+ return TERMKEY_EVENT_PRESS;
+ case 2:
+ return TERMKEY_EVENT_REPEAT;
+ case 3:
+ return TERMKEY_EVENT_RELEASE;
+ default:
+ return TERMKEY_EVENT_UNKNOWN;
+ }
+}
+
static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len,
TermKeyCsiParam params[], size_t *nargs, unsigned *commandp)
{
@@ -529,7 +568,7 @@ TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, in
if (c == ':') {
if (length == 0) {
*paramp = arg;
- } else {
+ } else if (subparams != NULL) {
subparams[length - 1] = arg;
}
@@ -544,7 +583,7 @@ TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, in
if (length == 0) {
*paramp = arg;
- } else {
+ } else if (subparams != NULL) {
subparams[length - 1] = arg;
}
diff --git a/src/nvim/tui/termkey/driver-csi.h b/src/nvim/tui/termkey/driver-csi.h
index 0abd8b5c2e..644cc9d58b 100644
--- a/src/nvim/tui/termkey/driver-csi.h
+++ b/src/nvim/tui/termkey/driver-csi.h
@@ -1,6 +1,6 @@
#pragma once
-#include "nvim/tui/termkey/termkey_defs.h"
+#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/termkey/driver-csi.h.generated.h"
diff --git a/src/nvim/tui/termkey/driver-ti.c b/src/nvim/tui/termkey/driver-ti.c
index 745ee9902f..e402f93e93 100644
--- a/src/nvim/tui/termkey/driver-ti.c
+++ b/src/nvim/tui/termkey/driver-ti.c
@@ -1,16 +1,16 @@
-#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unibilium.h>
+#include <uv.h>
#include "nvim/memory.h"
#include "nvim/tui/termkey/driver-ti.h"
#include "nvim/tui/termkey/termkey-internal.h"
-#include "nvim/tui/termkey/termkey.h"
+#include "nvim/tui/termkey/termkey_defs.h"
#ifndef _WIN32
# include <unistd.h>
diff --git a/src/nvim/tui/termkey/driver-ti.h b/src/nvim/tui/termkey/driver-ti.h
index df9bd72d5b..6dbcb11344 100644
--- a/src/nvim/tui/termkey/driver-ti.h
+++ b/src/nvim/tui/termkey/driver-ti.h
@@ -1,6 +1,6 @@
#pragma once
-#include "nvim/tui/termkey/termkey_defs.h"
+#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/termkey/driver-ti.h.generated.h"
diff --git a/src/nvim/tui/termkey/termkey-internal.h b/src/nvim/tui/termkey/termkey-internal.h
index 107591f950..97fae939c5 100644
--- a/src/nvim/tui/termkey/termkey-internal.h
+++ b/src/nvim/tui/termkey/termkey-internal.h
@@ -47,7 +47,7 @@ struct TermKey {
int canonflags;
unsigned char *buffer;
size_t buffstart; // First offset in buffer
- size_t buffcount; // NUMBER of entires valid in buffer
+ size_t buffcount; // NUMBER of entries valid in buffer
size_t buffsize; // Total malloc'ed size
size_t hightide; // Position beyond buffstart at which peekkey() should next start
// normally 0, but see also termkey_interpret_csi
diff --git a/src/nvim/tui/termkey/termkey.c b/src/nvim/tui/termkey/termkey.c
index e6440118f3..eabde2f9f7 100644
--- a/src/nvim/tui/termkey/termkey.c
+++ b/src/nvim/tui/termkey/termkey.c
@@ -1,9 +1,9 @@
#include <ctype.h>
#include <errno.h>
-#include <stdbool.h>
#include <stdio.h>
#include <string.h>
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/tui/termkey/driver-csi.h"
@@ -13,9 +13,10 @@
#include "nvim/tui/termkey/termkey_defs.h"
#ifndef _WIN32
-# include <poll.h>
-# include <strings.h>
-# include <unistd.h>
+// Include these directly instead of <termios.h> which is system-dependent. #31704
+# include <poll.h> // IWYU pragma: keep
+# include <strings.h> // IWYU pragma: keep
+# include <unistd.h> // IWYU pragma: keep
#else
# include <io.h>
#endif
@@ -633,40 +634,13 @@ static void eat_bytes(TermKey *tk, size_t count)
tk->buffcount -= count;
}
-// TODO(dundargoc): we should be able to replace this with utf_char2bytes from mbyte.c
int fill_utf8(int codepoint, char *str)
{
- int nbytes = utf_char2len(codepoint);
-
+ int nbytes = utf_char2bytes(codepoint, str);
str[nbytes] = 0;
-
- // This is easier done backwards
- int b = nbytes;
- while (b > 1) {
- b--;
- str[b] = (char)0x80 | (codepoint & 0x3f);
- codepoint >>= 6;
- }
-
- switch (nbytes) {
- case 1:
- str[0] = (codepoint & 0x7f); break;
- case 2:
- str[0] = (char)0xc0 | (codepoint & 0x1f); break;
- case 3:
- str[0] = (char)0xe0 | (codepoint & 0x0f); break;
- case 4:
- str[0] = (char)0xf0 | (codepoint & 0x07); break;
- case 5:
- str[0] = (char)0xf8 | (codepoint & 0x03); break;
- case 6:
- str[0] = (char)0xfc | (codepoint & 0x01); break;
- }
-
return nbytes;
}
-#define UTF8_INVALID 0xFFFD
static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, size_t *nbytep)
{
unsigned nbytes;
@@ -680,7 +654,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
return TERMKEY_RES_KEY;
} else if (b0 < 0xc0) {
// Starts with a continuation byte - that's not right
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
*nbytep = 1;
return TERMKEY_RES_KEY;
} else if (b0 < 0xe0) {
@@ -699,7 +673,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
nbytes = 6;
*cp = b0 & 0x01;
} else {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
*nbytep = 1;
return TERMKEY_RES_KEY;
}
@@ -713,7 +687,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
cb = bytes[b];
if (cb < 0x80 || cb >= 0xc0) {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
*nbytep = b;
return TERMKEY_RES_KEY;
}
@@ -724,14 +698,14 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
// Check for overlong sequences
if ((int)nbytes > utf_char2len(*cp)) {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
}
// Check for UTF-16 surrogates or invalid *cps
if ((*cp >= 0xD800 && *cp <= 0xDFFF)
|| *cp == 0xFFFE
|| *cp == 0xFFFF) {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
}
*nbytep = nbytes;
@@ -833,6 +807,9 @@ static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nb
return TERMKEY_RES_ERROR;
}
+ // Press is the default event type.
+ key->event = TERMKEY_EVENT_PRESS;
+
#ifdef DEBUG
fprintf(stderr, "getkey(force=%d): buffer ", force);
print_buffer(tk);
@@ -958,9 +935,9 @@ static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, siz
if (res == TERMKEY_RES_AGAIN && force) {
// There weren't enough bytes for a complete UTF-8 sequence but caller
// demands an answer. About the best thing we can do here is eat as many
- // bytes as we have, and emit a UTF8_INVALID. If the remaining bytes
+ // bytes as we have, and emit a UNICODE_INVALID. If the remaining bytes
// arrive later, they'll be invalid too.
- codepoint = UTF8_INVALID;
+ codepoint = UNICODE_INVALID;
*nbytep = tk->buffcount;
res = TERMKEY_RES_KEY;
}
diff --git a/src/nvim/tui/termkey/termkey.h b/src/nvim/tui/termkey/termkey.h
index 21ed141346..5cf9894cac 100644
--- a/src/nvim/tui/termkey/termkey.h
+++ b/src/nvim/tui/termkey/termkey.h
@@ -1,9 +1,9 @@
#pragma once
-#include <stdint.h>
-#include <stdlib.h>
+#include <stdint.h> // IWYU pragma: keep
+#include <stdlib.h> // IWYU pragma: keep
-#include "nvim/tui/termkey/termkey_defs.h"
+#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/termkey/termkey.h.generated.h"
diff --git a/src/nvim/tui/termkey/termkey_defs.h b/src/nvim/tui/termkey/termkey_defs.h
index 7c218ba7c2..87d3f63447 100644
--- a/src/nvim/tui/termkey/termkey_defs.h
+++ b/src/nvim/tui/termkey/termkey_defs.h
@@ -123,6 +123,13 @@ typedef enum {
TERMKEY_MOUSE_RELEASE,
} TermKeyMouseEvent;
+typedef enum {
+ TERMKEY_EVENT_UNKNOWN,
+ TERMKEY_EVENT_PRESS,
+ TERMKEY_EVENT_REPEAT,
+ TERMKEY_EVENT_RELEASE,
+} TermKeyEvent;
+
enum {
TERMKEY_KEYMOD_SHIFT = 1 << 0,
TERMKEY_KEYMOD_ALT = 1 << 1,
@@ -163,6 +170,8 @@ typedef struct {
int modifiers;
+ TermKeyEvent event;
+
// Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus
// terminating NUL
char utf8[7];
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 2839a665da..31f95a1006 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1,9 +1,9 @@
// Terminal UI functions. Invoked (by ui_client.c) on the UI process.
#include <assert.h>
+#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -22,8 +22,6 @@
#include "nvim/event/stream.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
@@ -107,7 +105,7 @@ struct TUIData {
bool busy, is_invisible, want_invisible;
bool cork, overflow;
bool set_cursor_color_as_str;
- bool cursor_color_changed;
+ bool cursor_has_color;
bool is_starting;
bool did_set_grapheme_cluster_mode;
FILE *screenshot;
@@ -241,16 +239,19 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state)
tui->unibi_ext.sync = (int)unibi_add_ext_str(tui->ut, "Sync",
"\x1b[?2026%?%p1%{1}%-%tl%eh%;");
break;
- case kTermModeResizeEvents:
- signal_watcher_stop(&tui->winch_handle);
- tui_set_term_mode(tui, mode, true);
- break;
case kTermModeGraphemeClusters:
if (!is_set) {
tui_set_term_mode(tui, mode, true);
tui->did_set_grapheme_cluster_mode = true;
}
break;
+ case kTermModeThemeUpdates:
+ tui_set_term_mode(tui, mode, true);
+ break;
+ case kTermModeResizeEvents:
+ signal_watcher_stop(&tui->winch_handle);
+ tui_set_term_mode(tui, mode, true);
+ break;
}
}
}
@@ -295,7 +296,10 @@ void tui_set_key_encoding(TUIData *tui)
{
switch (tui->input.key_encoding) {
case kKeyEncodingKitty:
- out(tui, S_LEN("\x1b[>1u"));
+ // Progressive enhancement flags:
+ // 0b01 (1) Disambiguate escape codes
+ // 0b10 (2) Report event types
+ out(tui, S_LEN("\x1b[>3u"));
break;
case kKeyEncodingXterm:
out(tui, S_LEN("\x1b[>4;2m"));
@@ -310,7 +314,7 @@ static void tui_reset_key_encoding(TUIData *tui)
{
switch (tui->input.key_encoding) {
case kKeyEncodingKitty:
- out(tui, S_LEN("\x1b[<1u"));
+ out(tui, S_LEN("\x1b[<u"));
break;
case kKeyEncodingXterm:
out(tui, S_LEN("\x1b[>4;0m"));
@@ -320,6 +324,18 @@ static void tui_reset_key_encoding(TUIData *tui)
}
}
+/// Write the OSC 11 sequence to the terminal emulator to query the current
+/// background color.
+///
+/// The response will be handled by the TermResponse autocommand created in
+/// _defaults.lua.
+void tui_query_bg_color(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ out(tui, S_LEN("\x1b]11;?\x07"));
+ flush_buf(tui);
+}
+
/// Enable the alternate screen and emit other control sequences to start the TUI.
///
/// This is also called when the TUI is resumed after being suspended. We reinitialize all state
@@ -336,7 +352,7 @@ static void terminfo_start(TUIData *tui)
tui->cork = false;
tui->overflow = false;
tui->set_cursor_color_as_str = false;
- tui->cursor_color_changed = false;
+ tui->cursor_has_color = false;
tui->showing_mode = SHAPE_IDX_N;
tui->unibi_ext.enable_mouse = -1;
tui->unibi_ext.disable_mouse = -1;
@@ -438,14 +454,13 @@ static void terminfo_start(TUIData *tui)
// Enable bracketed paste
unibi_out_ext(tui, tui->unibi_ext.enable_bracketed_paste);
- // Query support for mode 2026 (Synchronized Output). Some terminals also
- // support an older DCS sequence for synchronized output, but we will only use
- // mode 2026.
+ // Query support for private DEC modes that Nvim can take advantage of.
// Some terminals (such as Terminal.app) do not support DECRQM, so skip the query.
if (!nsterm) {
tui_request_term_mode(tui, kTermModeSynchronizedOutput);
- tui_request_term_mode(tui, kTermModeResizeEvents);
tui_request_term_mode(tui, kTermModeGraphemeClusters);
+ tui_request_term_mode(tui, kTermModeThemeUpdates);
+ tui_request_term_mode(tui, kTermModeResizeEvents);
}
// Don't use DECRQSS in screen or tmux, as they behave strangely when receiving it.
@@ -493,6 +508,10 @@ static void terminfo_start(TUIData *tui)
/// Disable the alternate screen and prepare for the TUI to close.
static void terminfo_stop(TUIData *tui)
{
+ // Disable theme update notifications. We do this first to avoid getting any
+ // more notifications after we reset the cursor and any color palette changes.
+ tui_set_term_mode(tui, kTermModeThemeUpdates, false);
+
// Destroy output stuff
tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N);
tui_mouse_off(tui);
@@ -509,6 +528,7 @@ static void terminfo_stop(TUIData *tui)
if (tui->did_set_grapheme_cluster_mode) {
tui_set_term_mode(tui, kTermModeGraphemeClusters, false);
}
+
// May restore old title before exiting alternate screen.
tui_set_title(tui, NULL_STRING);
if (ui_client_exit_status == 0) {
@@ -521,7 +541,7 @@ static void terminfo_stop(TUIData *tui)
// Exit alternate screen.
unibi_out(tui, unibi_exit_ca_mode);
}
- if (tui->cursor_color_changed) {
+ if (tui->cursor_has_color) {
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
// Disable bracketed paste
@@ -1302,11 +1322,12 @@ static void tui_set_mode(TUIData *tui, ModeShape mode)
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;
+ tui->cursor_has_color = true;
}
- } else if (c.id == 0) {
+ } else if (c.id == 0 && (tui->want_invisible || tui->cursor_has_color)) {
// No cursor color for this mode; reset to default.
tui->want_invisible = false;
+ tui->cursor_has_color = false;
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
@@ -1319,7 +1340,7 @@ static void tui_set_mode(TUIData *tui, ModeShape mode)
case SHAPE_VER:
shape = 5; break;
}
- UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0));
+ UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0 || c.blinkoff == 0));
unibi_out_ext(tui, tui->unibi_ext.set_cursor_style);
}
@@ -2273,6 +2294,7 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
bool putty = terminfo_is_term_family(term, "putty");
bool screen = terminfo_is_term_family(term, "screen");
bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
+ bool st = terminfo_is_term_family(term, "st");
bool iterm = terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iterm2")
|| terminfo_is_term_family(term, "iTerm.app")
@@ -2357,9 +2379,10 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
// would use a tmux control sequence and an extra if(screen) test.
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)
+ } else if ((xterm || hterm || rxvt || tmux || alacritty || st)
&& (vte_version == 0 || vte_version >= 3900)) {
// Supported in urxvt, newer VTE.
+ // Supported in st, but currently missing in ncurses definitions. #32217
tui->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
"\033]12;%p1%s\007");
}
diff --git a/src/nvim/tui/tui_defs.h b/src/nvim/tui/tui_defs.h
index bd99d6b0ad..5d6f027bf7 100644
--- a/src/nvim/tui/tui_defs.h
+++ b/src/nvim/tui/tui_defs.h
@@ -5,6 +5,7 @@ typedef struct TUIData TUIData;
typedef enum {
kTermModeSynchronizedOutput = 2026,
kTermModeGraphemeClusters = 2027,
+ kTermModeThemeUpdates = 2031,
kTermModeResizeEvents = 2048,
} TermMode;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 7c81110ae9..51815c36e1 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -6,7 +6,6 @@
#include <string.h>
#include <uv.h>
-#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/ui.h"
@@ -224,10 +223,10 @@ void ui_refresh(void)
// Reset 'cmdheight' for all tabpages when ext_messages toggles.
if (had_message != ui_ext[kUIMessages]) {
set_option_value(kOptCmdheight, NUMBER_OPTVAL(had_message), 0);
- command_height();
FOR_ALL_TABS(tp) {
tp->tp_ch_used = had_message;
}
+ msg_scroll_flush();
}
if (!ui_active()) {
@@ -325,7 +324,7 @@ void ui_busy_stop(void)
/// Emit a bell or visualbell as a warning
///
-/// val is one of the BO_ values, e.g., BO_OPER
+/// val is one of the OptBoFlags values, e.g., kOptBoFlagOperator
void vim_beep(unsigned val)
{
called_vim_beep = true;
@@ -334,7 +333,7 @@ void vim_beep(unsigned val)
return;
}
- if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
+ if (!((bo_flags & val) || (bo_flags & kOptBoFlagAll))) {
static int beeps = 0;
static uint64_t start_time = 0;
@@ -477,7 +476,7 @@ void ui_line(ScreenGrid *grid, int row, bool invalid_row, int startcol, int endc
(const sattr_T *)grid->attrs + off);
// 'writedelay': flush & delay each time.
- if (p_wd && (rdb_flags & RDB_LINE)) {
+ if (p_wd && (rdb_flags & kOptRdbFlagLine)) {
// If 'writedelay' is active, set the cursor to indicate what was drawn.
ui_call_grid_cursor_goto(grid->handle, row,
MIN(clearcol, (int)grid->cols - 1));
@@ -564,7 +563,7 @@ void ui_flush(void)
}
ui_call_flush();
- if (p_wd && (rdb_flags & RDB_FLUSH)) {
+ if (p_wd && (rdb_flags & kOptRdbFlagFlush)) {
os_sleep((uint64_t)llabs(p_wd));
}
}
@@ -718,10 +717,10 @@ void ui_call_event(char *name, bool fast, Array args)
bool handled = false;
UIEventCallback *event_cb;
- // Prompt messages should be shown immediately so must be safe
+ // Return prompt is still a non-fast event, other prompt messages are
+ // followed by a "cmdline_show" event.
if (strcmp(name, "msg_show") == 0) {
- char *kind = args.items[0].data.string.data;
- fast = !kind || (strncmp(kind, "confirm", 7) != 0 && strcmp(kind, "return_prompt") != 0);
+ fast = !strequal(args.items[0].data.string.data, "return_prompt");
}
map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, {
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index adaf3eadb8..af946d799a 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -1,5 +1,6 @@
/// Nvim's own UI client, which attaches to a child or remote Nvim server.
+#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index e28e8d4da7..38d882462d 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -101,6 +101,35 @@ bool ui_comp_should_draw(void)
return composed_uis != 0 && valid_screen;
}
+/// Raises or lowers the layer, syncing comp_index with zindex.
+///
+/// This function adjusts the position of a layer in the layers array
+/// based on its zindex, either raising or lowering it.
+///
+/// @param[in] layer_idx Index of the layer to be raised or lowered.
+/// @param[in] raise Raise the layer if true, else lower it.
+void ui_comp_layers_adjust(size_t layer_idx, bool raise)
+{
+ size_t size = layers.size;
+ ScreenGrid *layer = layers.items[layer_idx];
+
+ if (raise) {
+ while (layer_idx < size - 1 && layer->zindex > layers.items[layer_idx + 1]->zindex) {
+ layers.items[layer_idx] = layers.items[layer_idx + 1];
+ layers.items[layer_idx]->comp_index = layer_idx;
+ layer_idx++;
+ }
+ } else {
+ while (layer_idx > 0 && layer->zindex < layers.items[layer_idx - 1]->zindex) {
+ layers.items[layer_idx] = layers.items[layer_idx - 1];
+ layers.items[layer_idx]->comp_index = layer_idx;
+ layer_idx--;
+ }
+ }
+ layers.items[layer_idx] = layer;
+ layer->comp_index = layer_idx;
+}
+
/// Places `grid` at (col,row) position with (width * height) size.
/// Adds `grid` as the top layer if it is a new layer.
///
@@ -425,7 +454,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
for (int i = skipstart; i < (endcol - skipend) - startcol; i++) {
if (attrbuf[i] < 0) {
- if (rdb_flags & RDB_INVALID) {
+ if (rdb_flags & kOptRdbFlagInvalid) {
abort();
} else {
attrbuf[i] = 0;
@@ -441,7 +470,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
static void compose_debug(Integer startrow, Integer endrow, Integer startcol, Integer endcol,
int syn_id, bool delay)
{
- if (!(rdb_flags & RDB_COMPOSITOR) || startcol >= endcol) {
+ if (!(rdb_flags & kOptRdbFlagCompositor) || startcol >= endcol) {
return;
}
@@ -637,7 +666,7 @@ void ui_comp_grid_scroll(Integer grid, Integer top, Integer bot, Integer left, I
}
} else {
ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
- if (rdb_flags & RDB_COMPOSITOR) {
+ if (rdb_flags & kOptRdbFlagCompositor) {
debug_delay(2);
}
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 0f8857a6bd..8d791a4c38 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -89,7 +89,6 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/errors.h"
@@ -107,12 +106,10 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
-#include "nvim/marktree_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memline_defs.h"
@@ -1862,6 +1859,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
u_oldcount = -1;
}
+ msg_ext_set_kind("undo");
int count = startcount;
while (count--) {
// Do the change warning now, so that it triggers FileChangedRO when
@@ -2528,7 +2526,7 @@ static void u_undoredo(bool undo, bool do_buf_event)
/// @param absolute used ":undo N"
static void u_undo_end(bool did_undo, bool absolute, bool quiet)
{
- if ((fdo_flags & FDO_UNDO) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagUndo) && KeyTyped) {
foldOpenCursor();
}
@@ -2595,12 +2593,12 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
check_pos(curbuf, &VIsual);
}
- smsg_hl_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"),
- u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
- _(msgstr),
- did_undo ? _("before") : _("after"),
- uhp == NULL ? 0 : (int64_t)uhp->uh_seq,
- msgbuf);
+ smsg_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"),
+ u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
+ _(msgstr),
+ did_undo ? _("before") : _("after"),
+ uhp == NULL ? 0 : (int64_t)uhp->uh_seq,
+ msgbuf);
}
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index d27899b10f..2aa789a2ee 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -18,7 +18,6 @@
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
@@ -26,6 +25,7 @@
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 368bf79cf4..fc8819bfcc 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -9,7 +9,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
#include "auto/versiondef.h" // version info generated by the build system
#include "auto/versiondef_git.h"
@@ -23,6 +22,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/lua/executor.h"
@@ -33,6 +33,7 @@
#include "nvim/os/os.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
+#include "nvim/ui_defs.h"
#include "nvim/version.h"
#include "nvim/window.h"
diff --git a/src/vterm/LICENSE b/src/nvim/vterm/LICENSE
index 0d051634b2..0d051634b2 100644
--- a/src/vterm/LICENSE
+++ b/src/nvim/vterm/LICENSE
diff --git a/src/nvim/vterm/README.md b/src/nvim/vterm/README.md
new file mode 100644
index 0000000000..4cc43bfb94
--- /dev/null
+++ b/src/nvim/vterm/README.md
@@ -0,0 +1 @@
+Adopted from [libvterm](https://www.leonerd.org.uk/code/libvterm/)
diff --git a/src/nvim/vterm/encoding.c b/src/nvim/vterm/encoding.c
new file mode 100644
index 0000000000..f9061e8e50
--- /dev/null
+++ b/src/nvim/vterm/encoding.c
@@ -0,0 +1,271 @@
+#include "nvim/vterm/encoding.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/encoding.c.generated.h"
+#endif
+
+#define UNICODE_INVALID 0xFFFD
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_PRINT_UTF8
+#endif
+
+struct UTF8DecoderData {
+ // number of bytes remaining in this codepoint
+ int bytes_remaining;
+
+ // number of bytes total in this codepoint once it's finished
+ // (for detecting overlongs)
+ int bytes_total;
+
+ int this_cp;
+};
+
+static void init_utf8(VTermEncoding *enc, void *data_)
+{
+ struct UTF8DecoderData *data = data_;
+
+ data->bytes_remaining = 0;
+ data->bytes_total = 0;
+}
+
+static void decode_utf8(VTermEncoding *enc, void *data_, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct UTF8DecoderData *data = data_;
+
+#ifdef DEBUG_PRINT_UTF8
+ printf("BEGIN UTF-8\n");
+#endif
+
+ for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ uint8_t c = (uint8_t)bytes[*pos];
+
+#ifdef DEBUG_PRINT_UTF8
+ printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
+#endif
+
+ if (c < 0x20) { // C0
+ return;
+ } else if (c >= 0x20 && c < 0x7f) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ cp[(*cpi)++] = c;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 char: U+%04x\n", c);
+#endif
+ data->bytes_remaining = 0;
+ } else if (c == 0x7f) { // DEL
+ return;
+ } else if (c >= 0x80 && c < 0xc0) {
+ if (!data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ continue;
+ }
+
+ data->this_cp <<= 6;
+ data->this_cp |= c & 0x3f;
+ data->bytes_remaining--;
+
+ if (!data->bytes_remaining) {
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
+#endif
+ // Check for overlong sequences
+ switch (data->bytes_total) {
+ case 2:
+ if (data->this_cp < 0x0080) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 3:
+ if (data->this_cp < 0x0800) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 4:
+ if (data->this_cp < 0x10000) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 5:
+ if (data->this_cp < 0x200000) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 6:
+ if (data->this_cp < 0x4000000) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ }
+ // Now look for plain invalid ones
+ if ((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF)
+ || data->this_cp == 0xFFFE
+ || data->this_cp == 0xFFFF) {
+ data->this_cp = UNICODE_INVALID;
+ }
+#ifdef DEBUG_PRINT_UTF8
+ printf(" char: U+%04x\n", data->this_cp);
+#endif
+ cp[(*cpi)++] = (uint32_t)data->this_cp;
+ }
+ } else if (c >= 0xc0 && c < 0xe0) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x1f;
+ data->bytes_total = 2;
+ data->bytes_remaining = 1;
+ } else if (c >= 0xe0 && c < 0xf0) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x0f;
+ data->bytes_total = 3;
+ data->bytes_remaining = 2;
+ } else if (c >= 0xf0 && c < 0xf8) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x07;
+ data->bytes_total = 4;
+ data->bytes_remaining = 3;
+ } else if (c >= 0xf8 && c < 0xfc) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x03;
+ data->bytes_total = 5;
+ data->bytes_remaining = 4;
+ } else if (c >= 0xfc && c < 0xfe) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x01;
+ data->bytes_total = 6;
+ data->bytes_remaining = 5;
+ } else {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+ }
+}
+
+static VTermEncoding encoding_utf8 = {
+ .init = &init_utf8,
+ .decode = &decode_utf8,
+};
+
+static void decode_usascii(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ int is_gr = bytes[*pos] & 0x80;
+
+ for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ uint8_t c = (uint8_t)(bytes[*pos] ^ is_gr);
+
+ if (c < 0x20 || c == 0x7f || c >= 0x80) {
+ return;
+ }
+
+ cp[(*cpi)++] = c;
+ }
+}
+
+static VTermEncoding encoding_usascii = {
+ .decode = &decode_usascii,
+};
+
+struct StaticTableEncoding {
+ const VTermEncoding enc;
+ const uint32_t chars[128];
+};
+
+static void decode_table(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
+ int is_gr = bytes[*pos] & 0x80;
+
+ for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ uint8_t c = (uint8_t)(bytes[*pos] ^ is_gr);
+
+ if (c < 0x20 || c == 0x7f || c >= 0x80) {
+ return;
+ }
+
+ if (table->chars[c]) {
+ cp[(*cpi)++] = table->chars[c];
+ } else {
+ cp[(*cpi)++] = c;
+ }
+ }
+}
+
+// https://en.wikipedia.org/wiki/DEC_Special_Graphics
+static const struct StaticTableEncoding encoding_DECdrawing = {
+ { .decode = &decode_table },
+ {
+ [0x60] = 0x25C6, // BLACK DIAMOND
+ [0x61] = 0x2592, // MEDIUM SHADE (checkerboard)
+ [0x62] = 0x2409, // SYMBOL FOR HORIZONTAL TAB
+ [0x63] = 0x240C, // SYMBOL FOR FORM FEED
+ [0x64] = 0x240D, // SYMBOL FOR CARRIAGE RETURN
+ [0x65] = 0x240A, // SYMBOL FOR LINE FEED
+ [0x66] = 0x00B0, // DEGREE SIGN
+ [0x67] = 0x00B1, // PLUS-MINUS SIGN (plus or minus)
+ [0x68] = 0x2424, // SYMBOL FOR NEW LINE
+ [0x69] = 0x240B, // SYMBOL FOR VERTICAL TAB
+ [0x6a] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
+ [0x6b] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
+ [0x6c] = 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
+ [0x6d] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
+ [0x6e] = 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
+ [0x6f] = 0x23BA, // HORIZONTAL SCAN LINE-1
+ [0x70] = 0x23BB, // HORIZONTAL SCAN LINE-3
+ [0x71] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
+ [0x72] = 0x23BC, // HORIZONTAL SCAN LINE-7
+ [0x73] = 0x23BD, // HORIZONTAL SCAN LINE-9
+ [0x74] = 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ [0x75] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ [0x76] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ [0x77] = 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ [0x78] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
+ [0x79] = 0x2A7D, // LESS-THAN OR SLANTED EQUAL-TO
+ [0x7a] = 0x2A7E, // GREATER-THAN OR SLANTED EQUAL-TO
+ [0x7b] = 0x03C0, // GREEK SMALL LETTER PI
+ [0x7c] = 0x2260, // NOT EQUAL TO
+ [0x7d] = 0x00A3, // POUND SIGN
+ [0x7e] = 0x00B7, // MIDDLE DOT
+ }
+};
+
+static struct {
+ VTermEncodingType type;
+ char designation;
+ VTermEncoding *enc;
+}
+encodings[] = {
+ { ENC_UTF8, 'u', &encoding_utf8 },
+ { ENC_SINGLE_94, '0', (VTermEncoding *)&encoding_DECdrawing },
+ { ENC_SINGLE_94, 'B', &encoding_usascii },
+ { 0 },
+};
+
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
+{
+ for (int i = 0; encodings[i].designation; i++) {
+ if (encodings[i].type == type && encodings[i].designation == designation) {
+ return encodings[i].enc;
+ }
+ }
+ return NULL;
+}
diff --git a/src/nvim/vterm/encoding.h b/src/nvim/vterm/encoding.h
new file mode 100644
index 0000000000..204b6d90c9
--- /dev/null
+++ b/src/nvim/vterm/encoding.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/encoding.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/keyboard.c b/src/nvim/vterm/keyboard.c
new file mode 100644
index 0000000000..dd088ac40e
--- /dev/null
+++ b/src/nvim/vterm/keyboard.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+
+#include "nvim/ascii_defs.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/vterm/keyboard.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/keyboard.c.generated.h"
+#endif
+
+static VTermKeyEncodingFlags vterm_state_get_key_encoding_flags(const VTermState *state)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ const struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size > 0);
+ return stack->items[stack->size - 1];
+}
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
+{
+ bool passthru = false;
+ if (c == ' ') {
+ // Space is passed through only when there are no modifiers (including shift)
+ passthru = mod == VTERM_MOD_NONE;
+ } else {
+ // Otherwise pass through when there are no modifiers (ignoring shift)
+ passthru = (mod & (unsigned)~VTERM_MOD_SHIFT) == 0;
+ }
+
+ if (passthru) {
+ char str[6];
+ int seqlen = fill_utf8((int)c, str);
+ vterm_push_output_bytes(vt, str, (size_t)seqlen);
+ return;
+ }
+
+ VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state);
+ if (flags.disambiguate) {
+ // Always use unshifted codepoint
+ if (c >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ mod |= VTERM_MOD_SHIFT;
+ }
+
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod + 1);
+ return;
+ }
+
+ if (mod & VTERM_MOD_CTRL) {
+ // Handle special cases. These are taken from kitty, but seem mostly
+ // consistent across terminals.
+ switch (c) {
+ case '2':
+ case ' ':
+ // Ctrl+2 is NUL to match Ctrl+@ (which is Shift+2 on US keyboards)
+ // Ctrl+Space is also NUL for some reason
+ c = 0x00;
+ break;
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ // Ctrl+3 through Ctrl+7 are sequential starting from 0x1b. Importantly,
+ // this means that Ctrl+6 emits 0x1e (the same as Ctrl+^ on US keyboards)
+ c = 0x1b + c - '3';
+ break;
+ case '8':
+ // Ctrl+8 is DEL
+ c = 0x7f;
+ break;
+ case '/':
+ // Ctrl+/ is equivalent to Ctrl+_ for historic reasons
+ c = 0x1f;
+ break;
+ default:
+ if (c >= '@' && c <= 0x7f) {
+ c &= 0x1f;
+ }
+ break;
+ }
+ }
+
+ vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
+}
+
+typedef struct {
+ enum {
+ KEYCODE_NONE,
+ KEYCODE_LITERAL,
+ KEYCODE_TAB,
+ KEYCODE_ENTER,
+ KEYCODE_SS3,
+ KEYCODE_CSI,
+ KEYCODE_CSI_CURSOR,
+ KEYCODE_CSINUM,
+ KEYCODE_KEYPAD,
+ } type;
+ int literal;
+ int csinum;
+} keycodes_s;
+
+static keycodes_s keycodes[] = {
+ { KEYCODE_NONE, NUL, 0 }, // NONE
+
+ { KEYCODE_ENTER, '\r', 0 }, // ENTER
+ { KEYCODE_TAB, '\t', 0 }, // TAB
+ { KEYCODE_LITERAL, '\x7f', 0 }, // BACKSPACE == ASCII DEL
+ { KEYCODE_LITERAL, '\x1b', 0 }, // ESCAPE
+
+ { KEYCODE_CSI_CURSOR, 'A', 0 }, // UP
+ { KEYCODE_CSI_CURSOR, 'B', 0 }, // DOWN
+ { KEYCODE_CSI_CURSOR, 'D', 0 }, // LEFT
+ { KEYCODE_CSI_CURSOR, 'C', 0 }, // RIGHT
+
+ { KEYCODE_CSINUM, '~', 2 }, // INS
+ { KEYCODE_CSINUM, '~', 3 }, // DEL
+ { KEYCODE_CSI_CURSOR, 'H', 0 }, // HOME
+ { KEYCODE_CSI_CURSOR, 'F', 0 }, // END
+ { KEYCODE_CSINUM, '~', 5 }, // PAGEUP
+ { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
+};
+
+static keycodes_s keycodes_fn[] = {
+ { KEYCODE_NONE, NUL, 0 }, // F0 - shouldn't happen
+ { KEYCODE_SS3, 'P', 0 }, // F1
+ { KEYCODE_SS3, 'Q', 0 }, // F2
+ { KEYCODE_SS3, 'R', 0 }, // F3
+ { KEYCODE_SS3, 'S', 0 }, // F4
+ { KEYCODE_CSINUM, '~', 15 }, // F5
+ { KEYCODE_CSINUM, '~', 17 }, // F6
+ { KEYCODE_CSINUM, '~', 18 }, // F7
+ { KEYCODE_CSINUM, '~', 19 }, // F8
+ { KEYCODE_CSINUM, '~', 20 }, // F9
+ { KEYCODE_CSINUM, '~', 21 }, // F10
+ { KEYCODE_CSINUM, '~', 23 }, // F11
+ { KEYCODE_CSINUM, '~', 24 }, // F12
+};
+
+static keycodes_s keycodes_kp[] = {
+ { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
+ { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
+ { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
+ { KEYCODE_KEYPAD, '3', 's' }, // KP_3
+ { KEYCODE_KEYPAD, '4', 't' }, // KP_4
+ { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
+ { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
+ { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
+ { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
+ { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
+ { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
+};
+
+static keycodes_s keycodes_kp_csiu[] = {
+ { KEYCODE_KEYPAD, 57399, 'p' }, // KP_0
+ { KEYCODE_KEYPAD, 57400, 'q' }, // KP_1
+ { KEYCODE_KEYPAD, 57401, 'r' }, // KP_2
+ { KEYCODE_KEYPAD, 57402, 's' }, // KP_3
+ { KEYCODE_KEYPAD, 57403, 't' }, // KP_4
+ { KEYCODE_KEYPAD, 57404, 'u' }, // KP_5
+ { KEYCODE_KEYPAD, 57405, 'v' }, // KP_6
+ { KEYCODE_KEYPAD, 57406, 'w' }, // KP_7
+ { KEYCODE_KEYPAD, 57407, 'x' }, // KP_8
+ { KEYCODE_KEYPAD, 57408, 'y' }, // KP_9
+ { KEYCODE_KEYPAD, 57411, 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, 57413, 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, 57416, 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, 57412, 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, 57409, 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, 57410, 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, 57414, 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, 57415, 'X' }, // KP_EQUAL
+};
+
+void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
+{
+ if (key == VTERM_KEY_NONE) {
+ return;
+ }
+
+ VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state);
+
+ keycodes_s k;
+ if (key < VTERM_KEY_FUNCTION_0) {
+ if (key >= sizeof(keycodes)/sizeof(keycodes[0])) {
+ return;
+ }
+ k = keycodes[key];
+ } else if (key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
+ if ((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0])) {
+ return;
+ }
+ k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
+ } else if (key >= VTERM_KEY_KP_0) {
+ if ((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0])) {
+ return;
+ }
+
+ if (flags.disambiguate) {
+ k = keycodes_kp_csiu[key - VTERM_KEY_KP_0];
+ } else {
+ k = keycodes_kp[key - VTERM_KEY_KP_0];
+ }
+ }
+
+ switch (k.type) {
+ case KEYCODE_NONE:
+ break;
+
+ case KEYCODE_TAB:
+ // Shift-Tab is CSI Z but plain Tab is 0x09
+ if (flags.disambiguate) {
+ goto case_LITERAL;
+ } else if (mod == VTERM_MOD_SHIFT) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
+ } else if (mod & VTERM_MOD_SHIFT) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod + 1);
+ } else {
+ goto case_LITERAL;
+ }
+ break;
+
+ case KEYCODE_ENTER:
+ // Enter is CRLF in newline mode, but just LF in linefeed
+ if (vt->state->mode.newline) {
+ vterm_push_output_sprintf(vt, "\r\n");
+ } else {
+ goto case_LITERAL;
+ }
+ break;
+
+ case KEYCODE_LITERAL:
+ case_LITERAL:
+ if (flags.disambiguate) {
+ switch (key) {
+ case VTERM_KEY_TAB:
+ case VTERM_KEY_ENTER:
+ case VTERM_KEY_BACKSPACE:
+ // If there are no mods then leave these as-is
+ flags.disambiguate = mod != VTERM_MOD_NONE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (flags.disambiguate) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod + 1);
+ } else {
+ vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
+ }
+ break;
+
+ case KEYCODE_SS3:
+ case_SS3:
+ if (mod == 0) {
+ vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
+ } else {
+ goto case_CSI;
+ }
+ break;
+
+ case KEYCODE_CSI:
+ case_CSI:
+ if (mod == 0) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
+ } else {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
+ }
+ break;
+
+ case KEYCODE_CSINUM:
+ if (mod == 0) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
+ } else {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
+ }
+ break;
+
+ case KEYCODE_CSI_CURSOR:
+ if (vt->state->mode.cursor) {
+ goto case_SS3;
+ } else {
+ goto case_CSI;
+ }
+
+ case KEYCODE_KEYPAD:
+ if (vt->state->mode.keypad) {
+ k.literal = k.csinum;
+ goto case_SS3;
+ } else {
+ goto case_LITERAL;
+ }
+ }
+}
+
+void vterm_keyboard_start_paste(VTerm *vt)
+{
+ if (vt->state->mode.bracketpaste) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
+ }
+}
+
+void vterm_keyboard_end_paste(VTerm *vt)
+{
+ if (vt->state->mode.bracketpaste) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
+ }
+}
diff --git a/src/nvim/vterm/keyboard.h b/src/nvim/vterm/keyboard.h
new file mode 100644
index 0000000000..af5f4a3ed1
--- /dev/null
+++ b/src/nvim/vterm/keyboard.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/keyboard.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/mouse.c b/src/nvim/vterm/mouse.c
new file mode 100644
index 0000000000..2f3b1d9e2f
--- /dev/null
+++ b/src/nvim/vterm/mouse.c
@@ -0,0 +1,124 @@
+#include "nvim/math.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/vterm/mouse.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/mouse.c.generated.h"
+#endif
+
+static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
+{
+ modifiers <<= 2;
+
+ switch (state->mouse_protocol) {
+ case MOUSE_X10:
+ if (col + 0x21 > 0xff) {
+ col = 0xff - 0x21;
+ }
+ if (row + 0x21 > 0xff) {
+ row = 0xff - 0x21;
+ }
+
+ if (!pressed) {
+ code = 3;
+ }
+
+ if (code & 0x80) {
+ break;
+ }
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
+ (code | modifiers) + 0x20, col + 0x21, row + 0x21);
+ break;
+
+ case MOUSE_UTF8: {
+ char utf8[18];
+ size_t len = 0;
+
+ if (!pressed) {
+ code = 3;
+ }
+
+ len += (size_t)fill_utf8((code | modifiers) + 0x20, utf8 + len);
+ len += (size_t)fill_utf8(col + 0x21, utf8 + len);
+ len += (size_t)fill_utf8(row + 0x21, utf8 + len);
+ utf8[len] = 0;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
+ }
+ break;
+
+ case MOUSE_SGR:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
+ code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
+ break;
+
+ case MOUSE_RXVT:
+ if (!pressed) {
+ code = 3;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
+ code | modifiers, col + 1, row + 1);
+ break;
+ }
+}
+
+void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ if (col == state->mouse_col && row == state->mouse_row) {
+ return;
+ }
+
+ state->mouse_col = col;
+ state->mouse_row = row;
+
+ if ((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons)
+ || (state->mouse_flags & MOUSE_WANT_MOVE)) {
+ if (state->mouse_buttons) {
+ int button = xctz((uint64_t)state->mouse_buttons) + 1;
+ if (button < 4) {
+ output_mouse(state, button - 1 + 0x20, 1, (int)mod, col, row);
+ } else if (button >= 8 && button < 12) {
+ output_mouse(state, button - 8 + 0x80 + 0x20, 1, (int)mod, col, row);
+ }
+ } else {
+ output_mouse(state, 3 + 0x20, 1, (int)mod, col, row);
+ }
+ }
+}
+
+void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ int old_buttons = state->mouse_buttons;
+
+ if ((button > 0 && button <= 3) || (button >= 8 && button <= 11)) {
+ if (pressed) {
+ state->mouse_buttons |= (1 << (button - 1));
+ } else {
+ state->mouse_buttons &= ~(1 << (button - 1));
+ }
+ }
+
+ // Most of the time we don't get button releases from 4/5/6/7
+ if (state->mouse_buttons == old_buttons && (button < 4 || button > 7)) {
+ return;
+ }
+
+ if (!state->mouse_flags) {
+ return;
+ }
+
+ if (button < 4) {
+ output_mouse(state, button - 1, pressed, (int)mod, state->mouse_col, state->mouse_row);
+ } else if (button < 8) {
+ output_mouse(state, button - 4 + 0x40, pressed, (int)mod, state->mouse_col, state->mouse_row);
+ } else if (button < 12) {
+ output_mouse(state, button - 8 + 0x80, pressed, (int)mod, state->mouse_col, state->mouse_row);
+ }
+}
diff --git a/src/nvim/vterm/mouse.h b/src/nvim/vterm/mouse.h
new file mode 100644
index 0000000000..477f4028a2
--- /dev/null
+++ b/src/nvim/vterm/mouse.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/mouse.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/parser.c b/src/nvim/vterm/parser.c
new file mode 100644
index 0000000000..79d348f2c1
--- /dev/null
+++ b/src/nvim/vterm/parser.c
@@ -0,0 +1,411 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/vterm/parser.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/parser.c.generated.h"
+#endif
+
+#undef DEBUG_PARSER
+
+static bool is_intermed(uint8_t c)
+{
+ return c >= 0x20 && c <= 0x2f;
+}
+
+static void do_control(VTerm *vt, uint8_t control)
+{
+ if (vt->parser.callbacks && vt->parser.callbacks->control) {
+ if ((*vt->parser.callbacks->control)(control, vt->parser.cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
+}
+
+static void do_csi(VTerm *vt, char command)
+{
+#ifdef DEBUG_PARSER
+ printf("Parsed CSI args as:\n", arglen, args);
+ printf(" leader: %s\n", vt->parser.v.csi.leader);
+ for (int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
+ printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
+ if (!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi])) {
+ printf("\n");
+ }
+ printf(" intermed: %s\n", vt->parser.intermed);
+ }
+#endif
+
+ if (vt->parser.callbacks && vt->parser.callbacks->csi) {
+ if ((*vt->parser.callbacks->csi)(vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
+ vt->parser.v.csi.args,
+ vt->parser.v.csi.argi,
+ vt->parser.intermedlen ? vt->parser.intermed : NULL,
+ command,
+ vt->parser.cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
+}
+
+static void do_escape(VTerm *vt, char command)
+{
+ char seq[INTERMED_MAX + 1];
+
+ size_t len = (size_t)vt->parser.intermedlen;
+ strncpy(seq, vt->parser.intermed, len); // NOLINT(runtime/printf)
+ seq[len++] = command;
+ seq[len] = 0;
+
+ if (vt->parser.callbacks && vt->parser.callbacks->escape) {
+ if ((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
+}
+
+static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
+{
+ VTermStringFragment frag = {
+ .str = str,
+ .len = len,
+ .initial = vt->parser.string_initial,
+ .final = final,
+ };
+
+ switch (vt->parser.state) {
+ case OSC:
+ if (vt->parser.callbacks && vt->parser.callbacks->osc) {
+ (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
+ }
+ break;
+
+ case DCS_VTERM:
+ if (vt->parser.callbacks && vt->parser.callbacks->dcs) {
+ (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, (size_t)vt->parser.v.dcs.commandlen,
+ frag,
+ vt->parser.cbdata);
+ }
+ break;
+
+ case APC:
+ if (vt->parser.callbacks && vt->parser.callbacks->apc) {
+ (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
+ }
+ break;
+
+ case PM:
+ if (vt->parser.callbacks && vt->parser.callbacks->pm) {
+ (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
+ }
+ break;
+
+ case SOS:
+ if (vt->parser.callbacks && vt->parser.callbacks->sos) {
+ (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
+ }
+ break;
+
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ break;
+ }
+
+ vt->parser.string_initial = false;
+}
+
+size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
+{
+ size_t pos = 0;
+ const char *string_start;
+
+ switch (vt->parser.state) {
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ string_start = NULL;
+ break;
+ case OSC:
+ case DCS_VTERM:
+ case APC:
+ case PM:
+ case SOS:
+ string_start = bytes;
+ break;
+ }
+
+#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while (0)
+#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
+
+#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
+
+ for (; pos < len; pos++) {
+ uint8_t c = (uint8_t)bytes[pos];
+ bool c1_allowed = !vt->mode.utf8;
+
+ if (c == 0x00 || c == 0x7f) { // NUL, DEL
+ if (IS_STRING_STATE()) {
+ string_fragment(vt, string_start, (size_t)(bytes + pos - string_start), false);
+ string_start = bytes + pos + 1;
+ }
+ if (vt->parser.emit_nul) {
+ do_control(vt, c);
+ }
+ continue;
+ }
+ if (c == 0x18 || c == 0x1a) { // CAN, SUB
+ vt->parser.in_esc = false;
+ ENTER_NORMAL_STATE();
+ if (vt->parser.emit_nul) {
+ do_control(vt, c);
+ }
+ continue;
+ } else if (c == 0x1b) { // ESC
+ vt->parser.intermedlen = 0;
+ if (!IS_STRING_STATE()) {
+ vt->parser.state = NORMAL;
+ }
+ vt->parser.in_esc = true;
+ continue;
+ } else if (c == 0x07 // BEL, can stand for ST in OSC or DCS state
+ && IS_STRING_STATE()) {} else if (c < 0x20) { // other C0
+ if (vt->parser.state == SOS) {
+ continue; // All other C0s permitted in SOS
+ }
+ if (IS_STRING_STATE()) {
+ string_fragment(vt, string_start, (size_t)(bytes + pos - string_start), false);
+ }
+ do_control(vt, c);
+ if (IS_STRING_STATE()) {
+ string_start = bytes + pos + 1;
+ }
+ continue;
+ }
+
+ size_t string_len = (size_t)(bytes + pos - string_start);
+
+ if (vt->parser.in_esc) {
+ // Hoist an ESC letter into a C1 if we're not in a string mode
+ // Always accept ESC \ == ST even in string mode
+ if (!vt->parser.intermedlen
+ && c >= 0x40 && c < 0x60
+ && ((!IS_STRING_STATE() || c == 0x5c))) {
+ c += 0x40;
+ c1_allowed = true;
+ if (string_len) {
+ assert(string_len > 0);
+ string_len -= 1;
+ }
+ vt->parser.in_esc = false;
+ } else {
+ string_start = NULL;
+ vt->parser.state = NORMAL;
+ }
+ }
+
+ switch (vt->parser.state) {
+ case CSI_LEADER:
+ // Extract leader bytes 0x3c to 0x3f
+ if (c >= 0x3c && c <= 0x3f) {
+ if (vt->parser.v.csi.leaderlen < CSI_LEADER_MAX - 1) {
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = (char)c;
+ }
+ break;
+ }
+
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
+
+ vt->parser.v.csi.argi = 0;
+ vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
+ vt->parser.state = CSI_ARGS;
+
+ FALLTHROUGH;
+ case CSI_ARGS:
+ // Numerical value of argument
+ if (c >= '0' && c <= '9') {
+ if (vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING) {
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
+ }
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
+ break;
+ }
+ if (c == ':') {
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
+ c = ';';
+ }
+ if (c == ';') {
+ vt->parser.v.csi.argi++;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
+ break;
+ }
+
+ vt->parser.v.csi.argi++;
+ vt->parser.intermedlen = 0;
+ vt->parser.state = CSI_INTERMED;
+ FALLTHROUGH;
+ case CSI_INTERMED:
+ if (is_intermed(c)) {
+ if (vt->parser.intermedlen < INTERMED_MAX - 1) {
+ vt->parser.intermed[vt->parser.intermedlen++] = (char)c;
+ }
+ break;
+ } else if (c == 0x1b) {
+ // ESC in CSI cancels
+ } else if (c >= 0x40 && c <= 0x7e) {
+ vt->parser.intermed[vt->parser.intermedlen] = 0;
+ do_csi(vt, (char)c);
+ }
+ // else was invalid CSI
+
+ ENTER_NORMAL_STATE();
+ break;
+
+ case OSC_COMMAND:
+ // Numerical value of command
+ if (c >= '0' && c <= '9') {
+ if (vt->parser.v.osc.command == -1) {
+ vt->parser.v.osc.command = 0;
+ } else {
+ vt->parser.v.osc.command *= 10;
+ }
+ vt->parser.v.osc.command += c - '0';
+ break;
+ }
+ if (c == ';') {
+ vt->parser.state = OSC;
+ string_start = bytes + pos + 1;
+ break;
+ }
+
+ string_start = bytes + pos;
+ string_len = 0;
+ vt->parser.state = OSC;
+ goto string_state;
+
+ case DCS_COMMAND:
+ if (vt->parser.v.dcs.commandlen < CSI_LEADER_MAX) {
+ vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = (char)c;
+ }
+
+ if (c >= 0x40 && c <= 0x7e) {
+ string_start = bytes + pos + 1;
+ vt->parser.state = DCS_VTERM;
+ }
+ break;
+
+string_state:
+ case OSC:
+ case DCS_VTERM:
+ case APC:
+ case PM:
+ case SOS:
+ if (c == 0x07 || (c1_allowed && c == 0x9c)) {
+ string_fragment(vt, string_start, string_len, true);
+ ENTER_NORMAL_STATE();
+ }
+ break;
+
+ case NORMAL:
+ if (vt->parser.in_esc) {
+ if (is_intermed(c)) {
+ if (vt->parser.intermedlen < INTERMED_MAX - 1) {
+ vt->parser.intermed[vt->parser.intermedlen++] = (char)c;
+ }
+ } else if (c >= 0x30 && c < 0x7f) {
+ do_escape(vt, (char)c);
+ vt->parser.in_esc = 0;
+ ENTER_NORMAL_STATE();
+ } else {
+ DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
+ }
+ break;
+ }
+ if (c1_allowed && c >= 0x80 && c < 0xa0) {
+ switch (c) {
+ case 0x90: // DCS
+ vt->parser.string_initial = true;
+ vt->parser.v.dcs.commandlen = 0;
+ ENTER_STATE(DCS_COMMAND);
+ break;
+ case 0x98: // SOS
+ vt->parser.string_initial = true;
+ ENTER_STATE(SOS);
+ string_start = bytes + pos + 1;
+ break;
+ case 0x9b: // CSI
+ vt->parser.v.csi.leaderlen = 0;
+ ENTER_STATE(CSI_LEADER);
+ break;
+ case 0x9d: // OSC
+ vt->parser.v.osc.command = -1;
+ vt->parser.string_initial = true;
+ ENTER_STATE(OSC_COMMAND);
+ break;
+ case 0x9e: // PM
+ vt->parser.string_initial = true;
+ ENTER_STATE(PM);
+ string_start = bytes + pos + 1;
+ break;
+ case 0x9f: // APC
+ vt->parser.string_initial = true;
+ ENTER_STATE(APC);
+ string_start = bytes + pos + 1;
+ break;
+ default:
+ do_control(vt, c);
+ break;
+ }
+ } else {
+ size_t eaten = 0;
+ if (vt->parser.callbacks && vt->parser.callbacks->text) {
+ eaten = (size_t)(*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
+ }
+
+ if (!eaten) {
+ DEBUG_LOG("libvterm: Text callback did not consume any input\n");
+ // force it to make progress
+ eaten = 1;
+ }
+
+ pos += (eaten - 1); // we'll ++ it again in a moment
+ }
+ break;
+ }
+ }
+
+ if (string_start) {
+ size_t string_len = (size_t)(bytes + pos - string_start);
+ if (string_len > 0) {
+ if (vt->parser.in_esc) {
+ string_len -= 1;
+ }
+ string_fragment(vt, string_start, string_len, false);
+ }
+ }
+
+ return len;
+}
+
+void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
+{
+ vt->parser.callbacks = callbacks;
+ vt->parser.cbdata = user;
+}
diff --git a/src/nvim/vterm/parser.h b/src/nvim/vterm/parser.h
new file mode 100644
index 0000000000..168be830c0
--- /dev/null
+++ b/src/nvim/vterm/parser.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/parser.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/pen.c b/src/nvim/vterm/pen.c
new file mode 100644
index 0000000000..e7f50078ae
--- /dev/null
+++ b/src/nvim/vterm/pen.c
@@ -0,0 +1,644 @@
+#include <stdio.h>
+
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/pen.c.generated.h"
+#endif
+
+// Structure used to store RGB triples without the additional metadata stored in VTermColor.
+typedef struct {
+ uint8_t red, green, blue;
+} VTermRGB;
+
+static const VTermRGB ansi_colors[] = {
+ // R G B
+ { 0, 0, 0 }, // black
+ { 224, 0, 0 }, // red
+ { 0, 224, 0 }, // green
+ { 224, 224, 0 }, // yellow
+ { 0, 0, 224 }, // blue
+ { 224, 0, 224 }, // magenta
+ { 0, 224, 224 }, // cyan
+ { 224, 224, 224 }, // white == light grey
+
+ // high intensity
+ { 128, 128, 128 }, // black
+ { 255, 64, 64 }, // red
+ { 64, 255, 64 }, // green
+ { 255, 255, 64 }, // yellow
+ { 64, 64, 255 }, // blue
+ { 255, 64, 255 }, // magenta
+ { 64, 255, 255 }, // cyan
+ { 255, 255, 255 }, // white for real
+};
+
+static uint8_t ramp6[] = {
+ 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
+};
+
+static uint8_t ramp24[] = {
+ 0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
+ 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
+};
+
+static void lookup_default_colour_ansi(long idx, VTermColor *col)
+{
+ if (idx >= 0 && idx < 16) {
+ vterm_color_rgb(col,
+ ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
+ }
+}
+
+static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
+{
+ if (index >= 0 && index < 16) {
+ *col = state->colors[index];
+ return true;
+ }
+
+ return false;
+}
+
+static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
+{
+ if (index >= 0 && index < 16) {
+ // Normal 8 colours or high intensity - parse as palette 0
+ return lookup_colour_ansi(state, index, col);
+ } else if (index >= 16 && index < 232) {
+ // 216-colour cube
+ index -= 16;
+
+ vterm_color_rgb(col, ramp6[index/6/6 % 6],
+ ramp6[index/6 % 6],
+ ramp6[index % 6]);
+
+ return true;
+ } else if (index >= 232 && index < 256) {
+ // 24 greyscales
+ index -= 232;
+
+ vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
+
+ return true;
+ }
+
+ return false;
+}
+
+static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount,
+ VTermColor *col)
+{
+ switch (palette) {
+ case 2: // RGB mode - 3 args contain colour values directly
+ if (argcount < 3) {
+ return argcount;
+ }
+
+ vterm_color_rgb(col, (uint8_t)CSI_ARG(args[0]), (uint8_t)CSI_ARG(args[1]),
+ (uint8_t)CSI_ARG(args[2]));
+
+ return 3;
+
+ case 5: // XTerm 256-colour mode
+ if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
+ return argcount ? 1 : 0;
+ }
+
+ vterm_color_indexed(col, (uint8_t)args[0]);
+
+ return argcount ? 1 : 0;
+
+ default:
+ DEBUG_LOG("Unrecognised colour palette %d\n", palette);
+ return 0;
+ }
+}
+
+// Some conveniences
+
+static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
+{
+#ifdef DEBUG
+ if (type != vterm_get_attr_type(attr)) {
+ DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
+ attr, vterm_get_attr_type(attr), type);
+ return;
+ }
+#endif
+ if (state->callbacks && state->callbacks->setpenattr) {
+ (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+ }
+}
+
+static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
+{
+ VTermValue val = { .boolean = boolean };
+ setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
+}
+
+static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
+{
+ VTermValue val = { .number = number };
+ setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
+}
+
+static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
+{
+ VTermValue val = { .color = color };
+ setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
+}
+
+static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
+{
+ VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
+
+ vterm_color_indexed(colp, (uint8_t)col);
+
+ setpenattr_col(state, attr, *colp);
+}
+
+void vterm_state_newpen(VTermState *state)
+{
+ // 90% grey so that pure white is brighter
+ vterm_color_rgb(&state->default_fg, 240, 240, 240);
+ vterm_color_rgb(&state->default_bg, 0, 0, 0);
+ vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
+
+ for (int col = 0; col < 16; col++) {
+ lookup_default_colour_ansi(col, &state->colors[col]);
+ }
+}
+
+void vterm_state_resetpen(VTermState *state)
+{
+ state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ state->pen.underline = 0; setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+ state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ state->pen.font = 0; setpenattr_int(state, VTERM_ATTR_FONT, 0);
+ state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
+ state->pen.baseline = 0; setpenattr_int(state, VTERM_ATTR_BASELINE, 0);
+
+ state->pen.fg = state->default_fg;
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
+ state->pen.bg = state->default_bg;
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
+
+ state->pen.uri = 0; setpenattr_int(state, VTERM_ATTR_URI, 0);
+}
+
+void vterm_state_savepen(VTermState *state, int save)
+{
+ if (save) {
+ state->saved.pen = state->pen;
+ } else {
+ state->pen = state->saved.pen;
+
+ setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
+ setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
+ setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
+
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+
+ setpenattr_int(state, VTERM_ATTR_URI, state->pen.uri);
+ }
+}
+
+void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg,
+ const VTermColor *default_bg)
+{
+ if (default_fg) {
+ state->default_fg = *default_fg;
+ state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_FG;
+ }
+
+ if (default_bg) {
+ state->default_bg = *default_bg;
+ state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_BG;
+ }
+}
+
+void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
+{
+ if (index >= 0 && index < 16) {
+ state->colors[index] = *col;
+ }
+}
+
+/// Makes sure that the given color `col` is indeed an RGB colour. After this
+/// function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
+/// flags stored in `col->type` will have been reset.
+///
+/// @param state is the VTermState instance from which the colour palette should
+/// be extracted.
+/// @param col is a pointer at the VTermColor instance that should be converted
+/// to an RGB colour.
+void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
+{
+ if (VTERM_COLOR_IS_INDEXED(col)) { // Convert indexed colors to RGB
+ lookup_colour_palette(state, col->indexed.idx, col);
+ }
+ col->type &= VTERM_COLOR_TYPE_MASK; // Reset any metadata but the type
+}
+
+void vterm_state_setpen(VTermState *state, const long args[], int argcount)
+{
+ // SGR - ECMA-48 8.3.117
+
+ int argi = 0;
+ int value;
+
+ while (argi < argcount) {
+ // This logic is easier to do 'done' backwards; set it true, and make it
+ // false again in the 'default' case
+ int done = 1;
+
+ long arg;
+ switch (arg = CSI_ARG(args[argi])) {
+ case CSI_ARG_MISSING:
+ case 0: // Reset
+ vterm_state_resetpen(state);
+ break;
+
+ case 1: { // Bold on
+ const VTermColor *fg = &state->pen.fg;
+ state->pen.bold = 1;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
+ if (!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8
+ && state->bold_is_highbright) {
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
+ }
+ break;
+ }
+
+ case 3: // Italic on
+ state->pen.italic = 1;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
+ break;
+
+ case 4: // Underline
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ if (CSI_ARG_HAS_MORE(args[argi])) {
+ argi++;
+ switch (CSI_ARG(args[argi])) {
+ case 0:
+ state->pen.underline = 0;
+ break;
+ case 1:
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ break;
+ case 2:
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ break;
+ case 3:
+ state->pen.underline = VTERM_UNDERLINE_CURLY;
+ break;
+ }
+ }
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 5: // Blink
+ state->pen.blink = 1;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
+ break;
+
+ case 7: // Reverse on
+ state->pen.reverse = 1;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
+ break;
+
+ case 8: // Conceal on
+ state->pen.conceal = 1;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
+ break;
+
+ case 9: // Strikethrough on
+ state->pen.strike = 1;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
+ break;
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19: // Select font
+ state->pen.font = CSI_ARG(args[argi]) - 10;
+ setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+ break;
+
+ case 21: // Underline double
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 22: // Bold off
+ state->pen.bold = 0;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ break;
+
+ case 23: // Italic and Gothic (currently unsupported) off
+ state->pen.italic = 0;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ break;
+
+ case 24: // Underline off
+ state->pen.underline = 0;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+ break;
+
+ case 25: // Blink off
+ state->pen.blink = 0;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ break;
+
+ case 27: // Reverse off
+ state->pen.reverse = 0;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ break;
+
+ case 28: // Conceal off (Reveal)
+ state->pen.conceal = 0;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ break;
+
+ case 29: // Strikethrough off
+ state->pen.strike = 0;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ break;
+
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37: // Foreground colour palette
+ value = CSI_ARG(args[argi]) - 30;
+ if (state->pen.bold && state->bold_is_highbright) {
+ value += 8;
+ }
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 38: // Foreground colour alternative palette
+ if (argcount - argi < 1) {
+ return;
+ }
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
+ argcount - argi - 2, &state->pen.fg);
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 39: // Foreground colour default
+ state->pen.fg = state->default_fg;
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47: // Background colour palette
+ value = CSI_ARG(args[argi]) - 40;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ case 48: // Background colour alternative palette
+ if (argcount - argi < 1) {
+ return;
+ }
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
+ argcount - argi - 2, &state->pen.bg);
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 49: // Default background
+ state->pen.bg = state->default_bg;
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 73: // Superscript
+ case 74: // Subscript
+ case 75: // Superscript/subscript off
+ state->pen.small = (arg != 75);
+ state->pen.baseline =
+ (arg == 73) ? VTERM_BASELINE_RAISE
+ : (arg == 74) ? VTERM_BASELINE_LOWER
+ : VTERM_BASELINE_NORMAL;
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
+ break;
+
+ case 90:
+ case 91:
+ case 92:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 97: // Foreground colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 90 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 100:
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ case 107: // Background colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 100 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ default:
+ done = 0;
+ break;
+ }
+
+ if (!done) {
+ DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
+ }
+
+ while (CSI_ARG_HAS_MORE(args[argi++])) {}
+ }
+}
+
+static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
+{
+ // Do nothing if the given color is the default color
+ if ((fg && VTERM_COLOR_IS_DEFAULT_FG(col))
+ || (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
+ return argi;
+ }
+
+ // Decide whether to send an indexed color or an RGB color
+ if (VTERM_COLOR_IS_INDEXED(col)) {
+ const uint8_t idx = col->indexed.idx;
+ if (idx < 8) {
+ args[argi++] = (idx + (fg ? 30 : 40));
+ } else if (idx < 16) {
+ args[argi++] = (idx - 8 + (fg ? 90 : 100));
+ } else {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 5;
+ args[argi++] = idx;
+ }
+ } else if (VTERM_COLOR_IS_RGB(col)) {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 2;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
+ args[argi++] = col->rgb.blue;
+ }
+ return argi;
+}
+
+int vterm_state_getpen(VTermState *state, long args[], int argcount)
+{
+ int argi = 0;
+
+ if (state->pen.bold) {
+ args[argi++] = 1;
+ }
+
+ if (state->pen.italic) {
+ args[argi++] = 3;
+ }
+
+ if (state->pen.underline == VTERM_UNDERLINE_SINGLE) {
+ args[argi++] = 4;
+ }
+ if (state->pen.underline == VTERM_UNDERLINE_CURLY) {
+ args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
+ }
+
+ if (state->pen.blink) {
+ args[argi++] = 5;
+ }
+
+ if (state->pen.reverse) {
+ args[argi++] = 7;
+ }
+
+ if (state->pen.conceal) {
+ args[argi++] = 8;
+ }
+
+ if (state->pen.strike) {
+ args[argi++] = 9;
+ }
+
+ if (state->pen.font) {
+ args[argi++] = 10 + state->pen.font;
+ }
+
+ if (state->pen.underline == VTERM_UNDERLINE_DOUBLE) {
+ args[argi++] = 21;
+ }
+
+ argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
+
+ argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
+
+ if (state->pen.small) {
+ if (state->pen.baseline == VTERM_BASELINE_RAISE) {
+ args[argi++] = 73;
+ } else if (state->pen.baseline == VTERM_BASELINE_LOWER) {
+ args[argi++] = 74;
+ }
+ }
+
+ return argi;
+}
+
+int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
+{
+ if (!val) {
+ return 0;
+ }
+
+ if (type != vterm_get_attr_type(attr)) {
+ DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
+ attr, vterm_get_attr_type(attr), type);
+ return 0;
+ }
+
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ state->pen.bold = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_UNDERLINE:
+ state->pen.underline = (unsigned)val->number;
+ break;
+ case VTERM_ATTR_ITALIC:
+ state->pen.italic = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_BLINK:
+ state->pen.blink = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_REVERSE:
+ state->pen.reverse = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_CONCEAL:
+ state->pen.conceal = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_STRIKE:
+ state->pen.strike = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_FONT:
+ state->pen.font = (unsigned)val->number;
+ break;
+ case VTERM_ATTR_FOREGROUND:
+ state->pen.fg = val->color;
+ break;
+ case VTERM_ATTR_BACKGROUND:
+ state->pen.bg = val->color;
+ break;
+ case VTERM_ATTR_SMALL:
+ state->pen.small = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_BASELINE:
+ state->pen.baseline = (unsigned)val->number;
+ break;
+ case VTERM_ATTR_URI:
+ state->pen.uri = val->number;
+ break;
+ default:
+ return 0;
+ }
+
+ if (state->callbacks && state->callbacks->setpenattr) {
+ (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+ }
+
+ return 1;
+}
diff --git a/src/nvim/vterm/pen.h b/src/nvim/vterm/pen.h
new file mode 100644
index 0000000000..c5f5217420
--- /dev/null
+++ b/src/nvim/vterm/pen.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/pen.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/screen.c b/src/nvim/vterm/screen.c
new file mode 100644
index 0000000000..45bd5e2a27
--- /dev/null
+++ b/src/nvim/vterm/screen.c
@@ -0,0 +1,1115 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/grid.h"
+#include "nvim/mbyte.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/screen.c.generated.h"
+#endif
+
+#define UNICODE_SPACE 0x20
+#define UNICODE_LINEFEED 0x0a
+
+#undef DEBUG_REFLOW
+
+static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
+{
+ cell->schar = 0;
+ cell->pen = screen->pen;
+}
+
+ScreenCell *getcell(const VTermScreen *screen, int row, int col)
+{
+ if (row < 0 || row >= screen->rows) {
+ return NULL;
+ }
+ if (col < 0 || col >= screen->cols) {
+ return NULL;
+ }
+ return screen->buffer + (screen->cols * row) + col;
+}
+
+static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
+{
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(ScreenCell) * (size_t)rows * (size_t)cols);
+
+ for (int row = 0; row < rows; row++) {
+ for (int col = 0; col < cols; col++) {
+ clearcell(screen, &new_buffer[row * cols + col]);
+ }
+ }
+
+ return new_buffer;
+}
+
+static void damagerect(VTermScreen *screen, VTermRect rect)
+{
+ VTermRect emit;
+
+ switch (screen->damage_merge) {
+ case VTERM_DAMAGE_CELL:
+ // Always emit damage event
+ emit = rect;
+ break;
+
+ case VTERM_DAMAGE_ROW:
+ // Emit damage longer than one row. Try to merge with existing damage in the same row
+ if (rect.end_row > rect.start_row + 1) {
+ // Bigger than 1 line - flush existing, emit this
+ vterm_screen_flush_damage(screen);
+ emit = rect;
+ } else if (screen->damaged.start_row == -1) {
+ // None stored yet
+ screen->damaged = rect;
+ return;
+ } else if (rect.start_row == screen->damaged.start_row) {
+ // Merge with the stored line
+ if (screen->damaged.start_col > rect.start_col) {
+ screen->damaged.start_col = rect.start_col;
+ }
+ if (screen->damaged.end_col < rect.end_col) {
+ screen->damaged.end_col = rect.end_col;
+ }
+ return;
+ } else {
+ // Emit the currently stored line, store a new one
+ emit = screen->damaged;
+ screen->damaged = rect;
+ }
+ break;
+
+ case VTERM_DAMAGE_SCREEN:
+ case VTERM_DAMAGE_SCROLL:
+ // Never emit damage event
+ if (screen->damaged.start_row == -1) {
+ screen->damaged = rect;
+ } else {
+ rect_expand(&screen->damaged, &rect);
+ }
+ return;
+
+ default:
+ DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
+ return;
+ }
+
+ if (screen->callbacks && screen->callbacks->damage) {
+ (*screen->callbacks->damage)(emit, screen->cbdata);
+ }
+}
+
+static void damagescreen(VTermScreen *screen)
+{
+ VTermRect rect = {
+ .start_row = 0,
+ .end_row = screen->rows,
+ .start_col = 0,
+ .end_col = screen->cols,
+ };
+
+ damagerect(screen, rect);
+}
+
+static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
+{
+ VTermScreen *screen = user;
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+
+ if (!cell) {
+ return 0;
+ }
+
+ cell->schar = info->schar;
+ if (info->schar != 0) {
+ cell->pen = screen->pen;
+ }
+
+ for (int col = 1; col < info->width; col++) {
+ getcell(screen, pos.row, pos.col + col)->schar = (uint32_t)-1;
+ }
+
+ VTermRect rect = {
+ .start_row = pos.row,
+ .end_row = pos.row + 1,
+ .start_col = pos.col,
+ .end_col = pos.col + info->width,
+ };
+
+ cell->pen.protected_cell = info->protected_cell;
+ cell->pen.dwl = info->dwl;
+ cell->pen.dhl = info->dhl;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static void sb_pushline_from_row(VTermScreen *screen, int row)
+{
+ VTermPos pos = { .row = row };
+ for (pos.col = 0; pos.col < screen->cols; pos.col++) {
+ vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
+ }
+
+ (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
+}
+
+static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->sb_pushline
+ && dest.start_row == 0 && dest.start_col == 0 // starts top-left corner
+ && dest.end_col == screen->cols // full width
+ && screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
+ for (int row = 0; row < src.start_row; row++) {
+ sb_pushline_from_row(screen, row);
+ }
+ }
+
+ int cols = src.end_col - src.start_col;
+ int downward = src.start_row - dest.start_row;
+
+ int init_row, test_row, inc_row;
+ if (downward < 0) {
+ init_row = dest.end_row - 1;
+ test_row = dest.start_row - 1;
+ inc_row = -1;
+ } else {
+ init_row = dest.start_row;
+ test_row = dest.end_row;
+ inc_row = +1;
+ }
+
+ for (int row = init_row; row != test_row; row += inc_row) {
+ memmove(getcell(screen, row, dest.start_col),
+ getcell(screen, row + downward, src.start_col),
+ (size_t)cols * sizeof(ScreenCell));
+ }
+
+ return 1;
+}
+
+static int moverect_user(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->moverect) {
+ if (screen->damage_merge != VTERM_DAMAGE_SCROLL) {
+ // Avoid an infinite loop
+ vterm_screen_flush_damage(screen);
+ }
+
+ if ((*screen->callbacks->moverect)(dest, src, screen->cbdata)) {
+ return 1;
+ }
+ }
+
+ damagerect(screen, dest);
+
+ return 1;
+}
+
+static int erase_internal(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ for (int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
+ const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
+
+ for (int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if (selective && cell->pen.protected_cell) {
+ continue;
+ }
+
+ cell->schar = 0;
+ cell->pen = (ScreenPen){
+ // Only copy .fg and .bg; leave things like rv in reset state
+ .fg = screen->pen.fg,
+ .bg = screen->pen.bg,
+ };
+ cell->pen.dwl = info->doublewidth;
+ cell->pen.dhl = info->doubleheight;
+ }
+ }
+
+ return 1;
+}
+
+static int erase_user(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static int erase(VTermRect rect, int selective, void *user)
+{
+ erase_internal(rect, selective, user);
+ return erase_user(rect, 0, user);
+}
+
+static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->damage_merge != VTERM_DAMAGE_SCROLL) {
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ vterm_screen_flush_damage(screen);
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_user, erase_user, screen);
+
+ return 1;
+ }
+
+ if (screen->damaged.start_row != -1
+ && !rect_intersects(&rect, &screen->damaged)) {
+ vterm_screen_flush_damage(screen);
+ }
+
+ if (screen->pending_scrollrect.start_row == -1) {
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ } else if (rect_equal(&screen->pending_scrollrect, &rect)
+ && ((screen->pending_scroll_downward == 0 && downward == 0)
+ || (screen->pending_scroll_rightward == 0 && rightward == 0))) {
+ screen->pending_scroll_downward += downward;
+ screen->pending_scroll_rightward += rightward;
+ } else {
+ vterm_screen_flush_damage(screen);
+
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ }
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ if (screen->damaged.start_row == -1) {
+ return 1;
+ }
+
+ if (rect_contains(&rect, &screen->damaged)) {
+ // Scroll region entirely contains the damage; just move it
+ vterm_rect_move(&screen->damaged, -downward, -rightward);
+ rect_clip(&screen->damaged, &rect);
+ }
+ // There are a number of possible cases here, but lets restrict this to only the common case where
+ // we might actually gain some performance by optimising it. Namely, a vertical scroll that neatly
+ // cuts the damage region in half.
+ else if (rect.start_col <= screen->damaged.start_col
+ && rect.end_col >= screen->damaged.end_col
+ && rightward == 0) {
+ if (screen->damaged.start_row >= rect.start_row
+ && screen->damaged.start_row < rect.end_row) {
+ screen->damaged.start_row -= downward;
+ if (screen->damaged.start_row < rect.start_row) {
+ screen->damaged.start_row = rect.start_row;
+ }
+ if (screen->damaged.start_row > rect.end_row) {
+ screen->damaged.start_row = rect.end_row;
+ }
+ }
+ if (screen->damaged.end_row >= rect.start_row
+ && screen->damaged.end_row < rect.end_row) {
+ screen->damaged.end_row -= downward;
+ if (screen->damaged.end_row < rect.start_row) {
+ screen->damaged.end_row = rect.start_row;
+ }
+ if (screen->damaged.end_row > rect.end_row) {
+ screen->damaged.end_row = rect.end_row;
+ }
+ }
+ } else {
+ DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
+ ARGSrect(screen->damaged), ARGSrect(rect));
+ }
+
+ return 1;
+}
+
+static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->movecursor) {
+ return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
+ }
+
+ return 0;
+}
+
+static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ screen->pen.bold = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_UNDERLINE:
+ screen->pen.underline = (unsigned)val->number;
+ return 1;
+ case VTERM_ATTR_ITALIC:
+ screen->pen.italic = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_BLINK:
+ screen->pen.blink = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_REVERSE:
+ screen->pen.reverse = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_CONCEAL:
+ screen->pen.conceal = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_STRIKE:
+ screen->pen.strike = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_FONT:
+ screen->pen.font = (unsigned)val->number;
+ return 1;
+ case VTERM_ATTR_FOREGROUND:
+ screen->pen.fg = val->color;
+ return 1;
+ case VTERM_ATTR_BACKGROUND:
+ screen->pen.bg = val->color;
+ return 1;
+ case VTERM_ATTR_SMALL:
+ screen->pen.small = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_BASELINE:
+ screen->pen.baseline = (unsigned)val->number;
+ return 1;
+ case VTERM_ATTR_URI:
+ screen->pen.uri = val->number;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch (prop) {
+ case VTERM_PROP_ALTSCREEN:
+ if (val->boolean && !screen->buffers[BUFIDX_ALTSCREEN]) {
+ return 0;
+ }
+
+ screen->buffer =
+ val->boolean ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+ // only send a damage event on disable; because during enable there's an erase that sends a
+ // damage anyway
+ if (!val->boolean) {
+ damagescreen(screen);
+ }
+ break;
+ case VTERM_PROP_REVERSE:
+ screen->global_reverse = (unsigned)val->boolean;
+ damagescreen(screen);
+ break;
+ default:
+ ; // ignore
+ }
+
+ if (screen->callbacks && screen->callbacks->settermprop) {
+ return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
+ }
+
+ return 1;
+}
+
+static int bell(void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->bell) {
+ return (*screen->callbacks->bell)(screen->cbdata);
+ }
+
+ return 0;
+}
+
+/// How many cells are non-blank Returns the position of the first blank cell in the trailing blank
+/// end
+static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
+{
+ int col = cols - 1;
+ while (col >= 0 && buffer[row * cols + col].schar == 0) {
+ col--;
+ }
+ return col + 1;
+}
+
+static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active,
+ VTermStateFields *statefields)
+{
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ ScreenCell *old_buffer = screen->buffers[bufidx];
+ VTermLineInfo *old_lineinfo = statefields->lineinfos[bufidx];
+
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(ScreenCell) * (size_t)new_rows *
+ (size_t)new_cols);
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt,
+ sizeof(new_lineinfo[0]) * (size_t)new_rows);
+
+ int old_row = old_rows - 1;
+ int new_row = new_rows - 1;
+
+ VTermPos old_cursor = statefields->pos;
+ VTermPos new_cursor = { -1, -1 };
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
+ old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
+#endif
+
+ // Keep track of the final row that is knonw to be blank, so we know what spare space we have for
+ // scrolling into
+ int final_blank_row = new_rows;
+
+ while (old_row >= 0) {
+ int old_row_end = old_row;
+ // TODO(vterm): Stop if dwl or dhl
+ while (screen->reflow && old_lineinfo && old_row > 0 && old_lineinfo[old_row].continuation) {
+ old_row--;
+ }
+ int old_row_start = old_row;
+
+ int width = 0;
+ for (int row = old_row_start; row <= old_row_end; row++) {
+ if (screen->reflow && row < (old_rows - 1) && old_lineinfo[row + 1].continuation) {
+ width += old_cols;
+ } else {
+ width += line_popcount(old_buffer, row, old_rows, old_cols);
+ }
+ }
+
+ if (final_blank_row == (new_row + 1) && width == 0) {
+ final_blank_row = new_row;
+ }
+
+ int new_height = screen->reflow
+ ? width ? (width + new_cols - 1) / new_cols : 1
+ : 1;
+
+ int new_row_end = new_row;
+ int new_row_start = new_row - new_height + 1;
+
+ old_row = old_row_start;
+ int old_col = 0;
+
+ int spare_rows = new_rows - final_blank_row;
+
+ if (new_row_start < 0 // we'd fall off the top
+ && spare_rows >= 0 // we actually have spare rows
+ && (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows)) {
+ // Attempt to scroll content down into the blank rows at the bottom to make it fit
+ int downwards = -new_row_start;
+ if (downwards > spare_rows) {
+ downwards = spare_rows;
+ }
+ int rowcount = new_rows - downwards;
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " scroll %d rows +%d downwards\n", rowcount, downwards);
+#endif
+
+ memmove(&new_buffer[downwards * new_cols], &new_buffer[0],
+ (size_t)rowcount * (size_t)new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[downwards], &new_lineinfo[0],
+ (size_t)rowcount * sizeof(new_lineinfo[0]));
+
+ new_row += downwards;
+ new_row_start += downwards;
+ new_row_end += downwards;
+
+ if (new_cursor.row >= 0) {
+ new_cursor.row += downwards;
+ }
+
+ final_blank_row += downwards;
+ }
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " rows [%d..%d] <- [%d..%d] width=%d\n",
+ new_row_start, new_row_end, old_row_start, old_row_end, width);
+#endif
+
+ if (new_row_start < 0) {
+ if (old_row_start <= old_cursor.row && old_cursor.row <= old_row_end) {
+ new_cursor.row = 0;
+ new_cursor.col = old_cursor.col;
+ if (new_cursor.col >= new_cols) {
+ new_cursor.col = new_cols - 1;
+ }
+ }
+ break;
+ }
+
+ for (new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
+ int count = width >= new_cols ? new_cols : width;
+ width -= count;
+
+ int new_col = 0;
+
+ while (count) {
+ // TODO(vterm): This could surely be done a lot faster by memcpy()'ing the entire range
+ new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
+
+ if (old_cursor.row == old_row && old_cursor.col == old_col) {
+ new_cursor.row = new_row, new_cursor.col = new_col;
+ }
+
+ old_col++;
+ if (old_col == old_cols) {
+ old_row++;
+
+ if (!screen->reflow) {
+ new_col++;
+ break;
+ }
+ old_col = 0;
+ }
+
+ new_col++;
+ count--;
+ }
+
+ if (old_cursor.row == old_row && old_cursor.col >= old_col) {
+ new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
+ if (new_cursor.col >= new_cols) {
+ new_cursor.col = new_cols - 1;
+ }
+ }
+
+ while (new_col < new_cols) {
+ clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
+ new_col++;
+ }
+
+ new_lineinfo[new_row].continuation = (new_row > new_row_start);
+ }
+
+ old_row = old_row_start - 1;
+ new_row = new_row_start - 1;
+ }
+
+ if (old_cursor.row <= old_row) {
+ // cursor would have moved entirely off the top of the screen; lets just bring it within range
+ new_cursor.row = 0, new_cursor.col = old_cursor.col;
+ if (new_cursor.col >= new_cols) {
+ new_cursor.col = new_cols - 1;
+ }
+ }
+
+ // We really expect the cursor position to be set by now
+ if (active && (new_cursor.row == -1 || new_cursor.col == -1)) {
+ fprintf(stderr, "screen_resize failed to update cursor position\n");
+ abort();
+ }
+
+ if (old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
+ // Push spare lines to scrollback buffer
+ if (screen->callbacks && screen->callbacks->sb_pushline) {
+ for (int row = 0; row <= old_row; row++) {
+ sb_pushline_from_row(screen, row);
+ }
+ }
+ if (active) {
+ statefields->pos.row -= (old_row + 1);
+ }
+ }
+ if (new_row >= 0 && bufidx == BUFIDX_PRIMARY
+ && screen->callbacks && screen->callbacks->sb_popline) {
+ // Try to backfill rows by popping scrollback buffer
+ while (new_row >= 0) {
+ if (!(screen->callbacks->sb_popline(old_cols, screen->sb_buffer, screen->cbdata))) {
+ break;
+ }
+
+ VTermPos pos = { .row = new_row };
+ for (pos.col = 0; pos.col < old_cols && pos.col < new_cols;
+ pos.col += screen->sb_buffer[pos.col].width) {
+ VTermScreenCell *src = &screen->sb_buffer[pos.col];
+ ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
+
+ dst->schar = src->schar;
+
+ dst->pen.bold = src->attrs.bold;
+ dst->pen.underline = src->attrs.underline;
+ dst->pen.italic = src->attrs.italic;
+ dst->pen.blink = src->attrs.blink;
+ dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse;
+ dst->pen.conceal = src->attrs.conceal;
+ dst->pen.strike = src->attrs.strike;
+ dst->pen.font = src->attrs.font;
+ dst->pen.small = src->attrs.small;
+ dst->pen.baseline = src->attrs.baseline;
+
+ dst->pen.fg = src->fg;
+ dst->pen.bg = src->bg;
+
+ dst->pen.uri = src->uri;
+
+ if (src->width == 2 && pos.col < (new_cols - 1)) {
+ (dst + 1)->schar = (uint32_t)-1;
+ }
+ }
+ for (; pos.col < new_cols; pos.col++) {
+ clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
+ }
+ new_row--;
+
+ if (active) {
+ statefields->pos.row++;
+ }
+ }
+ }
+ if (new_row >= 0) {
+ // Scroll new rows back up to the top and fill in blanks at the bottom
+ int moverows = new_rows - new_row - 1;
+ memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols],
+ (size_t)moverows * (size_t)new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1],
+ (size_t)moverows * sizeof(new_lineinfo[0]));
+
+ new_cursor.row -= (new_row + 1);
+
+ for (new_row = moverows; new_row < new_rows; new_row++) {
+ for (int col = 0; col < new_cols; col++) {
+ clearcell(screen, &new_buffer[new_row * new_cols + col]);
+ }
+ new_lineinfo[new_row] = (VTermLineInfo){ 0 };
+ }
+ }
+
+ vterm_allocator_free(screen->vt, old_buffer);
+ screen->buffers[bufidx] = new_buffer;
+
+ vterm_allocator_free(screen->vt, old_lineinfo);
+ statefields->lineinfos[bufidx] = new_lineinfo;
+
+ if (active) {
+ statefields->pos = new_cursor;
+ }
+}
+
+static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
+{
+ VTermScreen *screen = user;
+
+ int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN]
+ && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]);
+
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ if (new_cols > old_cols) {
+ // Ensure that ->sb_buffer is large enough for a new or and old row
+ if (screen->sb_buffer) {
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+ }
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(VTermScreenCell) * (size_t)new_cols);
+ }
+
+ resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
+ if (screen->buffers[BUFIDX_ALTSCREEN]) {
+ resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
+ } else if (new_rows != old_rows) {
+ // We don't need a full resize of the altscreen because it isn't enabled but we should at least
+ // keep the lineinfo the right size
+ vterm_allocator_free(screen->vt, fields->lineinfos[BUFIDX_ALTSCREEN]);
+
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt,
+ sizeof(new_lineinfo[0]) *
+ (size_t)new_rows);
+ for (int row = 0; row < new_rows; row++) {
+ new_lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+
+ fields->lineinfos[BUFIDX_ALTSCREEN] = new_lineinfo;
+ }
+
+ screen->buffer =
+ altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+
+ screen->rows = new_rows;
+ screen->cols = new_cols;
+
+ if (new_cols <= old_cols) {
+ if (screen->sb_buffer) {
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+ }
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(VTermScreenCell) * (size_t)new_cols);
+ }
+
+ // TODO(vterm): Maaaaybe we can optimise this if there's no reflow happening
+ damagescreen(screen);
+
+ if (screen->callbacks && screen->callbacks->resize) {
+ return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
+ }
+
+ return 1;
+}
+
+static int theme(bool *dark, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->theme) {
+ return (*screen->callbacks->theme)(dark, screen->cbdata);
+ }
+
+ return 1;
+}
+
+static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
+ void *user)
+{
+ VTermScreen *screen = user;
+
+ if (newinfo->doublewidth != oldinfo->doublewidth
+ || newinfo->doubleheight != oldinfo->doubleheight) {
+ for (int col = 0; col < screen->cols; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+ cell->pen.dwl = newinfo->doublewidth;
+ cell->pen.dhl = newinfo->doubleheight;
+ }
+
+ VTermRect rect = {
+ .start_row = row,
+ .end_row = row + 1,
+ .start_col = 0,
+ .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
+ };
+ damagerect(screen, rect);
+
+ if (newinfo->doublewidth) {
+ rect.start_col = screen->cols / 2;
+ rect.end_col = screen->cols;
+
+ erase_internal(rect, 0, user);
+ }
+ }
+
+ return 1;
+}
+
+static int sb_clear(void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->sb_clear) {
+ if ((*screen->callbacks->sb_clear)(screen->cbdata)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static VTermStateCallbacks state_cbs = {
+ .putglyph = &putglyph,
+ .movecursor = &movecursor,
+ .scrollrect = &scrollrect,
+ .erase = &erase,
+ .setpenattr = &setpenattr,
+ .settermprop = &settermprop,
+ .bell = &bell,
+ .resize = &resize,
+ .theme = &theme,
+ .setlineinfo = &setlineinfo,
+ .sb_clear = &sb_clear,
+};
+
+static VTermScreen *screen_new(VTerm *vt)
+{
+ VTermState *state = vterm_obtain_state(vt);
+ if (!state) {
+ return NULL;
+ }
+
+ VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
+ int rows, cols;
+
+ vterm_get_size(vt, &rows, &cols);
+
+ screen->vt = vt;
+ screen->state = state;
+
+ screen->damage_merge = VTERM_DAMAGE_CELL;
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+
+ screen->rows = rows;
+ screen->cols = cols;
+
+ screen->global_reverse = false;
+ screen->reflow = false;
+
+ screen->callbacks = NULL;
+ screen->cbdata = NULL;
+
+ screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
+
+ screen->buffer = screen->buffers[BUFIDX_PRIMARY];
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * (size_t)cols);
+
+ vterm_state_set_callbacks(screen->state, &state_cbs, screen);
+
+ return screen;
+}
+
+void vterm_screen_free(VTermScreen *screen)
+{
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_PRIMARY]);
+ if (screen->buffers[BUFIDX_ALTSCREEN]) {
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_ALTSCREEN]);
+ }
+
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ vterm_allocator_free(screen->vt, screen);
+}
+
+void vterm_screen_reset(VTermScreen *screen, int hard)
+{
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+ vterm_state_reset(screen->state, hard);
+ vterm_screen_flush_damage(screen);
+}
+
+// Copy internal to external representation of a screen cell
+int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
+{
+ ScreenCell *intcell = getcell(screen, pos.row, pos.col);
+ if (!intcell) {
+ return 0;
+ }
+
+ cell->schar = (intcell->schar == (uint32_t)-1) ? 0 : intcell->schar;
+
+ cell->attrs.bold = intcell->pen.bold;
+ cell->attrs.underline = intcell->pen.underline;
+ cell->attrs.italic = intcell->pen.italic;
+ cell->attrs.blink = intcell->pen.blink;
+ cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
+ cell->attrs.conceal = intcell->pen.conceal;
+ cell->attrs.strike = intcell->pen.strike;
+ cell->attrs.font = intcell->pen.font;
+ cell->attrs.small = intcell->pen.small;
+ cell->attrs.baseline = intcell->pen.baseline;
+
+ cell->attrs.dwl = intcell->pen.dwl;
+ cell->attrs.dhl = intcell->pen.dhl;
+
+ cell->fg = intcell->pen.fg;
+ cell->bg = intcell->pen.bg;
+
+ cell->uri = intcell->pen.uri;
+
+ if (pos.col < (screen->cols - 1)
+ && getcell(screen, pos.row, pos.col + 1)->schar == (uint32_t)-1) {
+ cell->width = 2;
+ } else {
+ cell->width = 1;
+ }
+
+ return 1;
+}
+
+VTermScreen *vterm_obtain_screen(VTerm *vt)
+{
+ if (vt->screen) {
+ return vt->screen;
+ }
+
+ VTermScreen *screen = screen_new(vt);
+ vt->screen = screen;
+
+ return screen;
+}
+
+void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow)
+{
+ screen->reflow = reflow;
+}
+
+#undef vterm_screen_set_reflow
+void vterm_screen_set_reflow(VTermScreen *screen, bool reflow)
+{
+ vterm_screen_enable_reflow(screen, reflow);
+}
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
+{
+ if (!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
+ int rows, cols;
+ vterm_get_size(screen->vt, &rows, &cols);
+
+ screen->buffers[BUFIDX_ALTSCREEN] = alloc_buffer(screen, rows, cols);
+ }
+}
+
+void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks,
+ void *user)
+{
+ screen->callbacks = callbacks;
+ screen->cbdata = user;
+}
+
+void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen,
+ const VTermStateFallbacks *fallbacks, void *user)
+{
+ vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
+}
+
+void vterm_screen_flush_damage(VTermScreen *screen)
+{
+ if (screen->pending_scrollrect.start_row != -1) {
+ vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward,
+ screen->pending_scroll_rightward,
+ moverect_user, erase_user, screen);
+
+ screen->pending_scrollrect.start_row = -1;
+ }
+
+ if (screen->damaged.start_row != -1) {
+ if (screen->callbacks && screen->callbacks->damage) {
+ (*screen->callbacks->damage)(screen->damaged, screen->cbdata);
+ }
+
+ screen->damaged.start_row = -1;
+ }
+}
+
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
+{
+ vterm_screen_flush_damage(screen);
+ screen->damage_merge = size;
+}
+
+/// Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state` instance.
+void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
+{
+ vterm_state_convert_color_to_rgb(screen->state, col);
+}
+
+// Some utility functions on VTermRect structures
+
+#define STRFrect "(%d,%d-%d,%d)"
+#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
+
+// Expand dst to contain src as well
+void rect_expand(VTermRect *dst, VTermRect *src)
+{
+ if (dst->start_row > src->start_row) {
+ dst->start_row = src->start_row;
+ }
+ if (dst->start_col > src->start_col) {
+ dst->start_col = src->start_col;
+ }
+ if (dst->end_row < src->end_row) {
+ dst->end_row = src->end_row;
+ }
+ if (dst->end_col < src->end_col) {
+ dst->end_col = src->end_col;
+ }
+}
+
+// Clip the dst to ensure it does not step outside of bounds
+void rect_clip(VTermRect *dst, VTermRect *bounds)
+{
+ if (dst->start_row < bounds->start_row) {
+ dst->start_row = bounds->start_row;
+ }
+ if (dst->start_col < bounds->start_col) {
+ dst->start_col = bounds->start_col;
+ }
+ if (dst->end_row > bounds->end_row) {
+ dst->end_row = bounds->end_row;
+ }
+ if (dst->end_col > bounds->end_col) {
+ dst->end_col = bounds->end_col;
+ }
+ // Ensure it doesn't end up negatively-sized
+ if (dst->end_row < dst->start_row) {
+ dst->end_row = dst->start_row;
+ }
+ if (dst->end_col < dst->start_col) {
+ dst->end_col = dst->start_col;
+ }
+}
+
+// True if the two rectangles are equal
+int rect_equal(VTermRect *a, VTermRect *b)
+{
+ return (a->start_row == b->start_row)
+ && (a->start_col == b->start_col)
+ && (a->end_row == b->end_row)
+ && (a->end_col == b->end_col);
+}
+
+// True if small is contained entirely within big
+int rect_contains(VTermRect *big, VTermRect *small)
+{
+ if (small->start_row < big->start_row) {
+ return 0;
+ }
+ if (small->start_col < big->start_col) {
+ return 0;
+ }
+ if (small->end_row > big->end_row) {
+ return 0;
+ }
+ if (small->end_col > big->end_col) {
+ return 0;
+ }
+ return 1;
+}
+
+// True if the rectangles overlap at all
+int rect_intersects(VTermRect *a, VTermRect *b)
+{
+ if (a->start_row > b->end_row || b->start_row > a->end_row) {
+ return 0;
+ }
+ if (a->start_col > b->end_col || b->start_col > a->end_col) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/nvim/vterm/screen.h b/src/nvim/vterm/screen.h
new file mode 100644
index 0000000000..fa7520d75a
--- /dev/null
+++ b/src/nvim/vterm/screen.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/screen.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/state.c b/src/nvim/vterm/state.c
new file mode 100644
index 0000000000..0e43107347
--- /dev/null
+++ b/src/nvim/vterm/state.c
@@ -0,0 +1,2467 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/grid.h"
+#include "nvim/mbyte.h"
+#include "nvim/vterm/encoding.h"
+#include "nvim/vterm/parser.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/state.c.generated.h"
+#endif
+
+#define strneq(a, b, n) (strncmp(a, b, n) == 0)
+
+// Some convenient wrappers to make callback functions easier
+
+static void putglyph(VTermState *state, const schar_T schar, int width, VTermPos pos)
+{
+ VTermGlyphInfo info = {
+ .schar = schar,
+ .width = width,
+ .protected_cell = state->protected_cell,
+ .dwl = state->lineinfo[pos.row].doublewidth,
+ .dhl = state->lineinfo[pos.row].doubleheight,
+ };
+
+ if (state->callbacks && state->callbacks->putglyph) {
+ if ((*state->callbacks->putglyph)(&info, pos, state->cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
+}
+
+static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
+{
+ if (state->pos.col == oldpos->col && state->pos.row == oldpos->row) {
+ return;
+ }
+
+ if (cancel_phantom) {
+ state->at_phantom = 0;
+ }
+
+ if (state->callbacks && state->callbacks->movecursor) {
+ if ((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible,
+ state->cbdata)) {
+ return;
+ }
+ }
+}
+
+static void erase(VTermState *state, VTermRect rect, int selective)
+{
+ if (rect.end_col == state->cols) {
+ // If we're erasing the final cells of any lines, cancel the continuation marker on the
+ // subsequent line
+ for (int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++) {
+ state->lineinfo[row].continuation = 0;
+ }
+ }
+
+ if (state->callbacks && state->callbacks->erase) {
+ if ((*state->callbacks->erase)(rect, selective, state->cbdata)) {
+ return;
+ }
+ }
+}
+
+static VTermState *vterm_state_new(VTerm *vt)
+{
+ VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
+
+ state->vt = vt;
+
+ state->rows = vt->rows;
+ state->cols = vt->cols;
+
+ state->mouse_col = 0;
+ state->mouse_row = 0;
+ state->mouse_buttons = 0;
+
+ state->mouse_protocol = MOUSE_X10;
+
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+
+ state->selection.callbacks = NULL;
+ state->selection.user = NULL;
+ state->selection.buffer = NULL;
+
+ vterm_state_newpen(state);
+
+ state->bold_is_highbright = 0;
+
+ state->combine_pos.row = -1;
+
+ state->tabstops = vterm_allocator_malloc(state->vt, ((size_t)state->cols + 7) / 8);
+
+ state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt,
+ (size_t)state->rows *
+ sizeof(VTermLineInfo));
+ // TODO(vterm): Make an 'enable' function
+ state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt,
+ (size_t)state->rows *
+ sizeof(VTermLineInfo));
+ state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
+
+ state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
+ if (*state->encoding_utf8.enc->init) {
+ (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(state->key_encoding_stacks); i++) {
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[i];
+ for (size_t j = 0; j < ARRAY_SIZE(stack->items); j++) {
+ memset(&stack->items[j], 0, sizeof(stack->items[j]));
+ }
+
+ stack->size = 1;
+ }
+
+ return state;
+}
+
+void vterm_state_free(VTermState *state)
+{
+ vterm_allocator_free(state->vt, state->tabstops);
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
+ if (state->lineinfos[BUFIDX_ALTSCREEN]) {
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
+ }
+ vterm_allocator_free(state->vt, state);
+}
+
+static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
+{
+ if (!downward && !rightward) {
+ return;
+ }
+
+ int rows = rect.end_row - rect.start_row;
+ if (downward > rows) {
+ downward = rows;
+ } else if (downward < -rows) {
+ downward = -rows;
+ }
+
+ int cols = rect.end_col - rect.start_col;
+ if (rightward > cols) {
+ rightward = cols;
+ } else if (rightward < -cols) {
+ rightward = -cols;
+ }
+
+ // Update lineinfo if full line
+ if (rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
+ int height = rect.end_row - rect.start_row - abs(downward);
+
+ if (downward > 0) {
+ memmove(state->lineinfo + rect.start_row,
+ state->lineinfo + rect.start_row + downward,
+ (size_t)height * sizeof(state->lineinfo[0]));
+ for (int row = rect.end_row - downward; row < rect.end_row; row++) {
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ } else {
+ memmove(state->lineinfo + rect.start_row - downward,
+ state->lineinfo + rect.start_row,
+ (size_t)height * sizeof(state->lineinfo[0]));
+ for (int row = rect.start_row; row < rect.start_row - downward; row++) {
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ }
+ }
+
+ if (state->callbacks && state->callbacks->scrollrect) {
+ if ((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata)) {
+ return;
+ }
+ }
+
+ if (state->callbacks) {
+ vterm_scroll_rect(rect, downward, rightward,
+ state->callbacks->moverect, state->callbacks->erase, state->cbdata);
+ }
+}
+
+static void linefeed(VTermState *state)
+{
+ if (state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, 1, 0);
+ } else if (state->pos.row < state->rows - 1) {
+ state->pos.row++;
+ }
+}
+
+static void set_col_tabstop(VTermState *state, int col)
+{
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ state->tabstops[col >> 3] |= mask;
+}
+
+static void clear_col_tabstop(VTermState *state, int col)
+{
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ state->tabstops[col >> 3] &= ~mask;
+}
+
+static int is_col_tabstop(VTermState *state, int col)
+{
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ return state->tabstops[col >> 3] & mask;
+}
+
+static int is_cursor_in_scrollregion(const VTermState *state)
+{
+ if (state->pos.row < state->scrollregion_top
+ || state->pos.row >= SCROLLREGION_BOTTOM(state)) {
+ return 0;
+ }
+ if (state->pos.col < SCROLLREGION_LEFT(state)
+ || state->pos.col >= SCROLLREGION_RIGHT(state)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void tab(VTermState *state, int count, int direction)
+{
+ while (count > 0) {
+ if (direction > 0) {
+ if (state->pos.col >= THISROWWIDTH(state) - 1) {
+ return;
+ }
+
+ state->pos.col++;
+ } else if (direction < 0) {
+ if (state->pos.col < 1) {
+ return;
+ }
+
+ state->pos.col--;
+ }
+
+ if (is_col_tabstop(state, state->pos.col)) {
+ count--;
+ }
+ }
+}
+
+#define NO_FORCE 0
+#define FORCE 1
+
+#define DWL_OFF 0
+#define DWL_ON 1
+
+#define DHL_OFF 0
+#define DHL_TOP 1
+#define DHL_BOTTOM 2
+
+static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
+{
+ VTermLineInfo info = state->lineinfo[row];
+
+ if (dwl == DWL_OFF) {
+ info.doublewidth = DWL_OFF;
+ } else if (dwl == DWL_ON) {
+ info.doublewidth = DWL_ON;
+ }
+ // else -1 to ignore
+
+ if (dhl == DHL_OFF) {
+ info.doubleheight = DHL_OFF;
+ } else if (dhl == DHL_TOP) {
+ info.doubleheight = DHL_TOP;
+ } else if (dhl == DHL_BOTTOM) {
+ info.doubleheight = DHL_BOTTOM;
+ }
+
+ if ((state->callbacks
+ && state->callbacks->setlineinfo
+ && (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
+ || force) {
+ state->lineinfo[row] = info;
+ }
+}
+
+static int on_text(const char bytes[], size_t len, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
+ size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
+
+ int npoints = 0;
+ size_t eaten = 0;
+
+ VTermEncodingInstance *encoding =
+ state->gsingle_set ? &state->encoding[state->gsingle_set]
+ : !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set]
+ : state->vt->mode.utf8 ? &state->encoding_utf8
+ : &state->encoding[state->
+ gr_set];
+
+ (*encoding->enc->decode)(encoding->enc, encoding->data,
+ codepoints, &npoints, state->gsingle_set ? 1 : (int)maxpoints,
+ bytes, &eaten, len);
+
+ // There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet for even a single codepoint
+ if (!npoints) {
+ return (int)eaten;
+ }
+
+ if (state->gsingle_set && npoints) {
+ state->gsingle_set = 0;
+ }
+
+ int i = 0;
+ GraphemeState grapheme_state = GRAPHEME_STATE_INIT;
+ size_t grapheme_len = 0;
+ bool recombine = false;
+
+ // See if the cursor has moved since
+ if (state->pos.row == state->combine_pos.row
+ && state->pos.col == state->combine_pos.col + state->combine_width) {
+ // This is a combining char. that needs to be merged with the previous glyph output
+ if (utf_iscomposing((int)state->grapheme_last, (int)codepoints[i], &state->grapheme_state)) {
+ // Find where we need to append these combining chars
+ grapheme_len = state->grapheme_len;
+ grapheme_state = state->grapheme_state;
+ state->pos.col = state->combine_pos.col;
+ recombine = true;
+ } else {
+ DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
+ }
+ }
+
+ while (i < npoints) {
+ // Try to find combining characters following this
+ do {
+ if (grapheme_len < sizeof(state->grapheme_buf) - 4) {
+ grapheme_len += (size_t)utf_char2bytes((int)codepoints[i],
+ state->grapheme_buf + grapheme_len);
+ }
+ i++;
+ } while (i < npoints && utf_iscomposing((int)codepoints[i - 1], (int)codepoints[i],
+ &grapheme_state));
+
+ int width = utf_ptr2cells_len(state->grapheme_buf, (int)grapheme_len);
+
+ if (state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
+ linefeed(state);
+ state->pos.col = 0;
+ state->at_phantom = 0;
+ state->lineinfo[state->pos.row].continuation = 1;
+ }
+
+ if (state->mode.insert && !recombine) {
+ // TODO(vterm): This will be a little inefficient for large bodies of text, as it'll have to
+ // 'ICH' effectively before every glyph. We should scan ahead and ICH as many times as
+ // required
+ VTermRect rect = {
+ .start_row = state->pos.row,
+ .end_row = state->pos.row + 1,
+ .start_col = state->pos.col,
+ .end_col = THISROWWIDTH(state),
+ };
+ scroll(state, rect, 0, -1);
+ }
+
+ schar_T sc = schar_from_buf(state->grapheme_buf, grapheme_len);
+ putglyph(state, sc, width, state->pos);
+
+ if (i == npoints) {
+ // End of the buffer. Save the chars in case we have to combine with more on the next call
+ state->grapheme_len = grapheme_len;
+ state->grapheme_last = codepoints[i - 1];
+ state->grapheme_state = grapheme_state;
+ state->combine_width = width;
+ state->combine_pos = state->pos;
+ } else {
+ grapheme_len = 0;
+ recombine = false;
+ }
+
+ if (state->pos.col + width >= THISROWWIDTH(state)) {
+ if (state->mode.autowrap) {
+ state->at_phantom = 1;
+ }
+ } else {
+ state->pos.col += width;
+ }
+ }
+
+ updatecursor(state, &oldpos, 0);
+
+#ifdef DEBUG
+ if (state->pos.row < 0 || state->pos.row >= state->rows
+ || state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
+ state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return (int)eaten;
+}
+
+static int on_control(uint8_t control, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ switch (control) {
+ case 0x07: // BEL - ECMA-48 8.3.3
+ if (state->callbacks && state->callbacks->bell) {
+ (*state->callbacks->bell)(state->cbdata);
+ }
+ break;
+
+ case 0x08: // BS - ECMA-48 8.3.5
+ if (state->pos.col > 0) {
+ state->pos.col--;
+ }
+ break;
+
+ case 0x09: // HT - ECMA-48 8.3.60
+ tab(state, 1, +1);
+ break;
+
+ case 0x0a: // LF - ECMA-48 8.3.74
+ case 0x0b: // VT
+ case 0x0c: // FF
+ linefeed(state);
+ if (state->mode.newline) {
+ state->pos.col = 0;
+ }
+ break;
+
+ case 0x0d: // CR - ECMA-48 8.3.15
+ state->pos.col = 0;
+ break;
+
+ case 0x0e: // LS1 - ECMA-48 8.3.76
+ state->gl_set = 1;
+ break;
+
+ case 0x0f: // LS0 - ECMA-48 8.3.75
+ state->gl_set = 0;
+ break;
+
+ case 0x84: // IND - DEPRECATED but implemented for completeness
+ linefeed(state);
+ break;
+
+ case 0x85: // NEL - ECMA-48 8.3.86
+ linefeed(state);
+ state->pos.col = 0;
+ break;
+
+ case 0x88: // HTS - ECMA-48 8.3.62
+ set_col_tabstop(state, state->pos.col);
+ break;
+
+ case 0x8d: // RI - ECMA-48 8.3.104
+ if (state->pos.row == state->scrollregion_top) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, -1, 0);
+ } else if (state->pos.row > 0) {
+ state->pos.row--;
+ }
+ break;
+
+ case 0x8e: // SS2 - ECMA-48 8.3.141
+ state->gsingle_set = 2;
+ break;
+
+ case 0x8f: // SS3 - ECMA-48 8.3.142
+ state->gsingle_set = 3;
+ break;
+
+ default:
+ if (state->fallbacks && state->fallbacks->control) {
+ if ((*state->fallbacks->control)(control, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ updatecursor(state, &oldpos, 1);
+
+#ifdef DEBUG
+ if (state->pos.row < 0 || state->pos.row >= state->rows
+ || state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
+ control, state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static int settermprop_bool(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .boolean = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_int(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .number = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
+{
+ VTermValue val = { .string = frag };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static void savecursor(VTermState *state, int save)
+{
+ if (save) {
+ state->saved.pos = state->pos;
+ state->saved.mode.cursor_visible = state->mode.cursor_visible;
+ state->saved.mode.cursor_blink = state->mode.cursor_blink;
+ state->saved.mode.cursor_shape = state->mode.cursor_shape;
+
+ vterm_state_savepen(state, 1);
+ } else {
+ VTermPos oldpos = state->pos;
+
+ state->pos = state->saved.pos;
+
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
+
+ vterm_state_savepen(state, 0);
+
+ updatecursor(state, &oldpos, 1);
+ }
+}
+
+static int on_escape(const char *bytes, size_t len, void *user)
+{
+ VTermState *state = user;
+
+ // Easier to decode this from the first byte, even though the final byte terminates it
+ switch (bytes[0]) {
+ case ' ':
+ if (len != 2) {
+ return 0;
+ }
+
+ switch (bytes[1]) {
+ case 'F': // S7C1T
+ state->vt->mode.ctrl8bit = 0;
+ break;
+
+ case 'G': // S8C1T
+ state->vt->mode.ctrl8bit = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '#':
+ if (len != 2) {
+ return 0;
+ }
+
+ switch (bytes[1]) {
+ case '3': // DECDHL top
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
+ break;
+
+ case '4': // DECDHL bottom
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
+ break;
+
+ case '5': // DECSWL
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
+ break;
+
+ case '6': // DECDWL
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
+ break;
+
+ case '8': // DECALN
+ {
+ VTermPos pos;
+ schar_T E = schar_from_ascii('E'); // E
+ for (pos.row = 0; pos.row < state->rows; pos.row++) {
+ for (pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++) {
+ putglyph(state, E, 1, pos);
+ }
+ }
+ break;
+ }
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '(':
+ case ')':
+ case '*':
+ case '+': // SCS
+ if (len != 2) {
+ return 0;
+ }
+
+ {
+ int setnum = bytes[0] - 0x28;
+ VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
+
+ if (newenc) {
+ state->encoding[setnum].enc = newenc;
+
+ if (newenc->init) {
+ (*newenc->init)(newenc, state->encoding[setnum].data);
+ }
+ }
+ }
+
+ return 2;
+
+ case '7': // DECSC
+ savecursor(state, 1);
+ return 1;
+
+ case '8': // DECRC
+ savecursor(state, 0);
+ return 1;
+
+ case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
+ return 1;
+
+ case '=': // DECKPAM
+ state->mode.keypad = 1;
+ return 1;
+
+ case '>': // DECKPNM
+ state->mode.keypad = 0;
+ return 1;
+
+ case 'c': // RIS - ECMA-48 8.3.105
+ {
+ VTermPos oldpos = state->pos;
+ vterm_state_reset(state, 1);
+ if (state->callbacks && state->callbacks->movecursor) {
+ (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible,
+ state->cbdata);
+ }
+ return 1;
+ }
+
+ case 'n': // LS2 - ECMA-48 8.3.78
+ state->gl_set = 2;
+ return 1;
+
+ case 'o': // LS3 - ECMA-48 8.3.80
+ state->gl_set = 3;
+ return 1;
+
+ case '~': // LS1R - ECMA-48 8.3.77
+ state->gr_set = 1;
+ return 1;
+
+ case '}': // LS2R - ECMA-48 8.3.79
+ state->gr_set = 2;
+ return 1;
+
+ case '|': // LS3R - ECMA-48 8.3.81
+ state->gr_set = 3;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void set_mode(VTermState *state, int num, int val)
+{
+ switch (num) {
+ case 4: // IRM - ECMA-48 7.2.10
+ state->mode.insert = (unsigned)val;
+ break;
+
+ case 20: // LNM - ANSI X3.4-1977
+ state->mode.newline = (unsigned)val;
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown mode %d\n", num);
+ return;
+ }
+}
+
+static void set_dec_mode(VTermState *state, int num, int val)
+{
+ switch (num) {
+ case 1:
+ state->mode.cursor = (unsigned)val;
+ break;
+
+ case 5: // DECSCNM - screen mode
+ settermprop_bool(state, VTERM_PROP_REVERSE, val);
+ break;
+
+ case 6: // DECOM - origin mode
+ {
+ VTermPos oldpos = state->pos;
+ state->mode.origin = (unsigned)val;
+ state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
+ state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
+ updatecursor(state, &oldpos, 1);
+ }
+ break;
+
+ case 7:
+ state->mode.autowrap = (unsigned)val;
+ break;
+
+ case 12:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
+ break;
+
+ case 25:
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
+ break;
+
+ case 69: // DECVSSM - vertical split screen mode
+ // DECLRMM - left/right margin mode
+ state->mode.leftrightmargin = (unsigned)val;
+ if (val) {
+ // Setting DECVSSM must clear doublewidth/doubleheight state of every line
+ for (int row = 0; row < state->rows; row++) {
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ }
+ }
+
+ break;
+
+ case 1000:
+ case 1002:
+ case 1003:
+ settermprop_int(state, VTERM_PROP_MOUSE,
+ !val ? VTERM_PROP_MOUSE_NONE
+ : (num == 1000) ? VTERM_PROP_MOUSE_CLICK
+ : (num == 1002) ? VTERM_PROP_MOUSE_DRAG
+ : VTERM_PROP_MOUSE_MOVE);
+ break;
+
+ case 1004:
+ settermprop_bool(state, VTERM_PROP_FOCUSREPORT, val);
+ state->mode.report_focus = (unsigned)val;
+ break;
+
+ case 1005:
+ state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
+ break;
+
+ case 1006:
+ state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
+ break;
+
+ case 1015:
+ state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
+ break;
+
+ case 1047:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ break;
+
+ case 1048:
+ savecursor(state, val);
+ break;
+
+ case 1049:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ savecursor(state, val);
+ break;
+
+ case 2004:
+ state->mode.bracketpaste = (unsigned)val;
+ break;
+
+ case 2031:
+ settermprop_bool(state, VTERM_PROP_THEMEUPDATES, val);
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
+ return;
+ }
+}
+
+static void request_dec_mode(VTermState *state, int num)
+{
+ int reply;
+
+ switch (num) {
+ case 1:
+ reply = state->mode.cursor;
+ break;
+
+ case 5:
+ reply = state->mode.screen;
+ break;
+
+ case 6:
+ reply = state->mode.origin;
+ break;
+
+ case 7:
+ reply = state->mode.autowrap;
+ break;
+
+ case 12:
+ reply = state->mode.cursor_blink;
+ break;
+
+ case 25:
+ reply = state->mode.cursor_visible;
+ break;
+
+ case 69:
+ reply = state->mode.leftrightmargin;
+ break;
+
+ case 1000:
+ reply = state->mouse_flags == MOUSE_WANT_CLICK;
+ break;
+
+ case 1002:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
+ break;
+
+ case 1003:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
+ break;
+
+ case 1004:
+ reply = state->mode.report_focus;
+ break;
+
+ case 1005:
+ reply = state->mouse_protocol == MOUSE_UTF8;
+ break;
+
+ case 1006:
+ reply = state->mouse_protocol == MOUSE_SGR;
+ break;
+
+ case 1015:
+ reply = state->mouse_protocol == MOUSE_RXVT;
+ break;
+
+ case 1047:
+ reply = state->mode.alt_screen;
+ break;
+
+ case 2004:
+ reply = state->mode.bracketpaste;
+ break;
+
+ case 2031:
+ reply = state->mode.theme_updates;
+ break;
+
+ default:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
+ return;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
+}
+
+static void request_version_string(VTermState *state)
+{
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, ">|libvterm(%d.%d)",
+ VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR);
+}
+
+static void request_key_encoding_flags(VTermState *state)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+
+ int reply = 0;
+
+ assert(stack->size > 0);
+ VTermKeyEncodingFlags flags = stack->items[stack->size - 1];
+
+ if (flags.disambiguate) {
+ reply |= KEY_ENCODING_DISAMBIGUATE;
+ }
+
+ if (flags.report_events) {
+ reply |= KEY_ENCODING_REPORT_EVENTS;
+ }
+
+ if (flags.report_alternate) {
+ reply |= KEY_ENCODING_REPORT_ALTERNATE;
+ }
+
+ if (flags.report_all_keys) {
+ reply |= KEY_ENCODING_REPORT_ALL_KEYS;
+ }
+
+ if (flags.report_associated) {
+ reply |= KEY_ENCODING_REPORT_ASSOCIATED;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%du", reply);
+}
+
+static void set_key_encoding_flags(VTermState *state, int arg, int mode)
+{
+ // When mode is 3, bits set in arg reset the corresponding mode
+ bool set = mode != 3;
+
+ // When mode is 1, unset bits are reset
+ bool reset_unset = mode == 1;
+
+ struct VTermKeyEncodingFlags flags = { 0 };
+ if (arg & KEY_ENCODING_DISAMBIGUATE) {
+ flags.disambiguate = set;
+ } else if (reset_unset) {
+ flags.disambiguate = false;
+ }
+
+ if (arg & KEY_ENCODING_REPORT_EVENTS) {
+ flags.report_events = set;
+ } else if (reset_unset) {
+ flags.report_events = false;
+ }
+
+ if (arg & KEY_ENCODING_REPORT_ALTERNATE) {
+ flags.report_alternate = set;
+ } else if (reset_unset) {
+ flags.report_alternate = false;
+ }
+ if (arg & KEY_ENCODING_REPORT_ALL_KEYS) {
+ flags.report_all_keys = set;
+ } else if (reset_unset) {
+ flags.report_all_keys = false;
+ }
+
+ if (arg & KEY_ENCODING_REPORT_ASSOCIATED) {
+ flags.report_associated = set;
+ } else if (reset_unset) {
+ flags.report_associated = false;
+ }
+
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size > 0);
+ stack->items[stack->size - 1] = flags;
+}
+
+static void push_key_encoding_flags(VTermState *state, int arg)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size <= ARRAY_SIZE(stack->items));
+
+ if (stack->size == ARRAY_SIZE(stack->items)) {
+ // Evict oldest entry when stack is full
+ for (size_t i = 0; i < ARRAY_SIZE(stack->items) - 1; i++) {
+ stack->items[i] = stack->items[i + 1];
+ }
+ } else {
+ stack->size++;
+ }
+
+ set_key_encoding_flags(state, arg, 1);
+}
+
+static void pop_key_encoding_flags(VTermState *state, int arg)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ if (arg >= stack->size) {
+ stack->size = 1;
+
+ // If a pop request is received that empties the stack, all flags are reset.
+ memset(&stack->items[0], 0, sizeof(stack->items[0]));
+ } else if (arg > 0) {
+ stack->size -= arg;
+ }
+}
+
+static int on_csi(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user)
+{
+ VTermState *state = user;
+ int leader_byte = 0;
+ int intermed_byte = 0;
+ int cancel_phantom = 1;
+
+ if (leader && leader[0]) {
+ if (leader[1]) { // longer than 1 char
+ return 0;
+ }
+
+ switch (leader[0]) {
+ case '?':
+ case '>':
+ case '<':
+ case '=':
+ leader_byte = (int)leader[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if (intermed && intermed[0]) {
+ if (intermed[1]) { // longer than 1 char
+ return 0;
+ }
+
+ switch (intermed[0]) {
+ case ' ':
+ case '!':
+ case '"':
+ case '$':
+ case '\'':
+ intermed_byte = (int)intermed[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ VTermPos oldpos = state->pos;
+
+ // Some temporaries for later code
+ int count, val;
+ int row, col;
+ VTermRect rect;
+ int selective;
+
+#define LBOUND(v, min) if ((v) < (min))(v) = (min)
+#define UBOUND(v, max) if ((v) > (max))(v) = (max)
+
+#define LEADER(l, b) ((l << 8) | b)
+#define INTERMED(i, b) ((i << 16) | b)
+
+ switch (intermed_byte << 16 | leader_byte << 8 | command) {
+ case 0x40: // ICH - ECMA-48 8.3.64
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if (state->mode.leftrightmargin) {
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ } else {
+ rect.end_col = THISROWWIDTH(state);
+ }
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case 0x41: // CUU - ECMA-48 8.3.22
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x42: // CUD - ECMA-48 8.3.19
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x43: // CUF - ECMA-48 8.3.20
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x44: // CUB - ECMA-48 8.3.18
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x45: // CNL - ECMA-48 8.3.12
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x46: // CPL - ECMA-48 8.3.13
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x47: // CHA - ECMA-48 8.3.9
+ val = CSI_ARG_OR(args[0], 1);
+ state->pos.col = val - 1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x48: // CUP - ECMA-48 8.3.21
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row - 1;
+ state->pos.col = col - 1;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x49: // CHT - ECMA-48 8.3.10
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, +1);
+ break;
+
+ case 0x4a: // ED - ECMA-48 8.3.39
+ case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
+ selective = (leader_byte == '?');
+ switch (CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col; rect.end_col = state->cols;
+ if (rect.end_col > rect.start_col) {
+ erase(state, rect, selective);
+ }
+
+ rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
+ rect.start_col = 0;
+ for (int row_ = rect.start_row; row_ < rect.end_row; row_++) {
+ set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
+ }
+ if (rect.end_row > rect.start_row) {
+ erase(state, rect, selective);
+ }
+ break;
+
+ case 1:
+ rect.start_row = 0; rect.end_row = state->pos.row;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for (int row_ = rect.start_row; row_ < rect.end_row; row_++) {
+ set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
+ }
+ if (rect.end_col > rect.start_col) {
+ erase(state, rect, selective);
+ }
+
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.end_col = state->pos.col + 1;
+ if (rect.end_row > rect.start_row) {
+ erase(state, rect, selective);
+ }
+ break;
+
+ case 2:
+ rect.start_row = 0; rect.end_row = state->rows;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for (int row_ = rect.start_row; row_ < rect.end_row; row_++) {
+ set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
+ }
+ erase(state, rect, selective);
+ break;
+
+ case 3:
+ if (state->callbacks && state->callbacks->sb_clear) {
+ if ((*state->callbacks->sb_clear)(state->cbdata)) {
+ return 1;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 0x4b: // EL - ECMA-48 8.3.41
+ case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
+ selective = (leader_byte == '?');
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+
+ switch (CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
+ case 1:
+ rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
+ case 2:
+ rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
+ default:
+ return 0;
+ }
+
+ if (rect.end_col > rect.start_col) {
+ erase(state, rect, selective);
+ }
+
+ break;
+
+ case 0x4c: // IL - ECMA-48 8.3.67
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x4d: // DL - ECMA-48 8.3.32
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x50: // DCH - ECMA-48 8.3.26
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if (state->mode.leftrightmargin) {
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ } else {
+ rect.end_col = THISROWWIDTH(state);
+ }
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ case 0x53: // SU - ECMA-48 8.3.147
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x54: // SD - ECMA-48 8.3.113
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x58: // ECH - ECMA-48 8.3.38
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ rect.end_col = state->pos.col + count;
+ UBOUND(rect.end_col, THISROWWIDTH(state));
+
+ erase(state, rect, 0);
+ break;
+
+ case 0x5a: // CBT - ECMA-48 8.3.7
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, -1);
+ break;
+
+ case 0x60: // HPA - ECMA-48 8.3.57
+ col = CSI_ARG_OR(args[0], 1);
+ state->pos.col = col - 1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x61: // HPR - ECMA-48 8.3.59
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x62: { // REP - ECMA-48 8.3.103
+ const int row_width = THISROWWIDTH(state);
+ count = CSI_ARG_COUNT(args[0]);
+ col = state->pos.col + count;
+ UBOUND(col, row_width);
+ schar_T sc = schar_from_buf(state->grapheme_buf, state->grapheme_len);
+ while (state->pos.col < col) {
+ putglyph(state, sc, state->combine_width, state->pos);
+ state->pos.col += state->combine_width;
+ }
+ if (state->pos.col + state->combine_width >= row_width) {
+ if (state->mode.autowrap) {
+ state->at_phantom = 1;
+ cancel_phantom = 0;
+ }
+ }
+ break;
+ }
+
+ case 0x63: // DA - ECMA-48 8.3.24
+ val = CSI_ARG_OR(args[0], 0);
+ if (val == 0) {
+ // DEC VT100 response
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
+ }
+ break;
+
+ case LEADER('>', 0x63): // DEC secondary Device Attributes
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
+ break;
+
+ case 0x64: // VPA - ECMA-48 8.3.158
+ row = CSI_ARG_OR(args[0], 1);
+ state->pos.row = row - 1;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x65: // VPR - ECMA-48 8.3.160
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x66: // HVP - ECMA-48 8.3.63
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row - 1;
+ state->pos.col = col - 1;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x67: // TBC - ECMA-48 8.3.154
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch (val) {
+ case 0:
+ clear_col_tabstop(state, state->pos.col);
+ break;
+ case 3:
+ case 5:
+ for (col = 0; col < state->cols; col++) {
+ clear_col_tabstop(state, col);
+ }
+ break;
+ case 1:
+ case 2:
+ case 4:
+ break;
+ // TODO(vterm): 1, 2 and 4 aren't meaningful yet without line tab stops
+ default:
+ return 0;
+ }
+ break;
+
+ case 0x68: // SM - ECMA-48 8.3.125
+ if (!CSI_ARG_IS_MISSING(args[0])) {
+ set_mode(state, CSI_ARG(args[0]), 1);
+ }
+ break;
+
+ case LEADER('?', 0x68): // DEC private mode set
+ for (int i = 0; i < argcount; i++) {
+ if (!CSI_ARG_IS_MISSING(args[i])) {
+ set_dec_mode(state, CSI_ARG(args[i]), 1);
+ }
+ }
+ break;
+
+ case 0x6a: // HPB - ECMA-48 8.3.58
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6b: // VPB - ECMA-48 8.3.159
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6c: // RM - ECMA-48 8.3.106
+ if (!CSI_ARG_IS_MISSING(args[0])) {
+ set_mode(state, CSI_ARG(args[0]), 0);
+ }
+ break;
+
+ case LEADER('?', 0x6c): // DEC private mode reset
+ for (int i = 0; i < argcount; i++) {
+ if (!CSI_ARG_IS_MISSING(args[i])) {
+ set_dec_mode(state, CSI_ARG(args[i]), 0);
+ }
+ }
+ break;
+
+ case 0x6d: // SGR - ECMA-48 8.3.117
+ vterm_state_setpen(state, args, argcount);
+ break;
+
+ case LEADER('?', 0x6d): // DECSGR
+ // No actual DEC terminal recognised these, but some printers did. These are alternative ways to
+ // request subscript/superscript/off
+ for (int argi = 0; argi < argcount; argi++) {
+ long arg;
+ switch (arg = CSI_ARG(args[argi])) {
+ case 4: // Superscript on
+ arg = 73;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 5: // Subscript on
+ arg = 74;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 24: // Super+subscript off
+ arg = 75;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ }
+ }
+ break;
+
+ case 0x6e: // DSR - ECMA-48 8.3.35
+ case LEADER('?', 0x6e): // DECDSR
+ val = CSI_ARG_OR(args[0], 0);
+
+ {
+ char *qmark = (leader_byte == '?') ? "?" : "";
+ bool dark = false;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ // ignore - these are replies
+ break;
+ case 5:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
+ break;
+ case 6: // CPR - cursor position report
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1,
+ state->pos.col + 1);
+ break;
+ case 996:
+ if (state->callbacks && state->callbacks->theme) {
+ if (state->callbacks->theme(&dark, state->cbdata)) {
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?997;%cn", dark ? '1' : '2');
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset
+ vterm_state_reset(state, 0);
+ break;
+
+ case LEADER('?', INTERMED('$', 0x70)):
+ request_dec_mode(state, CSI_ARG(args[0]));
+ break;
+
+ case LEADER('>', 0x71): // XTVERSION - xterm query version string
+ request_version_string(state);
+ break;
+
+ case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
+ val = CSI_ARG_OR(args[0], 1);
+
+ switch (val) {
+ case 0:
+ case 1:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 2:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 3:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 4:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 5:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ case 6:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ }
+
+ break;
+
+ case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch (val) {
+ case 0:
+ case 2:
+ state->protected_cell = 0;
+ break;
+ case 1:
+ state->protected_cell = 1;
+ break;
+ }
+
+ break;
+
+ case 0x72: // DECSTBM - DEC custom
+ state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_bottom = argcount < 2
+ || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_top, 0);
+ UBOUND(state->scrollregion_top, state->rows);
+ LBOUND(state->scrollregion_bottom, -1);
+ if (state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows) {
+ state->scrollregion_bottom = -1;
+ } else {
+ UBOUND(state->scrollregion_bottom, state->rows);
+ }
+
+ if (SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ // Invalid
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case 0x73: // DECSLRM - DEC custom
+ // Always allow setting these margins, just they won't take effect without DECVSSM
+ state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_left, 0);
+ UBOUND(state->scrollregion_left, state->cols);
+ LBOUND(state->scrollregion_right, -1);
+ if (state->scrollregion_left == 0 && state->scrollregion_right == state->cols) {
+ state->scrollregion_right = -1;
+ } else {
+ UBOUND(state->scrollregion_right, state->cols);
+ }
+
+ if (state->scrollregion_right > -1
+ && state->scrollregion_right <= state->scrollregion_left) {
+ // Invalid
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case LEADER('?', 0x75): // Kitty query
+ request_key_encoding_flags(state);
+ break;
+
+ case LEADER('>', 0x75): // Kitty push flags
+ push_key_encoding_flags(state, CSI_ARG_OR(args[0], 0));
+ break;
+
+ case LEADER('<', 0x75): // Kitty pop flags
+ pop_key_encoding_flags(state, CSI_ARG_OR(args[0], 1));
+ break;
+
+ case LEADER('=', 0x75): // Kitty set flags
+ val = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ set_key_encoding_flags(state, CSI_ARG_OR(args[0], 0), val);
+ break;
+
+ case INTERMED('\'', 0x7D): // DECIC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case INTERMED('\'', 0x7E): // DECDC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ default:
+ if (state->fallbacks && state->fallbacks->csi) {
+ if ((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ if (state->mode.origin) {
+ LBOUND(state->pos.row, state->scrollregion_top);
+ UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state) - 1);
+ LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
+ UBOUND(state->pos.col, SCROLLREGION_RIGHT(state) - 1);
+ } else {
+ LBOUND(state->pos.row, 0);
+ UBOUND(state->pos.row, state->rows - 1);
+ LBOUND(state->pos.col, 0);
+ UBOUND(state->pos.col, THISROWWIDTH(state) - 1);
+ }
+
+ updatecursor(state, &oldpos, cancel_phantom);
+
+#ifdef DEBUG
+ if (state->pos.row < 0 || state->pos.row >= state->rows
+ || state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
+ command, state->pos.row, state->pos.col);
+ abort();
+ }
+
+ if (SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
+ abort();
+ }
+
+ if (SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
+ fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static uint8_t unbase64one(char c)
+{
+ if (c >= 'A' && c <= 'Z') {
+ return (uint8_t)c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ return (uint8_t)c - 'a' + 26;
+ } else if (c >= '0' && c <= '9') {
+ return (uint8_t)c - '0' + 52;
+ } else if (c == '+') {
+ return 62;
+ } else if (c == '/') {
+ return 63;
+ }
+
+ return 0xFF;
+}
+
+static void osc_selection(VTermState *state, VTermStringFragment frag)
+{
+ if (frag.initial) {
+ state->tmp.selection.mask = 0;
+ state->tmp.selection.state = SELECTION_INITIAL;
+ }
+
+ while (!state->tmp.selection.state && frag.len) {
+ // Parse selection parameter
+ switch (frag.str[0]) {
+ case 'c':
+ state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD;
+ break;
+ case 'p':
+ state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY;
+ break;
+ case 'q':
+ state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY;
+ break;
+ case 's':
+ state->tmp.selection.mask |= VTERM_SELECTION_SELECT;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
+ break;
+
+ case ';':
+ state->tmp.selection.state = SELECTION_SELECTED;
+ if (!state->tmp.selection.mask) {
+ state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0;
+ }
+ break;
+ }
+
+ frag.str++;
+ frag.len--;
+ }
+
+ if (!frag.len) {
+ // Clear selection if we're already finished but didn't do anything
+ if (frag.final && state->selection.callbacks->set) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = NULL,
+ .len = 0,
+ .initial = state->tmp.selection.state != SELECTION_SET,
+ .final = true,
+ }, state->selection.user);
+ }
+ return;
+ }
+
+ if (state->tmp.selection.state == SELECTION_SELECTED) {
+ if (frag.str[0] == '?') {
+ state->tmp.selection.state = SELECTION_QUERY;
+ } else {
+ state->tmp.selection.state = SELECTION_SET_INITIAL;
+ state->tmp.selection.recvpartial = 0;
+ }
+ }
+
+ if (state->tmp.selection.state == SELECTION_QUERY) {
+ if (state->selection.callbacks->query) {
+ (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
+ }
+ return;
+ }
+
+ if (state->tmp.selection.state == SELECTION_INVALID) {
+ return;
+ }
+
+ if (state->selection.callbacks->set) {
+ size_t bufcur = 0;
+ char *buffer = state->selection.buffer;
+
+ uint32_t x = 0; // Current decoding value
+ int n = 0; // Number of sextets consumed
+
+ if (state->tmp.selection.recvpartial) {
+ n = state->tmp.selection.recvpartial >> 24;
+ x = state->tmp.selection.recvpartial & 0x03FFFF; // could be up to 18 bits of state in here
+
+ state->tmp.selection.recvpartial = 0;
+ }
+
+ while ((state->selection.buflen - bufcur) >= 3 && frag.len) {
+ if (frag.str[0] == '=') {
+ if (n == 2) {
+ buffer[0] = (char)(x >> 4 & 0xFF);
+ buffer += 1, bufcur += 1;
+ }
+ if (n == 3) {
+ buffer[0] = (char)(x >> 10 & 0xFF);
+ buffer[1] = (char)(x >> 2 & 0xFF);
+ buffer += 2, bufcur += 2;
+ }
+
+ while (frag.len && frag.str[0] == '=') {
+ frag.str++, frag.len--;
+ }
+
+ n = 0;
+ } else {
+ uint8_t b = unbase64one(frag.str[0]);
+ if (b == 0xFF) {
+ DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
+
+ state->tmp.selection.state = SELECTION_INVALID;
+ if (state->selection.callbacks->set) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = NULL,
+ .len = 0,
+ .initial = true,
+ .final = true,
+ }, state->selection.user);
+ }
+ break;
+ }
+
+ x = (x << 6) | b;
+ n++;
+ frag.str++, frag.len--;
+
+ if (n == 4) {
+ buffer[0] = (char)(x >> 16 & 0xFF);
+ buffer[1] = (char)(x >> 8 & 0xFF);
+ buffer[2] = (char)(x >> 0 & 0xFF);
+
+ buffer += 3, bufcur += 3;
+ x = 0;
+ n = 0;
+ }
+ }
+
+ if (!frag.len || (state->selection.buflen - bufcur) < 3) {
+ if (bufcur) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = state->selection.buffer,
+ .len = bufcur,
+ .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
+ .final = frag.final && !frag.len,
+ }, state->selection.user);
+ state->tmp.selection.state = SELECTION_SET;
+ }
+
+ buffer = state->selection.buffer;
+ bufcur = 0;
+ }
+ }
+
+ if (n) {
+ state->tmp.selection.recvpartial = (uint32_t)(n << 24) | x;
+ }
+ }
+}
+
+static int on_osc(int command, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ switch (command) {
+ case 0:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 1:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ return 1;
+
+ case 2:
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 52:
+ if (state->selection.callbacks) {
+ osc_selection(state, frag);
+ }
+
+ return 1;
+
+ default:
+ if (state->fallbacks && state->fallbacks->osc) {
+ if ((*state->fallbacks->osc)(command, frag, state->fbdata)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void request_status_string(VTermState *state, VTermStringFragment frag)
+{
+ VTerm *vt = state->vt;
+
+ char *tmp = state->tmp.decrqss;
+
+ if (frag.initial) {
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+ }
+
+ size_t i = 0;
+ while (i < sizeof(state->tmp.decrqss) - 1 && tmp[i]) {
+ i++;
+ }
+ while (i < sizeof(state->tmp.decrqss) - 1 && frag.len--) {
+ tmp[i++] = (frag.str++)[0];
+ }
+ tmp[i] = 0;
+
+ if (!frag.final) {
+ return;
+ }
+
+ switch (tmp[0] | tmp[1] << 8 | tmp[2] << 16) {
+ case 'm': {
+ // Query SGR
+ long args[20];
+ int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
+ size_t cur = 0;
+
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ for (int argi = 0; argi < argc; argi++) {
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ argi == argc - 1 ? "%ld"
+ : CSI_ARG_HAS_MORE(args[argi]) ? "%ld:"
+ : "%ld;",
+ CSI_ARG(args[argi]));
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+ }
+
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+ return;
+ }
+
+ case 'r':
+ // Query DECSTBM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%dr", state->scrollregion_top + 1,
+ SCROLLREGION_BOTTOM(state));
+ return;
+
+ case 's':
+ // Query DECSLRM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%ds", SCROLLREGION_LEFT(state) + 1,
+ SCROLLREGION_RIGHT(state));
+ return;
+
+ case ' '|('q' << 8): {
+ // Query DECSCUSR
+ int reply = 0;
+ switch (state->mode.cursor_shape) {
+ case VTERM_PROP_CURSORSHAPE_BLOCK:
+ reply = 2; break;
+ case VTERM_PROP_CURSORSHAPE_UNDERLINE:
+ reply = 4; break;
+ case VTERM_PROP_CURSORSHAPE_BAR_LEFT:
+ reply = 6; break;
+ }
+ if (state->mode.cursor_blink) {
+ reply--;
+ }
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d q", reply);
+ return;
+ }
+
+ case '\"'|('q' << 8):
+ // Query DECSCA
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d\"q", state->protected_cell ? 1 : 2);
+ return;
+ }
+
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r");
+}
+
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (commandlen == 2 && strneq(command, "$q", 2)) {
+ request_status_string(state, frag);
+ return 1;
+ } else if (state->fallbacks && state->fallbacks->dcs) {
+ if ((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
+ return 0;
+}
+
+static int on_apc(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (state->fallbacks && state->fallbacks->apc) {
+ if ((*state->fallbacks->apc)(frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ // No DEBUG_LOG because all APCs are unhandled
+ return 0;
+}
+
+static int on_pm(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (state->fallbacks && state->fallbacks->pm) {
+ if ((*state->fallbacks->pm)(frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ // No DEBUG_LOG because all PMs are unhandled
+ return 0;
+}
+
+static int on_sos(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (state->fallbacks && state->fallbacks->sos) {
+ if ((*state->fallbacks->sos)(frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ // No DEBUG_LOG because all SOSs are unhandled
+ return 0;
+}
+
+static int on_resize(int rows, int cols, void *user)
+{
+ VTermState *state = user;
+ VTermPos oldpos = state->pos;
+
+ if (cols != state->cols) {
+ uint8_t *newtabstops = vterm_allocator_malloc(state->vt, ((size_t)cols + 7) / 8);
+
+ // TODO(vterm): This can all be done much more efficiently bytewise
+ int col;
+ for (col = 0; col < state->cols && col < cols; col++) {
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ if (state->tabstops[col >> 3] & mask) {
+ newtabstops[col >> 3] |= mask;
+ } else {
+ newtabstops[col >> 3] &= ~mask;
+ }
+ }
+
+ for (; col < cols; col++) {
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ if (col % 8 == 0) {
+ newtabstops[col >> 3] |= mask;
+ } else {
+ newtabstops[col >> 3] &= ~mask;
+ }
+ }
+
+ vterm_allocator_free(state->vt, state->tabstops);
+ state->tabstops = newtabstops;
+ }
+
+ state->rows = rows;
+ state->cols = cols;
+
+ if (state->scrollregion_bottom > -1) {
+ UBOUND(state->scrollregion_bottom, state->rows);
+ }
+ if (state->scrollregion_right > -1) {
+ UBOUND(state->scrollregion_right, state->cols);
+ }
+
+ VTermStateFields fields = {
+ .pos = state->pos,
+ .lineinfos = {[0] = state->lineinfos[0], [1] = state->lineinfos[1] },
+ };
+
+ if (state->callbacks && state->callbacks->resize) {
+ (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
+ state->pos = fields.pos;
+
+ state->lineinfos[0] = fields.lineinfos[0];
+ state->lineinfos[1] = fields.lineinfos[1];
+ } else {
+ if (rows != state->rows) {
+ for (int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
+ VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
+ if (!oldlineinfo) {
+ continue;
+ }
+
+ VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt,
+ (size_t)rows * sizeof(VTermLineInfo));
+
+ int row;
+ for (row = 0; row < state->rows && row < rows; row++) {
+ newlineinfo[row] = oldlineinfo[row];
+ }
+
+ for (; row < rows; row++) {
+ newlineinfo[row] = (VTermLineInfo){
+ .doublewidth = 0,
+ };
+ }
+
+ vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
+ state->lineinfos[bufidx] = newlineinfo;
+ }
+ }
+ }
+
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+
+ if (state->at_phantom && state->pos.col < cols - 1) {
+ state->at_phantom = 0;
+ state->pos.col++;
+ }
+
+ if (state->pos.row < 0) {
+ state->pos.row = 0;
+ }
+ if (state->pos.row >= rows) {
+ state->pos.row = rows - 1;
+ }
+ if (state->pos.col < 0) {
+ state->pos.col = 0;
+ }
+ if (state->pos.col >= cols) {
+ state->pos.col = cols - 1;
+ }
+
+ updatecursor(state, &oldpos, 1);
+
+ return 1;
+}
+
+static const VTermParserCallbacks parser_callbacks = {
+ .text = on_text,
+ .control = on_control,
+ .escape = on_escape,
+ .csi = on_csi,
+ .osc = on_osc,
+ .dcs = on_dcs,
+ .apc = on_apc,
+ .pm = on_pm,
+ .sos = on_sos,
+ .resize = on_resize,
+};
+
+VTermState *vterm_obtain_state(VTerm *vt)
+{
+ if (vt->state) {
+ return vt->state;
+ }
+
+ VTermState *state = vterm_state_new(vt);
+ vt->state = state;
+
+ vterm_parser_set_callbacks(vt, &parser_callbacks, state);
+
+ return state;
+}
+
+void vterm_state_reset(VTermState *state, int hard)
+{
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+
+ state->mode.keypad = 0;
+ state->mode.cursor = 0;
+ state->mode.autowrap = 1;
+ state->mode.insert = 0;
+ state->mode.newline = 0;
+ state->mode.alt_screen = 0;
+ state->mode.origin = 0;
+ state->mode.leftrightmargin = 0;
+ state->mode.bracketpaste = 0;
+ state->mode.report_focus = 0;
+
+ state->mouse_flags = 0;
+
+ state->vt->mode.ctrl8bit = 0;
+
+ for (int col = 0; col < state->cols; col++) {
+ if (col % 8 == 0) {
+ set_col_tabstop(state, col);
+ } else {
+ clear_col_tabstop(state, col);
+ }
+ }
+
+ for (int row = 0; row < state->rows; row++) {
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ }
+
+ if (state->callbacks && state->callbacks->initpen) {
+ (*state->callbacks->initpen)(state->cbdata);
+ }
+
+ vterm_state_resetpen(state);
+
+ VTermEncoding *default_enc = state->vt->mode.utf8
+ ? vterm_lookup_encoding(ENC_UTF8, 'u')
+ : vterm_lookup_encoding(ENC_SINGLE_94, 'B');
+
+ for (int i = 0; i < 4; i++) {
+ state->encoding[i].enc = default_enc;
+ if (default_enc->init) {
+ (*default_enc->init)(default_enc, state->encoding[i].data);
+ }
+ }
+
+ state->gl_set = 0;
+ state->gr_set = 1;
+ state->gsingle_set = 0;
+
+ state->protected_cell = 0;
+
+ // Initialise the props
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+
+ if (hard) {
+ state->pos.row = 0;
+ state->pos.col = 0;
+ state->at_phantom = 0;
+
+ VTermRect rect = { 0, state->rows, 0, state->cols };
+ erase(state, rect, 0);
+ }
+}
+
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
+{
+ if (callbacks) {
+ state->callbacks = callbacks;
+ state->cbdata = user;
+
+ if (state->callbacks && state->callbacks->initpen) {
+ (*state->callbacks->initpen)(state->cbdata);
+ }
+ } else {
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+ }
+}
+
+void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks,
+ void *user)
+{
+ if (fallbacks) {
+ state->fallbacks = fallbacks;
+ state->fbdata = user;
+ } else {
+ state->fallbacks = NULL;
+ state->fbdata = NULL;
+ }
+}
+
+int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
+{
+ // Only store the new value of the property if usercode said it was happy. This is especially
+ // important for altscreen switching
+ if (state->callbacks && state->callbacks->settermprop) {
+ if (!(*state->callbacks->settermprop)(prop, val, state->cbdata)) {
+ return 0;
+ }
+ }
+
+ switch (prop) {
+ case VTERM_PROP_TITLE:
+ case VTERM_PROP_ICONNAME:
+ // we don't store these, just transparently pass through
+ return 1;
+ case VTERM_PROP_CURSORVISIBLE:
+ state->mode.cursor_visible = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORBLINK:
+ state->mode.cursor_blink = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORSHAPE:
+ state->mode.cursor_shape = (unsigned)val->number;
+ return 1;
+ case VTERM_PROP_REVERSE:
+ state->mode.screen = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_ALTSCREEN:
+ state->mode.alt_screen = (unsigned)val->boolean;
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+ if (state->mode.alt_screen) {
+ VTermRect rect = {
+ .start_row = 0,
+ .start_col = 0,
+ .end_row = state->rows,
+ .end_col = state->cols,
+ };
+ erase(state, rect, 0);
+ }
+ return 1;
+ case VTERM_PROP_MOUSE:
+ state->mouse_flags = 0;
+ if (val->number) {
+ state->mouse_flags |= MOUSE_WANT_CLICK;
+ }
+ if (val->number == VTERM_PROP_MOUSE_DRAG) {
+ state->mouse_flags |= MOUSE_WANT_DRAG;
+ }
+ if (val->number == VTERM_PROP_MOUSE_MOVE) {
+ state->mouse_flags |= MOUSE_WANT_MOVE;
+ }
+ return 1;
+ case VTERM_PROP_FOCUSREPORT:
+ state->mode.report_focus = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_THEMEUPDATES:
+ state->mode.theme_updates = (unsigned)val->boolean;
+ return 1;
+
+ case VTERM_N_PROPS:
+ return 0;
+ }
+
+ return 0;
+}
+
+void vterm_state_focus_in(VTermState *state)
+{
+ if (state->mode.report_focus) {
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
+ }
+}
+
+void vterm_state_focus_out(VTermState *state)
+{
+ if (state->mode.report_focus) {
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
+ }
+}
+
+const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
+{
+ return state->lineinfo + row;
+}
+
+void vterm_state_set_selection_callbacks(VTermState *state,
+ const VTermSelectionCallbacks *callbacks, void *user,
+ char *buffer, size_t buflen)
+{
+ if (buflen && !buffer) {
+ buffer = vterm_allocator_malloc(state->vt, buflen);
+ }
+
+ state->selection.callbacks = callbacks;
+ state->selection.user = user;
+ state->selection.buffer = buffer;
+ state->selection.buflen = buflen;
+}
diff --git a/src/nvim/vterm/state.h b/src/nvim/vterm/state.h
new file mode 100644
index 0000000000..2f59cf7eec
--- /dev/null
+++ b/src/nvim/vterm/state.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/state.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/vterm.c b/src/nvim/vterm/vterm.c
new file mode 100644
index 0000000000..76d5dc3808
--- /dev/null
+++ b/src/nvim/vterm/vterm.c
@@ -0,0 +1,335 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auto/config.h"
+#include "nvim/memory.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/vterm.c.generated.h"
+#endif
+
+// *****************
+// * API functions *
+// *****************
+
+static void *default_malloc(size_t size, void *allocdata)
+{
+ void *ptr = xmalloc(size);
+ if (ptr) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+}
+
+static void default_free(void *ptr, void *allocdata)
+{
+ xfree(ptr);
+}
+
+static VTermAllocatorFunctions default_allocator = {
+ .malloc = &default_malloc,
+ .free = &default_free,
+};
+
+/// Convenient shortcut for default cases
+VTerm *vterm_new(int rows, int cols)
+{
+ return vterm_build(&(const struct VTermBuilder){
+ .rows = rows,
+ .cols = cols,
+ });
+}
+
+// A handy macro for defaulting values out of builder fields
+#define DEFAULT(v, def) ((v) ? (v) : (def))
+
+VTerm *vterm_build(const struct VTermBuilder *builder)
+{
+ const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
+
+ // Need to bootstrap using the allocator function directly
+ VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
+
+ vt->allocator = allocator;
+ vt->allocdata = builder->allocdata;
+
+ vt->rows = builder->rows;
+ vt->cols = builder->cols;
+
+ vt->parser.state = NORMAL;
+
+ vt->parser.callbacks = NULL;
+ vt->parser.cbdata = NULL;
+
+ vt->parser.emit_nul = false;
+
+ vt->outfunc = NULL;
+ vt->outdata = NULL;
+
+ vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
+ vt->outbuffer_cur = 0;
+ vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
+
+ vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
+ vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
+
+ return vt;
+}
+
+void vterm_free(VTerm *vt)
+{
+ if (vt->screen) {
+ vterm_screen_free(vt->screen);
+ }
+
+ if (vt->state) {
+ vterm_state_free(vt->state);
+ }
+
+ vterm_allocator_free(vt, vt->outbuffer);
+ vterm_allocator_free(vt, vt->tmpbuffer);
+
+ vterm_allocator_free(vt, vt);
+}
+
+void *vterm_allocator_malloc(VTerm *vt, size_t size)
+{
+ return (*vt->allocator->malloc)(size, vt->allocdata);
+}
+
+void vterm_allocator_free(VTerm *vt, void *ptr)
+{
+ (*vt->allocator->free)(ptr, vt->allocdata);
+}
+
+void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
+{
+ if (rowsp) {
+ *rowsp = vt->rows;
+ }
+ if (colsp) {
+ *colsp = vt->cols;
+ }
+}
+
+void vterm_set_size(VTerm *vt, int rows, int cols)
+{
+ if (rows < 1 || cols < 1) {
+ return;
+ }
+
+ vt->rows = rows;
+ vt->cols = cols;
+
+ if (vt->parser.callbacks && vt->parser.callbacks->resize) {
+ (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
+ }
+}
+
+void vterm_set_utf8(VTerm *vt, int is_utf8)
+{
+ vt->mode.utf8 = (unsigned)is_utf8;
+}
+
+void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
+{
+ vt->outfunc = func;
+ vt->outdata = user;
+}
+
+void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
+{
+ if (vt->outfunc) {
+ (vt->outfunc)(bytes, len, vt->outdata);
+ return;
+ }
+
+ if (len > vt->outbuffer_len - vt->outbuffer_cur) {
+ return;
+ }
+
+ memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
+ vt->outbuffer_cur += len;
+}
+
+void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
+ FUNC_ATTR_PRINTF(2, 3)
+{
+ va_list args;
+ va_start(args, format);
+ size_t len = (size_t)vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, format, args);
+ vterm_push_output_bytes(vt, vt->tmpbuffer, len);
+ va_end(args);
+}
+
+void vterm_push_output_sprintf_ctrl(VTerm *vt, uint8_t ctrl, const char *fmt, ...)
+ FUNC_ATTR_PRINTF(3, 4)
+{
+ size_t cur;
+
+ if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
+ } else {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
+ }
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
+ va_end(args);
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+void vterm_push_output_sprintf_str(VTerm *vt, uint8_t ctrl, bool term, const char *fmt, ...)
+ FUNC_ATTR_PRINTF(4, 5)
+{
+ size_t cur = 0;
+
+ if (ctrl) {
+ if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
+ } else {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
+ }
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
+ va_end(args);
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ if (term) {
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+VTermValueType vterm_get_attr_type(VTermAttr attr)
+{
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_UNDERLINE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_ITALIC:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BLINK:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_REVERSE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_CONCEAL:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_STRIKE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_FONT:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_FOREGROUND:
+ return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_BACKGROUND:
+ return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_SMALL:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BASELINE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_URI:
+ return VTERM_VALUETYPE_INT;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+ return 0; // UNREACHABLE
+}
+
+void vterm_scroll_rect(VTermRect rect, int downward, int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user), void *user)
+{
+ VTermRect src;
+ VTermRect dest;
+
+ if (abs(downward) >= rect.end_row - rect.start_row
+ || abs(rightward) >= rect.end_col - rect.start_col) {
+ // Scroll more than area; just erase the lot
+ (*eraserect)(rect, 0, user);
+ return;
+ }
+
+ if (rightward >= 0) {
+ // rect: [XXX................]
+ // src: [----------------]
+ // dest: [----------------]
+ dest.start_col = rect.start_col;
+ dest.end_col = rect.end_col - rightward;
+ src.start_col = rect.start_col + rightward;
+ src.end_col = rect.end_col;
+ } else {
+ // rect: [................XXX]
+ // src: [----------------]
+ // dest: [----------------]
+ int leftward = -rightward;
+ dest.start_col = rect.start_col + leftward;
+ dest.end_col = rect.end_col;
+ src.start_col = rect.start_col;
+ src.end_col = rect.end_col - leftward;
+ }
+
+ if (downward >= 0) {
+ dest.start_row = rect.start_row;
+ dest.end_row = rect.end_row - downward;
+ src.start_row = rect.start_row + downward;
+ src.end_row = rect.end_row;
+ } else {
+ int upward = -downward;
+ dest.start_row = rect.start_row + upward;
+ dest.end_row = rect.end_row;
+ src.start_row = rect.start_row;
+ src.end_row = rect.end_row - upward;
+ }
+
+ if (moverect) {
+ (*moverect)(dest, src, user);
+ }
+
+ if (downward > 0) {
+ rect.start_row = rect.end_row - downward;
+ } else if (downward < 0) {
+ rect.end_row = rect.start_row - downward;
+ }
+
+ if (rightward > 0) {
+ rect.start_col = rect.end_col - rightward;
+ } else if (rightward < 0) {
+ rect.end_col = rect.start_col - rightward;
+ }
+
+ (*eraserect)(rect, 0, user);
+}
diff --git a/src/nvim/vterm/vterm.h b/src/nvim/vterm/vterm.h
new file mode 100644
index 0000000000..e66f40425a
--- /dev/null
+++ b/src/nvim/vterm/vterm.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "nvim/macros_defs.h"
+#include "nvim/types_defs.h"
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/vterm.h.generated.h"
+#endif
+
+#define VTERM_VERSION_MAJOR 0
+#define VTERM_VERSION_MINOR 3
+
+// move a rect
+static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
+{
+ rect->start_row += row_delta; rect->end_row += row_delta;
+ rect->start_col += col_delta; rect->end_col += col_delta;
+}
+
+// Bit-field describing the content of the tagged union `VTermColor`.
+typedef enum {
+ // If the lower bit of `type` is not set, the colour is 24-bit RGB.
+ VTERM_COLOR_RGB = 0x00,
+
+ // The colour is an index into a palette of 256 colours.
+ VTERM_COLOR_INDEXED = 0x01,
+
+ // Mask that can be used to extract the RGB/Indexed bit.
+ VTERM_COLOR_TYPE_MASK = 0x01,
+
+ // If set, indicates that this colour should be the default foreground color, i.e. there was no
+ // SGR request for another colour. When rendering this colour it is possible to ignore "idx" and
+ // just use a colour that is not in the palette.
+ VTERM_COLOR_DEFAULT_FG = 0x02,
+
+ // If set, indicates that this colour should be the default background color, i.e. there was no
+ // SGR request for another colour. A common option when rendering this colour is to not render a
+ // background at all, for example by rendering the window transparently at this spot.
+ VTERM_COLOR_DEFAULT_BG = 0x04,
+
+ // Mask that can be used to extract the default foreground/background bit.
+ VTERM_COLOR_DEFAULT_MASK = 0x06,
+} VTermColorType;
+
+// Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the given VTermColor
+// instance is an indexed colour.
+#define VTERM_COLOR_IS_INDEXED(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
+
+// Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that the given VTermColor
+// instance is an rgb colour.
+#define VTERM_COLOR_IS_RGB(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
+
+// Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating that the given
+// VTermColor instance corresponds to the default foreground color.
+#define VTERM_COLOR_IS_DEFAULT_FG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_FG))
+
+// Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating that the given
+// VTermColor instance corresponds to the default background color.
+#define VTERM_COLOR_IS_DEFAULT_BG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_BG))
+
+// Constructs a new VTermColor instance representing the given RGB values.
+static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, uint8_t blue)
+{
+ col->type = VTERM_COLOR_RGB;
+ col->rgb.red = red;
+ col->rgb.green = green;
+ col->rgb.blue = blue;
+}
+
+// Construct a new VTermColor instance representing an indexed color with the given index.
+static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
+{
+ col->type = VTERM_COLOR_INDEXED;
+ col->indexed.idx = idx;
+}
+
+// ------------
+// Parser layer
+// ------------
+
+/// Flag to indicate non-final subparameters in a single CSI parameter.
+/// Consider
+/// CSI 1;2:3:4;5a
+/// 1 4 and 5 are final.
+/// 2 and 3 are non-final and will have this bit set
+///
+/// Don't confuse this with the final byte of the CSI escape; 'a' in this case.
+#define CSI_ARG_FLAG_MORE (1U << 31)
+#define CSI_ARG_MASK (~(1U << 31))
+
+#define CSI_ARG_HAS_MORE(a) ((a)& CSI_ARG_FLAG_MORE)
+#define CSI_ARG(a) ((a)& CSI_ARG_MASK)
+
+// Can't use -1 to indicate a missing argument; use this instead
+#define CSI_ARG_MISSING ((1UL<<31) - 1)
+
+#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
+#define CSI_ARG_OR(a, def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
+#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
+
+enum {
+ VTERM_UNDERLINE_OFF,
+ VTERM_UNDERLINE_SINGLE,
+ VTERM_UNDERLINE_DOUBLE,
+ VTERM_UNDERLINE_CURLY,
+};
+
+enum {
+ VTERM_BASELINE_NORMAL,
+ VTERM_BASELINE_RAISE,
+ VTERM_BASELINE_LOWER,
+};
+
+// Back-compat alias for the brief time it was in 0.3-RC1
+#define vterm_screen_set_reflow vterm_screen_enable_reflow
+
+void vterm_scroll_rect(VTermRect rect, int downward, int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user), void *user);
+
+struct VTermScreen {
+ VTerm *vt;
+ VTermState *state;
+
+ const VTermScreenCallbacks *callbacks;
+ void *cbdata;
+
+ VTermDamageSize damage_merge;
+ // start_row == -1 => no damage
+ VTermRect damaged;
+ VTermRect pending_scrollrect;
+ int pending_scroll_downward, pending_scroll_rightward;
+
+ int rows;
+ int cols;
+
+ unsigned global_reverse : 1;
+ unsigned reflow : 1;
+
+ // Primary and Altscreen. buffers[1] is lazily allocated as needed
+ ScreenCell *buffers[2];
+
+ // buffer will == buffers[0] or buffers[1], depending on altscreen
+ ScreenCell *buffer;
+
+ // buffer for a single screen row used in scrollback storage callbacks
+ VTermScreenCell *sb_buffer;
+
+ ScreenPen pen;
+};
diff --git a/src/nvim/vterm/vterm_defs.h b/src/nvim/vterm/vterm_defs.h
new file mode 100644
index 0000000000..9aa933bef0
--- /dev/null
+++ b/src/nvim/vterm/vterm_defs.h
@@ -0,0 +1,322 @@
+#pragma once
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "nvim/types_defs.h"
+
+typedef struct VTerm VTerm;
+typedef struct VTermState VTermState;
+typedef struct VTermScreen VTermScreen;
+
+typedef struct {
+ int row;
+ int col;
+} VTermPos;
+
+// some small utility functions; we can just keep these static here
+
+typedef struct {
+ int start_row;
+ int end_row;
+ int start_col;
+ int end_col;
+} VTermRect;
+
+// Tagged union storing either an RGB color or an index into a colour palette. In order to convert
+// indexed colours to RGB, you may use the vterm_state_convert_color_to_rgb() or
+// vterm_screen_convert_color_to_rgb() functions which lookup the RGB colour from the palette
+// maintained by a VTermState or VTermScreen instance.
+typedef union {
+ // Tag indicating which union member is actually valid. This variable coincides with the `type`
+ // member of the `rgb` and the `indexed` struct in memory. Please use the `VTERM_COLOR_IS_*` test
+ // macros to check whether a particular type flag is set.
+ uint8_t type;
+
+ // Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
+ struct {
+ // Same as the top-level `type` member stored in VTermColor.
+ uint8_t type;
+
+ // The actual 8-bit red, green, blue colour values.
+ uint8_t red, green, blue;
+ } rgb;
+
+ // If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into the colour palette.
+ struct {
+ // Same as the top-level `type` member stored in VTermColor.
+ uint8_t type;
+
+ // Index into the colour map.
+ uint8_t idx;
+ } indexed;
+} VTermColor;
+
+typedef struct {
+ unsigned bold : 1;
+ unsigned underline : 2;
+ unsigned italic : 1;
+ unsigned blink : 1;
+ unsigned reverse : 1;
+ unsigned conceal : 1;
+ unsigned strike : 1;
+ unsigned font : 4; // 0 to 9
+ unsigned dwl : 1; // On a DECDWL or DECDHL line
+ unsigned dhl : 2; // On a DECDHL line (1=top 2=bottom)
+ unsigned small : 1;
+ unsigned baseline : 2;
+} VTermScreenCellAttrs;
+
+typedef struct {
+ schar_T schar;
+ char width;
+ VTermScreenCellAttrs attrs;
+ VTermColor fg, bg;
+ int uri;
+} VTermScreenCell;
+
+typedef enum {
+ // VTERM_PROP_NONE = 0
+ VTERM_PROP_CURSORVISIBLE = 1, // bool
+ VTERM_PROP_CURSORBLINK, // bool
+ VTERM_PROP_ALTSCREEN, // bool
+ VTERM_PROP_TITLE, // string
+ VTERM_PROP_ICONNAME, // string
+ VTERM_PROP_REVERSE, // bool
+ VTERM_PROP_CURSORSHAPE, // number
+ VTERM_PROP_MOUSE, // number
+ VTERM_PROP_FOCUSREPORT, // bool
+ VTERM_PROP_THEMEUPDATES, // bool
+
+ VTERM_N_PROPS,
+} VTermProp;
+
+typedef struct {
+ const char *str;
+ size_t len : 30;
+ bool initial : 1;
+ bool final : 1;
+} VTermStringFragment;
+
+typedef union {
+ int boolean;
+ int number;
+ VTermStringFragment string;
+ VTermColor color;
+} VTermValue;
+
+typedef struct {
+ int (*damage)(VTermRect rect, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, void *user);
+ int (*theme)(bool *dark, void *user);
+ int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
+ int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
+ int (*sb_clear)(void *user);
+} VTermScreenCallbacks;
+
+typedef struct {
+ int (*control)(uint8_t control, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+} VTermStateFallbacks;
+
+typedef enum {
+ VTERM_DAMAGE_CELL, // every cell
+ VTERM_DAMAGE_ROW, // entire rows
+ VTERM_DAMAGE_SCREEN, // entire screen
+ VTERM_DAMAGE_SCROLL, // entire screen + scrollrect
+
+ VTERM_N_DAMAGES,
+} VTermDamageSize;
+
+typedef enum {
+ VTERM_ATTR_BOLD_MASK = 1 << 0,
+ VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
+ VTERM_ATTR_ITALIC_MASK = 1 << 2,
+ VTERM_ATTR_BLINK_MASK = 1 << 3,
+ VTERM_ATTR_REVERSE_MASK = 1 << 4,
+ VTERM_ATTR_STRIKE_MASK = 1 << 5,
+ VTERM_ATTR_FONT_MASK = 1 << 6,
+ VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
+ VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
+ VTERM_ATTR_CONCEAL_MASK = 1 << 9,
+ VTERM_ATTR_SMALL_MASK = 1 << 10,
+ VTERM_ATTR_BASELINE_MASK = 1 << 11,
+ VTERM_ATTR_URI_MASK = 1 << 12,
+
+ VTERM_ALL_ATTRS_MASK = (1 << 13) - 1,
+} VTermAttrMask;
+
+typedef enum {
+ // VTERM_VALUETYPE_NONE = 0
+ VTERM_VALUETYPE_BOOL = 1,
+ VTERM_VALUETYPE_INT,
+ VTERM_VALUETYPE_STRING,
+ VTERM_VALUETYPE_COLOR,
+
+ VTERM_N_VALUETYPES,
+} VTermValueType;
+
+typedef enum {
+ // VTERM_ATTR_NONE = 0
+ VTERM_ATTR_BOLD = 1, // bool: 1, 22
+ VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
+ VTERM_ATTR_ITALIC, // bool: 3, 23
+ VTERM_ATTR_BLINK, // bool: 5, 25
+ VTERM_ATTR_REVERSE, // bool: 7, 27
+ VTERM_ATTR_CONCEAL, // bool: 8, 28
+ VTERM_ATTR_STRIKE, // bool: 9, 29
+ VTERM_ATTR_FONT, // number: 10-19
+ VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
+ VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
+ VTERM_ATTR_SMALL, // bool: 73, 74, 75
+ VTERM_ATTR_BASELINE, // number: 73, 74, 75
+ VTERM_ATTR_URI, // number
+
+ VTERM_N_ATTRS,
+} VTermAttr;
+
+enum {
+ VTERM_PROP_CURSORSHAPE_BLOCK = 1,
+ VTERM_PROP_CURSORSHAPE_UNDERLINE,
+ VTERM_PROP_CURSORSHAPE_BAR_LEFT,
+
+ VTERM_N_PROP_CURSORSHAPES,
+};
+
+enum {
+ VTERM_PROP_MOUSE_NONE = 0,
+ VTERM_PROP_MOUSE_CLICK,
+ VTERM_PROP_MOUSE_DRAG,
+ VTERM_PROP_MOUSE_MOVE,
+
+ VTERM_N_PROP_MOUSES,
+};
+
+typedef enum {
+ VTERM_SELECTION_CLIPBOARD = (1<<0),
+ VTERM_SELECTION_PRIMARY = (1<<1),
+ VTERM_SELECTION_SECONDARY = (1<<2),
+ VTERM_SELECTION_SELECT = (1<<3),
+ VTERM_SELECTION_CUT0 = (1<<4), // also CUT1 .. CUT7 by bitshifting
+} VTermSelectionMask;
+
+typedef struct {
+ schar_T schar;
+ int width;
+ unsigned protected_cell:1; // DECSCA-protected against DECSEL/DECSED
+ unsigned dwl:1; // DECDWL or DECDHL double-width line
+ unsigned dhl:2; // DECDHL double-height line (1=top 2=bottom)
+} VTermGlyphInfo;
+
+typedef struct {
+ unsigned doublewidth:1; // DECDWL or DECDHL line
+ unsigned doubleheight:2; // DECDHL line (1=top 2=bottom)
+ unsigned continuation:1; // Line is a flow continuation of the previous
+} VTermLineInfo;
+
+// Copies of VTermState fields that the 'resize' callback might have reason to edit. 'resize'
+// callback gets total control of these fields and may free-and-reallocate them if required. They
+// will be copied back from the struct after the callback has returned.
+typedef struct {
+ VTermPos pos; // current cursor position
+ VTermLineInfo *lineinfos[2]; // [1] may be NULL
+} VTermStateFields;
+
+typedef struct {
+ // libvterm relies on this memory to be zeroed out before it is returned by the allocator.
+ void *(*malloc)(size_t size, void *allocdata);
+ void (*free)(void *ptr, void *allocdata);
+} VTermAllocatorFunctions;
+
+// Setting output callback will override the buffer logic
+typedef void VTermOutputCallback(const char *s, size_t len, void *user);
+
+struct VTermBuilder {
+ int ver; // currently unused but reserved for some sort of ABI version flag
+
+ int rows, cols;
+
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ // Override default sizes for various structures
+ size_t outbuffer_len; // default: 4096
+ size_t tmpbuffer_len; // default: 4096
+};
+
+typedef struct {
+ int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*erase)(VTermRect rect, int selective, void *user);
+ int (*initpen)(void *user);
+ int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
+ int (*theme)(bool *dark, void *user);
+ int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
+ void *user);
+ int (*sb_clear)(void *user);
+} VTermStateCallbacks;
+
+typedef struct {
+ int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
+ int (*query)(VTermSelectionMask mask, void *user);
+} VTermSelectionCallbacks;
+
+typedef struct {
+ int (*text)(const char *bytes, size_t len, void *user);
+ int (*control)(uint8_t control, void *user);
+ int (*escape)(const char *bytes, size_t len, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+ int (*resize)(int rows, int cols, void *user);
+} VTermParserCallbacks;
+
+// State of the pen at some moment in time, also used in a cell
+typedef struct {
+ // After the bitfield
+ VTermColor fg, bg;
+
+ // Opaque ID that maps to a URI in a set
+ int uri;
+
+ unsigned bold : 1;
+ unsigned underline : 2;
+ unsigned italic : 1;
+ unsigned blink : 1;
+ unsigned reverse : 1;
+ unsigned conceal : 1;
+ unsigned strike : 1;
+ unsigned font : 4; // 0 to 9
+ unsigned small : 1;
+ unsigned baseline : 2;
+
+ // Extra state storage that isn't strictly pen-related
+ unsigned protected_cell : 1;
+ unsigned dwl : 1; // on a DECDWL or DECDHL line
+ unsigned dhl : 2; // on a DECDHL line (1=top 2=bottom)
+} ScreenPen;
+
+// Internal representation of a screen cell
+typedef struct {
+ schar_T schar;
+ ScreenPen pen;
+} ScreenCell;
diff --git a/src/nvim/vterm/vterm_internal_defs.h b/src/nvim/vterm/vterm_internal_defs.h
new file mode 100644
index 0000000000..19e809490f
--- /dev/null
+++ b/src/nvim/vterm/vterm_internal_defs.h
@@ -0,0 +1,292 @@
+#pragma once
+
+#include <stdarg.h>
+
+#include "nvim/mbyte_defs.h"
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef DEBUG
+# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DEBUG_LOG(...)
+#endif
+
+#define ESC_S "\x1b"
+
+#define INTERMED_MAX 16
+
+#define CSI_ARGS_MAX 16
+#define CSI_LEADER_MAX 16
+
+#define BUFIDX_PRIMARY 0
+#define BUFIDX_ALTSCREEN 1
+
+#define KEY_ENCODING_DISAMBIGUATE 0x1
+#define KEY_ENCODING_REPORT_EVENTS 0x2
+#define KEY_ENCODING_REPORT_ALTERNATE 0x4
+#define KEY_ENCODING_REPORT_ALL_KEYS 0x8
+#define KEY_ENCODING_REPORT_ASSOCIATED 0x10
+
+typedef struct VTermEncoding VTermEncoding;
+typedef struct VTermKeyEncodingFlags VTermKeyEncodingFlags;
+
+typedef struct {
+ VTermEncoding *enc;
+
+ // This size should be increased if required by other stateful encodings
+ char data[4 * sizeof(uint32_t)];
+} VTermEncodingInstance;
+
+struct VTermPen {
+ VTermColor fg;
+ VTermColor bg;
+ int uri;
+ unsigned bold:1;
+ unsigned underline:2;
+ unsigned italic:1;
+ unsigned blink:1;
+ unsigned reverse:1;
+ unsigned conceal:1;
+ unsigned strike:1;
+ unsigned font:4; // To store 0-9
+ unsigned small:1;
+ unsigned baseline:2;
+};
+
+// https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
+struct VTermKeyEncodingFlags {
+ bool disambiguate:1;
+ bool report_events:1;
+ bool report_alternate:1;
+ bool report_all_keys:1;
+ bool report_associated:1;
+};
+
+struct VTermKeyEncodingStack {
+ VTermKeyEncodingFlags items[16];
+ uint8_t size; ///< Number of items in the stack. This is at least 1 and at
+ ///< most the length of the "items" array.
+};
+
+struct VTermState {
+ VTerm *vt;
+
+ const VTermStateCallbacks *callbacks;
+ void *cbdata;
+
+ const VTermStateFallbacks *fallbacks;
+ void *fbdata;
+
+ int rows;
+ int cols;
+
+ // Current cursor position
+ VTermPos pos;
+
+ int at_phantom; // True if we're on the "81st" phantom column to defer a wraparound
+
+ int scrollregion_top;
+ int scrollregion_bottom; // -1 means unbounded
+#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > \
+ -1 ? (state)->scrollregion_bottom : (state)->rows)
+ int scrollregion_left;
+#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
+ int scrollregion_right; // -1 means unbounded
+#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin \
+ && (state)->scrollregion_right > \
+ -1 ? (state)->scrollregion_right : (state)->cols)
+
+ // Bitvector of tab stops
+ uint8_t *tabstops;
+
+ // Primary and Altscreen; lineinfos[1] is lazily allocated as needed
+ VTermLineInfo *lineinfos[2];
+
+ // lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen
+ VTermLineInfo *lineinfo;
+#define ROWWIDTH(state, \
+ row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
+#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
+
+ // Mouse state
+ int mouse_col, mouse_row;
+ int mouse_buttons;
+ int mouse_flags;
+#define MOUSE_WANT_CLICK 0x01
+#define MOUSE_WANT_DRAG 0x02
+#define MOUSE_WANT_MOVE 0x04
+
+ enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT, } mouse_protocol;
+
+// Last glyph output, for Unicode recombining purposes
+ char grapheme_buf[MAX_SCHAR_SIZE];
+ size_t grapheme_len;
+ uint32_t grapheme_last; // last added UTF-32 char
+ GraphemeState grapheme_state;
+ int combine_width; // The width of the glyph above
+ VTermPos combine_pos; // Position before movement
+
+ struct {
+ unsigned keypad:1;
+ unsigned cursor:1;
+ unsigned autowrap:1;
+ unsigned insert:1;
+ unsigned newline:1;
+ unsigned cursor_visible:1;
+ unsigned cursor_blink:1;
+ unsigned cursor_shape:2;
+ unsigned alt_screen:1;
+ unsigned origin:1;
+ unsigned screen:1;
+ unsigned leftrightmargin:1;
+ unsigned bracketpaste:1;
+ unsigned report_focus:1;
+ unsigned theme_updates:1;
+ } mode;
+
+ VTermEncodingInstance encoding[4], encoding_utf8;
+ int gl_set, gr_set, gsingle_set;
+
+ struct VTermPen pen;
+
+ VTermColor default_fg;
+ VTermColor default_bg;
+ VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
+
+ int bold_is_highbright;
+
+ unsigned protected_cell : 1;
+
+// Saved state under DEC mode 1048/1049
+ struct {
+ VTermPos pos;
+ struct VTermPen pen;
+
+ struct {
+ unsigned cursor_visible:1;
+ unsigned cursor_blink:1;
+ unsigned cursor_shape:2;
+ } mode;
+ } saved;
+
+// Temporary state for DECRQSS parsing
+ union {
+ char decrqss[4];
+ struct {
+ uint16_t mask;
+ enum {
+ SELECTION_INITIAL,
+ SELECTION_SELECTED,
+ SELECTION_QUERY,
+ SELECTION_SET_INITIAL,
+ SELECTION_SET,
+ SELECTION_INVALID,
+ } state : 8;
+ uint32_t recvpartial;
+ uint32_t sendpartial;
+ } selection;
+ } tmp;
+
+ struct {
+ const VTermSelectionCallbacks *callbacks;
+ void *user;
+ char *buffer;
+ size_t buflen;
+ } selection;
+
+ // Maintain two stacks, one for primary screen and one for altscreen
+ struct VTermKeyEncodingStack key_encoding_stacks[2];
+};
+
+struct VTerm {
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ int rows;
+ int cols;
+
+ struct {
+ unsigned utf8:1;
+ unsigned ctrl8bit:1;
+ } mode;
+
+ struct {
+ enum VTermParserState {
+ NORMAL,
+ CSI_LEADER,
+ CSI_ARGS,
+ CSI_INTERMED,
+ DCS_COMMAND,
+ // below here are the "string states"
+ OSC_COMMAND,
+ OSC,
+ DCS_VTERM,
+ APC,
+ PM,
+ SOS,
+ } state;
+
+ bool in_esc : 1;
+
+ int intermedlen;
+ char intermed[INTERMED_MAX];
+
+ union {
+ struct {
+ int leaderlen;
+ char leader[CSI_LEADER_MAX];
+
+ int argi;
+ long args[CSI_ARGS_MAX];
+ } csi;
+ struct {
+ int command;
+ } osc;
+ struct {
+ int commandlen;
+ char command[CSI_LEADER_MAX];
+ } dcs;
+ } v;
+
+ const VTermParserCallbacks *callbacks;
+ void *cbdata;
+
+ bool string_initial;
+
+ bool emit_nul;
+ } parser;
+
+ // len == malloc()ed size; cur == number of valid bytes
+
+ VTermOutputCallback *outfunc;
+ void *outdata;
+
+ char *outbuffer;
+ size_t outbuffer_len;
+ size_t outbuffer_cur;
+
+ char *tmpbuffer;
+ size_t tmpbuffer_len;
+
+ VTermState *state;
+ VTermScreen *screen;
+};
+
+struct VTermEncoding {
+ void (*init)(VTermEncoding *enc, void *data);
+ void (*decode)(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t len);
+};
+
+typedef enum {
+ ENC_UTF8,
+ ENC_SINGLE_94,
+} VTermEncodingType;
+
+enum {
+ C1_SS3 = 0x8f,
+ C1_DCS = 0x90,
+ C1_CSI = 0x9b,
+ C1_ST = 0x9c,
+ C1_OSC = 0x9d,
+};
diff --git a/src/vterm/vterm_keycodes.h b/src/nvim/vterm/vterm_keycodes_defs.h
index 661759febd..70db05af54 100644
--- a/src/vterm/vterm_keycodes.h
+++ b/src/nvim/vterm/vterm_keycodes_defs.h
@@ -1,5 +1,4 @@
-#ifndef __VTERM_INPUT_H__
-#define __VTERM_INPUT_H__
+#pragma once
typedef enum {
VTERM_MOD_NONE = 0x00,
@@ -7,7 +6,7 @@ typedef enum {
VTERM_MOD_ALT = 0x02,
VTERM_MOD_CTRL = 0x04,
- VTERM_ALL_MODS_MASK = 0x07
+ VTERM_ALL_MODS_MASK = 0x07,
} VTermModifier;
typedef enum {
@@ -52,10 +51,8 @@ typedef enum {
VTERM_KEY_KP_ENTER,
VTERM_KEY_KP_EQUAL,
- VTERM_KEY_MAX, // Must be last
- VTERM_N_KEYS = VTERM_KEY_MAX
+ VTERM_KEY_MAX, // Must be last
+ VTERM_N_KEYS = VTERM_KEY_MAX,
} VTermKey;
-#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
-
-#endif
+#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0 + (n))
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
index 5fc014a50c..056e281c0b 100644
--- a/src/nvim/vvars.lua
+++ b/src/nvim/vvars.lua
@@ -10,6 +10,7 @@ M.vars = {
]=],
},
char = {
+ type = 'string',
desc = [=[
Argument for evaluating 'formatexpr' and used for the typed
character when using <expr> in an abbreviation |:map-<expr>|.
@@ -63,6 +64,7 @@ M.vars = {
]=],
},
completed_item = {
+ type = 'vim.v.completed_item',
desc = [=[
Dictionary containing the |complete-items| for the most
recently completed word after |CompleteDone|. Empty if the
@@ -94,6 +96,7 @@ M.vars = {
]=],
},
ctype = {
+ type = 'string',
desc = [=[
The current locale setting for characters of the runtime
environment. This allows Vim scripts to be aware of the
@@ -158,6 +161,7 @@ M.vars = {
]=],
},
event = {
+ type = 'vim.v.event',
desc = [=[
Dictionary of event data for the current |autocommand|. Valid
only during the event lifetime; storing or passing v:event is
@@ -208,13 +212,16 @@ M.vars = {
changing window (or tab) on |DirChanged|.
status Job status or exit code, -1 means "unknown". |TermClose|
reason Reason for completion being done. |CompleteDone|
+ complete_word The word that was selected, empty if abandoned complete.
+ complete_type See |complete_info_mode|
]=],
},
exception = {
type = 'string',
desc = [=[
The value of the exception most recently caught and not
- finished. See also |v:throwpoint| and |throw-variables|.
+ finished. See also |v:stacktrace|, |v:throwpoint|, and
+ |throw-variables|.
Example: >vim
try
throw "oops"
@@ -236,6 +243,7 @@ M.vars = {
]=],
},
exiting = {
+ type = 'integer?',
desc = [=[
Exit code, or |v:null| before invoking the |VimLeavePre|
and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
@@ -468,6 +476,7 @@ M.vars = {
]=],
},
msgpack_types = {
+ type = 'table',
desc = [=[
Dictionary containing msgpack types used by |msgpackparse()|
and |msgpackdump()|. All types inside dictionary are fixed
@@ -633,6 +642,7 @@ M.vars = {
]=],
},
scrollstart = {
+ type = 'string',
desc = [=[
String describing the script or function that caused the
screen to scroll up. It's only set when it is empty, thus the
@@ -692,6 +702,15 @@ M.vars = {
<
]=],
},
+ stacktrace = {
+ type = 'table[]',
+ desc = [=[
+ The stack trace of the exception most recently caught and
+ not finished. Refer to |getstacktrace()| for the structure of
+ stack trace. See also |v:exception|, |v:throwpoint|, and
+ |throw-variables|.
+ ]=],
+ },
statusmsg = {
type = 'string',
desc = [=[
@@ -796,11 +815,13 @@ M.vars = {
]=],
},
testing = {
+ type = 'integer',
desc = [=[
Must be set before using `test_garbagecollect_now()`.
]=],
},
this_session = {
+ type = 'string',
desc = [=[
Full filename of the last loaded or saved session file.
Empty when no session file has been saved. See |:mksession|.
@@ -808,10 +829,11 @@ M.vars = {
]=],
},
throwpoint = {
+ type = 'string',
desc = [=[
The point where the exception most recently caught and not
finished was thrown. Not set when commands are typed. See
- also |v:exception| and |throw-variables|.
+ also |v:exception|, |v:stacktrace|, and |throw-variables|.
Example: >vim
try
throw "oops"
diff --git a/src/nvim/window.c b/src/nvim/window.c
index c3f3e075f1..fa2bfec138 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1,9 +1,7 @@
#include <assert.h>
-#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -52,7 +50,6 @@
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
#include "nvim/match.h"
-#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -186,13 +183,13 @@ win_T *swbuf_goto_win_with_buf(buf_T *buf)
// If 'switchbuf' contains "useopen": jump to first window in the current
// tab page containing "buf" if one exists.
- if (swb_flags & SWB_USEOPEN) {
+ if (swb_flags & kOptSwbFlagUseopen) {
wp = buf_jump_open_win(buf);
}
// If 'switchbuf' contains "usetab": jump to first window in any tab page
// containing "buf" if one exists.
- if (wp == NULL && (swb_flags & SWB_USETAB)) {
+ if (wp == NULL && (swb_flags & kOptSwbFlagUsetab)) {
wp = buf_jump_open_tab(buf);
}
@@ -583,7 +580,7 @@ wingotofile:
// If 'switchbuf' is set to 'useopen' or 'usetab' and the
// file is already opened in a window, then jump to it.
win_T *wp = NULL;
- if ((swb_flags & (SWB_USEOPEN | SWB_USETAB))
+ if ((swb_flags & (kOptSwbFlagUseopen | kOptSwbFlagUsetab))
&& cmdmod.cmod_tab == 0) {
wp = swbuf_goto_win_with_buf(buflist_findname_exp(ptr));
}
@@ -746,40 +743,28 @@ void win_set_buf(win_T *win, buf_T *buf, Error *err)
RedrawingDisabled++;
switchwin_T switchwin;
- if (switch_win_noblock(&switchwin, win, tab, true) == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to window %d",
- win->handle);
- goto cleanup;
- }
-
- try_start();
-
- const int save_acd = p_acd;
- if (!switchwin.sw_same_win) {
- // Temporarily disable 'autochdir' when setting buffer in another window.
- p_acd = false;
- }
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
+ TRY_WRAP(err, {
+ int win_result = switch_win_noblock(&switchwin, win, tab, true);
+ if (win_result != FAIL) {
+ const int save_acd = p_acd;
+ if (!switchwin.sw_same_win) {
+ // Temporarily disable 'autochdir' when setting buffer in another window.
+ p_acd = false;
+ }
- if (!switchwin.sw_same_win) {
- p_acd = save_acd;
- }
+ do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
- if (!try_end(err) && result == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to set buffer %d",
- buf->handle);
- }
+ if (!switchwin.sw_same_win) {
+ p_acd = save_acd;
+ }
+ }
+ });
// If window is not current, state logic will not validate its cursor. So do it now.
// Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set).
validate_cursor(curwin);
-cleanup:
restore_win_noblock(&switchwin, true);
RedrawingDisabled--;
}
@@ -850,9 +835,19 @@ void ui_ext_win_position(win_T *wp, bool validate)
col += tcol - 1;
}
}
+ } else if (c.relative == kFloatRelativeLaststatus) {
+ row += Rows - (int)p_ch - last_stl_height(false);
+ } else if (c.relative == kFloatRelativeTabline) {
+ row += tabline_height();
}
+ bool resort = wp->w_grid_alloc.comp_index != 0
+ && wp->w_grid_alloc.zindex != wp->w_config.zindex;
+ bool raise = resort && wp->w_grid_alloc.zindex < wp->w_config.zindex;
wp->w_grid_alloc.zindex = wp->w_config.zindex;
+ if (resort) {
+ ui_comp_layers_adjust(wp->w_grid_alloc.comp_index, raise);
+ }
if (ui_has(kUIMultigrid)) {
String anchor = cstr_as_string(float_anchor_str[c.anchor]);
if (!c.hide) {
@@ -1075,6 +1070,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_fl
return NULL;
}
need_status = STATUS_HEIGHT;
+ win_float_anchor_laststatus();
}
bool do_equal = false;
@@ -1471,7 +1467,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_fl
frame_add_statusline(curfrp);
}
}
- frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false);
+ frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false, false);
} else {
win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
}
@@ -2145,7 +2141,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
|| topfr->fr_width != width
|| topfr->fr_win->w_wincol != col) {
topfr->fr_win->w_winrow = row;
- frame_new_height(topfr, height, false, false);
+ frame_new_height(topfr, height, false, false, false);
topfr->fr_win->w_wincol = col;
frame_new_width(topfr, width, false, false);
redraw_all_later(UPD_NOT_VALID);
@@ -3163,7 +3159,7 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp, frame_T **unflat_al
if (*dirp == 'v') {
frame_new_height(altfr, altfr->fr_height + frp_close->fr_height,
- altfr == frp_close->fr_next, false);
+ altfr == frp_close->fr_next, false, false);
} else {
assert(*dirp == 'h');
frame_new_width(altfr, altfr->fr_width + frp_close->fr_width,
@@ -3365,7 +3361,7 @@ void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr)
// adjusts window sizes to fit restored statuslines/separators, if needed.
if (dir == 'v') {
frame_new_height(unflat_altfr, unflat_altfr->fr_height - frp->fr_height,
- unflat_altfr == frp->fr_next, false);
+ unflat_altfr == frp->fr_next, false, false);
} else if (dir == 'h') {
frame_new_width(unflat_altfr, unflat_altfr->fr_width - frp->fr_width,
unflat_altfr == frp->fr_next, false);
@@ -3448,13 +3444,13 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp)
static tabpage_T *alt_tabpage(void)
{
// Use the last accessed tab page, if possible.
- if ((tcl_flags & TCL_USELAST) && valid_tabpage(lastused_tabpage)) {
+ if ((tcl_flags & kOptTclFlagUselast) && valid_tabpage(lastused_tabpage)) {
return lastused_tabpage;
}
// Use the next tab page, if possible.
bool forward = curtab->tp_next != NULL
- && ((tcl_flags & TCL_LEFT) == 0 || curtab == first_tabpage);
+ && ((tcl_flags & kOptTclFlagLeft) == 0 || curtab == first_tabpage);
tabpage_T *tp;
if (forward) {
@@ -3508,15 +3504,31 @@ static bool is_bottom_win(win_T *wp)
}
return true;
}
+
+// 'cmdheight' value explicitly set by the user: window commands are allowed to
+// resize the topframe to values higher than this minimum, but not lower.
+static OptInt min_set_ch = 1;
+
/// Set a new height for a frame. Recursively sets the height for contained
/// frames and windows. Caller must take care of positions.
///
/// @param topfirst resize topmost contained frame first.
/// @param wfh obey 'winfixheight' when there is a choice;
/// may cause the height not to be set.
-void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
+/// @param set_ch set 'cmdheight' to resize topframe.
+void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh, bool set_ch)
FUNC_ATTR_NONNULL_ALL
{
+ if (topfrp->fr_parent == NULL && set_ch) {
+ // topframe: update the command line height, with side effects.
+ OptInt new_ch = MAX(min_set_ch, p_ch + topfrp->fr_height - height);
+ if (new_ch != p_ch) {
+ const OptInt save_ch = min_set_ch;
+ set_option_value(kOptCmdheight, NUMBER_OPTVAL(new_ch), 0);
+ min_set_ch = save_ch;
+ }
+ height = (int)MIN(ROWS_AVAIL, height);
+ }
if (topfrp->fr_win != NULL) {
// Simple case: just one window.
win_T *wp = topfrp->fr_win;
@@ -3529,7 +3541,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
do {
// All frames in this row get the same new height.
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
- frame_new_height(frp, height, topfirst, wfh);
+ frame_new_height(frp, height, topfirst, wfh, set_ch);
if (frp->fr_height > height) {
// Could not fit the windows, make the whole row higher.
height = frp->fr_height;
@@ -3571,10 +3583,9 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
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);
+ frame_new_height(frp, h, topfirst, wfh, set_ch);
} else {
- frame_new_height(frp, frp->fr_height + extra_lines,
- topfirst, wfh);
+ frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh, set_ch);
break;
}
if (topfirst) {
@@ -3593,7 +3604,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
}
} else if (extra_lines > 0) {
// increase height of bottom or top frame
- frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh);
+ frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh, set_ch);
}
}
topfrp->fr_height = height;
@@ -4010,6 +4021,10 @@ void unuse_tabpage(tabpage_T *tp)
tp->tp_curwin = curwin;
}
+// When switching tabpage, handle other side-effects in command_height(), but
+// avoid setting frame sizes which are still correct.
+static bool command_frame_height = true;
+
/// Set the relevant pointers to use tab page "tp". May want to call
/// unuse_tabpage() first.
void use_tabpage(tabpage_T *tp)
@@ -4411,6 +4426,16 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
use_tabpage(tp);
+ if (p_ch != curtab->tp_ch_used) {
+ // Use the stored value of p_ch, so that it can be different for each tab page.
+ // Handle other side-effects but avoid setting frame sizes, which are still correct.
+ OptInt new_ch = curtab->tp_ch_used;
+ curtab->tp_ch_used = p_ch;
+ command_frame_height = false;
+ set_option_value(kOptCmdheight, NUMBER_OPTVAL(new_ch), 0);
+ command_frame_height = true;
+ }
+
if (old_curtab != curtab) {
tabpage_check_windows(old_curtab);
}
@@ -4424,27 +4449,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
prevwin = next_prevwin;
last_status(false); // status line may appear or disappear
- const int row = win_comp_pos(); // recompute w_winrow for all windows
+ win_comp_pos(); // recompute w_winrow for all windows
diff_need_scrollbind = true;
- // Use the stored value of p_ch, so that it can be different for each tab page.
- if (p_ch != curtab->tp_ch_used) {
- clear_cmdline = true;
- if (msg_grid.chars && p_ch < curtab->tp_ch_used) {
- // TODO(bfredl): a bit expensive, should be enough to invalidate the
- // region between the old and the new p_ch.
- grid_invalidate(&msg_grid);
- }
- }
- p_ch = curtab->tp_ch_used;
-
- // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is
- // changed but p_ch and tp_ch_used are not changed. Thus we also need to
- // check cmdline_row.
- if (row < cmdline_row && cmdline_row <= Rows - p_ch) {
- clear_cmdline = true;
- }
-
// If there was a click in a window, it won't be usable for a following
// drag.
reset_dragwin();
@@ -5169,8 +5176,8 @@ win_T *win_alloc(win_T *after, bool hidden)
return new_wp;
}
-// Free one wininfo_T.
-void free_wininfo(wininfo_T *wip, buf_T *bp)
+// Free one WinInfo.
+void free_wininfo(WinInfo *wip, buf_T *bp)
{
if (wip->wi_optset) {
clear_winopt(&wip->wi_opt);
@@ -5235,30 +5242,27 @@ void win_free(win_T *wp, tabpage_T *tp)
// 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 (wininfo_T *wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ WinInfo *wip_wp = NULL;
+ size_t pos_wip = kv_size(buf->b_wininfo);
+ size_t pos_null = kv_size(buf->b_wininfo);
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == wp) {
- wininfo_T *wip2;
-
- // If there already is an entry with "wi_win" set to NULL it
- // must be removed, it would never be used.
- // Skip "wip" itself, otherwise Coverity complains.
- for (wip2 = buf->b_wininfo; wip2 != NULL; wip2 = wip2->wi_next) {
- // `wip2 != wip` to satisfy Coverity. #14884
- if (wip2 != wip && wip2->wi_win == NULL) {
- if (wip2->wi_next != NULL) {
- wip2->wi_next->wi_prev = wip2->wi_prev;
- }
- if (wip2->wi_prev == NULL) {
- buf->b_wininfo = wip2->wi_next;
- } else {
- wip2->wi_prev->wi_next = wip2->wi_next;
- }
- free_wininfo(wip2, buf);
- break;
- }
- }
+ wip_wp = wip;
+ pos_wip = i;
+ } else if (wip->wi_win == NULL) {
+ pos_null = i;
+ }
+ }
- wip->wi_win = NULL;
+ if (wip_wp) {
+ wip_wp->wi_win = NULL;
+ // If there already is an entry with "wi_win" set to NULL, only
+ // the first entry with NULL will ever be used, delete the other one.
+ if (pos_null < kv_size(buf->b_wininfo)) {
+ size_t pos_delete = MAX(pos_null, pos_wip);
+ free_wininfo(kv_A(buf->b_wininfo, pos_delete), buf);
+ kv_shift(buf->b_wininfo, pos_delete, 1);
}
}
}
@@ -5422,9 +5426,9 @@ void win_new_screen_rows(void)
// First try setting the heights of windows with 'winfixheight'. If
// that doesn't result in the right height, forget about that option.
- frame_new_height(topframe, h, false, true);
+ frame_new_height(topframe, h, false, true, false);
if (!frame_check_height(topframe, h)) {
- frame_new_height(topframe, h, false, false);
+ frame_new_height(topframe, h, false, false, false);
}
win_comp_pos(); // recompute w_winrow and w_wincol
@@ -5859,22 +5863,7 @@ void win_setheight_win(int height, win_T *win)
frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
// recompute the window positions
- int row = win_comp_pos();
-
- // If there is extra space created between the last window and the command
- // line, clear it.
- if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
- grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0);
- if (msg_grid.chars) {
- clear_cmdline = true;
- }
- }
- cmdline_row = row;
- p_ch = MAX(Rows - cmdline_row, 0);
- curtab->tp_ch_used = p_ch;
- msg_row = row;
- msg_col = 0;
-
+ win_comp_pos();
win_fix_scroll(true);
redraw_all_later(UPD_NOT_VALID);
@@ -5902,14 +5891,8 @@ static void frame_setheight(frame_T *curfrp, int height)
if (curfrp->fr_parent == NULL) {
// topframe: can only change the command line height
- 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 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);
+ frame_new_height(curfrp, height, false, false, true);
}
} else if (curfrp->fr_parent->fr_layout == FR_ROW) {
// Row of frames: Also need to resize frames left and right of this
@@ -5988,7 +5971,7 @@ static void frame_setheight(frame_T *curfrp, int height)
}
// set the current frame to the new height
- frame_new_height(curfrp, height, false, false);
+ frame_new_height(curfrp, height, false, false, true);
// First take lines from the frames after the current frame. If
// that is not enough, takes lines from frames above the current
@@ -6010,15 +5993,15 @@ static void frame_setheight(frame_T *curfrp, int height)
room_reserved = frp->fr_height - take;
}
take -= frp->fr_height - room_reserved;
- frame_new_height(frp, room_reserved, false, false);
+ frame_new_height(frp, room_reserved, false, false, true);
room_reserved = 0;
}
} else {
if (frp->fr_height - take < h) {
take -= frp->fr_height - h;
- frame_new_height(frp, h, false, false);
+ frame_new_height(frp, h, false, false, true);
} else {
- frame_new_height(frp, frp->fr_height - take, false, false);
+ frame_new_height(frp, frp->fr_height - take, false, false, true);
take = 0;
}
}
@@ -6189,7 +6172,7 @@ const char *did_set_winminheight(optset_T *args FUNC_ATTR_UNUSED)
// loop until there is a 'winminheight' that is possible
while (p_wmh > 0) {
const int room = Rows - (int)p_ch;
- const int needed = min_rows();
+ const int needed = min_rows_for_all_tabpages();
if (room >= needed) {
break;
}
@@ -6276,7 +6259,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
room = Rows - cmdline_row;
if (curfr->fr_next != NULL) {
room -= (int)p_ch + global_stl_height();
- } else if (!p_ch_was_zero) {
+ } else if (min_set_ch > 0) {
room--;
}
room = MAX(room, 0);
@@ -6296,7 +6279,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
// 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);
+ frame_new_height(fr, fr->fr_height + offset, up, false, true);
}
if (up) {
@@ -6309,9 +6292,9 @@ void win_drag_status_line(win_T *dragwin, int offset)
int n = frame_minheight(fr, NULL);
if (fr->fr_height - offset <= n) {
offset -= fr->fr_height - n;
- frame_new_height(fr, n, !up, false);
+ frame_new_height(fr, n, !up, false, true);
} else {
- frame_new_height(fr, fr->fr_height - offset, !up, false);
+ frame_new_height(fr, fr->fr_height - offset, !up, false, true);
break;
}
if (up) {
@@ -6320,15 +6303,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
fr = fr->fr_next;
}
}
- int row = win_comp_pos();
- grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0);
- if (msg_grid.chars) {
- clear_cmdline = true;
- }
- cmdline_row = row;
- p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1);
- curtab->tp_ch_used = p_ch;
-
+ win_comp_pos();
win_fix_scroll(true);
redraw_all_later(UPD_SOME_VALID);
@@ -6390,7 +6365,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
fr = curfr; // put fr at window that grows
}
- // If not enough room thn move as far as we can
+ // If not enough room then move as far as we can
offset = MIN(offset, room);
// No room at all, quit.
@@ -6765,21 +6740,6 @@ void command_height(void)
{
int old_p_ch = (int)curtab->tp_ch_used;
- // Use the value of p_ch that we remembered. This is needed for when the
- // GUI starts up, we can't be sure in what order things happen. And when
- // p_ch was changed in another tab page.
- curtab->tp_ch_used = p_ch;
-
- // 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
- // properly for the new value.
- if (cmdline_row < Rows - p_ch) {
- old_p_ch = Rows - cmdline_row;
- }
-
// Find bottom frame with width of screen.
frame_T *frp = lastwin_nofloating()->w_frame;
while (frp->fr_width != Columns && frp->fr_parent != NULL) {
@@ -6787,60 +6747,53 @@ void command_height(void)
}
// Avoid changing the height of a window with 'winfixheight' set.
- while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
- && frp->fr_win->w_p_wfh) {
+ while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF && frp->fr_win->w_p_wfh) {
frp = frp->fr_prev;
}
- if (starting != NO_SCREEN) {
- cmdline_row = Rows - (int)p_ch;
-
- if (p_ch > old_p_ch) { // p_ch got bigger
- while (p_ch > old_p_ch) {
- if (frp == NULL) {
- emsg(_(e_noroom));
- p_ch = old_p_ch;
- curtab->tp_ch_used = p_ch;
- cmdline_row = Rows - (int)p_ch;
- break;
- }
- int h = frp->fr_height - frame_minheight(frp, NULL);
- h = MIN(h, (int)p_ch - old_p_ch);
- old_p_ch += h;
- frame_add_height(frp, -h);
- frp = frp->fr_prev;
- }
-
- // Recompute window positions.
- win_comp_pos();
-
- if (!need_wait_return) {
- // clear the lines added to cmdline
- if (full_screen) {
- grid_clear(&default_grid, cmdline_row, Rows, 0, Columns, 0);
- }
- msg_row = cmdline_row;
- }
- redraw_cmdline = true;
- return;
+ while (p_ch > old_p_ch && command_frame_height) {
+ if (frp == NULL) {
+ emsg(_(e_noroom));
+ p_ch = old_p_ch;
+ break;
}
-
- msg_row = MAX(msg_row, cmdline_row);
- redraw_cmdline = true;
+ int h = MIN((int)(p_ch - old_p_ch), frp->fr_height - frame_minheight(frp, NULL));
+ frame_add_height(frp, -h);
+ old_p_ch += h;
+ frp = frp->fr_prev;
+ }
+ if (p_ch < old_p_ch && command_frame_height && frp != NULL) {
+ frame_add_height(frp, (int)(old_p_ch - p_ch));
}
- frame_add_height(frp, (int)(old_p_ch - p_ch));
// Recompute window positions.
- if (frp != lastwin->w_frame) {
- win_comp_pos();
+ win_comp_pos();
+ cmdline_row = Rows - (int)p_ch;
+ redraw_cmdline = true;
+
+ // Clear the cmdheight area.
+ if (msg_scrolled == 0 && full_screen) {
+ ScreenGrid *grid = &default_grid;
+ if (!ui_has(kUIMessages)) {
+ msg_grid_validate();
+ grid = &msg_grid_adj;
+ }
+ grid_clear(grid, cmdline_row, Rows, 0, Columns, 0);
+ msg_row = cmdline_row;
}
+
+ // Use the value of p_ch that we remembered. This is needed for when the
+ // GUI starts up, we can't be sure in what order things happen. And when
+ // p_ch was changed in another tab page.
+ curtab->tp_ch_used = p_ch;
+ min_set_ch = p_ch;
}
// 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);
+ frame_new_height(frp, frp->fr_height + n, false, false, false);
while (true) {
frp = frp->fr_parent;
if (frp == NULL) {
@@ -6858,6 +6811,7 @@ void last_status(bool morewin)
{
// Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, last_stl_height(morewin) > 0, global_stl_height() > 0);
+ win_float_anchor_laststatus();
}
// Remove status line from window, replacing it with a horizontal separator if needed.
@@ -6910,7 +6864,7 @@ static bool resize_frame_for_status(frame_T *fr)
emsg(_(e_noroom));
return false;
} else if (fp != fr) {
- frame_new_height(fp, fp->fr_height - 1, false, false);
+ frame_new_height(fp, fp->fr_height - 1, false, false, false);
frame_fix_height(wp);
win_comp_pos();
} else {
@@ -6931,7 +6885,7 @@ static bool resize_frame_for_winbar(frame_T *fr)
emsg(_(e_noroom));
return false;
}
- frame_new_height(fp, fp->fr_height - 1, false, false);
+ frame_new_height(fp, fp->fr_height - 1, false, false, false);
win_new_height(wp, wp->w_height + 1);
frame_fix_height(wp);
win_comp_pos();
@@ -7065,8 +7019,24 @@ int last_stl_height(bool morewin)
}
/// Return the minimal number of rows that is needed on the screen to display
-/// the current number of windows.
-int min_rows(void)
+/// the current number of windows for the given tab page.
+int min_rows(tabpage_T *tp) FUNC_ATTR_NONNULL_ALL
+{
+ if (firstwin == NULL) { // not initialized yet
+ return MIN_LINES;
+ }
+
+ int total = frame_minheight(tp->tp_topframe, NULL);
+ total += tabline_height() + global_stl_height();
+ if ((tp == curtab ? p_ch : tp->tp_ch_used) > 0) {
+ total++; // count the room for the command line
+ }
+ return total;
+}
+
+/// Return the minimal number of rows that is needed on the screen to display
+/// the current number of windows for all tab pages.
+int min_rows_for_all_tabpages(void)
{
if (firstwin == NULL) { // not initialized yet
return MIN_LINES;
@@ -7075,12 +7045,12 @@ int min_rows(void)
int total = 0;
FOR_ALL_TABS(tp) {
int n = frame_minheight(tp->tp_topframe, NULL);
+ if ((tp == curtab ? p_ch : tp->tp_ch_used) > 0) {
+ n++; // count the room for the command line
+ }
total = MAX(total, n);
}
total += tabline_height() + global_stl_height();
- if (p_ch > 0) {
- total += 1; // count the room for the command line
- }
return total;
}
@@ -7304,7 +7274,7 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
fr->fr_height = sn->fr_height;
fr->fr_width = sn->fr_width;
if (fr->fr_layout == FR_LEAF) {
- frame_new_height(fr, fr->fr_height, false, false);
+ frame_new_height(fr, fr->fr_height, false, false, false);
frame_new_width(fr, fr->fr_width, false, false);
wp = sn->fr_win;
}
diff --git a/src/nvim/window.h b/src/nvim/window.h
index 9618ff1c2a..b5808d3451 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -35,9 +35,6 @@ enum {
EXTERN int tabpage_move_disallowed INIT( = 0); ///< moving tabpages around disallowed
-/// 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/nvim/winfloat.c b/src/nvim/winfloat.c
index b8a51d686d..d11b965dfc 100644
--- a/src/nvim/winfloat.c
+++ b/src/nvim/winfloat.c
@@ -10,7 +10,6 @@
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
-#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
#include "nvim/globals.h"
@@ -308,6 +307,15 @@ void win_check_anchored_floats(win_T *win)
}
}
+void win_float_anchor_laststatus(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (win->w_config.relative == kFloatRelativeLaststatus) {
+ win->w_pos_changed = true;
+ }
+ }
+}
+
void win_reconfig_floats(void)
{
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
diff --git a/src/vterm/encoding.c b/src/vterm/encoding.c
deleted file mode 100644
index 434ac3f99b..0000000000
--- a/src/vterm/encoding.c
+++ /dev/null
@@ -1,230 +0,0 @@
-#include "vterm_internal.h"
-
-#define UNICODE_INVALID 0xFFFD
-
-#if defined(DEBUG) && DEBUG > 1
-# define DEBUG_PRINT_UTF8
-#endif
-
-struct UTF8DecoderData {
- // number of bytes remaining in this codepoint
- int bytes_remaining;
-
- // number of bytes total in this codepoint once it's finished
- // (for detecting overlongs)
- int bytes_total;
-
- int this_cp;
-};
-
-static void init_utf8(VTermEncoding *enc, void *data_)
-{
- struct UTF8DecoderData *data = data_;
-
- data->bytes_remaining = 0;
- data->bytes_total = 0;
-}
-
-static void decode_utf8(VTermEncoding *enc, void *data_,
- uint32_t cp[], int *cpi, int cplen,
- const char bytes[], size_t *pos, size_t bytelen)
-{
- struct UTF8DecoderData *data = data_;
-
-#ifdef DEBUG_PRINT_UTF8
- printf("BEGIN UTF-8\n");
-#endif
-
- for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
- unsigned char c = bytes[*pos];
-
-#ifdef DEBUG_PRINT_UTF8
- printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
-#endif
-
- if(c < 0x20) // C0
- return;
-
- else if(c >= 0x20 && c < 0x7f) {
- if(data->bytes_remaining)
- cp[(*cpi)++] = UNICODE_INVALID;
-
- cp[(*cpi)++] = c;
-#ifdef DEBUG_PRINT_UTF8
- printf(" UTF-8 char: U+%04x\n", c);
-#endif
- data->bytes_remaining = 0;
- }
-
- else if(c == 0x7f) // DEL
- return;
-
- else if(c >= 0x80 && c < 0xc0) {
- if(!data->bytes_remaining) {
- cp[(*cpi)++] = UNICODE_INVALID;
- continue;
- }
-
- data->this_cp <<= 6;
- data->this_cp |= c & 0x3f;
- data->bytes_remaining--;
-
- if(!data->bytes_remaining) {
-#ifdef DEBUG_PRINT_UTF8
- printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
-#endif
- // Check for overlong sequences
- switch(data->bytes_total) {
- case 2:
- if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
- break;
- case 3:
- if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
- break;
- case 4:
- if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
- break;
- case 5:
- if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
- break;
- case 6:
- if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
- break;
- }
- // Now look for plain invalid ones
- if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
- data->this_cp == 0xFFFE ||
- data->this_cp == 0xFFFF)
- data->this_cp = UNICODE_INVALID;
-#ifdef DEBUG_PRINT_UTF8
- printf(" char: U+%04x\n", data->this_cp);
-#endif
- cp[(*cpi)++] = data->this_cp;
- }
- }
-
- else if(c >= 0xc0 && c < 0xe0) {
- if(data->bytes_remaining)
- cp[(*cpi)++] = UNICODE_INVALID;
-
- data->this_cp = c & 0x1f;
- data->bytes_total = 2;
- data->bytes_remaining = 1;
- }
-
- else if(c >= 0xe0 && c < 0xf0) {
- if(data->bytes_remaining)
- cp[(*cpi)++] = UNICODE_INVALID;
-
- data->this_cp = c & 0x0f;
- data->bytes_total = 3;
- data->bytes_remaining = 2;
- }
-
- else if(c >= 0xf0 && c < 0xf8) {
- if(data->bytes_remaining)
- cp[(*cpi)++] = UNICODE_INVALID;
-
- data->this_cp = c & 0x07;
- data->bytes_total = 4;
- data->bytes_remaining = 3;
- }
-
- else if(c >= 0xf8 && c < 0xfc) {
- if(data->bytes_remaining)
- cp[(*cpi)++] = UNICODE_INVALID;
-
- data->this_cp = c & 0x03;
- data->bytes_total = 5;
- data->bytes_remaining = 4;
- }
-
- else if(c >= 0xfc && c < 0xfe) {
- if(data->bytes_remaining)
- cp[(*cpi)++] = UNICODE_INVALID;
-
- data->this_cp = c & 0x01;
- data->bytes_total = 6;
- data->bytes_remaining = 5;
- }
-
- else {
- cp[(*cpi)++] = UNICODE_INVALID;
- }
- }
-}
-
-static VTermEncoding encoding_utf8 = {
- .init = &init_utf8,
- .decode = &decode_utf8,
-};
-
-static void decode_usascii(VTermEncoding *enc, void *data,
- uint32_t cp[], int *cpi, int cplen,
- const char bytes[], size_t *pos, size_t bytelen)
-{
- int is_gr = bytes[*pos] & 0x80;
-
- for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
- unsigned char c = bytes[*pos] ^ is_gr;
-
- if(c < 0x20 || c == 0x7f || c >= 0x80)
- return;
-
- cp[(*cpi)++] = c;
- }
-}
-
-static VTermEncoding encoding_usascii = {
- .decode = &decode_usascii,
-};
-
-struct StaticTableEncoding {
- const VTermEncoding enc;
- const uint32_t chars[128];
-};
-
-static void decode_table(VTermEncoding *enc, void *data,
- uint32_t cp[], int *cpi, int cplen,
- const char bytes[], size_t *pos, size_t bytelen)
-{
- struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
- int is_gr = bytes[*pos] & 0x80;
-
- for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
- unsigned char c = bytes[*pos] ^ is_gr;
-
- if(c < 0x20 || c == 0x7f || c >= 0x80)
- return;
-
- if(table->chars[c])
- cp[(*cpi)++] = table->chars[c];
- else
- cp[(*cpi)++] = c;
- }
-}
-
-#include "encoding/DECdrawing.inc"
-#include "encoding/uk.inc"
-
-static struct {
- VTermEncodingType type;
- char designation;
- VTermEncoding *enc;
-}
-encodings[] = {
- { ENC_UTF8, 'u', &encoding_utf8 },
- { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
- { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
- { ENC_SINGLE_94, 'B', &encoding_usascii },
- { 0 },
-};
-
-/* This ought to be INTERNAL but isn't because it's used by unit testing */
-VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
-{
- for(int i = 0; encodings[i].designation; i++)
- if(encodings[i].type == type && encodings[i].designation == designation)
- return encodings[i].enc;
- return NULL;
-}
diff --git a/src/vterm/encoding/DECdrawing.inc b/src/vterm/encoding/DECdrawing.inc
deleted file mode 100644
index 627397bcc2..0000000000
--- a/src/vterm/encoding/DECdrawing.inc
+++ /dev/null
@@ -1,36 +0,0 @@
-static const struct StaticTableEncoding encoding_DECdrawing = {
- { .decode = &decode_table },
- {
- [0x60] = 0x25C6, // BLACK DIAMOND
- [0x61] = 0x2592, // MEDIUM SHADE (checkerboard)
- [0x62] = 0x2409, // SYMBOL FOR HORIZONTAL TAB
- [0x63] = 0x240C, // SYMBOL FOR FORM FEED
- [0x64] = 0x240D, // SYMBOL FOR CARRIAGE RETURN
- [0x65] = 0x240A, // SYMBOL FOR LINE FEED
- [0x66] = 0x00B0, // DEGREE SIGN
- [0x67] = 0x00B1, // PLUS-MINUS SIGN (plus or minus)
- [0x68] = 0x2424, // SYMBOL FOR NEW LINE
- [0x69] = 0x240B, // SYMBOL FOR VERTICAL TAB
- [0x6a] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
- [0x6b] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
- [0x6c] = 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
- [0x6d] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
- [0x6e] = 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
- [0x6f] = 0x23BA, // HORIZONTAL SCAN LINE-1
- [0x70] = 0x23BB, // HORIZONTAL SCAN LINE-3
- [0x71] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
- [0x72] = 0x23BC, // HORIZONTAL SCAN LINE-7
- [0x73] = 0x23BD, // HORIZONTAL SCAN LINE-9
- [0x74] = 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
- [0x75] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
- [0x76] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
- [0x77] = 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
- [0x78] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
- [0x79] = 0x2A7D, // LESS-THAN OR SLANTED EQUAL-TO
- [0x7a] = 0x2A7E, // GREATER-THAN OR SLANTED EQUAL-TO
- [0x7b] = 0x03C0, // GREEK SMALL LETTER PI
- [0x7c] = 0x2260, // NOT EQUAL TO
- [0x7d] = 0x00A3, // POUND SIGN
- [0x7e] = 0x00B7, // MIDDLE DOT
- }
-};
diff --git a/src/vterm/encoding/uk.inc b/src/vterm/encoding/uk.inc
deleted file mode 100644
index 5c7700226b..0000000000
--- a/src/vterm/encoding/uk.inc
+++ /dev/null
@@ -1,6 +0,0 @@
-static const struct StaticTableEncoding encoding_uk = {
- { .decode = &decode_table },
- {
- [0x23] = 0x00a3, // £
- }
-};
diff --git a/src/vterm/fullwidth.inc b/src/vterm/fullwidth.inc
deleted file mode 100644
index a703529a76..0000000000
--- a/src/vterm/fullwidth.inc
+++ /dev/null
@@ -1,111 +0,0 @@
- { 0x1100, 0x115f },
- { 0x231a, 0x231b },
- { 0x2329, 0x232a },
- { 0x23e9, 0x23ec },
- { 0x23f0, 0x23f0 },
- { 0x23f3, 0x23f3 },
- { 0x25fd, 0x25fe },
- { 0x2614, 0x2615 },
- { 0x2648, 0x2653 },
- { 0x267f, 0x267f },
- { 0x2693, 0x2693 },
- { 0x26a1, 0x26a1 },
- { 0x26aa, 0x26ab },
- { 0x26bd, 0x26be },
- { 0x26c4, 0x26c5 },
- { 0x26ce, 0x26ce },
- { 0x26d4, 0x26d4 },
- { 0x26ea, 0x26ea },
- { 0x26f2, 0x26f3 },
- { 0x26f5, 0x26f5 },
- { 0x26fa, 0x26fa },
- { 0x26fd, 0x26fd },
- { 0x2705, 0x2705 },
- { 0x270a, 0x270b },
- { 0x2728, 0x2728 },
- { 0x274c, 0x274c },
- { 0x274e, 0x274e },
- { 0x2753, 0x2755 },
- { 0x2757, 0x2757 },
- { 0x2795, 0x2797 },
- { 0x27b0, 0x27b0 },
- { 0x27bf, 0x27bf },
- { 0x2b1b, 0x2b1c },
- { 0x2b50, 0x2b50 },
- { 0x2b55, 0x2b55 },
- { 0x2e80, 0x2e99 },
- { 0x2e9b, 0x2ef3 },
- { 0x2f00, 0x2fd5 },
- { 0x2ff0, 0x2ffb },
- { 0x3000, 0x303e },
- { 0x3041, 0x3096 },
- { 0x3099, 0x30ff },
- { 0x3105, 0x312f },
- { 0x3131, 0x318e },
- { 0x3190, 0x31ba },
- { 0x31c0, 0x31e3 },
- { 0x31f0, 0x321e },
- { 0x3220, 0x3247 },
- { 0x3250, 0x4dbf },
- { 0x4e00, 0xa48c },
- { 0xa490, 0xa4c6 },
- { 0xa960, 0xa97c },
- { 0xac00, 0xd7a3 },
- { 0xf900, 0xfaff },
- { 0xfe10, 0xfe19 },
- { 0xfe30, 0xfe52 },
- { 0xfe54, 0xfe66 },
- { 0xfe68, 0xfe6b },
- { 0xff01, 0xff60 },
- { 0xffe0, 0xffe6 },
- { 0x16fe0, 0x16fe3 },
- { 0x17000, 0x187f7 },
- { 0x18800, 0x18af2 },
- { 0x1b000, 0x1b11e },
- { 0x1b150, 0x1b152 },
- { 0x1b164, 0x1b167 },
- { 0x1b170, 0x1b2fb },
- { 0x1f004, 0x1f004 },
- { 0x1f0cf, 0x1f0cf },
- { 0x1f18e, 0x1f18e },
- { 0x1f191, 0x1f19a },
- { 0x1f200, 0x1f202 },
- { 0x1f210, 0x1f23b },
- { 0x1f240, 0x1f248 },
- { 0x1f250, 0x1f251 },
- { 0x1f260, 0x1f265 },
- { 0x1f300, 0x1f320 },
- { 0x1f32d, 0x1f335 },
- { 0x1f337, 0x1f37c },
- { 0x1f37e, 0x1f393 },
- { 0x1f3a0, 0x1f3ca },
- { 0x1f3cf, 0x1f3d3 },
- { 0x1f3e0, 0x1f3f0 },
- { 0x1f3f4, 0x1f3f4 },
- { 0x1f3f8, 0x1f43e },
- { 0x1f440, 0x1f440 },
- { 0x1f442, 0x1f4fc },
- { 0x1f4ff, 0x1f53d },
- { 0x1f54b, 0x1f54e },
- { 0x1f550, 0x1f567 },
- { 0x1f57a, 0x1f57a },
- { 0x1f595, 0x1f596 },
- { 0x1f5a4, 0x1f5a4 },
- { 0x1f5fb, 0x1f64f },
- { 0x1f680, 0x1f6c5 },
- { 0x1f6cc, 0x1f6cc },
- { 0x1f6d0, 0x1f6d2 },
- { 0x1f6d5, 0x1f6d5 },
- { 0x1f6eb, 0x1f6ec },
- { 0x1f6f4, 0x1f6fa },
- { 0x1f7e0, 0x1f7eb },
- { 0x1f90d, 0x1f971 },
- { 0x1f973, 0x1f976 },
- { 0x1f97a, 0x1f9a2 },
- { 0x1f9a5, 0x1f9aa },
- { 0x1f9ae, 0x1f9ca },
- { 0x1f9cd, 0x1f9ff },
- { 0x1fa70, 0x1fa73 },
- { 0x1fa78, 0x1fa7a },
- { 0x1fa80, 0x1fa82 },
- { 0x1fa90, 0x1fa95 },
diff --git a/src/vterm/keyboard.c b/src/vterm/keyboard.c
deleted file mode 100644
index 7e062c8c02..0000000000
--- a/src/vterm/keyboard.c
+++ /dev/null
@@ -1,225 +0,0 @@
-#include "vterm_internal.h"
-#include <stdio.h>
-
-#include "nvim/tui/termkey/termkey.h"
-
-void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
-{
- /* The shift modifier is never important for Unicode characters
- * apart from Space
- */
- if(c != ' ')
- mod &= ~VTERM_MOD_SHIFT;
-
- if(mod == 0) {
- // Normal text - ignore just shift
- char str[6];
- int seqlen = fill_utf8(c, str);
- vterm_push_output_bytes(vt, str, seqlen);
- return;
- }
-
- int needs_CSIu;
- switch(c) {
- /* Special Ctrl- letters that can't be represented elsewise */
- case 'i': case 'j': case 'm': case '[':
- needs_CSIu = 1;
- break;
- /* Ctrl-\ ] ^ _ don't need CSUu */
- case '\\': case ']': case '^': case '_':
- needs_CSIu = 0;
- break;
- /* Shift-space needs CSIu */
- case ' ':
- needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
- break;
- /* All other characters needs CSIu except for letters a-z */
- default:
- needs_CSIu = (c < 'a' || c > 'z');
- }
-
- /* ALT we can just prefix with ESC; anything else requires CSI u */
- if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
- return;
- }
-
- if(mod & VTERM_MOD_CTRL)
- c &= 0x1f;
-
- vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
-}
-
-typedef struct {
- enum {
- KEYCODE_NONE,
- KEYCODE_LITERAL,
- KEYCODE_TAB,
- KEYCODE_ENTER,
- KEYCODE_SS3,
- KEYCODE_CSI,
- KEYCODE_CSI_CURSOR,
- KEYCODE_CSINUM,
- KEYCODE_KEYPAD,
- } type;
- char literal;
- int csinum;
-} keycodes_s;
-
-static keycodes_s keycodes[] = {
- { KEYCODE_NONE }, // NONE
-
- { KEYCODE_ENTER, '\r' }, // ENTER
- { KEYCODE_TAB, '\t' }, // TAB
- { KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
- { KEYCODE_LITERAL, '\x1b' }, // ESCAPE
-
- { KEYCODE_CSI_CURSOR, 'A' }, // UP
- { KEYCODE_CSI_CURSOR, 'B' }, // DOWN
- { KEYCODE_CSI_CURSOR, 'D' }, // LEFT
- { KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
-
- { KEYCODE_CSINUM, '~', 2 }, // INS
- { KEYCODE_CSINUM, '~', 3 }, // DEL
- { KEYCODE_CSI_CURSOR, 'H' }, // HOME
- { KEYCODE_CSI_CURSOR, 'F' }, // END
- { KEYCODE_CSINUM, '~', 5 }, // PAGEUP
- { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
-};
-
-static keycodes_s keycodes_fn[] = {
- { KEYCODE_NONE }, // F0 - shouldn't happen
- { KEYCODE_SS3, 'P' }, // F1
- { KEYCODE_SS3, 'Q' }, // F2
- { KEYCODE_SS3, 'R' }, // F3
- { KEYCODE_SS3, 'S' }, // F4
- { KEYCODE_CSINUM, '~', 15 }, // F5
- { KEYCODE_CSINUM, '~', 17 }, // F6
- { KEYCODE_CSINUM, '~', 18 }, // F7
- { KEYCODE_CSINUM, '~', 19 }, // F8
- { KEYCODE_CSINUM, '~', 20 }, // F9
- { KEYCODE_CSINUM, '~', 21 }, // F10
- { KEYCODE_CSINUM, '~', 23 }, // F11
- { KEYCODE_CSINUM, '~', 24 }, // F12
-};
-
-static keycodes_s keycodes_kp[] = {
- { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
- { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
- { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
- { KEYCODE_KEYPAD, '3', 's' }, // KP_3
- { KEYCODE_KEYPAD, '4', 't' }, // KP_4
- { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
- { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
- { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
- { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
- { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
- { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
- { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
- { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
- { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
- { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
- { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
- { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
- { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
-};
-
-void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
-{
- if(key == VTERM_KEY_NONE)
- return;
-
- keycodes_s k;
- if(key < VTERM_KEY_FUNCTION_0) {
- if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
- return;
- k = keycodes[key];
- }
- else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
- if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
- return;
- k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
- }
- else if(key >= VTERM_KEY_KP_0) {
- if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
- return;
- k = keycodes_kp[key - VTERM_KEY_KP_0];
- }
-
- switch(k.type) {
- case KEYCODE_NONE:
- break;
-
- case KEYCODE_TAB:
- /* Shift-Tab is CSI Z but plain Tab is 0x09 */
- if(mod == VTERM_MOD_SHIFT)
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
- else if(mod & VTERM_MOD_SHIFT)
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
- else
- goto case_LITERAL;
- break;
-
- case KEYCODE_ENTER:
- /* Enter is CRLF in newline mode, but just LF in linefeed */
- if(vt->state->mode.newline)
- vterm_push_output_sprintf(vt, "\r\n");
- else
- goto case_LITERAL;
- break;
-
- case KEYCODE_LITERAL: case_LITERAL:
- if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
- else
- vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
- break;
-
- case KEYCODE_SS3: case_SS3:
- if(mod == 0)
- vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
- else
- goto case_CSI;
- break;
-
- case KEYCODE_CSI: case_CSI:
- if(mod == 0)
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
- else
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
- break;
-
- case KEYCODE_CSINUM:
- if(mod == 0)
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
- else
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
- break;
-
- case KEYCODE_CSI_CURSOR:
- if(vt->state->mode.cursor)
- goto case_SS3;
- else
- goto case_CSI;
-
- case KEYCODE_KEYPAD:
- if(vt->state->mode.keypad) {
- k.literal = k.csinum;
- goto case_SS3;
- }
- else
- goto case_LITERAL;
- }
-}
-
-void vterm_keyboard_start_paste(VTerm *vt)
-{
- if(vt->state->mode.bracketpaste)
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
-}
-
-void vterm_keyboard_end_paste(VTerm *vt)
-{
- if(vt->state->mode.bracketpaste)
- vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
-}
diff --git a/src/vterm/mouse.c b/src/vterm/mouse.c
deleted file mode 100644
index a9d3fe4ca9..0000000000
--- a/src/vterm/mouse.c
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "vterm_internal.h"
-
-#include "nvim/tui/termkey/termkey.h"
-
-static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
-{
- modifiers <<= 2;
-
- switch(state->mouse_protocol) {
- case MOUSE_X10:
- if(col + 0x21 > 0xff)
- col = 0xff - 0x21;
- if(row + 0x21 > 0xff)
- row = 0xff - 0x21;
-
- if(!pressed)
- code = 3;
-
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
- (code | modifiers) + 0x20, col + 0x21, row + 0x21);
- break;
-
- case MOUSE_UTF8:
- {
- char utf8[18]; size_t len = 0;
-
- if(!pressed)
- code = 3;
-
- len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
- len += fill_utf8(col + 0x21, utf8 + len);
- len += fill_utf8(row + 0x21, utf8 + len);
- utf8[len] = 0;
-
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
- }
- break;
-
- case MOUSE_SGR:
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
- code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
- break;
-
- case MOUSE_RXVT:
- if(!pressed)
- code = 3;
-
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
- code | modifiers, col + 1, row + 1);
- break;
- }
-}
-
-void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
-{
- VTermState *state = vt->state;
-
- if(col == state->mouse_col && row == state->mouse_row)
- return;
-
- state->mouse_col = col;
- state->mouse_row = row;
-
- if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
- (state->mouse_flags & MOUSE_WANT_MOVE)) {
- int button = state->mouse_buttons & 0x01 ? 1 :
- state->mouse_buttons & 0x02 ? 2 :
- state->mouse_buttons & 0x04 ? 3 : 4;
- output_mouse(state, button-1 + 0x20, 1, mod, col, row);
- }
-}
-
-void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
-{
- VTermState *state = vt->state;
-
- int old_buttons = state->mouse_buttons;
-
- if(button > 0 && button <= 3) {
- if(pressed)
- state->mouse_buttons |= (1 << (button-1));
- else
- state->mouse_buttons &= ~(1 << (button-1));
- }
-
- /* Most of the time we don't get button releases from 4/5 */
- if(state->mouse_buttons == old_buttons && button < 4)
- return;
-
- if(!state->mouse_flags)
- return;
-
- if(button < 4) {
- output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row);
- }
- else if(button < 8) {
- output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
- }
-}
diff --git a/src/vterm/parser.c b/src/vterm/parser.c
deleted file mode 100644
index 84d017a791..0000000000
--- a/src/vterm/parser.c
+++ /dev/null
@@ -1,408 +0,0 @@
-#include "vterm_internal.h"
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-#undef DEBUG_PARSER
-
-static bool is_intermed(unsigned char c)
-{
- return c >= 0x20 && c <= 0x2f;
-}
-
-static void do_control(VTerm *vt, unsigned char control)
-{
- if(vt->parser.callbacks && vt->parser.callbacks->control)
- if((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
- return;
-
- DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
-}
-
-static void do_csi(VTerm *vt, char command)
-{
-#ifdef DEBUG_PARSER
- printf("Parsed CSI args as:\n", arglen, args);
- printf(" leader: %s\n", vt->parser.v.csi.leader);
- for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
- printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
- if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
- printf("\n");
- printf(" intermed: %s\n", vt->parser.intermed);
- }
-#endif
-
- if(vt->parser.callbacks && vt->parser.callbacks->csi)
- if((*vt->parser.callbacks->csi)(
- vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
- vt->parser.v.csi.args,
- vt->parser.v.csi.argi,
- vt->parser.intermedlen ? vt->parser.intermed : NULL,
- command,
- vt->parser.cbdata))
- return;
-
- DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
-}
-
-static void do_escape(VTerm *vt, char command)
-{
- char seq[INTERMED_MAX+1];
-
- size_t len = vt->parser.intermedlen;
- strncpy(seq, vt->parser.intermed, len);
- seq[len++] = command;
- seq[len] = 0;
-
- if(vt->parser.callbacks && vt->parser.callbacks->escape)
- if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
- return;
-
- DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
-}
-
-static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
-{
- VTermStringFragment frag = {
- .str = str,
- .len = len,
- .initial = vt->parser.string_initial,
- .final = final,
- };
-
- switch(vt->parser.state) {
- case OSC:
- if(vt->parser.callbacks && vt->parser.callbacks->osc)
- (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
- break;
-
- case DCS:
- if(vt->parser.callbacks && vt->parser.callbacks->dcs)
- (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
- break;
-
- case APC:
- if(vt->parser.callbacks && vt->parser.callbacks->apc)
- (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
- break;
-
- case PM:
- if(vt->parser.callbacks && vt->parser.callbacks->pm)
- (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
- break;
-
- case SOS:
- if(vt->parser.callbacks && vt->parser.callbacks->sos)
- (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
- break;
-
- case NORMAL:
- case CSI_LEADER:
- case CSI_ARGS:
- case CSI_INTERMED:
- case OSC_COMMAND:
- case DCS_COMMAND:
- break;
- }
-
- vt->parser.string_initial = false;
-}
-
-size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
-{
- size_t pos = 0;
- const char *string_start;
-
- switch(vt->parser.state) {
- case NORMAL:
- case CSI_LEADER:
- case CSI_ARGS:
- case CSI_INTERMED:
- case OSC_COMMAND:
- case DCS_COMMAND:
- string_start = NULL;
- break;
- case OSC:
- case DCS:
- case APC:
- case PM:
- case SOS:
- string_start = bytes;
- break;
- }
-
-#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
-#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
-
-#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
-
- for( ; pos < len; pos++) {
- unsigned char c = bytes[pos];
- bool c1_allowed = !vt->mode.utf8;
-
- if(c == 0x00 || c == 0x7f) { // NUL, DEL
- if(IS_STRING_STATE()) {
- string_fragment(vt, string_start, bytes + pos - string_start, false);
- string_start = bytes + pos + 1;
- }
- if(vt->parser.emit_nul)
- do_control(vt, c);
- continue;
- }
- if(c == 0x18 || c == 0x1a) { // CAN, SUB
- vt->parser.in_esc = false;
- ENTER_NORMAL_STATE();
- if(vt->parser.emit_nul)
- do_control(vt, c);
- continue;
- }
- else if(c == 0x1b) { // ESC
- vt->parser.intermedlen = 0;
- if(!IS_STRING_STATE())
- vt->parser.state = NORMAL;
- vt->parser.in_esc = true;
- continue;
- }
- else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
- IS_STRING_STATE()) {
- // fallthrough
- }
- else if(c < 0x20) { // other C0
- if(vt->parser.state == SOS)
- continue; // All other C0s permitted in SOS
-
- if(IS_STRING_STATE())
- string_fragment(vt, string_start, bytes + pos - string_start, false);
- do_control(vt, c);
- if(IS_STRING_STATE())
- string_start = bytes + pos + 1;
- continue;
- }
- // else fallthrough
-
- size_t string_len = bytes + pos - string_start;
-
- if(vt->parser.in_esc) {
- // Hoist an ESC letter into a C1 if we're not in a string mode
- // Always accept ESC \ == ST even in string mode
- if(!vt->parser.intermedlen &&
- c >= 0x40 && c < 0x60 &&
- ((!IS_STRING_STATE() || c == 0x5c))) {
- c += 0x40;
- c1_allowed = true;
- if(string_len) {
- assert(string_len > 0);
- string_len -= 1;
- }
- vt->parser.in_esc = false;
- }
- else {
- string_start = NULL;
- vt->parser.state = NORMAL;
- }
- }
-
- switch(vt->parser.state) {
- case CSI_LEADER:
- /* Extract leader bytes 0x3c to 0x3f */
- if(c >= 0x3c && c <= 0x3f) {
- if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
- vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
- break;
- }
-
- /* else fallthrough */
- vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
-
- vt->parser.v.csi.argi = 0;
- vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
- vt->parser.state = CSI_ARGS;
-
- /* fallthrough */
- case CSI_ARGS:
- /* Numerical value of argument */
- if(c >= '0' && c <= '9') {
- if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
- vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
- vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
- vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
- break;
- }
- if(c == ':') {
- vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
- c = ';';
- }
- if(c == ';') {
- vt->parser.v.csi.argi++;
- vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
- break;
- }
-
- /* else fallthrough */
- vt->parser.v.csi.argi++;
- vt->parser.intermedlen = 0;
- vt->parser.state = CSI_INTERMED;
- case CSI_INTERMED:
- if(is_intermed(c)) {
- if(vt->parser.intermedlen < INTERMED_MAX-1)
- vt->parser.intermed[vt->parser.intermedlen++] = c;
- break;
- }
- else if(c == 0x1b) {
- /* ESC in CSI cancels */
- }
- else if(c >= 0x40 && c <= 0x7e) {
- vt->parser.intermed[vt->parser.intermedlen] = 0;
- do_csi(vt, c);
- }
- /* else was invalid CSI */
-
- ENTER_NORMAL_STATE();
- break;
-
- case OSC_COMMAND:
- /* Numerical value of command */
- if(c >= '0' && c <= '9') {
- if(vt->parser.v.osc.command == -1)
- vt->parser.v.osc.command = 0;
- else
- vt->parser.v.osc.command *= 10;
- vt->parser.v.osc.command += c - '0';
- break;
- }
- if(c == ';') {
- vt->parser.state = OSC;
- string_start = bytes + pos + 1;
- break;
- }
-
- /* else fallthrough */
- string_start = bytes + pos;
- string_len = 0;
- vt->parser.state = OSC;
- goto string_state;
-
- case DCS_COMMAND:
- if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
- vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
-
- if(c >= 0x40 && c<= 0x7e) {
- string_start = bytes + pos + 1;
- vt->parser.state = DCS;
- }
- break;
-
-string_state:
- case OSC:
- case DCS:
- case APC:
- case PM:
- case SOS:
- if(c == 0x07 || (c1_allowed && c == 0x9c)) {
- string_fragment(vt, string_start, string_len, true);
- ENTER_NORMAL_STATE();
- }
- break;
-
- case NORMAL:
- if(vt->parser.in_esc) {
- if(is_intermed(c)) {
- if(vt->parser.intermedlen < INTERMED_MAX-1)
- vt->parser.intermed[vt->parser.intermedlen++] = c;
- }
- else if(c >= 0x30 && c < 0x7f) {
- do_escape(vt, c);
- vt->parser.in_esc = 0;
- ENTER_NORMAL_STATE();
- }
- else {
- DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
- }
- break;
- }
- if(c1_allowed && c >= 0x80 && c < 0xa0) {
- switch(c) {
- case 0x90: // DCS
- vt->parser.string_initial = true;
- vt->parser.v.dcs.commandlen = 0;
- ENTER_STATE(DCS_COMMAND);
- break;
- case 0x98: // SOS
- vt->parser.string_initial = true;
- ENTER_STATE(SOS);
- string_start = bytes + pos + 1;
- string_len = 0;
- break;
- case 0x9b: // CSI
- vt->parser.v.csi.leaderlen = 0;
- ENTER_STATE(CSI_LEADER);
- break;
- case 0x9d: // OSC
- vt->parser.v.osc.command = -1;
- vt->parser.string_initial = true;
- string_start = bytes + pos + 1;
- ENTER_STATE(OSC_COMMAND);
- break;
- case 0x9e: // PM
- vt->parser.string_initial = true;
- ENTER_STATE(PM);
- string_start = bytes + pos + 1;
- string_len = 0;
- break;
- case 0x9f: // APC
- vt->parser.string_initial = true;
- ENTER_STATE(APC);
- string_start = bytes + pos + 1;
- string_len = 0;
- break;
- default:
- do_control(vt, c);
- break;
- }
- }
- else {
- size_t eaten = 0;
- if(vt->parser.callbacks && vt->parser.callbacks->text)
- eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
-
- if(!eaten) {
- DEBUG_LOG("libvterm: Text callback did not consume any input\n");
- /* force it to make progress */
- eaten = 1;
- }
-
- pos += (eaten - 1); // we'll ++ it again in a moment
- }
- break;
- }
- }
-
- if(string_start) {
- size_t string_len = bytes + pos - string_start;
- if (string_len > 0) {
- if(vt->parser.in_esc) {
- string_len -= 1;
- }
- string_fragment(vt, string_start, string_len, false);
- }
- }
-
- return len;
-}
-
-void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
-{
- vt->parser.callbacks = callbacks;
- vt->parser.cbdata = user;
-}
-
-void *vterm_parser_get_cbdata(VTerm *vt)
-{
- return vt->parser.cbdata;
-}
-
-void vterm_parser_set_emit_nul(VTerm *vt, bool emit)
-{
- vt->parser.emit_nul = emit;
-}
diff --git a/src/vterm/pen.c b/src/vterm/pen.c
deleted file mode 100644
index 1876eb9881..0000000000
--- a/src/vterm/pen.c
+++ /dev/null
@@ -1,678 +0,0 @@
-#include "vterm_internal.h"
-
-#include <stdio.h>
-
-/**
- * Structure used to store RGB triples without the additional metadata stored in
- * VTermColor.
- */
-typedef struct {
- uint8_t red, green, blue;
-} VTermRGB;
-
-static const VTermRGB ansi_colors[] = {
- /* R G B */
- { 0, 0, 0 }, // black
- { 224, 0, 0 }, // red
- { 0, 224, 0 }, // green
- { 224, 224, 0 }, // yellow
- { 0, 0, 224 }, // blue
- { 224, 0, 224 }, // magenta
- { 0, 224, 224 }, // cyan
- { 224, 224, 224 }, // white == light grey
-
- // high intensity
- { 128, 128, 128 }, // black
- { 255, 64, 64 }, // red
- { 64, 255, 64 }, // green
- { 255, 255, 64 }, // yellow
- { 64, 64, 255 }, // blue
- { 255, 64, 255 }, // magenta
- { 64, 255, 255 }, // cyan
- { 255, 255, 255 }, // white for real
-};
-
-static int ramp6[] = {
- 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
-};
-
-static int ramp24[] = {
- 0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
- 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
-};
-
-static void lookup_default_colour_ansi(long idx, VTermColor *col)
-{
- if (idx >= 0 && idx < 16) {
- vterm_color_rgb(
- col,
- ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
- }
-}
-
-static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
-{
- if(index >= 0 && index < 16) {
- *col = state->colors[index];
- return true;
- }
-
- return false;
-}
-
-static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
-{
- if(index >= 0 && index < 16) {
- // Normal 8 colours or high intensity - parse as palette 0
- return lookup_colour_ansi(state, index, col);
- }
- else if(index >= 16 && index < 232) {
- // 216-colour cube
- index -= 16;
-
- vterm_color_rgb(col, ramp6[index/6/6 % 6],
- ramp6[index/6 % 6],
- ramp6[index % 6]);
-
- return true;
- }
- else if(index >= 232 && index < 256) {
- // 24 greyscales
- index -= 232;
-
- vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
-
- return true;
- }
-
- return false;
-}
-
-static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
-{
- switch(palette) {
- case 2: // RGB mode - 3 args contain colour values directly
- if(argcount < 3)
- return argcount;
-
- vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
-
- return 3;
-
- case 5: // XTerm 256-colour mode
- if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
- return argcount ? 1 : 0;
- }
-
- vterm_color_indexed(col, args[0]);
-
- return argcount ? 1 : 0;
-
- default:
- DEBUG_LOG("Unrecognised colour palette %d\n", palette);
- return 0;
- }
-}
-
-// Some conveniences
-
-static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
-{
-#ifdef DEBUG
- if(type != vterm_get_attr_type(attr)) {
- DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
- attr, vterm_get_attr_type(attr), type);
- return;
- }
-#endif
- if(state->callbacks && state->callbacks->setpenattr)
- (*state->callbacks->setpenattr)(attr, val, state->cbdata);
-}
-
-static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
-{
- VTermValue val = { .boolean = boolean };
- setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
-}
-
-static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
-{
- VTermValue val = { .number = number };
- setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
-}
-
-static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
-{
- VTermValue val = { .color = color };
- setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
-}
-
-static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
-{
- VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
-
- vterm_color_indexed(colp, col);
-
- setpenattr_col(state, attr, *colp);
-}
-
-INTERNAL void vterm_state_newpen(VTermState *state)
-{
- // 90% grey so that pure white is brighter
- vterm_color_rgb(&state->default_fg, 240, 240, 240);
- vterm_color_rgb(&state->default_bg, 0, 0, 0);
- vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
-
- for(int col = 0; col < 16; col++)
- lookup_default_colour_ansi(col, &state->colors[col]);
-}
-
-INTERNAL void vterm_state_resetpen(VTermState *state)
-{
- state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
- state->pen.underline = 0; setpenattr_int (state, VTERM_ATTR_UNDERLINE, 0);
- state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
- state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
- state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
- state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
- state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
- state->pen.font = 0; setpenattr_int (state, VTERM_ATTR_FONT, 0);
- state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
- state->pen.baseline = 0; setpenattr_int (state, VTERM_ATTR_BASELINE, 0);
-
- state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
- state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
-
- state->pen.uri = 0; setpenattr_int(state, VTERM_ATTR_URI, 0);
-}
-
-INTERNAL void vterm_state_savepen(VTermState *state, int save)
-{
- if(save) {
- state->saved.pen = state->pen;
- }
- else {
- state->pen = state->saved.pen;
-
- setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
- setpenattr_int (state, VTERM_ATTR_UNDERLINE, state->pen.underline);
- setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
- setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
- setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
- setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
- setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
- setpenattr_int (state, VTERM_ATTR_FONT, state->pen.font);
- setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
- setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
-
- setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
- setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
-
- setpenattr_int( state, VTERM_ATTR_URI, state->pen.uri);
- }
-}
-
-int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
-{
- /* First make sure that the two colours are of the same type (RGB/Indexed) */
- if (a->type != b->type) {
- return false;
- }
-
- /* Depending on the type inspect the corresponding members */
- if (VTERM_COLOR_IS_INDEXED(a)) {
- return a->indexed.idx == b->indexed.idx;
- }
- else if (VTERM_COLOR_IS_RGB(a)) {
- return (a->rgb.red == b->rgb.red)
- && (a->rgb.green == b->rgb.green)
- && (a->rgb.blue == b->rgb.blue);
- }
-
- return 0;
-}
-
-void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
-{
- *default_fg = state->default_fg;
- *default_bg = state->default_bg;
-}
-
-void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
-{
- lookup_colour_palette(state, index, col);
-}
-
-void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
-{
- if(default_fg) {
- state->default_fg = *default_fg;
- state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
- | VTERM_COLOR_DEFAULT_FG;
- }
-
- if(default_bg) {
- state->default_bg = *default_bg;
- state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
- | VTERM_COLOR_DEFAULT_BG;
- }
-}
-
-void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
-{
- if(index >= 0 && index < 16)
- state->colors[index] = *col;
-}
-
-void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
-{
- if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
- lookup_colour_palette(state, col->indexed.idx, col);
- }
- col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
-}
-
-void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
-{
- state->bold_is_highbright = bold_is_highbright;
-}
-
-INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
-{
- // SGR - ECMA-48 8.3.117
-
- int argi = 0;
- int value;
-
- while(argi < argcount) {
- // This logic is easier to do 'done' backwards; set it true, and make it
- // false again in the 'default' case
- int done = 1;
-
- long arg;
- switch(arg = CSI_ARG(args[argi])) {
- case CSI_ARG_MISSING:
- case 0: // Reset
- vterm_state_resetpen(state);
- break;
-
- case 1: { // Bold on
- const VTermColor *fg = &state->pen.fg;
- state->pen.bold = 1;
- setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
- if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
- set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
- break;
- }
-
- case 3: // Italic on
- state->pen.italic = 1;
- setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
- break;
-
- case 4: // Underline
- state->pen.underline = VTERM_UNDERLINE_SINGLE;
- if(CSI_ARG_HAS_MORE(args[argi])) {
- argi++;
- switch(CSI_ARG(args[argi])) {
- case 0:
- state->pen.underline = 0;
- break;
- case 1:
- state->pen.underline = VTERM_UNDERLINE_SINGLE;
- break;
- case 2:
- state->pen.underline = VTERM_UNDERLINE_DOUBLE;
- break;
- case 3:
- state->pen.underline = VTERM_UNDERLINE_CURLY;
- break;
- }
- }
- setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
- break;
-
- case 5: // Blink
- state->pen.blink = 1;
- setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
- break;
-
- case 7: // Reverse on
- state->pen.reverse = 1;
- setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
- break;
-
- case 8: // Conceal on
- state->pen.conceal = 1;
- setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
- break;
-
- case 9: // Strikethrough on
- state->pen.strike = 1;
- setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
- break;
-
- case 10: case 11: case 12: case 13: case 14:
- case 15: case 16: case 17: case 18: case 19: // Select font
- state->pen.font = CSI_ARG(args[argi]) - 10;
- setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
- break;
-
- case 21: // Underline double
- state->pen.underline = VTERM_UNDERLINE_DOUBLE;
- setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
- break;
-
- case 22: // Bold off
- state->pen.bold = 0;
- setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
- break;
-
- case 23: // Italic and Gothic (currently unsupported) off
- state->pen.italic = 0;
- setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
- break;
-
- case 24: // Underline off
- state->pen.underline = 0;
- setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
- break;
-
- case 25: // Blink off
- state->pen.blink = 0;
- setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
- break;
-
- case 27: // Reverse off
- state->pen.reverse = 0;
- setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
- break;
-
- case 28: // Conceal off (Reveal)
- state->pen.conceal = 0;
- setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
- break;
-
- case 29: // Strikethrough off
- state->pen.strike = 0;
- setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
- break;
-
- case 30: case 31: case 32: case 33:
- case 34: case 35: case 36: case 37: // Foreground colour palette
- value = CSI_ARG(args[argi]) - 30;
- if(state->pen.bold && state->bold_is_highbright)
- value += 8;
- set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
- break;
-
- case 38: // Foreground colour alternative palette
- if(argcount - argi < 1)
- return;
- argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
- setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
- break;
-
- case 39: // Foreground colour default
- state->pen.fg = state->default_fg;
- setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
- break;
-
- case 40: case 41: case 42: case 43:
- case 44: case 45: case 46: case 47: // Background colour palette
- value = CSI_ARG(args[argi]) - 40;
- set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
- break;
-
- case 48: // Background colour alternative palette
- if(argcount - argi < 1)
- return;
- argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
- setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
- break;
-
- case 49: // Default background
- state->pen.bg = state->default_bg;
- setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
- break;
-
- case 73: // Superscript
- case 74: // Subscript
- case 75: // Superscript/subscript off
- state->pen.small = (arg != 75);
- state->pen.baseline =
- (arg == 73) ? VTERM_BASELINE_RAISE :
- (arg == 74) ? VTERM_BASELINE_LOWER :
- VTERM_BASELINE_NORMAL;
- setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
- setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
- break;
-
- case 90: case 91: case 92: case 93:
- case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
- value = CSI_ARG(args[argi]) - 90 + 8;
- set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
- break;
-
- case 100: case 101: case 102: case 103:
- case 104: case 105: case 106: case 107: // Background colour high-intensity palette
- value = CSI_ARG(args[argi]) - 100 + 8;
- set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
- break;
-
- default:
- done = 0;
- break;
- }
-
- if(!done) {
- DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
- }
-
- while(CSI_ARG_HAS_MORE(args[argi++]));
- }
-}
-
-static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
-{
- /* Do nothing if the given color is the default color */
- if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
- (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
- return argi;
- }
-
- /* Decide whether to send an indexed color or an RGB color */
- if (VTERM_COLOR_IS_INDEXED(col)) {
- const uint8_t idx = col->indexed.idx;
- if (idx < 8) {
- args[argi++] = (idx + (fg ? 30 : 40));
- }
- else if (idx < 16) {
- args[argi++] = (idx - 8 + (fg ? 90 : 100));
- }
- else {
- args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
- args[argi++] = CSI_ARG_FLAG_MORE | 5;
- args[argi++] = idx;
- }
- }
- else if (VTERM_COLOR_IS_RGB(col)) {
- args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
- args[argi++] = CSI_ARG_FLAG_MORE | 2;
- args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
- args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
- args[argi++] = col->rgb.blue;
- }
- return argi;
-}
-
-INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount)
-{
- int argi = 0;
-
- if(state->pen.bold)
- args[argi++] = 1;
-
- if(state->pen.italic)
- args[argi++] = 3;
-
- if(state->pen.underline == VTERM_UNDERLINE_SINGLE)
- args[argi++] = 4;
- if(state->pen.underline == VTERM_UNDERLINE_CURLY)
- args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
-
- if(state->pen.blink)
- args[argi++] = 5;
-
- if(state->pen.reverse)
- args[argi++] = 7;
-
- if(state->pen.conceal)
- args[argi++] = 8;
-
- if(state->pen.strike)
- args[argi++] = 9;
-
- if(state->pen.font)
- args[argi++] = 10 + state->pen.font;
-
- if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
- args[argi++] = 21;
-
- argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
-
- argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
-
- if(state->pen.small) {
- if(state->pen.baseline == VTERM_BASELINE_RAISE)
- args[argi++] = 73;
- else if(state->pen.baseline == VTERM_BASELINE_LOWER)
- args[argi++] = 74;
- }
-
- return argi;
-}
-
-int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
-{
- switch(attr) {
- case VTERM_ATTR_BOLD:
- val->boolean = state->pen.bold;
- return 1;
-
- case VTERM_ATTR_UNDERLINE:
- val->number = state->pen.underline;
- return 1;
-
- case VTERM_ATTR_ITALIC:
- val->boolean = state->pen.italic;
- return 1;
-
- case VTERM_ATTR_BLINK:
- val->boolean = state->pen.blink;
- return 1;
-
- case VTERM_ATTR_REVERSE:
- val->boolean = state->pen.reverse;
- return 1;
-
- case VTERM_ATTR_CONCEAL:
- val->boolean = state->pen.conceal;
- return 1;
-
- case VTERM_ATTR_STRIKE:
- val->boolean = state->pen.strike;
- return 1;
-
- case VTERM_ATTR_FONT:
- val->number = state->pen.font;
- return 1;
-
- case VTERM_ATTR_FOREGROUND:
- val->color = state->pen.fg;
- return 1;
-
- case VTERM_ATTR_BACKGROUND:
- val->color = state->pen.bg;
- return 1;
-
- case VTERM_ATTR_SMALL:
- val->boolean = state->pen.small;
- return 1;
-
- case VTERM_ATTR_BASELINE:
- val->number = state->pen.baseline;
- return 1;
-
- case VTERM_ATTR_URI:
- val->number = state->pen.uri;
- return 1;
-
- case VTERM_N_ATTRS:
- return 0;
- }
-
- return 0;
-}
-
-int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
-{
- if (!val) {
- return 0;
- }
-
- if(type != vterm_get_attr_type(attr)) {
- DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
- attr, vterm_get_attr_type(attr), type);
- return 0;
- }
-
- switch (attr) {
- case VTERM_ATTR_BOLD:
- state->pen.bold = val->boolean;
- break;
- case VTERM_ATTR_UNDERLINE:
- state->pen.underline = val->number;
- break;
- case VTERM_ATTR_ITALIC:
- state->pen.italic = val->boolean;
- break;
- case VTERM_ATTR_BLINK:
- state->pen.blink = val->boolean;
- break;
- case VTERM_ATTR_REVERSE:
- state->pen.reverse = val->boolean;
- break;
- case VTERM_ATTR_CONCEAL:
- state->pen.conceal = val->boolean;
- break;
- case VTERM_ATTR_STRIKE:
- state->pen.strike = val->boolean;
- break;
- case VTERM_ATTR_FONT:
- state->pen.font = val->number;
- break;
- case VTERM_ATTR_FOREGROUND:
- state->pen.fg = val->color;
- break;
- case VTERM_ATTR_BACKGROUND:
- state->pen.bg = val->color;
- break;
- case VTERM_ATTR_SMALL:
- state->pen.small = val->boolean;
- break;
- case VTERM_ATTR_BASELINE:
- state->pen.baseline = val->number;
- break;
- case VTERM_ATTR_URI:
- state->pen.uri = val->number;
- break;
- default:
- return 0;
- }
-
- if(state->callbacks && state->callbacks->setpenattr)
- (*state->callbacks->setpenattr)(attr, val, state->cbdata);
-
- return 1;
-}
diff --git a/src/vterm/rect.h b/src/vterm/rect.h
deleted file mode 100644
index 2114f24c1b..0000000000
--- a/src/vterm/rect.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Some utility functions on VTermRect structures
- */
-
-#define STRFrect "(%d,%d-%d,%d)"
-#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
-
-/* Expand dst to contain src as well */
-static void rect_expand(VTermRect *dst, VTermRect *src)
-{
- if(dst->start_row > src->start_row) dst->start_row = src->start_row;
- if(dst->start_col > src->start_col) dst->start_col = src->start_col;
- if(dst->end_row < src->end_row) dst->end_row = src->end_row;
- if(dst->end_col < src->end_col) dst->end_col = src->end_col;
-}
-
-/* Clip the dst to ensure it does not step outside of bounds */
-static void rect_clip(VTermRect *dst, VTermRect *bounds)
-{
- if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
- if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
- if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
- if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
- /* Ensure it doesn't end up negatively-sized */
- if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
- if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
-}
-
-/* True if the two rectangles are equal */
-static int rect_equal(VTermRect *a, VTermRect *b)
-{
- return (a->start_row == b->start_row) &&
- (a->start_col == b->start_col) &&
- (a->end_row == b->end_row) &&
- (a->end_col == b->end_col);
-}
-
-/* True if small is contained entirely within big */
-static int rect_contains(VTermRect *big, VTermRect *small)
-{
- if(small->start_row < big->start_row) return 0;
- if(small->start_col < big->start_col) return 0;
- if(small->end_row > big->end_row) return 0;
- if(small->end_col > big->end_col) return 0;
- return 1;
-}
-
-/* True if the rectangles overlap at all */
-static int rect_intersects(VTermRect *a, VTermRect *b)
-{
- if(a->start_row > b->end_row || b->start_row > a->end_row)
- return 0;
- if(a->start_col > b->end_col || b->start_col > a->end_col)
- return 0;
- return 1;
-}
diff --git a/src/vterm/screen.c b/src/vterm/screen.c
deleted file mode 100644
index 1f5bb36114..0000000000
--- a/src/vterm/screen.c
+++ /dev/null
@@ -1,1202 +0,0 @@
-#include "vterm_internal.h"
-
-#include <stdio.h>
-#include <string.h>
-#include "nvim/mbyte.h"
-#include "nvim/tui/termkey/termkey.h"
-
-#include "rect.h"
-
-#define UNICODE_SPACE 0x20
-#define UNICODE_LINEFEED 0x0a
-
-#undef DEBUG_REFLOW
-
-/* State of the pen at some moment in time, also used in a cell */
-typedef struct
-{
- /* After the bitfield */
- VTermColor fg, bg;
-
- /* Opaque ID that maps to a URI in a set */
- int uri;
-
- unsigned int bold : 1;
- unsigned int underline : 2;
- unsigned int italic : 1;
- unsigned int blink : 1;
- unsigned int reverse : 1;
- unsigned int conceal : 1;
- unsigned int strike : 1;
- unsigned int font : 4; /* 0 to 9 */
- unsigned int small : 1;
- unsigned int baseline : 2;
-
- /* Extra state storage that isn't strictly pen-related */
- unsigned int protected_cell : 1;
- unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
- unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
-} ScreenPen;
-
-/* Internal representation of a screen cell */
-typedef struct
-{
- uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
- ScreenPen pen;
-} ScreenCell;
-
-struct VTermScreen
-{
- VTerm *vt;
- VTermState *state;
-
- const VTermScreenCallbacks *callbacks;
- void *cbdata;
-
- VTermDamageSize damage_merge;
- /* start_row == -1 => no damage */
- VTermRect damaged;
- VTermRect pending_scrollrect;
- int pending_scroll_downward, pending_scroll_rightward;
-
- int rows;
- int cols;
-
- unsigned int global_reverse : 1;
- unsigned int reflow : 1;
-
- /* Primary and Altscreen. buffers[1] is lazily allocated as needed */
- ScreenCell *buffers[2];
-
- /* buffer will == buffers[0] or buffers[1], depending on altscreen */
- ScreenCell *buffer;
-
- /* buffer for a single screen row used in scrollback storage callbacks */
- VTermScreenCell *sb_buffer;
-
- ScreenPen pen;
-};
-
-static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
-{
- cell->chars[0] = 0;
- cell->pen = screen->pen;
-}
-
-static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col)
-{
- if(row < 0 || row >= screen->rows)
- return NULL;
- if(col < 0 || col >= screen->cols)
- return NULL;
- return screen->buffer + (screen->cols * row) + col;
-}
-
-static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
-{
- ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * rows * cols);
-
- for(int row = 0; row < rows; row++) {
- for(int col = 0; col < cols; col++) {
- clearcell(screen, &new_buffer[row * cols + col]);
- }
- }
-
- return new_buffer;
-}
-
-static void damagerect(VTermScreen *screen, VTermRect rect)
-{
- VTermRect emit;
-
- switch(screen->damage_merge) {
- case VTERM_DAMAGE_CELL:
- /* Always emit damage event */
- emit = rect;
- break;
-
- case VTERM_DAMAGE_ROW:
- /* Emit damage longer than one row. Try to merge with existing damage in
- * the same row */
- if(rect.end_row > rect.start_row + 1) {
- // Bigger than 1 line - flush existing, emit this
- vterm_screen_flush_damage(screen);
- emit = rect;
- }
- else if(screen->damaged.start_row == -1) {
- // None stored yet
- screen->damaged = rect;
- return;
- }
- else if(rect.start_row == screen->damaged.start_row) {
- // Merge with the stored line
- if(screen->damaged.start_col > rect.start_col)
- screen->damaged.start_col = rect.start_col;
- if(screen->damaged.end_col < rect.end_col)
- screen->damaged.end_col = rect.end_col;
- return;
- }
- else {
- // Emit the currently stored line, store a new one
- emit = screen->damaged;
- screen->damaged = rect;
- }
- break;
-
- case VTERM_DAMAGE_SCREEN:
- case VTERM_DAMAGE_SCROLL:
- /* Never emit damage event */
- if(screen->damaged.start_row == -1)
- screen->damaged = rect;
- else {
- rect_expand(&screen->damaged, &rect);
- }
- return;
-
- default:
- DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
- return;
- }
-
- if(screen->callbacks && screen->callbacks->damage)
- (*screen->callbacks->damage)(emit, screen->cbdata);
-}
-
-static void damagescreen(VTermScreen *screen)
-{
- VTermRect rect = {
- .start_row = 0,
- .end_row = screen->rows,
- .start_col = 0,
- .end_col = screen->cols,
- };
-
- damagerect(screen, rect);
-}
-
-static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
-{
- VTermScreen *screen = user;
- ScreenCell *cell = getcell(screen, pos.row, pos.col);
-
- if(!cell)
- return 0;
-
- int i;
- for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
- cell->chars[i] = info->chars[i];
- cell->pen = screen->pen;
- }
- if(i < VTERM_MAX_CHARS_PER_CELL)
- cell->chars[i] = 0;
-
- for(int col = 1; col < info->width; col++)
- getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
-
- VTermRect rect = {
- .start_row = pos.row,
- .end_row = pos.row+1,
- .start_col = pos.col,
- .end_col = pos.col+info->width,
- };
-
- cell->pen.protected_cell = info->protected_cell;
- cell->pen.dwl = info->dwl;
- cell->pen.dhl = info->dhl;
-
- damagerect(screen, rect);
-
- return 1;
-}
-
-static void sb_pushline_from_row(VTermScreen *screen, int row)
-{
- VTermPos pos = { .row = row };
- for(pos.col = 0; pos.col < screen->cols; pos.col++)
- vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
-
- (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
-}
-
-static int moverect_internal(VTermRect dest, VTermRect src, void *user)
-{
- VTermScreen *screen = user;
-
- if(screen->callbacks && screen->callbacks->sb_pushline &&
- dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
- dest.end_col == screen->cols && // full width
- screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
- for(int row = 0; row < src.start_row; row++)
- sb_pushline_from_row(screen, row);
- }
-
- int cols = src.end_col - src.start_col;
- int downward = src.start_row - dest.start_row;
-
- int init_row, test_row, inc_row;
- if(downward < 0) {
- init_row = dest.end_row - 1;
- test_row = dest.start_row - 1;
- inc_row = -1;
- }
- else {
- init_row = dest.start_row;
- test_row = dest.end_row;
- inc_row = +1;
- }
-
- for(int row = init_row; row != test_row; row += inc_row)
- memmove(getcell(screen, row, dest.start_col),
- getcell(screen, row + downward, src.start_col),
- cols * sizeof(ScreenCell));
-
- return 1;
-}
-
-static int moverect_user(VTermRect dest, VTermRect src, void *user)
-{
- VTermScreen *screen = user;
-
- if(screen->callbacks && screen->callbacks->moverect) {
- if(screen->damage_merge != VTERM_DAMAGE_SCROLL)
- // Avoid an infinite loop
- vterm_screen_flush_damage(screen);
-
- if((*screen->callbacks->moverect)(dest, src, screen->cbdata))
- return 1;
- }
-
- damagerect(screen, dest);
-
- return 1;
-}
-
-static int erase_internal(VTermRect rect, int selective, void *user)
-{
- VTermScreen *screen = user;
-
- for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
- const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
-
- for(int col = rect.start_col; col < rect.end_col; col++) {
- ScreenCell *cell = getcell(screen, row, col);
-
- if(selective && cell->pen.protected_cell)
- continue;
-
- cell->chars[0] = 0;
- cell->pen = (ScreenPen){
- /* Only copy .fg and .bg; leave things like rv in reset state */
- .fg = screen->pen.fg,
- .bg = screen->pen.bg,
- };
- cell->pen.dwl = info->doublewidth;
- cell->pen.dhl = info->doubleheight;
- }
- }
-
- return 1;
-}
-
-static int erase_user(VTermRect rect, int selective, void *user)
-{
- VTermScreen *screen = user;
-
- damagerect(screen, rect);
-
- return 1;
-}
-
-static int erase(VTermRect rect, int selective, void *user)
-{
- erase_internal(rect, selective, user);
- return erase_user(rect, 0, user);
-}
-
-static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
-{
- VTermScreen *screen = user;
-
- if(screen->damage_merge != VTERM_DAMAGE_SCROLL) {
- vterm_scroll_rect(rect, downward, rightward,
- moverect_internal, erase_internal, screen);
-
- vterm_screen_flush_damage(screen);
-
- vterm_scroll_rect(rect, downward, rightward,
- moverect_user, erase_user, screen);
-
- return 1;
- }
-
- if(screen->damaged.start_row != -1 &&
- !rect_intersects(&rect, &screen->damaged)) {
- vterm_screen_flush_damage(screen);
- }
-
- if(screen->pending_scrollrect.start_row == -1) {
- screen->pending_scrollrect = rect;
- screen->pending_scroll_downward = downward;
- screen->pending_scroll_rightward = rightward;
- }
- else if(rect_equal(&screen->pending_scrollrect, &rect) &&
- ((screen->pending_scroll_downward == 0 && downward == 0) ||
- (screen->pending_scroll_rightward == 0 && rightward == 0))) {
- screen->pending_scroll_downward += downward;
- screen->pending_scroll_rightward += rightward;
- }
- else {
- vterm_screen_flush_damage(screen);
-
- screen->pending_scrollrect = rect;
- screen->pending_scroll_downward = downward;
- screen->pending_scroll_rightward = rightward;
- }
-
- vterm_scroll_rect(rect, downward, rightward,
- moverect_internal, erase_internal, screen);
-
- if(screen->damaged.start_row == -1)
- return 1;
-
- if(rect_contains(&rect, &screen->damaged)) {
- /* Scroll region entirely contains the damage; just move it */
- vterm_rect_move(&screen->damaged, -downward, -rightward);
- rect_clip(&screen->damaged, &rect);
- }
- /* There are a number of possible cases here, but lets restrict this to only
- * the common case where we might actually gain some performance by
- * optimising it. Namely, a vertical scroll that neatly cuts the damage
- * region in half.
- */
- else if(rect.start_col <= screen->damaged.start_col &&
- rect.end_col >= screen->damaged.end_col &&
- rightward == 0) {
- if(screen->damaged.start_row >= rect.start_row &&
- screen->damaged.start_row < rect.end_row) {
- screen->damaged.start_row -= downward;
- if(screen->damaged.start_row < rect.start_row)
- screen->damaged.start_row = rect.start_row;
- if(screen->damaged.start_row > rect.end_row)
- screen->damaged.start_row = rect.end_row;
- }
- if(screen->damaged.end_row >= rect.start_row &&
- screen->damaged.end_row < rect.end_row) {
- screen->damaged.end_row -= downward;
- if(screen->damaged.end_row < rect.start_row)
- screen->damaged.end_row = rect.start_row;
- if(screen->damaged.end_row > rect.end_row)
- screen->damaged.end_row = rect.end_row;
- }
- }
- else {
- DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
- ARGSrect(screen->damaged), ARGSrect(rect));
- }
-
- return 1;
-}
-
-static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
-{
- VTermScreen *screen = user;
-
- if(screen->callbacks && screen->callbacks->movecursor)
- return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
-
- return 0;
-}
-
-static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
-{
- VTermScreen *screen = user;
-
- switch(attr) {
- case VTERM_ATTR_BOLD:
- screen->pen.bold = val->boolean;
- return 1;
- case VTERM_ATTR_UNDERLINE:
- screen->pen.underline = val->number;
- return 1;
- case VTERM_ATTR_ITALIC:
- screen->pen.italic = val->boolean;
- return 1;
- case VTERM_ATTR_BLINK:
- screen->pen.blink = val->boolean;
- return 1;
- case VTERM_ATTR_REVERSE:
- screen->pen.reverse = val->boolean;
- return 1;
- case VTERM_ATTR_CONCEAL:
- screen->pen.conceal = val->boolean;
- return 1;
- case VTERM_ATTR_STRIKE:
- screen->pen.strike = val->boolean;
- return 1;
- case VTERM_ATTR_FONT:
- screen->pen.font = val->number;
- return 1;
- case VTERM_ATTR_FOREGROUND:
- screen->pen.fg = val->color;
- return 1;
- case VTERM_ATTR_BACKGROUND:
- screen->pen.bg = val->color;
- return 1;
- case VTERM_ATTR_SMALL:
- screen->pen.small = val->boolean;
- return 1;
- case VTERM_ATTR_BASELINE:
- screen->pen.baseline = val->number;
- return 1;
- case VTERM_ATTR_URI:
- screen->pen.uri = val->number;
- return 1;
-
- case VTERM_N_ATTRS:
- return 0;
- }
-
- return 0;
-}
-
-static int settermprop(VTermProp prop, VTermValue *val, void *user)
-{
- VTermScreen *screen = user;
-
- switch(prop) {
- case VTERM_PROP_ALTSCREEN:
- if(val->boolean && !screen->buffers[BUFIDX_ALTSCREEN])
- return 0;
-
- screen->buffer = val->boolean ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
- /* only send a damage event on disable; because during enable there's an
- * erase that sends a damage anyway
- */
- if(!val->boolean)
- damagescreen(screen);
- break;
- case VTERM_PROP_REVERSE:
- screen->global_reverse = val->boolean;
- damagescreen(screen);
- break;
- default:
- ; /* ignore */
- }
-
- if(screen->callbacks && screen->callbacks->settermprop)
- return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
-
- return 1;
-}
-
-static int bell(void *user)
-{
- VTermScreen *screen = user;
-
- if(screen->callbacks && screen->callbacks->bell)
- return (*screen->callbacks->bell)(screen->cbdata);
-
- return 0;
-}
-
-/* How many cells are non-blank
- * Returns the position of the first blank cell in the trailing blank end */
-static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
-{
- int col = cols - 1;
- while(col >= 0 && buffer[row * cols + col].chars[0] == 0)
- col--;
- return col + 1;
-}
-
-static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active, VTermStateFields *statefields)
-{
- int old_rows = screen->rows;
- int old_cols = screen->cols;
-
- ScreenCell *old_buffer = screen->buffers[bufidx];
- VTermLineInfo *old_lineinfo = statefields->lineinfos[bufidx];
-
- ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
- VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
-
- int old_row = old_rows - 1;
- int new_row = new_rows - 1;
-
- VTermPos old_cursor = statefields->pos;
- VTermPos new_cursor = { -1, -1 };
-
-#ifdef DEBUG_REFLOW
- fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
- old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
-#endif
-
- /* Keep track of the final row that is knonw to be blank, so we know what
- * spare space we have for scrolling into
- */
- int final_blank_row = new_rows;
-
- while(old_row >= 0) {
- int old_row_end = old_row;
- /* TODO: Stop if dwl or dhl */
- while(screen->reflow && old_lineinfo && old_row > 0 && old_lineinfo[old_row].continuation)
- old_row--;
- int old_row_start = old_row;
-
- int width = 0;
- for(int row = old_row_start; row <= old_row_end; row++) {
- if(screen->reflow && row < (old_rows - 1) && old_lineinfo[row + 1].continuation)
- width += old_cols;
- else
- width += line_popcount(old_buffer, row, old_rows, old_cols);
- }
-
- if(final_blank_row == (new_row + 1) && width == 0)
- final_blank_row = new_row;
-
- int new_height = screen->reflow
- ? width ? (width + new_cols - 1) / new_cols : 1
- : 1;
-
- int new_row_end = new_row;
- int new_row_start = new_row - new_height + 1;
-
- old_row = old_row_start;
- int old_col = 0;
-
- int spare_rows = new_rows - final_blank_row;
-
- if(new_row_start < 0 && /* we'd fall off the top */
- spare_rows >= 0 && /* we actually have spare rows */
- (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows))
- {
- /* Attempt to scroll content down into the blank rows at the bottom to
- * make it fit
- */
- int downwards = -new_row_start;
- if(downwards > spare_rows)
- downwards = spare_rows;
- int rowcount = new_rows - downwards;
-
-#ifdef DEBUG_REFLOW
- fprintf(stderr, " scroll %d rows +%d downwards\n", rowcount, downwards);
-#endif
-
- memmove(&new_buffer[downwards * new_cols], &new_buffer[0], (unsigned long) rowcount * new_cols * sizeof(ScreenCell));
- memmove(&new_lineinfo[downwards], &new_lineinfo[0], rowcount * sizeof(new_lineinfo[0]));
-
- new_row += downwards;
- new_row_start += downwards;
- new_row_end += downwards;
-
- if(new_cursor.row >= 0)
- new_cursor.row += downwards;
-
- final_blank_row += downwards;
- }
-
-#ifdef DEBUG_REFLOW
- fprintf(stderr, " rows [%d..%d] <- [%d..%d] width=%d\n",
- new_row_start, new_row_end, old_row_start, old_row_end, width);
-#endif
-
- if(new_row_start < 0) {
- if(old_row_start <= old_cursor.row && old_cursor.row <= old_row_end) {
- new_cursor.row = 0;
- new_cursor.col = old_cursor.col;
- if(new_cursor.col >= new_cols)
- new_cursor.col = new_cols-1;
- }
- break;
- }
-
- for(new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
- int count = width >= new_cols ? new_cols : width;
- width -= count;
-
- int new_col = 0;
-
- while(count) {
- /* TODO: This could surely be done a lot faster by memcpy()'ing the entire range */
- new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
-
- if(old_cursor.row == old_row && old_cursor.col == old_col)
- new_cursor.row = new_row, new_cursor.col = new_col;
-
- old_col++;
- if(old_col == old_cols) {
- old_row++;
-
- if(!screen->reflow) {
- new_col++;
- break;
- }
- old_col = 0;
- }
-
- new_col++;
- count--;
- }
-
- if(old_cursor.row == old_row && old_cursor.col >= old_col) {
- new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
- if(new_cursor.col >= new_cols)
- new_cursor.col = new_cols-1;
- }
-
- while(new_col < new_cols) {
- clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
- new_col++;
- }
-
- new_lineinfo[new_row].continuation = (new_row > new_row_start);
- }
-
- old_row = old_row_start - 1;
- new_row = new_row_start - 1;
- }
-
- if(old_cursor.row <= old_row) {
- /* cursor would have moved entirely off the top of the screen; lets just
- * bring it within range */
- new_cursor.row = 0, new_cursor.col = old_cursor.col;
- if(new_cursor.col >= new_cols)
- new_cursor.col = new_cols-1;
- }
-
- /* We really expect the cursor position to be set by now */
- if(active && (new_cursor.row == -1 || new_cursor.col == -1)) {
- fprintf(stderr, "screen_resize failed to update cursor position\n");
- abort();
- }
-
- if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
- /* Push spare lines to scrollback buffer */
- if(screen->callbacks && screen->callbacks->sb_pushline)
- for(int row = 0; row <= old_row; row++)
- sb_pushline_from_row(screen, row);
- if(active)
- statefields->pos.row -= (old_row + 1);
- }
- if(new_row >= 0 && bufidx == BUFIDX_PRIMARY &&
- screen->callbacks && screen->callbacks->sb_popline) {
- /* Try to backfill rows by popping scrollback buffer */
- while(new_row >= 0) {
- if(!(screen->callbacks->sb_popline(old_cols, screen->sb_buffer, screen->cbdata)))
- break;
-
- VTermPos pos = { .row = new_row };
- for(pos.col = 0; pos.col < old_cols && pos.col < new_cols; pos.col += screen->sb_buffer[pos.col].width) {
- VTermScreenCell *src = &screen->sb_buffer[pos.col];
- ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
-
- for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
- dst->chars[i] = src->chars[i];
- if(!src->chars[i])
- break;
- }
-
- dst->pen.bold = src->attrs.bold;
- dst->pen.underline = src->attrs.underline;
- dst->pen.italic = src->attrs.italic;
- dst->pen.blink = src->attrs.blink;
- dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse;
- dst->pen.conceal = src->attrs.conceal;
- dst->pen.strike = src->attrs.strike;
- dst->pen.font = src->attrs.font;
- dst->pen.small = src->attrs.small;
- dst->pen.baseline = src->attrs.baseline;
-
- dst->pen.fg = src->fg;
- dst->pen.bg = src->bg;
-
- dst->pen.uri = src->uri;
-
- if(src->width == 2 && pos.col < (new_cols-1))
- (dst + 1)->chars[0] = (uint32_t) -1;
- }
- for( ; pos.col < new_cols; pos.col++)
- clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
- new_row--;
-
- if(active)
- statefields->pos.row++;
- }
- }
- if(new_row >= 0) {
- /* Scroll new rows back up to the top and fill in blanks at the bottom */
- int moverows = new_rows - new_row - 1;
- memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols], (unsigned long) moverows * new_cols * sizeof(ScreenCell));
- memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1], moverows * sizeof(new_lineinfo[0]));
-
- new_cursor.row -= (new_row + 1);
-
- for(new_row = moverows; new_row < new_rows; new_row++) {
- for(int col = 0; col < new_cols; col++)
- clearcell(screen, &new_buffer[new_row * new_cols + col]);
- new_lineinfo[new_row] = (VTermLineInfo){ 0 };
- }
- }
-
- vterm_allocator_free(screen->vt, old_buffer);
- screen->buffers[bufidx] = new_buffer;
-
- vterm_allocator_free(screen->vt, old_lineinfo);
- statefields->lineinfos[bufidx] = new_lineinfo;
-
- if(active)
- statefields->pos = new_cursor;
-
- return;
-}
-
-static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
-{
- VTermScreen *screen = user;
-
- int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN] && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]);
-
- int old_rows = screen->rows;
- int old_cols = screen->cols;
-
- if(new_cols > old_cols) {
- /* Ensure that ->sb_buffer is large enough for a new or and old row */
- if(screen->sb_buffer)
- vterm_allocator_free(screen->vt, screen->sb_buffer);
-
- screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
- }
-
- resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
- if(screen->buffers[BUFIDX_ALTSCREEN])
- resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
- else if(new_rows != old_rows) {
- /* We don't need a full resize of the altscreen because it isn't enabled
- * but we should at least keep the lineinfo the right size */
- vterm_allocator_free(screen->vt, fields->lineinfos[BUFIDX_ALTSCREEN]);
-
- VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
- for(int row = 0; row < new_rows; row++)
- new_lineinfo[row] = (VTermLineInfo){ 0 };
-
- fields->lineinfos[BUFIDX_ALTSCREEN] = new_lineinfo;
- }
-
- screen->buffer = altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
-
- screen->rows = new_rows;
- screen->cols = new_cols;
-
- if(new_cols <= old_cols) {
- if(screen->sb_buffer)
- vterm_allocator_free(screen->vt, screen->sb_buffer);
-
- screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
- }
-
- /* TODO: Maaaaybe we can optimise this if there's no reflow happening */
- damagescreen(screen);
-
- if(screen->callbacks && screen->callbacks->resize)
- return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
-
- return 1;
-}
-
-static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
-{
- VTermScreen *screen = user;
-
- if(newinfo->doublewidth != oldinfo->doublewidth ||
- newinfo->doubleheight != oldinfo->doubleheight) {
- for(int col = 0; col < screen->cols; col++) {
- ScreenCell *cell = getcell(screen, row, col);
- cell->pen.dwl = newinfo->doublewidth;
- cell->pen.dhl = newinfo->doubleheight;
- }
-
- VTermRect rect = {
- .start_row = row,
- .end_row = row + 1,
- .start_col = 0,
- .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
- };
- damagerect(screen, rect);
-
- if(newinfo->doublewidth) {
- rect.start_col = screen->cols / 2;
- rect.end_col = screen->cols;
-
- erase_internal(rect, 0, user);
- }
- }
-
- return 1;
-}
-
-static int sb_clear(void *user) {
- VTermScreen *screen = user;
-
- if(screen->callbacks && screen->callbacks->sb_clear)
- if((*screen->callbacks->sb_clear)(screen->cbdata))
- return 1;
-
- return 0;
-}
-
-static VTermStateCallbacks state_cbs = {
- .putglyph = &putglyph,
- .movecursor = &movecursor,
- .scrollrect = &scrollrect,
- .erase = &erase,
- .setpenattr = &setpenattr,
- .settermprop = &settermprop,
- .bell = &bell,
- .resize = &resize,
- .setlineinfo = &setlineinfo,
- .sb_clear = &sb_clear,
-};
-
-static VTermScreen *screen_new(VTerm *vt)
-{
- VTermState *state = vterm_obtain_state(vt);
- if(!state)
- return NULL;
-
- VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
- int rows, cols;
-
- vterm_get_size(vt, &rows, &cols);
-
- screen->vt = vt;
- screen->state = state;
-
- screen->damage_merge = VTERM_DAMAGE_CELL;
- screen->damaged.start_row = -1;
- screen->pending_scrollrect.start_row = -1;
-
- screen->rows = rows;
- screen->cols = cols;
-
- screen->global_reverse = false;
- screen->reflow = false;
-
- screen->callbacks = NULL;
- screen->cbdata = NULL;
-
- screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
-
- screen->buffer = screen->buffers[BUFIDX_PRIMARY];
-
- screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols);
-
- vterm_state_set_callbacks(screen->state, &state_cbs, screen);
-
- return screen;
-}
-
-INTERNAL void vterm_screen_free(VTermScreen *screen)
-{
- vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_PRIMARY]);
- if(screen->buffers[BUFIDX_ALTSCREEN])
- vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_ALTSCREEN]);
-
- vterm_allocator_free(screen->vt, screen->sb_buffer);
-
- vterm_allocator_free(screen->vt, screen);
-}
-
-void vterm_screen_reset(VTermScreen *screen, int hard)
-{
- screen->damaged.start_row = -1;
- screen->pending_scrollrect.start_row = -1;
- vterm_state_reset(screen->state, hard);
- vterm_screen_flush_damage(screen);
-}
-
-static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
-{
- size_t outpos = 0;
- int padding = 0;
-
-#define PUT(c) \
- if(utf8) { \
- size_t thislen = utf_char2len(c); \
- if(buffer && outpos + thislen <= len) \
- outpos += fill_utf8((c), (char *)buffer + outpos); \
- else \
- outpos += thislen; \
- } \
- else { \
- if(buffer && outpos + 1 <= len) \
- ((uint32_t*)buffer)[outpos++] = (c); \
- else \
- outpos++; \
- }
-
- for(int row = rect.start_row; row < rect.end_row; row++) {
- for(int col = rect.start_col; col < rect.end_col; col++) {
- ScreenCell *cell = getcell(screen, row, col);
-
- if(cell->chars[0] == 0)
- // Erased cell, might need a space
- padding++;
- else if(cell->chars[0] == (uint32_t)-1)
- // Gap behind a double-width char, do nothing
- ;
- else {
- while(padding) {
- PUT(UNICODE_SPACE);
- padding--;
- }
- for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
- PUT(cell->chars[i]);
- }
- }
- }
-
- if(row < rect.end_row - 1) {
- PUT(UNICODE_LINEFEED);
- padding = 0;
- }
- }
-
- return outpos;
-}
-
-size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
-{
- return _get_chars(screen, 0, chars, len, rect);
-}
-
-size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
-{
- return _get_chars(screen, 1, str, len, rect);
-}
-
-/* Copy internal to external representation of a screen cell */
-int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
-{
- ScreenCell *intcell = getcell(screen, pos.row, pos.col);
- if(!intcell)
- return 0;
-
- for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
- cell->chars[i] = intcell->chars[i];
- if(!intcell->chars[i])
- break;
- }
-
- cell->attrs.bold = intcell->pen.bold;
- cell->attrs.underline = intcell->pen.underline;
- cell->attrs.italic = intcell->pen.italic;
- cell->attrs.blink = intcell->pen.blink;
- cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
- cell->attrs.conceal = intcell->pen.conceal;
- cell->attrs.strike = intcell->pen.strike;
- cell->attrs.font = intcell->pen.font;
- cell->attrs.small = intcell->pen.small;
- cell->attrs.baseline = intcell->pen.baseline;
-
- cell->attrs.dwl = intcell->pen.dwl;
- cell->attrs.dhl = intcell->pen.dhl;
-
- cell->fg = intcell->pen.fg;
- cell->bg = intcell->pen.bg;
-
- cell->uri = intcell->pen.uri;
-
- if(pos.col < (screen->cols - 1) &&
- getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1)
- cell->width = 2;
- else
- cell->width = 1;
-
- return 1;
-}
-
-int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
-{
- /* This cell is EOL if this and every cell to the right is black */
- for(; pos.col < screen->cols; pos.col++) {
- ScreenCell *cell = getcell(screen, pos.row, pos.col);
- if(cell->chars[0] != 0)
- return 0;
- }
-
- return 1;
-}
-
-VTermScreen *vterm_obtain_screen(VTerm *vt)
-{
- if(vt->screen)
- return vt->screen;
-
- VTermScreen *screen = screen_new(vt);
- vt->screen = screen;
-
- return screen;
-}
-
-void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow)
-{
- screen->reflow = reflow;
-}
-
-#undef vterm_screen_set_reflow
-void vterm_screen_set_reflow(VTermScreen *screen, bool reflow)
-{
- vterm_screen_enable_reflow(screen, reflow);
-}
-
-void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
-{
- if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
- int rows, cols;
- vterm_get_size(screen->vt, &rows, &cols);
-
- screen->buffers[BUFIDX_ALTSCREEN] = alloc_buffer(screen, rows, cols);
- }
-}
-
-void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user)
-{
- screen->callbacks = callbacks;
- screen->cbdata = user;
-}
-
-void *vterm_screen_get_cbdata(VTermScreen *screen)
-{
- return screen->cbdata;
-}
-
-void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
-{
- vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
-}
-
-void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen)
-{
- return vterm_state_get_unrecognised_fbdata(screen->state);
-}
-
-void vterm_screen_flush_damage(VTermScreen *screen)
-{
- if(screen->pending_scrollrect.start_row != -1) {
- vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward,
- moverect_user, erase_user, screen);
-
- screen->pending_scrollrect.start_row = -1;
- }
-
- if(screen->damaged.start_row != -1) {
- if(screen->callbacks && screen->callbacks->damage)
- (*screen->callbacks->damage)(screen->damaged, screen->cbdata);
-
- screen->damaged.start_row = -1;
- }
-}
-
-void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
-{
- vterm_screen_flush_damage(screen);
- screen->damage_merge = size;
-}
-
-static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
-{
- if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
- return 1;
- if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
- return 1;
- if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
- return 1;
- if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
- return 1;
- if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
- return 1;
- if((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal))
- return 1;
- if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
- return 1;
- if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
- return 1;
- if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg))
- return 1;
- if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
- return 1;
- if((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small))
- return 1;
- if((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline))
- return 1;
- if((attrs & VTERM_ATTR_URI_MASK) && (a->pen.uri != b->pen.uri))
- return 1;
-
- return 0;
-}
-
-int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
-{
- ScreenCell *target = getcell(screen, pos.row, pos.col);
-
- // TODO: bounds check
- extent->start_row = pos.row;
- extent->end_row = pos.row + 1;
-
- if(extent->start_col < 0)
- extent->start_col = 0;
- if(extent->end_col < 0)
- extent->end_col = screen->cols;
-
- int col;
-
- for(col = pos.col - 1; col >= extent->start_col; col--)
- if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
- break;
- extent->start_col = col + 1;
-
- for(col = pos.col + 1; col < extent->end_col; col++)
- if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
- break;
- extent->end_col = col - 1;
-
- return 1;
-}
-
-void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
-{
- vterm_state_convert_color_to_rgb(screen->state, col);
-}
-
-static void reset_default_colours(VTermScreen *screen, ScreenCell *buffer)
-{
- for(int row = 0; row <= screen->rows - 1; row++)
- for(int col = 0; col <= screen->cols - 1; col++) {
- ScreenCell *cell = &buffer[row * screen->cols + col];
- if(VTERM_COLOR_IS_DEFAULT_FG(&cell->pen.fg))
- cell->pen.fg = screen->pen.fg;
- if(VTERM_COLOR_IS_DEFAULT_BG(&cell->pen.bg))
- cell->pen.bg = screen->pen.bg;
- }
-}
-
-void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg)
-{
- vterm_state_set_default_colors(screen->state, default_fg, default_bg);
-
- if(default_fg && VTERM_COLOR_IS_DEFAULT_FG(&screen->pen.fg)) {
- screen->pen.fg = *default_fg;
- screen->pen.fg.type = (screen->pen.fg.type & ~VTERM_COLOR_DEFAULT_MASK)
- | VTERM_COLOR_DEFAULT_FG;
- }
-
- if(default_bg && VTERM_COLOR_IS_DEFAULT_BG(&screen->pen.bg)) {
- screen->pen.bg = *default_bg;
- screen->pen.bg.type = (screen->pen.bg.type & ~VTERM_COLOR_DEFAULT_MASK)
- | VTERM_COLOR_DEFAULT_BG;
- }
-
- reset_default_colours(screen, screen->buffers[0]);
- if(screen->buffers[1])
- reset_default_colours(screen, screen->buffers[1]);
-}
diff --git a/src/vterm/state.c b/src/vterm/state.c
deleted file mode 100644
index e09b39436a..0000000000
--- a/src/vterm/state.c
+++ /dev/null
@@ -1,2347 +0,0 @@
-#include "vterm_internal.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#define strneq(a,b,n) (strncmp(a,b,n)==0)
-
-#if defined(DEBUG) && DEBUG > 1
-# define DEBUG_GLYPH_COMBINE
-#endif
-
-/* Some convenient wrappers to make callback functions easier */
-
-static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
-{
- VTermGlyphInfo info = {
- .chars = chars,
- .width = width,
- .protected_cell = state->protected_cell,
- .dwl = state->lineinfo[pos.row].doublewidth,
- .dhl = state->lineinfo[pos.row].doubleheight,
- };
-
- if(state->callbacks && state->callbacks->putglyph)
- if((*state->callbacks->putglyph)(&info, pos, state->cbdata))
- return;
-
- DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
-}
-
-static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
-{
- if(state->pos.col == oldpos->col && state->pos.row == oldpos->row)
- return;
-
- if(cancel_phantom)
- state->at_phantom = 0;
-
- if(state->callbacks && state->callbacks->movecursor)
- if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata))
- return;
-}
-
-static void erase(VTermState *state, VTermRect rect, int selective)
-{
- if(rect.end_col == state->cols) {
- /* If we're erasing the final cells of any lines, cancel the continuation
- * marker on the subsequent line
- */
- for(int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
- state->lineinfo[row].continuation = 0;
- }
-
- if(state->callbacks && state->callbacks->erase)
- if((*state->callbacks->erase)(rect, selective, state->cbdata))
- return;
-}
-
-static VTermState *vterm_state_new(VTerm *vt)
-{
- VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
-
- state->vt = vt;
-
- state->rows = vt->rows;
- state->cols = vt->cols;
-
- state->mouse_col = 0;
- state->mouse_row = 0;
- state->mouse_buttons = 0;
-
- state->mouse_protocol = MOUSE_X10;
-
- state->callbacks = NULL;
- state->cbdata = NULL;
-
- state->selection.callbacks = NULL;
- state->selection.user = NULL;
- state->selection.buffer = NULL;
-
- vterm_state_newpen(state);
-
- state->bold_is_highbright = 0;
-
- state->combine_chars_size = 16;
- state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
-
- state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
-
- state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
- /* TODO: Make an 'enable' function */
- state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
- state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
-
- state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
- if(*state->encoding_utf8.enc->init)
- (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
-
- return state;
-}
-
-INTERNAL void vterm_state_free(VTermState *state)
-{
- vterm_allocator_free(state->vt, state->tabstops);
- vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
- if(state->lineinfos[BUFIDX_ALTSCREEN])
- vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
- vterm_allocator_free(state->vt, state->combine_chars);
- vterm_allocator_free(state->vt, state);
-}
-
-static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
-{
- if(!downward && !rightward)
- return;
-
- int rows = rect.end_row - rect.start_row;
- if(downward > rows)
- downward = rows;
- else if(downward < -rows)
- downward = -rows;
-
- int cols = rect.end_col - rect.start_col;
- if(rightward > cols)
- rightward = cols;
- else if(rightward < -cols)
- rightward = -cols;
-
- // Update lineinfo if full line
- if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
- int height = rect.end_row - rect.start_row - abs(downward);
-
- if(downward > 0) {
- memmove(state->lineinfo + rect.start_row,
- state->lineinfo + rect.start_row + downward,
- height * sizeof(state->lineinfo[0]));
- for(int row = rect.end_row - downward; row < rect.end_row; row++)
- state->lineinfo[row] = (VTermLineInfo){ 0 };
- }
- else {
- memmove(state->lineinfo + rect.start_row - downward,
- state->lineinfo + rect.start_row,
- height * sizeof(state->lineinfo[0]));
- for(int row = rect.start_row; row < rect.start_row - downward; row++)
- state->lineinfo[row] = (VTermLineInfo){ 0 };
- }
- }
-
- if(state->callbacks && state->callbacks->scrollrect)
- if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata))
- return;
-
- if(state->callbacks)
- vterm_scroll_rect(rect, downward, rightward,
- state->callbacks->moverect, state->callbacks->erase, state->cbdata);
-}
-
-static void linefeed(VTermState *state)
-{
- if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
- VTermRect rect = {
- .start_row = state->scrollregion_top,
- .end_row = SCROLLREGION_BOTTOM(state),
- .start_col = SCROLLREGION_LEFT(state),
- .end_col = SCROLLREGION_RIGHT(state),
- };
-
- scroll(state, rect, 1, 0);
- }
- else if(state->pos.row < state->rows-1)
- state->pos.row++;
-}
-
-static void grow_combine_buffer(VTermState *state)
-{
- size_t new_size = state->combine_chars_size * 2;
- uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0]));
-
- memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0]));
-
- vterm_allocator_free(state->vt, state->combine_chars);
-
- state->combine_chars = new_chars;
- state->combine_chars_size = new_size;
-}
-
-static void set_col_tabstop(VTermState *state, int col)
-{
- unsigned char mask = 1 << (col & 7);
- state->tabstops[col >> 3] |= mask;
-}
-
-static void clear_col_tabstop(VTermState *state, int col)
-{
- unsigned char mask = 1 << (col & 7);
- state->tabstops[col >> 3] &= ~mask;
-}
-
-static int is_col_tabstop(VTermState *state, int col)
-{
- unsigned char mask = 1 << (col & 7);
- return state->tabstops[col >> 3] & mask;
-}
-
-static int is_cursor_in_scrollregion(const VTermState *state)
-{
- if(state->pos.row < state->scrollregion_top ||
- state->pos.row >= SCROLLREGION_BOTTOM(state))
- return 0;
- if(state->pos.col < SCROLLREGION_LEFT(state) ||
- state->pos.col >= SCROLLREGION_RIGHT(state))
- return 0;
-
- return 1;
-}
-
-static void tab(VTermState *state, int count, int direction)
-{
- while(count > 0) {
- if(direction > 0) {
- if(state->pos.col >= THISROWWIDTH(state)-1)
- return;
-
- state->pos.col++;
- }
- else if(direction < 0) {
- if(state->pos.col < 1)
- return;
-
- state->pos.col--;
- }
-
- if(is_col_tabstop(state, state->pos.col))
- count--;
- }
-}
-
-#define NO_FORCE 0
-#define FORCE 1
-
-#define DWL_OFF 0
-#define DWL_ON 1
-
-#define DHL_OFF 0
-#define DHL_TOP 1
-#define DHL_BOTTOM 2
-
-static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
-{
- VTermLineInfo info = state->lineinfo[row];
-
- if(dwl == DWL_OFF)
- info.doublewidth = DWL_OFF;
- else if(dwl == DWL_ON)
- info.doublewidth = DWL_ON;
- // else -1 to ignore
-
- if(dhl == DHL_OFF)
- info.doubleheight = DHL_OFF;
- else if(dhl == DHL_TOP)
- info.doubleheight = DHL_TOP;
- else if(dhl == DHL_BOTTOM)
- info.doubleheight = DHL_BOTTOM;
-
- if((state->callbacks &&
- state->callbacks->setlineinfo &&
- (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
- || force)
- state->lineinfo[row] = info;
-}
-
-static int on_text(const char bytes[], size_t len, void *user)
-{
- VTermState *state = user;
-
- VTermPos oldpos = state->pos;
-
- uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
- size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
-
- int npoints = 0;
- size_t eaten = 0;
-
- VTermEncodingInstance *encoding =
- state->gsingle_set ? &state->encoding[state->gsingle_set] :
- !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] :
- state->vt->mode.utf8 ? &state->encoding_utf8 :
- &state->encoding[state->gr_set];
-
- (*encoding->enc->decode)(encoding->enc, encoding->data,
- codepoints, &npoints, state->gsingle_set ? 1 : maxpoints,
- bytes, &eaten, len);
-
- /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet
- * for even a single codepoint
- */
- if(!npoints)
- return eaten;
-
- if(state->gsingle_set && npoints)
- state->gsingle_set = 0;
-
- int i = 0;
-
- /* This is a combining char. that needs to be merged with the previous
- * glyph output */
- if(vterm_unicode_is_combining(codepoints[i])) {
- /* See if the cursor has moved since */
- if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) {
-#ifdef DEBUG_GLYPH_COMBINE
- int printpos;
- printf("DEBUG: COMBINING SPLIT GLYPH of chars {");
- for(printpos = 0; state->combine_chars[printpos]; printpos++)
- printf("U+%04x ", state->combine_chars[printpos]);
- printf("} + {");
-#endif
-
- /* Find where we need to append these combining chars */
- int saved_i = 0;
- while(state->combine_chars[saved_i])
- saved_i++;
-
- /* Add extra ones */
- while(i < npoints && vterm_unicode_is_combining(codepoints[i])) {
- if(saved_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[saved_i++] = codepoints[i++];
- }
- if(saved_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[saved_i] = 0;
-
-#ifdef DEBUG_GLYPH_COMBINE
- for(; state->combine_chars[printpos]; printpos++)
- printf("U+%04x ", state->combine_chars[printpos]);
- printf("}\n");
-#endif
-
- /* Now render it */
- putglyph(state, state->combine_chars, state->combine_width, state->combine_pos);
- }
- else {
- DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
- }
- }
-
- for(; i < npoints; i++) {
- // Try to find combining characters following this
- int glyph_starts = i;
- int glyph_ends;
- for(glyph_ends = i + 1;
- (glyph_ends < npoints) && (glyph_ends < glyph_starts + VTERM_MAX_CHARS_PER_CELL);
- glyph_ends++)
- if(!vterm_unicode_is_combining(codepoints[glyph_ends]))
- break;
-
- int width = 0;
-
- uint32_t chars[VTERM_MAX_CHARS_PER_CELL + 1];
-
- for( ; i < glyph_ends; i++) {
- chars[i - glyph_starts] = codepoints[i];
- int this_width = vterm_unicode_width(codepoints[i]);
-#ifdef DEBUG
- if(this_width < 0) {
- fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]);
- abort();
- }
-#endif
- width += this_width;
- }
-
- while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
- i++;
-
- chars[glyph_ends - glyph_starts] = 0;
- i--;
-
-#ifdef DEBUG_GLYPH_COMBINE
- int printpos;
- printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts);
- for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++)
- printf("U+%04x ", chars[printpos]);
- printf("}, onscreen width %d\n", width);
-#endif
-
- if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
- linefeed(state);
- state->pos.col = 0;
- state->at_phantom = 0;
- state->lineinfo[state->pos.row].continuation = 1;
- }
-
- if(state->mode.insert) {
- /* TODO: This will be a little inefficient for large bodies of text, as
- * it'll have to 'ICH' effectively before every glyph. We should scan
- * ahead and ICH as many times as required
- */
- VTermRect rect = {
- .start_row = state->pos.row,
- .end_row = state->pos.row + 1,
- .start_col = state->pos.col,
- .end_col = THISROWWIDTH(state),
- };
- scroll(state, rect, 0, -1);
- }
-
- putglyph(state, chars, width, state->pos);
-
- if(i == npoints - 1) {
- /* End of the buffer. Save the chars in case we have to combine with
- * more on the next call */
- int save_i;
- for(save_i = 0; chars[save_i]; save_i++) {
- if(save_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[save_i] = chars[save_i];
- }
- if(save_i >= state->combine_chars_size)
- grow_combine_buffer(state);
- state->combine_chars[save_i] = 0;
- state->combine_width = width;
- state->combine_pos = state->pos;
- }
-
- if(state->pos.col + width >= THISROWWIDTH(state)) {
- if(state->mode.autowrap)
- state->at_phantom = 1;
- }
- else {
- state->pos.col += width;
- }
- }
-
- updatecursor(state, &oldpos, 0);
-
-#ifdef DEBUG
- if(state->pos.row < 0 || state->pos.row >= state->rows ||
- state->pos.col < 0 || state->pos.col >= state->cols) {
- fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
- state->pos.row, state->pos.col);
- abort();
- }
-#endif
-
- return eaten;
-}
-
-static int on_control(unsigned char control, void *user)
-{
- VTermState *state = user;
-
- VTermPos oldpos = state->pos;
-
- switch(control) {
- case 0x07: // BEL - ECMA-48 8.3.3
- if(state->callbacks && state->callbacks->bell)
- (*state->callbacks->bell)(state->cbdata);
- break;
-
- case 0x08: // BS - ECMA-48 8.3.5
- if(state->pos.col > 0)
- state->pos.col--;
- break;
-
- case 0x09: // HT - ECMA-48 8.3.60
- tab(state, 1, +1);
- break;
-
- case 0x0a: // LF - ECMA-48 8.3.74
- case 0x0b: // VT
- case 0x0c: // FF
- linefeed(state);
- if(state->mode.newline)
- state->pos.col = 0;
- break;
-
- case 0x0d: // CR - ECMA-48 8.3.15
- state->pos.col = 0;
- break;
-
- case 0x0e: // LS1 - ECMA-48 8.3.76
- state->gl_set = 1;
- break;
-
- case 0x0f: // LS0 - ECMA-48 8.3.75
- state->gl_set = 0;
- break;
-
- case 0x84: // IND - DEPRECATED but implemented for completeness
- linefeed(state);
- break;
-
- case 0x85: // NEL - ECMA-48 8.3.86
- linefeed(state);
- state->pos.col = 0;
- break;
-
- case 0x88: // HTS - ECMA-48 8.3.62
- set_col_tabstop(state, state->pos.col);
- break;
-
- case 0x8d: // RI - ECMA-48 8.3.104
- if(state->pos.row == state->scrollregion_top) {
- VTermRect rect = {
- .start_row = state->scrollregion_top,
- .end_row = SCROLLREGION_BOTTOM(state),
- .start_col = SCROLLREGION_LEFT(state),
- .end_col = SCROLLREGION_RIGHT(state),
- };
-
- scroll(state, rect, -1, 0);
- }
- else if(state->pos.row > 0)
- state->pos.row--;
- break;
-
- case 0x8e: // SS2 - ECMA-48 8.3.141
- state->gsingle_set = 2;
- break;
-
- case 0x8f: // SS3 - ECMA-48 8.3.142
- state->gsingle_set = 3;
- break;
-
- default:
- if(state->fallbacks && state->fallbacks->control)
- if((*state->fallbacks->control)(control, state->fbdata))
- return 1;
-
- return 0;
- }
-
- updatecursor(state, &oldpos, 1);
-
-#ifdef DEBUG
- if(state->pos.row < 0 || state->pos.row >= state->rows ||
- state->pos.col < 0 || state->pos.col >= state->cols) {
- fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
- control, state->pos.row, state->pos.col);
- abort();
- }
-#endif
-
- return 1;
-}
-
-static int settermprop_bool(VTermState *state, VTermProp prop, int v)
-{
- VTermValue val = { .boolean = v };
- return vterm_state_set_termprop(state, prop, &val);
-}
-
-static int settermprop_int(VTermState *state, VTermProp prop, int v)
-{
- VTermValue val = { .number = v };
- return vterm_state_set_termprop(state, prop, &val);
-}
-
-static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
-{
- VTermValue val = { .string = frag };
- return vterm_state_set_termprop(state, prop, &val);
-}
-
-static void savecursor(VTermState *state, int save)
-{
- if(save) {
- state->saved.pos = state->pos;
- state->saved.mode.cursor_visible = state->mode.cursor_visible;
- state->saved.mode.cursor_blink = state->mode.cursor_blink;
- state->saved.mode.cursor_shape = state->mode.cursor_shape;
-
- vterm_state_savepen(state, 1);
- }
- else {
- VTermPos oldpos = state->pos;
-
- state->pos = state->saved.pos;
-
- settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
-
- vterm_state_savepen(state, 0);
-
- updatecursor(state, &oldpos, 1);
- }
-}
-
-static int on_escape(const char *bytes, size_t len, void *user)
-{
- VTermState *state = user;
-
- /* Easier to decode this from the first byte, even though the final
- * byte terminates it
- */
- switch(bytes[0]) {
- case ' ':
- if(len != 2)
- return 0;
-
- switch(bytes[1]) {
- case 'F': // S7C1T
- state->vt->mode.ctrl8bit = 0;
- break;
-
- case 'G': // S8C1T
- state->vt->mode.ctrl8bit = 1;
- break;
-
- default:
- return 0;
- }
- return 2;
-
- case '#':
- if(len != 2)
- return 0;
-
- switch(bytes[1]) {
- case '3': // DECDHL top
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
- break;
-
- case '4': // DECDHL bottom
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
- break;
-
- case '5': // DECSWL
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
- break;
-
- case '6': // DECDWL
- if(state->mode.leftrightmargin)
- break;
- set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
- break;
-
- case '8': // DECALN
- {
- VTermPos pos;
- uint32_t E[] = { 'E', 0 };
- for(pos.row = 0; pos.row < state->rows; pos.row++)
- for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++)
- putglyph(state, E, 1, pos);
- break;
- }
-
- default:
- return 0;
- }
- return 2;
-
- case '(': case ')': case '*': case '+': // SCS
- if(len != 2)
- return 0;
-
- {
- int setnum = bytes[0] - 0x28;
- VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
-
- if(newenc) {
- state->encoding[setnum].enc = newenc;
-
- if(newenc->init)
- (*newenc->init)(newenc, state->encoding[setnum].data);
- }
- }
-
- return 2;
-
- case '7': // DECSC
- savecursor(state, 1);
- return 1;
-
- case '8': // DECRC
- savecursor(state, 0);
- return 1;
-
- case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
- return 1;
-
- case '=': // DECKPAM
- state->mode.keypad = 1;
- return 1;
-
- case '>': // DECKPNM
- state->mode.keypad = 0;
- return 1;
-
- case 'c': // RIS - ECMA-48 8.3.105
- {
- VTermPos oldpos = state->pos;
- vterm_state_reset(state, 1);
- if(state->callbacks && state->callbacks->movecursor)
- (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata);
- return 1;
- }
-
- case 'n': // LS2 - ECMA-48 8.3.78
- state->gl_set = 2;
- return 1;
-
- case 'o': // LS3 - ECMA-48 8.3.80
- state->gl_set = 3;
- return 1;
-
- case '~': // LS1R - ECMA-48 8.3.77
- state->gr_set = 1;
- return 1;
-
- case '}': // LS2R - ECMA-48 8.3.79
- state->gr_set = 2;
- return 1;
-
- case '|': // LS3R - ECMA-48 8.3.81
- state->gr_set = 3;
- return 1;
-
- default:
- return 0;
- }
-}
-
-static void set_mode(VTermState *state, int num, int val)
-{
- switch(num) {
- case 4: // IRM - ECMA-48 7.2.10
- state->mode.insert = val;
- break;
-
- case 20: // LNM - ANSI X3.4-1977
- state->mode.newline = val;
- break;
-
- default:
- DEBUG_LOG("libvterm: Unknown mode %d\n", num);
- return;
- }
-}
-
-static void set_dec_mode(VTermState *state, int num, int val)
-{
- switch(num) {
- case 1:
- state->mode.cursor = val;
- break;
-
- case 5: // DECSCNM - screen mode
- settermprop_bool(state, VTERM_PROP_REVERSE, val);
- break;
-
- case 6: // DECOM - origin mode
- {
- VTermPos oldpos = state->pos;
- state->mode.origin = val;
- state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
- state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
- updatecursor(state, &oldpos, 1);
- }
- break;
-
- case 7:
- state->mode.autowrap = val;
- break;
-
- case 12:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
- break;
-
- case 25:
- settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
- break;
-
- case 69: // DECVSSM - vertical split screen mode
- // DECLRMM - left/right margin mode
- state->mode.leftrightmargin = val;
- if(val) {
- // Setting DECVSSM must clear doublewidth/doubleheight state of every line
- for(int row = 0; row < state->rows; row++)
- set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
- }
-
- break;
-
- case 1000:
- case 1002:
- case 1003:
- settermprop_int(state, VTERM_PROP_MOUSE,
- !val ? VTERM_PROP_MOUSE_NONE :
- (num == 1000) ? VTERM_PROP_MOUSE_CLICK :
- (num == 1002) ? VTERM_PROP_MOUSE_DRAG :
- VTERM_PROP_MOUSE_MOVE);
- break;
-
- case 1004:
- settermprop_bool(state, VTERM_PROP_FOCUSREPORT, val);
- state->mode.report_focus = val;
- break;
-
- case 1005:
- state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
- break;
-
- case 1006:
- state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
- break;
-
- case 1015:
- state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
- break;
-
- case 1047:
- settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
- break;
-
- case 1048:
- savecursor(state, val);
- break;
-
- case 1049:
- settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
- savecursor(state, val);
- break;
-
- case 2004:
- state->mode.bracketpaste = val;
- break;
-
- default:
- DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
- return;
- }
-}
-
-static void request_dec_mode(VTermState *state, int num)
-{
- int reply;
-
- switch(num) {
- case 1:
- reply = state->mode.cursor;
- break;
-
- case 5:
- reply = state->mode.screen;
- break;
-
- case 6:
- reply = state->mode.origin;
- break;
-
- case 7:
- reply = state->mode.autowrap;
- break;
-
- case 12:
- reply = state->mode.cursor_blink;
- break;
-
- case 25:
- reply = state->mode.cursor_visible;
- break;
-
- case 69:
- reply = state->mode.leftrightmargin;
- break;
-
- case 1000:
- reply = state->mouse_flags == MOUSE_WANT_CLICK;
- break;
-
- case 1002:
- reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
- break;
-
- case 1003:
- reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
- break;
-
- case 1004:
- reply = state->mode.report_focus;
- break;
-
- case 1005:
- reply = state->mouse_protocol == MOUSE_UTF8;
- break;
-
- case 1006:
- reply = state->mouse_protocol == MOUSE_SGR;
- break;
-
- case 1015:
- reply = state->mouse_protocol == MOUSE_RXVT;
- break;
-
- case 1047:
- reply = state->mode.alt_screen;
- break;
-
- case 2004:
- reply = state->mode.bracketpaste;
- break;
-
- default:
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
- return;
- }
-
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
-}
-
-static void request_version_string(VTermState *state)
-{
- vterm_push_output_sprintf_str(state->vt, C1_DCS, true, ">|libvterm(%d.%d)",
- VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR);
-}
-
-static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
-{
- VTermState *state = user;
- int leader_byte = 0;
- int intermed_byte = 0;
- int cancel_phantom = 1;
-
- if(leader && leader[0]) {
- if(leader[1]) // longer than 1 char
- return 0;
-
- switch(leader[0]) {
- case '?':
- case '>':
- leader_byte = leader[0];
- break;
- default:
- return 0;
- }
- }
-
- if(intermed && intermed[0]) {
- if(intermed[1]) // longer than 1 char
- return 0;
-
- switch(intermed[0]) {
- case ' ':
- case '!':
- case '"':
- case '$':
- case '\'':
- intermed_byte = intermed[0];
- break;
- default:
- return 0;
- }
- }
-
- VTermPos oldpos = state->pos;
-
- // Some temporaries for later code
- int count, val;
- int row, col;
- VTermRect rect;
- int selective;
-
-#define LBOUND(v,min) if((v) < (min)) (v) = (min)
-#define UBOUND(v,max) if((v) > (max)) (v) = (max)
-
-#define LEADER(l,b) ((l << 8) | b)
-#define INTERMED(i,b) ((i << 16) | b)
-
- switch(intermed_byte << 16 | leader_byte << 8 | command) {
- case 0x40: // ICH - ECMA-48 8.3.64
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col;
- if(state->mode.leftrightmargin)
- rect.end_col = SCROLLREGION_RIGHT(state);
- else
- rect.end_col = THISROWWIDTH(state);
-
- scroll(state, rect, 0, -count);
-
- break;
-
- case 0x41: // CUU - ECMA-48 8.3.22
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row -= count;
- state->at_phantom = 0;
- break;
-
- case 0x42: // CUD - ECMA-48 8.3.19
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row += count;
- state->at_phantom = 0;
- break;
-
- case 0x43: // CUF - ECMA-48 8.3.20
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col += count;
- state->at_phantom = 0;
- break;
-
- case 0x44: // CUB - ECMA-48 8.3.18
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col -= count;
- state->at_phantom = 0;
- break;
-
- case 0x45: // CNL - ECMA-48 8.3.12
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col = 0;
- state->pos.row += count;
- state->at_phantom = 0;
- break;
-
- case 0x46: // CPL - ECMA-48 8.3.13
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col = 0;
- state->pos.row -= count;
- state->at_phantom = 0;
- break;
-
- case 0x47: // CHA - ECMA-48 8.3.9
- val = CSI_ARG_OR(args[0], 1);
- state->pos.col = val-1;
- state->at_phantom = 0;
- break;
-
- case 0x48: // CUP - ECMA-48 8.3.21
- row = CSI_ARG_OR(args[0], 1);
- col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
- // zero-based
- state->pos.row = row-1;
- state->pos.col = col-1;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
- state->at_phantom = 0;
- break;
-
- case 0x49: // CHT - ECMA-48 8.3.10
- count = CSI_ARG_COUNT(args[0]);
- tab(state, count, +1);
- break;
-
- case 0x4a: // ED - ECMA-48 8.3.39
- case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
- selective = (leader_byte == '?');
- switch(CSI_ARG(args[0])) {
- case CSI_ARG_MISSING:
- case 0:
- rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col; rect.end_col = state->cols;
- if(rect.end_col > rect.start_col)
- erase(state, rect, selective);
-
- rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
- rect.start_col = 0;
- for(int row_ = rect.start_row; row_ < rect.end_row; row_++)
- set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
- if(rect.end_row > rect.start_row)
- erase(state, rect, selective);
- break;
-
- case 1:
- rect.start_row = 0; rect.end_row = state->pos.row;
- rect.start_col = 0; rect.end_col = state->cols;
- for(int row_ = rect.start_row; row_ < rect.end_row; row_++)
- set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
- if(rect.end_col > rect.start_col)
- erase(state, rect, selective);
-
- rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
- rect.end_col = state->pos.col + 1;
- if(rect.end_row > rect.start_row)
- erase(state, rect, selective);
- break;
-
- case 2:
- rect.start_row = 0; rect.end_row = state->rows;
- rect.start_col = 0; rect.end_col = state->cols;
- for(int row_ = rect.start_row; row_ < rect.end_row; row_++)
- set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
- erase(state, rect, selective);
- break;
-
- case 3:
- if(state->callbacks && state->callbacks->sb_clear)
- if((*state->callbacks->sb_clear)(state->cbdata))
- return 1;
- break;
- }
- break;
-
- case 0x4b: // EL - ECMA-48 8.3.41
- case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
- selective = (leader_byte == '?');
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
-
- switch(CSI_ARG(args[0])) {
- case CSI_ARG_MISSING:
- case 0:
- rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
- case 1:
- rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
- case 2:
- rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
- default:
- return 0;
- }
-
- if(rect.end_col > rect.start_col)
- erase(state, rect, selective);
-
- break;
-
- case 0x4c: // IL - ECMA-48 8.3.67
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, -count, 0);
-
- break;
-
- case 0x4d: // DL - ECMA-48 8.3.32
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, count, 0);
-
- break;
-
- case 0x50: // DCH - ECMA-48 8.3.26
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col;
- if(state->mode.leftrightmargin)
- rect.end_col = SCROLLREGION_RIGHT(state);
- else
- rect.end_col = THISROWWIDTH(state);
-
- scroll(state, rect, 0, count);
-
- break;
-
- case 0x53: // SU - ECMA-48 8.3.147
- count = CSI_ARG_COUNT(args[0]);
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, count, 0);
-
- break;
-
- case 0x54: // SD - ECMA-48 8.3.113
- count = CSI_ARG_COUNT(args[0]);
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = SCROLLREGION_LEFT(state);
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, -count, 0);
-
- break;
-
- case 0x58: // ECH - ECMA-48 8.3.38
- count = CSI_ARG_COUNT(args[0]);
-
- rect.start_row = state->pos.row;
- rect.end_row = state->pos.row + 1;
- rect.start_col = state->pos.col;
- rect.end_col = state->pos.col + count;
- UBOUND(rect.end_col, THISROWWIDTH(state));
-
- erase(state, rect, 0);
- break;
-
- case 0x5a: // CBT - ECMA-48 8.3.7
- count = CSI_ARG_COUNT(args[0]);
- tab(state, count, -1);
- break;
-
- case 0x60: // HPA - ECMA-48 8.3.57
- col = CSI_ARG_OR(args[0], 1);
- state->pos.col = col-1;
- state->at_phantom = 0;
- break;
-
- case 0x61: // HPR - ECMA-48 8.3.59
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col += count;
- state->at_phantom = 0;
- break;
-
- case 0x62: { // REP - ECMA-48 8.3.103
- const int row_width = THISROWWIDTH(state);
- count = CSI_ARG_COUNT(args[0]);
- col = state->pos.col + count;
- UBOUND(col, row_width);
- while (state->pos.col < col) {
- putglyph(state, state->combine_chars, state->combine_width, state->pos);
- state->pos.col += state->combine_width;
- }
- if (state->pos.col + state->combine_width >= row_width) {
- if (state->mode.autowrap) {
- state->at_phantom = 1;
- cancel_phantom = 0;
- }
- }
- break;
- }
-
- case 0x63: // DA - ECMA-48 8.3.24
- val = CSI_ARG_OR(args[0], 0);
- if(val == 0)
- // DEC VT100 response
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
- break;
-
- case LEADER('>', 0x63): // DEC secondary Device Attributes
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
- break;
-
- case 0x64: // VPA - ECMA-48 8.3.158
- row = CSI_ARG_OR(args[0], 1);
- state->pos.row = row-1;
- if(state->mode.origin)
- state->pos.row += state->scrollregion_top;
- state->at_phantom = 0;
- break;
-
- case 0x65: // VPR - ECMA-48 8.3.160
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row += count;
- state->at_phantom = 0;
- break;
-
- case 0x66: // HVP - ECMA-48 8.3.63
- row = CSI_ARG_OR(args[0], 1);
- col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
- // zero-based
- state->pos.row = row-1;
- state->pos.col = col-1;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
- state->at_phantom = 0;
- break;
-
- case 0x67: // TBC - ECMA-48 8.3.154
- val = CSI_ARG_OR(args[0], 0);
-
- switch(val) {
- case 0:
- clear_col_tabstop(state, state->pos.col);
- break;
- case 3:
- case 5:
- for(col = 0; col < state->cols; col++)
- clear_col_tabstop(state, col);
- break;
- case 1:
- case 2:
- case 4:
- break;
- /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */
- default:
- return 0;
- }
- break;
-
- case 0x68: // SM - ECMA-48 8.3.125
- if(!CSI_ARG_IS_MISSING(args[0]))
- set_mode(state, CSI_ARG(args[0]), 1);
- break;
-
- case LEADER('?', 0x68): // DEC private mode set
- for(int i = 0; i < argcount; i++) {
- if(!CSI_ARG_IS_MISSING(args[i]))
- set_dec_mode(state, CSI_ARG(args[i]), 1);
- }
- break;
-
- case 0x6a: // HPB - ECMA-48 8.3.58
- count = CSI_ARG_COUNT(args[0]);
- state->pos.col -= count;
- state->at_phantom = 0;
- break;
-
- case 0x6b: // VPB - ECMA-48 8.3.159
- count = CSI_ARG_COUNT(args[0]);
- state->pos.row -= count;
- state->at_phantom = 0;
- break;
-
- case 0x6c: // RM - ECMA-48 8.3.106
- if(!CSI_ARG_IS_MISSING(args[0]))
- set_mode(state, CSI_ARG(args[0]), 0);
- break;
-
- case LEADER('?', 0x6c): // DEC private mode reset
- for(int i = 0; i < argcount; i++) {
- if(!CSI_ARG_IS_MISSING(args[i]))
- set_dec_mode(state, CSI_ARG(args[i]), 0);
- }
- break;
-
- case 0x6d: // SGR - ECMA-48 8.3.117
- vterm_state_setpen(state, args, argcount);
- break;
-
- case LEADER('?', 0x6d): // DECSGR
- /* No actual DEC terminal recognised these, but some printers did. These
- * are alternative ways to request subscript/superscript/off
- */
- for(int argi = 0; argi < argcount; argi++) {
- long arg;
- switch(arg = CSI_ARG(args[argi])) {
- case 4: // Superscript on
- arg = 73;
- vterm_state_setpen(state, &arg, 1);
- break;
- case 5: // Subscript on
- arg = 74;
- vterm_state_setpen(state, &arg, 1);
- break;
- case 24: // Super+subscript off
- arg = 75;
- vterm_state_setpen(state, &arg, 1);
- break;
- }
- }
- break;
-
- case 0x6e: // DSR - ECMA-48 8.3.35
- case LEADER('?', 0x6e): // DECDSR
- val = CSI_ARG_OR(args[0], 0);
-
- {
- char *qmark = (leader_byte == '?') ? "?" : "";
-
- switch(val) {
- case 0: case 1: case 2: case 3: case 4:
- // ignore - these are replies
- break;
- case 5:
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
- break;
- case 6: // CPR - cursor position report
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1);
- break;
- }
- }
- break;
-
-
- case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset
- vterm_state_reset(state, 0);
- break;
-
- case LEADER('?', INTERMED('$', 0x70)):
- request_dec_mode(state, CSI_ARG(args[0]));
- break;
-
- case LEADER('>', 0x71): // XTVERSION - xterm query version string
- request_version_string(state);
- break;
-
- case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
- val = CSI_ARG_OR(args[0], 1);
-
- switch(val) {
- case 0: case 1:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
- break;
- case 2:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
- break;
- case 3:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
- break;
- case 4:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
- break;
- case 5:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
- break;
- case 6:
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
- break;
- }
-
- break;
-
- case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
- val = CSI_ARG_OR(args[0], 0);
-
- switch(val) {
- case 0: case 2:
- state->protected_cell = 0;
- break;
- case 1:
- state->protected_cell = 1;
- break;
- }
-
- break;
-
- case 0x72: // DECSTBM - DEC custom
- state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
- state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
- LBOUND(state->scrollregion_top, 0);
- UBOUND(state->scrollregion_top, state->rows);
- LBOUND(state->scrollregion_bottom, -1);
- if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows)
- state->scrollregion_bottom = -1;
- else
- UBOUND(state->scrollregion_bottom, state->rows);
-
- if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
- // Invalid
- state->scrollregion_top = 0;
- state->scrollregion_bottom = -1;
- }
-
- // Setting the scrolling region restores the cursor to the home position
- state->pos.row = 0;
- state->pos.col = 0;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
-
- break;
-
- case 0x73: // DECSLRM - DEC custom
- // Always allow setting these margins, just they won't take effect without DECVSSM
- state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
- state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
- LBOUND(state->scrollregion_left, 0);
- UBOUND(state->scrollregion_left, state->cols);
- LBOUND(state->scrollregion_right, -1);
- if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols)
- state->scrollregion_right = -1;
- else
- UBOUND(state->scrollregion_right, state->cols);
-
- if(state->scrollregion_right > -1 &&
- state->scrollregion_right <= state->scrollregion_left) {
- // Invalid
- state->scrollregion_left = 0;
- state->scrollregion_right = -1;
- }
-
- // Setting the scrolling region restores the cursor to the home position
- state->pos.row = 0;
- state->pos.col = 0;
- if(state->mode.origin) {
- state->pos.row += state->scrollregion_top;
- state->pos.col += SCROLLREGION_LEFT(state);
- }
-
- break;
-
- case INTERMED('\'', 0x7D): // DECIC
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = state->pos.col;
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, 0, -count);
-
- break;
-
- case INTERMED('\'', 0x7E): // DECDC
- count = CSI_ARG_COUNT(args[0]);
-
- if(!is_cursor_in_scrollregion(state))
- break;
-
- rect.start_row = state->scrollregion_top;
- rect.end_row = SCROLLREGION_BOTTOM(state);
- rect.start_col = state->pos.col;
- rect.end_col = SCROLLREGION_RIGHT(state);
-
- scroll(state, rect, 0, count);
-
- break;
-
- default:
- if(state->fallbacks && state->fallbacks->csi)
- if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata))
- return 1;
-
- return 0;
- }
-
- if(state->mode.origin) {
- LBOUND(state->pos.row, state->scrollregion_top);
- UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1);
- LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
- UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1);
- }
- else {
- LBOUND(state->pos.row, 0);
- UBOUND(state->pos.row, state->rows-1);
- LBOUND(state->pos.col, 0);
- UBOUND(state->pos.col, THISROWWIDTH(state)-1);
- }
-
- updatecursor(state, &oldpos, cancel_phantom);
-
-#ifdef DEBUG
- if(state->pos.row < 0 || state->pos.row >= state->rows ||
- state->pos.col < 0 || state->pos.col >= state->cols) {
- fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
- command, state->pos.row, state->pos.col);
- abort();
- }
-
- if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
- fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
- command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
- abort();
- }
-
- if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
- fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
- command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
- abort();
- }
-#endif
-
- return 1;
-}
-
-static char base64_one(uint8_t b)
-{
- if(b < 26)
- return 'A' + b;
- else if(b < 52)
- return 'a' + b - 26;
- else if(b < 62)
- return '0' + b - 52;
- else if(b == 62)
- return '+';
- else if(b == 63)
- return '/';
- return 0;
-}
-
-static uint8_t unbase64one(char c)
-{
- if(c >= 'A' && c <= 'Z')
- return c - 'A';
- else if(c >= 'a' && c <= 'z')
- return c - 'a' + 26;
- else if(c >= '0' && c <= '9')
- return c - '0' + 52;
- else if(c == '+')
- return 62;
- else if(c == '/')
- return 63;
-
- return 0xFF;
-}
-
-static void osc_selection(VTermState *state, VTermStringFragment frag)
-{
- if(frag.initial) {
- state->tmp.selection.mask = 0;
- state->tmp.selection.state = SELECTION_INITIAL;
- }
-
- while(!state->tmp.selection.state && frag.len) {
- /* Parse selection parameter */
- switch(frag.str[0]) {
- case 'c':
- state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD;
- break;
- case 'p':
- state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY;
- break;
- case 'q':
- state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY;
- break;
- case 's':
- state->tmp.selection.mask |= VTERM_SELECTION_SELECT;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
- break;
-
- case ';':
- state->tmp.selection.state = SELECTION_SELECTED;
- if(!state->tmp.selection.mask)
- state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0;
- break;
- }
-
- frag.str++;
- frag.len--;
- }
-
- if(!frag.len) {
- /* Clear selection if we're already finished but didn't do anything */
- if(frag.final && state->selection.callbacks->set) {
- (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
- .str = NULL,
- .len = 0,
- .initial = state->tmp.selection.state != SELECTION_SET,
- .final = true,
- }, state->selection.user);
- }
- return;
- }
-
- if(state->tmp.selection.state == SELECTION_SELECTED) {
- if(frag.str[0] == '?') {
- state->tmp.selection.state = SELECTION_QUERY;
- }
- else {
- state->tmp.selection.state = SELECTION_SET_INITIAL;
- state->tmp.selection.recvpartial = 0;
- }
- }
-
- if(state->tmp.selection.state == SELECTION_QUERY) {
- if(state->selection.callbacks->query)
- (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
- return;
- }
-
- if(state->tmp.selection.state == SELECTION_INVALID)
- return;
-
- if(state->selection.callbacks->set) {
- size_t bufcur = 0;
- char *buffer = state->selection.buffer;
-
- uint32_t x = 0; /* Current decoding value */
- int n = 0; /* Number of sextets consumed */
-
- if(state->tmp.selection.recvpartial) {
- n = state->tmp.selection.recvpartial >> 24;
- x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */
-
- state->tmp.selection.recvpartial = 0;
- }
-
- while((state->selection.buflen - bufcur) >= 3 && frag.len) {
- if(frag.str[0] == '=') {
- if(n == 2) {
- buffer[0] = (x >> 4) & 0xFF;
- buffer += 1, bufcur += 1;
- }
- if(n == 3) {
- buffer[0] = (x >> 10) & 0xFF;
- buffer[1] = (x >> 2) & 0xFF;
- buffer += 2, bufcur += 2;
- }
-
- while(frag.len && frag.str[0] == '=')
- frag.str++, frag.len--;
-
- n = 0;
- }
- else {
- uint8_t b = unbase64one(frag.str[0]);
- if(b == 0xFF) {
- DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
-
- state->tmp.selection.state = SELECTION_INVALID;
- if(state->selection.callbacks->set) {
- (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
- .str = NULL,
- .len = 0,
- .initial = true,
- .final = true,
- }, state->selection.user);
- }
- break;
- }
-
- x = (x << 6) | b;
- n++;
- frag.str++, frag.len--;
-
- if(n == 4) {
- buffer[0] = (x >> 16) & 0xFF;
- buffer[1] = (x >> 8) & 0xFF;
- buffer[2] = (x >> 0) & 0xFF;
-
- buffer += 3, bufcur += 3;
- x = 0;
- n = 0;
- }
- }
-
- if(!frag.len || (state->selection.buflen - bufcur) < 3) {
- if(bufcur) {
- (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
- .str = state->selection.buffer,
- .len = bufcur,
- .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
- .final = frag.final && !frag.len,
- }, state->selection.user);
- state->tmp.selection.state = SELECTION_SET;
- }
-
- buffer = state->selection.buffer;
- bufcur = 0;
- }
- }
-
- if(n)
- state->tmp.selection.recvpartial = (n << 24) | x;
- }
-}
-
-static int on_osc(int command, VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- switch(command) {
- case 0:
- settermprop_string(state, VTERM_PROP_ICONNAME, frag);
- settermprop_string(state, VTERM_PROP_TITLE, frag);
- return 1;
-
- case 1:
- settermprop_string(state, VTERM_PROP_ICONNAME, frag);
- return 1;
-
- case 2:
- settermprop_string(state, VTERM_PROP_TITLE, frag);
- return 1;
-
- case 52:
- if(state->selection.callbacks)
- osc_selection(state, frag);
-
- return 1;
-
- default:
- if(state->fallbacks && state->fallbacks->osc)
- if((*state->fallbacks->osc)(command, frag, state->fbdata))
- return 1;
- }
-
- return 0;
-}
-
-static void request_status_string(VTermState *state, VTermStringFragment frag)
-{
- VTerm *vt = state->vt;
-
- char *tmp = state->tmp.decrqss;
-
- if(frag.initial)
- tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
-
- int i = 0;
- while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
- i++;
- while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
- tmp[i++] = (frag.str++)[0];
- tmp[i] = 0;
-
- if(!frag.final)
- return;
-
- switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
- case 'm': {
- // Query SGR
- long args[20];
- int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
- size_t cur = 0;
-
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
- if(cur >= vt->tmpbuffer_len)
- return;
-
- for(int argi = 0; argi < argc; argi++) {
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- argi == argc - 1 ? "%ld" :
- CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
- "%ld;",
- CSI_ARG(args[argi]));
- if(cur >= vt->tmpbuffer_len)
- return;
- }
-
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
- if(cur >= vt->tmpbuffer_len)
- return;
-
- vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
- return;
- }
-
- case 'r':
- // Query DECSTBM
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
- return;
-
- case 's':
- // Query DECSLRM
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
- return;
-
- case ' '|('q'<<8): {
- // Query DECSCUSR
- int reply;
- switch(state->mode.cursor_shape) {
- case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
- case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break;
- case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break;
- }
- if(state->mode.cursor_blink)
- reply--;
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d q", reply);
- return;
- }
-
- case '\"'|('q'<<8):
- // Query DECSCA
- vterm_push_output_sprintf_str(vt, C1_DCS, true,
- "1$r%d\"q", state->protected_cell ? 1 : 2);
- return;
- }
-
- vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r");
-}
-
-static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(commandlen == 2 && strneq(command, "$q", 2)) {
- request_status_string(state, frag);
- return 1;
- }
- else if(state->fallbacks && state->fallbacks->dcs)
- if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
- return 1;
-
- DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
- return 0;
-}
-
-static int on_apc(VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(state->fallbacks && state->fallbacks->apc)
- if((*state->fallbacks->apc)(frag, state->fbdata))
- return 1;
-
- /* No DEBUG_LOG because all APCs are unhandled */
- return 0;
-}
-
-static int on_pm(VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(state->fallbacks && state->fallbacks->pm)
- if((*state->fallbacks->pm)(frag, state->fbdata))
- return 1;
-
- /* No DEBUG_LOG because all PMs are unhandled */
- return 0;
-}
-
-static int on_sos(VTermStringFragment frag, void *user)
-{
- VTermState *state = user;
-
- if(state->fallbacks && state->fallbacks->sos)
- if((*state->fallbacks->sos)(frag, state->fbdata))
- return 1;
-
- /* No DEBUG_LOG because all SOSs are unhandled */
- return 0;
-}
-
-static int on_resize(int rows, int cols, void *user)
-{
- VTermState *state = user;
- VTermPos oldpos = state->pos;
-
- if(cols != state->cols) {
- unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
-
- /* TODO: This can all be done much more efficiently bytewise */
- int col;
- for(col = 0; col < state->cols && col < cols; col++) {
- unsigned char mask = 1 << (col & 7);
- if(state->tabstops[col >> 3] & mask)
- newtabstops[col >> 3] |= mask;
- else
- newtabstops[col >> 3] &= ~mask;
- }
-
- for( ; col < cols; col++) {
- unsigned char mask = 1 << (col & 7);
- if(col % 8 == 0)
- newtabstops[col >> 3] |= mask;
- else
- newtabstops[col >> 3] &= ~mask;
- }
-
- vterm_allocator_free(state->vt, state->tabstops);
- state->tabstops = newtabstops;
- }
-
- state->rows = rows;
- state->cols = cols;
-
- if(state->scrollregion_bottom > -1)
- UBOUND(state->scrollregion_bottom, state->rows);
- if(state->scrollregion_right > -1)
- UBOUND(state->scrollregion_right, state->cols);
-
- VTermStateFields fields = {
- .pos = state->pos,
- .lineinfos = { [0] = state->lineinfos[0], [1] = state->lineinfos[1] },
- };
-
- if(state->callbacks && state->callbacks->resize) {
- (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
- state->pos = fields.pos;
-
- state->lineinfos[0] = fields.lineinfos[0];
- state->lineinfos[1] = fields.lineinfos[1];
- }
- else {
- if(rows != state->rows) {
- for(int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
- VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
- if(!oldlineinfo)
- continue;
-
- VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
-
- int row;
- for(row = 0; row < state->rows && row < rows; row++) {
- newlineinfo[row] = oldlineinfo[row];
- }
-
- for( ; row < rows; row++) {
- newlineinfo[row] = (VTermLineInfo){
- .doublewidth = 0,
- };
- }
-
- vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
- state->lineinfos[bufidx] = newlineinfo;
- }
- }
- }
-
- state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
-
- if(state->at_phantom && state->pos.col < cols-1) {
- state->at_phantom = 0;
- state->pos.col++;
- }
-
- if(state->pos.row < 0)
- state->pos.row = 0;
- if(state->pos.row >= rows)
- state->pos.row = rows - 1;
- if(state->pos.col < 0)
- state->pos.col = 0;
- if(state->pos.col >= cols)
- state->pos.col = cols - 1;
-
- updatecursor(state, &oldpos, 1);
-
- return 1;
-}
-
-static const VTermParserCallbacks parser_callbacks = {
- .text = on_text,
- .control = on_control,
- .escape = on_escape,
- .csi = on_csi,
- .osc = on_osc,
- .dcs = on_dcs,
- .apc = on_apc,
- .pm = on_pm,
- .sos = on_sos,
- .resize = on_resize,
-};
-
-VTermState *vterm_obtain_state(VTerm *vt)
-{
- if(vt->state)
- return vt->state;
-
- VTermState *state = vterm_state_new(vt);
- vt->state = state;
-
- vterm_parser_set_callbacks(vt, &parser_callbacks, state);
-
- return state;
-}
-
-void vterm_state_reset(VTermState *state, int hard)
-{
- state->scrollregion_top = 0;
- state->scrollregion_bottom = -1;
- state->scrollregion_left = 0;
- state->scrollregion_right = -1;
-
- state->mode.keypad = 0;
- state->mode.cursor = 0;
- state->mode.autowrap = 1;
- state->mode.insert = 0;
- state->mode.newline = 0;
- state->mode.alt_screen = 0;
- state->mode.origin = 0;
- state->mode.leftrightmargin = 0;
- state->mode.bracketpaste = 0;
- state->mode.report_focus = 0;
-
- state->mouse_flags = 0;
-
- state->vt->mode.ctrl8bit = 0;
-
- for(int col = 0; col < state->cols; col++)
- if(col % 8 == 0)
- set_col_tabstop(state, col);
- else
- clear_col_tabstop(state, col);
-
- for(int row = 0; row < state->rows; row++)
- set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
-
- if(state->callbacks && state->callbacks->initpen)
- (*state->callbacks->initpen)(state->cbdata);
-
- vterm_state_resetpen(state);
-
- VTermEncoding *default_enc = state->vt->mode.utf8 ?
- vterm_lookup_encoding(ENC_UTF8, 'u') :
- vterm_lookup_encoding(ENC_SINGLE_94, 'B');
-
- for(int i = 0; i < 4; i++) {
- state->encoding[i].enc = default_enc;
- if(default_enc->init)
- (*default_enc->init)(default_enc, state->encoding[i].data);
- }
-
- state->gl_set = 0;
- state->gr_set = 1;
- state->gsingle_set = 0;
-
- state->protected_cell = 0;
-
- // Initialise the props
- settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
- settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
- settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
-
- if(hard) {
- state->pos.row = 0;
- state->pos.col = 0;
- state->at_phantom = 0;
-
- VTermRect rect = { 0, state->rows, 0, state->cols };
- erase(state, rect, 0);
- }
-}
-
-void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
-{
- *cursorpos = state->pos;
-}
-
-void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
-{
- if(callbacks) {
- state->callbacks = callbacks;
- state->cbdata = user;
-
- if(state->callbacks && state->callbacks->initpen)
- (*state->callbacks->initpen)(state->cbdata);
- }
- else {
- state->callbacks = NULL;
- state->cbdata = NULL;
- }
-}
-
-void *vterm_state_get_cbdata(VTermState *state)
-{
- return state->cbdata;
-}
-
-void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
-{
- if(fallbacks) {
- state->fallbacks = fallbacks;
- state->fbdata = user;
- }
- else {
- state->fallbacks = NULL;
- state->fbdata = NULL;
- }
-}
-
-void *vterm_state_get_unrecognised_fbdata(VTermState *state)
-{
- return state->fbdata;
-}
-
-int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
-{
- /* Only store the new value of the property if usercode said it was happy.
- * This is especially important for altscreen switching */
- if(state->callbacks && state->callbacks->settermprop)
- if(!(*state->callbacks->settermprop)(prop, val, state->cbdata))
- return 0;
-
- switch(prop) {
- case VTERM_PROP_TITLE:
- case VTERM_PROP_ICONNAME:
- // we don't store these, just transparently pass through
- return 1;
- case VTERM_PROP_CURSORVISIBLE:
- state->mode.cursor_visible = val->boolean;
- return 1;
- case VTERM_PROP_CURSORBLINK:
- state->mode.cursor_blink = val->boolean;
- return 1;
- case VTERM_PROP_CURSORSHAPE:
- state->mode.cursor_shape = val->number;
- return 1;
- case VTERM_PROP_REVERSE:
- state->mode.screen = val->boolean;
- return 1;
- case VTERM_PROP_ALTSCREEN:
- state->mode.alt_screen = val->boolean;
- state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
- if(state->mode.alt_screen) {
- VTermRect rect = {
- .start_row = 0,
- .start_col = 0,
- .end_row = state->rows,
- .end_col = state->cols,
- };
- erase(state, rect, 0);
- }
- return 1;
- case VTERM_PROP_MOUSE:
- state->mouse_flags = 0;
- if(val->number)
- state->mouse_flags |= MOUSE_WANT_CLICK;
- if(val->number == VTERM_PROP_MOUSE_DRAG)
- state->mouse_flags |= MOUSE_WANT_DRAG;
- if(val->number == VTERM_PROP_MOUSE_MOVE)
- state->mouse_flags |= MOUSE_WANT_MOVE;
- return 1;
- case VTERM_PROP_FOCUSREPORT:
- state->mode.report_focus = val->boolean;
- return 1;
-
- case VTERM_N_PROPS:
- return 0;
- }
-
- return 0;
-}
-
-void vterm_state_focus_in(VTermState *state)
-{
- if(state->mode.report_focus)
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
-}
-
-void vterm_state_focus_out(VTermState *state)
-{
- if(state->mode.report_focus)
- vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
-}
-
-const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
-{
- return state->lineinfo + row;
-}
-
-void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
- char *buffer, size_t buflen)
-{
- if(buflen && !buffer)
- buffer = vterm_allocator_malloc(state->vt, buflen);
-
- state->selection.callbacks = callbacks;
- state->selection.user = user;
- state->selection.buffer = buffer;
- state->selection.buflen = buflen;
-}
-
-void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag)
-{
- VTerm *vt = state->vt;
-
- if(frag.initial) {
- /* TODO: support sending more than one mask bit */
- static const char selection_chars[] = "cpqs";
- int idx;
- for(idx = 0; idx < 4; idx++)
- if(mask & (1 << idx))
- break;
-
- vterm_push_output_sprintf_str(vt, C1_OSC, false, "52;%c;", selection_chars[idx]);
-
- state->tmp.selection.sendpartial = 0;
- }
-
- if(frag.len) {
- size_t bufcur = 0;
- char *buffer = state->selection.buffer;
-
- uint32_t x = 0;
- int n = 0;
-
- if(state->tmp.selection.sendpartial) {
- n = state->tmp.selection.sendpartial >> 24;
- x = state->tmp.selection.sendpartial & 0xFFFFFF;
-
- state->tmp.selection.sendpartial = 0;
- }
-
- while((state->selection.buflen - bufcur) >= 4 && frag.len) {
- x = (x << 8) | frag.str[0];
- n++;
- frag.str++, frag.len--;
-
- if(n == 3) {
- buffer[0] = base64_one((x >> 18) & 0x3F);
- buffer[1] = base64_one((x >> 12) & 0x3F);
- buffer[2] = base64_one((x >> 6) & 0x3F);
- buffer[3] = base64_one((x >> 0) & 0x3F);
-
- buffer += 4, bufcur += 4;
- x = 0;
- n = 0;
- }
-
- if(!frag.len || (state->selection.buflen - bufcur) < 4) {
- if(bufcur)
- vterm_push_output_bytes(vt, state->selection.buffer, bufcur);
-
- buffer = state->selection.buffer;
- bufcur = 0;
- }
- }
-
- if(n)
- state->tmp.selection.sendpartial = (n << 24) | x;
- }
-
- if(frag.final) {
- if(state->tmp.selection.sendpartial) {
- int n = state->tmp.selection.sendpartial >> 24;
- uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF;
- char *buffer = state->selection.buffer;
-
- /* n is either 1 or 2 now */
- x <<= (n == 1) ? 16 : 8;
-
- buffer[0] = base64_one((x >> 18) & 0x3F);
- buffer[1] = base64_one((x >> 12) & 0x3F);
- buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F);
- buffer[3] = '=';
-
- vterm_push_output_sprintf_str(vt, 0, true, "%.*s", 4, buffer);
- }
- else
- vterm_push_output_sprintf_str(vt, 0, true, "");
- }
-}
diff --git a/src/vterm/unicode.c b/src/vterm/unicode.c
deleted file mode 100644
index 67a155c19f..0000000000
--- a/src/vterm/unicode.c
+++ /dev/null
@@ -1,313 +0,0 @@
-#include "vterm_internal.h"
-
-// ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
-// With modifications:
-// made functions static
-// moved 'combining' table to file scope, so other functions can see it
-// ###################################################################
-
-/*
- * This is an implementation of wcwidth() and wcswidth() (defined in
- * IEEE Std 1002.1-2001) for Unicode.
- *
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
- *
- * In fixed-width output devices, Latin characters all occupy a single
- * "cell" position of equal width, whereas ideographic CJK characters
- * occupy two such cells. Interoperability between terminal-line
- * applications and (teletype-style) character terminals using the
- * UTF-8 encoding requires agreement on which character should advance
- * the cursor by how many cell positions. No established formal
- * standards exist at present on which Unicode character shall occupy
- * how many cell positions on character terminals. These routines are
- * a first attempt of defining such behavior based on simple rules
- * applied to data provided by the Unicode Consortium.
- *
- * For some graphical characters, the Unicode standard explicitly
- * defines a character-cell width via the definition of the East Asian
- * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
- * In all these cases, there is no ambiguity about which width a
- * terminal shall use. For characters in the East Asian Ambiguous (A)
- * class, the width choice depends purely on a preference of backward
- * compatibility with either historic CJK or Western practice.
- * Choosing single-width for these characters is easy to justify as
- * the appropriate long-term solution, as the CJK practice of
- * displaying these characters as double-width comes from historic
- * implementation simplicity (8-bit encoded characters were displayed
- * single-width and 16-bit ones double-width, even for Greek,
- * Cyrillic, etc.) and not any typographic considerations.
- *
- * Much less clear is the choice of width for the Not East Asian
- * (Neutral) class. Existing practice does not dictate a width for any
- * of these characters. It would nevertheless make sense
- * typographically to allocate two character cells to characters such
- * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
- * represented adequately with a single-width glyph. The following
- * routines at present merely assign a single-cell width to all
- * neutral characters, in the interest of simplicity. This is not
- * entirely satisfactory and should be reconsidered before
- * establishing a formal standard in this area. At the moment, the
- * decision which Not East Asian (Neutral) characters should be
- * represented by double-width glyphs cannot yet be answered by
- * applying a simple rule from the Unicode database content. Setting
- * up a proper standard for the behavior of UTF-8 character terminals
- * will require a careful analysis not only of each Unicode character,
- * but also of each presentation form, something the author of these
- * routines has avoided to do so far.
- *
- * http://www.unicode.org/unicode/reports/tr11/
- *
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- *
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-
-struct interval {
- int first;
- int last;
-};
-
-/* sorted list of non-overlapping intervals of non-spacing characters */
-/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
-static const struct interval combining[] = {
- { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
- { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
- { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
- { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
- { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
- { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
- { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
- { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
- { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
- { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
- { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
- { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
- { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
- { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
- { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
- { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
- { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
- { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
- { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
- { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
- { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
- { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
- { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
- { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
- { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
- { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
- { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
- { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
- { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
- { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
- { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
- { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
- { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
- { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
- { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
- { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
- { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
- { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
- { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
- { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
- { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
- { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
- { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
- { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
- { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
- { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
- { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
- { 0xE0100, 0xE01EF }
-};
-
-
-/* auxiliary function for binary search in interval table */
-static int bisearch(uint32_t ucs, const struct interval *table, int max) {
- int min = 0;
- int mid;
-
- if (ucs < (uint32_t) table[0].first || ucs > (uint32_t) table[max].last)
- return 0;
- while (max >= min) {
- mid = (min + max) / 2;
- if (ucs > (uint32_t) table[mid].last)
- min = mid + 1;
- else if (ucs < (uint32_t) table[mid].first)
- max = mid - 1;
- else
- return 1;
- }
-
- return 0;
-}
-
-
-/* The following two functions define the column width of an ISO 10646
- * character as follows:
- *
- * - The null character (U+0000) has a column width of 0.
- *
- * - Other C0/C1 control characters and DEL will lead to a return
- * value of -1.
- *
- * - Non-spacing and enclosing combining characters (general
- * category code Mn or Me in the Unicode database) have a
- * column width of 0.
- *
- * - SOFT HYPHEN (U+00AD) has a column width of 1.
- *
- * - Other format characters (general category code Cf in the Unicode
- * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
- *
- * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
- * have a column width of 0.
- *
- * - Spacing characters in the East Asian Wide (W) or East Asian
- * Full-width (F) category as defined in Unicode Technical
- * Report #11 have a column width of 2.
- *
- * - All remaining characters (including all printable
- * ISO 8859-1 and WGL4 characters, Unicode control characters,
- * etc.) have a column width of 1.
- *
- * This implementation assumes that uint32_t characters are encoded
- * in ISO 10646.
- */
-
-
-static int mk_wcwidth(uint32_t ucs)
-{
- /* test for 8-bit control characters */
- if (ucs == 0)
- return 0;
- if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
- return -1;
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, combining,
- sizeof(combining) / sizeof(struct interval) - 1))
- return 0;
-
- /* if we arrive here, ucs is not a combining or C0/C1 control character */
-
- return 1 +
- (ucs >= 0x1100 &&
- (ucs <= 0x115f || /* Hangul Jamo init. consonants */
- ucs == 0x2329 || ucs == 0x232a ||
- (ucs >= 0x2e80 && ucs <= 0xa4cf &&
- ucs != 0x303f) || /* CJK ... Yi */
- (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
- (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
- (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
- (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
- (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
- (ucs >= 0xffe0 && ucs <= 0xffe6) ||
- (ucs >= 0x20000 && ucs <= 0x2fffd) ||
- (ucs >= 0x30000 && ucs <= 0x3fffd)));
-}
-
-
-#ifdef USE_MK_WCWIDTH_CJK
-
-/*
- * The following functions are the same as mk_wcwidth() and
- * mk_wcswidth(), except that spacing characters in the East Asian
- * Ambiguous (A) category as defined in Unicode Technical Report #11
- * have a column width of 2. This variant might be useful for users of
- * CJK legacy encodings who want to migrate to UCS without changing
- * the traditional terminal character-width behaviour. It is not
- * otherwise recommended for general use.
- */
-static int mk_wcwidth_cjk(uint32_t ucs)
-{
- /* sorted list of non-overlapping intervals of East Asian Ambiguous
- * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
- static const struct interval ambiguous[] = {
- { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
- { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
- { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
- { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
- { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
- { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
- { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
- { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
- { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
- { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
- { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
- { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
- { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
- { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
- { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
- { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
- { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
- { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
- { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
- { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
- { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
- { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
- { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
- { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
- { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
- { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
- { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
- { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
- { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
- { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
- { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
- { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
- { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
- { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
- { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
- { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
- { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
- { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
- { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
- { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
- { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
- { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
- { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
- { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
- { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
- { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
- { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
- { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
- { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
- { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
- { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
- { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
- };
-
- /* binary search in table of non-spacing characters */
- if (bisearch(ucs, ambiguous,
- sizeof(ambiguous) / sizeof(struct interval) - 1))
- return 2;
-
- return mk_wcwidth(ucs);
-}
-
-#endif
-
-// ################################
-// ### The rest added by Paul Evans
-
-static const struct interval fullwidth[] = {
-#include "fullwidth.inc"
-};
-
-INTERNAL int vterm_unicode_width(uint32_t codepoint)
-{
- if(bisearch(codepoint, fullwidth, sizeof(fullwidth) / sizeof(fullwidth[0]) - 1))
- return 2;
-
- return mk_wcwidth(codepoint);
-}
-
-INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
-{
- return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
-}
diff --git a/src/vterm/vterm.c b/src/vterm/vterm.c
deleted file mode 100644
index e8c87929e2..0000000000
--- a/src/vterm/vterm.c
+++ /dev/null
@@ -1,938 +0,0 @@
-#include "vterm_internal.h"
-
-#include "auto/config.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/*****************
- * API functions *
- *****************/
-
-static void *default_malloc(size_t size, void *allocdata)
-{
- void *ptr = malloc(size);
- if(ptr)
- memset(ptr, 0, size);
- return ptr;
-}
-
-static void default_free(void *ptr, void *allocdata)
-{
- free(ptr);
-}
-
-static VTermAllocatorFunctions default_allocator = {
- .malloc = &default_malloc,
- .free = &default_free,
-};
-
-VTerm *vterm_new(int rows, int cols)
-{
- return vterm_build(&(const struct VTermBuilder){
- .rows = rows,
- .cols = cols,
- });
-}
-
-VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
-{
- return vterm_build(&(const struct VTermBuilder){
- .rows = rows,
- .cols = cols,
- .allocator = funcs,
- .allocdata = allocdata,
- });
-}
-
-/* A handy macro for defaulting values out of builder fields */
-#define DEFAULT(v, def) ((v) ? (v) : (def))
-
-VTerm *vterm_build(const struct VTermBuilder *builder)
-{
- const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
-
- /* Need to bootstrap using the allocator function directly */
- VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
-
- vt->allocator = allocator;
- vt->allocdata = builder->allocdata;
-
- vt->rows = builder->rows;
- vt->cols = builder->cols;
-
- vt->parser.state = NORMAL;
-
- vt->parser.callbacks = NULL;
- vt->parser.cbdata = NULL;
-
- vt->parser.emit_nul = false;
-
- vt->outfunc = NULL;
- vt->outdata = NULL;
-
- vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
- vt->outbuffer_cur = 0;
- vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
-
- vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
- vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
-
- return vt;
-}
-
-void vterm_free(VTerm *vt)
-{
- if(vt->screen)
- vterm_screen_free(vt->screen);
-
- if(vt->state)
- vterm_state_free(vt->state);
-
- vterm_allocator_free(vt, vt->outbuffer);
- vterm_allocator_free(vt, vt->tmpbuffer);
-
- vterm_allocator_free(vt, vt);
-}
-
-INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
-{
- return (*vt->allocator->malloc)(size, vt->allocdata);
-}
-
-INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
-{
- (*vt->allocator->free)(ptr, vt->allocdata);
-}
-
-void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
-{
- if(rowsp)
- *rowsp = vt->rows;
- if(colsp)
- *colsp = vt->cols;
-}
-
-void vterm_set_size(VTerm *vt, int rows, int cols)
-{
- if(rows < 1 || cols < 1)
- return;
-
- vt->rows = rows;
- vt->cols = cols;
-
- if(vt->parser.callbacks && vt->parser.callbacks->resize)
- (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
-}
-
-int vterm_get_utf8(const VTerm *vt)
-{
- return vt->mode.utf8;
-}
-
-void vterm_set_utf8(VTerm *vt, int is_utf8)
-{
- vt->mode.utf8 = is_utf8;
-}
-
-void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
-{
- vt->outfunc = func;
- vt->outdata = user;
-}
-
-INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
-{
- if(vt->outfunc) {
- (vt->outfunc)(bytes, len, vt->outdata);
- return;
- }
-
- if(len > vt->outbuffer_len - vt->outbuffer_cur)
- return;
-
- memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
- vt->outbuffer_cur += len;
-}
-
-INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
-{
- size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len,
- format, args);
-
- vterm_push_output_bytes(vt, vt->tmpbuffer, len);
-}
-
-INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- vterm_push_output_vsprintf(vt, format, args);
- va_end(args);
-}
-
-INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
-{
- size_t cur;
-
- if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
- cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
- ESC_S "%c", ctrl - 0x40);
- else
- cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
- "%c", ctrl);
-
- if(cur >= vt->tmpbuffer_len)
- return;
-
- va_list args;
- va_start(args, fmt);
- cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- fmt, args);
- va_end(args);
-
- if(cur >= vt->tmpbuffer_len)
- return;
-
- vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
-}
-
-INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...)
-{
- size_t cur = 0;
-
- if(ctrl) {
- if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
- cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
- ESC_S "%c", ctrl - 0x40);
- else
- cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
- "%c", ctrl);
-
- if(cur >= vt->tmpbuffer_len)
- return;
- }
-
- va_list args;
- va_start(args, fmt);
- cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- fmt, args);
- va_end(args);
-
- if(cur >= vt->tmpbuffer_len)
- return;
-
- if(term) {
- cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
- vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
-
- if(cur >= vt->tmpbuffer_len)
- return;
- }
-
- vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
-}
-
-size_t vterm_output_get_buffer_size(const VTerm *vt)
-{
- return vt->outbuffer_len;
-}
-
-size_t vterm_output_get_buffer_current(const VTerm *vt)
-{
- return vt->outbuffer_cur;
-}
-
-size_t vterm_output_get_buffer_remaining(const VTerm *vt)
-{
- return vt->outbuffer_len - vt->outbuffer_cur;
-}
-
-size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
-{
- if(len > vt->outbuffer_cur)
- len = vt->outbuffer_cur;
-
- memcpy(buffer, vt->outbuffer, len);
-
- if(len < vt->outbuffer_cur)
- memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
-
- vt->outbuffer_cur -= len;
-
- return len;
-}
-
-VTermValueType vterm_get_attr_type(VTermAttr attr)
-{
- switch(attr) {
- case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
- case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
- case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
- case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
- case VTERM_ATTR_SMALL: return VTERM_VALUETYPE_BOOL;
- case VTERM_ATTR_BASELINE: return VTERM_VALUETYPE_INT;
- case VTERM_ATTR_URI: return VTERM_VALUETYPE_INT;
-
- case VTERM_N_ATTRS: return 0;
- }
- return 0; /* UNREACHABLE */
-}
-
-VTermValueType vterm_get_prop_type(VTermProp prop)
-{
- switch(prop) {
- case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
- case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
- case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
- case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
- case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
- case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
- case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
- case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
- case VTERM_PROP_FOCUSREPORT: return VTERM_VALUETYPE_BOOL;
-
- case VTERM_N_PROPS: return 0;
- }
- return 0; /* UNREACHABLE */
-}
-
-void vterm_scroll_rect(VTermRect rect,
- int downward,
- int rightward,
- int (*moverect)(VTermRect src, VTermRect dest, void *user),
- int (*eraserect)(VTermRect rect, int selective, void *user),
- void *user)
-{
- VTermRect src;
- VTermRect dest;
-
- if(abs(downward) >= rect.end_row - rect.start_row ||
- abs(rightward) >= rect.end_col - rect.start_col) {
- /* Scroll more than area; just erase the lot */
- (*eraserect)(rect, 0, user);
- return;
- }
-
- if(rightward >= 0) {
- /* rect: [XXX................]
- * src: [----------------]
- * dest: [----------------]
- */
- dest.start_col = rect.start_col;
- dest.end_col = rect.end_col - rightward;
- src.start_col = rect.start_col + rightward;
- src.end_col = rect.end_col;
- }
- else {
- /* rect: [................XXX]
- * src: [----------------]
- * dest: [----------------]
- */
- int leftward = -rightward;
- dest.start_col = rect.start_col + leftward;
- dest.end_col = rect.end_col;
- src.start_col = rect.start_col;
- src.end_col = rect.end_col - leftward;
- }
-
- if(downward >= 0) {
- dest.start_row = rect.start_row;
- dest.end_row = rect.end_row - downward;
- src.start_row = rect.start_row + downward;
- src.end_row = rect.end_row;
- }
- else {
- int upward = -downward;
- dest.start_row = rect.start_row + upward;
- dest.end_row = rect.end_row;
- src.start_row = rect.start_row;
- src.end_row = rect.end_row - upward;
- }
-
- if(moverect)
- (*moverect)(dest, src, user);
-
- if(downward > 0)
- rect.start_row = rect.end_row - downward;
- else if(downward < 0)
- rect.end_row = rect.start_row - downward;
-
- if(rightward > 0)
- rect.start_col = rect.end_col - rightward;
- else if(rightward < 0)
- rect.end_col = rect.start_col - rightward;
-
- (*eraserect)(rect, 0, user);
-}
-
-void vterm_copy_cells(VTermRect dest,
- VTermRect src,
- void (*copycell)(VTermPos dest, VTermPos src, void *user),
- void *user)
-{
- int downward = src.start_row - dest.start_row;
- int rightward = src.start_col - dest.start_col;
-
- int init_row, test_row, init_col, test_col;
- int inc_row, inc_col;
-
- if(downward < 0) {
- init_row = dest.end_row - 1;
- test_row = dest.start_row - 1;
- inc_row = -1;
- }
- else /* downward >= 0 */ {
- init_row = dest.start_row;
- test_row = dest.end_row;
- inc_row = +1;
- }
-
- if(rightward < 0) {
- init_col = dest.end_col - 1;
- test_col = dest.start_col - 1;
- inc_col = -1;
- }
- else /* rightward >= 0 */ {
- init_col = dest.start_col;
- test_col = dest.end_col;
- inc_col = +1;
- }
-
- VTermPos pos;
- for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
- for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
- VTermPos srcpos = { pos.row + downward, pos.col + rightward };
- (*copycell)(pos, srcpos, user);
- }
-}
-
-void vterm_check_version(int major, int minor)
-{
- if(major != VTERM_VERSION_MAJOR) {
- fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n",
- major, VTERM_VERSION_MAJOR);
- exit(1);
- }
-
- if(minor > VTERM_VERSION_MINOR) {
- fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n",
- minor, VTERM_VERSION_MINOR);
- exit(1);
- }
-
- // Happy
-}
-
-// For unit tests.
-#ifndef NDEBUG
-
-int parser_text(const char bytes[], size_t len, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "text ");
- int i;
- for(i = 0; i < len; i++) {
- unsigned char b = bytes[i];
- if(b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) {
- break;
- }
- fprintf(f, i ? ",%x" : "%x", b);
- }
- fprintf(f, "\n");
- fclose(f);
-
- return i;
-}
-
-static void printchars(const char *s, size_t len, FILE *f)
-{
- while(len--) {
- fprintf(f, "%c", (s++)[0]);
- }
-}
-
-int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "csi %02x", command);
-
- if(leader && leader[0]) {
- fprintf(f, " L=");
- for(int i = 0; leader[i]; i++) {
- fprintf(f, "%02x", leader[i]);
- }
- }
-
- for(int i = 0; i < argcount; i++) {
- char sep = i ? ',' : ' ';
-
- if(args[i] == CSI_ARG_MISSING) {
- fprintf(f, "%c*", sep);
- } else {
- fprintf(f, "%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : "");
- }
- }
-
- if(intermed && intermed[0]) {
- fprintf(f, " I=");
- for(int i = 0; intermed[i]; i++) {
- fprintf(f, "%02x", intermed[i]);
- }
- }
-
- fprintf(f, "\n");
-
- fclose(f);
-
- return 1;
-}
-
-int parser_osc(int command, VTermStringFragment frag, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "osc ");
-
- if(frag.initial) {
- if(command == -1) {
- fprintf(f, "[");
- } else {
- fprintf(f, "[%d;", command);
- }
- }
-
- printchars(frag.str, frag.len, f);
-
- if(frag.final) {
- fprintf(f, "]");
- }
-
- fprintf(f, "\n");
- fclose(f);
-
- return 1;
-}
-
-int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "dcs ");
-
- if(frag.initial) {
- fprintf(f, "[");
- for(int i = 0; i < commandlen; i++) {
- fprintf(f, "%c", command[i]);
- }
- }
-
- printchars(frag.str, frag.len,f);
-
- if(frag.final) {
- fprintf(f, "]");
- }
-
- fprintf(f, "\n");
- fclose(f);
-
- return 1;
-}
-
-int parser_apc(VTermStringFragment frag, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "apc ");
-
- if(frag.initial) {
- fprintf(f, "[");
- }
-
- printchars(frag.str, frag.len, f);
-
- if(frag.final) {
- fprintf(f, "]");
- }
-
- fprintf(f, "\n");
- fclose(f);
-
- return 1;
-}
-
-int parser_pm(VTermStringFragment frag, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "pm ");
-
- if(frag.initial) {
- fprintf(f, "[");
- }
-
- printchars(frag.str, frag.len,f);
-
- if(frag.final) {
- fprintf(f, "]");
- }
-
- fprintf(f, "\n");
- fclose(f);
-
- return 1;
-}
-
-int parser_sos(VTermStringFragment frag, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "sos ");
-
- if(frag.initial) {
- fprintf(f, "[");
- }
-
- printchars(frag.str, frag.len,f);
-
- if(frag.final) {
- fprintf(f, "]");
- }
-
- fprintf(f, "\n");
- fclose(f);
-
- return 1;
-}
-
-int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "selection-set mask=%04X ", mask);
- if(frag.initial) {
- fprintf(f, "[");
-}
- printchars(frag.str, frag.len, f);
- if(frag.final) {
- fprintf(f, "]");
-}
- fprintf(f,"\n");
-
- fclose(f);
- return 1;
-}
-
-int selection_query(VTermSelectionMask mask, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f,"selection-query mask=%04X\n", mask);
-
- fclose(f);
- return 1;
-}
-
-bool want_state_putglyph;
-int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
-{
- if(!want_state_putglyph) {
- return 1;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "putglyph ");
- for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
- fprintf(f, i ? ",%x" : "%x", info->chars[i]);
- }
- fprintf(f, " %d %d,%d", info->width, pos.row, pos.col);
- if(info->protected_cell) {
- fprintf(f, " prot");
- }
- if(info->dwl) {
- fprintf(f, " dwl");
- }
- if(info->dhl) {
- fprintf(f, " dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?" );
- }
- fprintf(f, "\n");
-
- fclose(f);
-
- return 1;
-}
-
-bool want_state_movecursor;
-VTermPos state_pos;
-int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- state_pos = pos;
-
- if(want_state_movecursor) {
- fprintf(f,"movecursor %d,%d\n", pos.row, pos.col);
- }
-
- fclose(f);
- return 1;
-}
-
-bool want_state_scrollrect;
-int state_scrollrect(VTermRect rect, int downward, int rightward, void *user)
-{
- if(!want_state_scrollrect) {
- return 0;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
-
- fprintf(f,"scrollrect %d..%d,%d..%d => %+d,%+d\n",
- rect.start_row, rect.end_row, rect.start_col, rect.end_col,
- downward, rightward);
-
- fclose(f);
- return 1;
-}
-
-bool want_state_moverect;
-int state_moverect(VTermRect dest, VTermRect src, void *user)
-{
- if(!want_state_moverect) {
- return 0;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f,"moverect %d..%d,%d..%d -> %d..%d,%d..%d\n",
- src.start_row, src.end_row, src.start_col, src.end_col,
- dest.start_row, dest.end_row, dest.start_col, dest.end_col);
-
- fclose(f);
- return 1;
-}
-
-void print_color(const VTermColor *col)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- if (VTERM_COLOR_IS_RGB(col)) {
- fprintf(f,"rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue);
- }
- else if (VTERM_COLOR_IS_INDEXED(col)) {
- fprintf(f,"idx(%d", col->indexed.idx);
- }
- else {
- fprintf(f,"invalid(%d", col->type);
- }
- if (VTERM_COLOR_IS_DEFAULT_FG(col)) {
- fprintf(f,",is_default_fg");
- }
- if (VTERM_COLOR_IS_DEFAULT_BG(col)) {
- fprintf(f,",is_default_bg");
- }
- fprintf(f,")");
- fclose(f);
-}
-
-bool want_state_settermprop;
-int state_settermprop(VTermProp prop, VTermValue *val, void *user)
-{
- if(!want_state_settermprop) {
- return 1;
- }
-
- int errcode = 0;
- FILE *f = fopen(VTERM_TEST_FILE, "a");
-
- VTermValueType type = vterm_get_prop_type(prop);
- switch(type) {
- case VTERM_VALUETYPE_BOOL:
- fprintf(f,"settermprop %d %s\n", prop, val->boolean ? "true" : "false");
- errcode = 1;
- goto end;
- case VTERM_VALUETYPE_INT:
- fprintf(f,"settermprop %d %d\n", prop, val->number);
- errcode = 1;
- goto end;
- case VTERM_VALUETYPE_STRING:
- fprintf(f,"settermprop %d %s\"%.*s\"%s\n", prop,
- val->string.initial ? "[" : "", (int)val->string.len, val->string.str, val->string.final ? "]" : "");
- errcode=0;
- goto end;
- case VTERM_VALUETYPE_COLOR:
- fprintf(f,"settermprop %d ", prop);
- print_color(&val->color);
- fprintf(f,"\n");
- errcode=1;
- goto end;
- case VTERM_N_VALUETYPES:
- goto end;
- }
-
-end:
- fclose(f);
- return errcode;
-}
-
-bool want_state_erase;
-int state_erase(VTermRect rect, int selective, void *user)
-{
- if(!want_state_erase) {
- return 1;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
-
- fprintf(f,"erase %d..%d,%d..%d%s\n",
- rect.start_row, rect.end_row, rect.start_col, rect.end_col,
- selective ? " selective" : "");
-
- fclose(f);
- return 1;
-}
-
-struct {
- int bold;
- int underline;
- int italic;
- int blink;
- int reverse;
- int conceal;
- int strike;
- int font;
- int small;
- int baseline;
- VTermColor foreground;
- VTermColor background;
-} state_pen;
-
-int state_setpenattr(VTermAttr attr, VTermValue *val, void *user)
-{
- switch(attr) {
- case VTERM_ATTR_BOLD:
- state_pen.bold = val->boolean;
- break;
- case VTERM_ATTR_UNDERLINE:
- state_pen.underline = val->number;
- break;
- case VTERM_ATTR_ITALIC:
- state_pen.italic = val->boolean;
- break;
- case VTERM_ATTR_BLINK:
- state_pen.blink = val->boolean;
- break;
- case VTERM_ATTR_REVERSE:
- state_pen.reverse = val->boolean;
- break;
- case VTERM_ATTR_CONCEAL:
- state_pen.conceal = val->boolean;
- break;
- case VTERM_ATTR_STRIKE:
- state_pen.strike = val->boolean;
- break;
- case VTERM_ATTR_FONT:
- state_pen.font = val->number;
- break;
- case VTERM_ATTR_SMALL:
- state_pen.small = val->boolean;
- break;
- case VTERM_ATTR_BASELINE:
- state_pen.baseline = val->number;
- break;
- case VTERM_ATTR_FOREGROUND:
- state_pen.foreground = val->color;
- break;
- case VTERM_ATTR_BACKGROUND:
- state_pen.background = val->color;
- break;
-
- case VTERM_N_ATTRS:
- return 0;
- default:
- break;
- }
-
- return 1;
-}
-
-bool want_state_scrollback;
-int state_sb_clear(void *user) {
- if(!want_state_scrollback) {
- return 1;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f,"sb_clear\n");
- fclose(f);
-
- return 0;
-}
-
-bool want_screen_scrollback;
-int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user)
-{
- if(!want_screen_scrollback) {
- return 1;
- }
-
- int eol = cols;
- while(eol && !cells[eol-1].chars[0]) {
- eol--;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "sb_pushline %d =", cols);
- for(int c = 0; c < eol; c++) {
- fprintf(f, " %02X", cells[c].chars[0]);
- }
- fprintf(f, "\n");
-
- fclose(f);
-
- return 1;
-}
-
-int screen_sb_popline(int cols, VTermScreenCell *cells, void *user)
-{
- if(!want_screen_scrollback) {
- return 0;
- }
-
- // All lines of scrollback contain "ABCDE"
- for(int col = 0; col < cols; col++) {
- if(col < 5) {
- cells[col].chars[0] = 'A' + col;
- } else {
- cells[col].chars[0] = 0;
- }
-
- cells[col].width = 1;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f,"sb_popline %d\n", cols);
- fclose(f);
- return 1;
-}
-
-int screen_sb_clear(void *user)
-{
- if(!want_screen_scrollback) {
- return 1;
- }
-
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "sb_clear\n");
- fclose(f);
- return 0;
-}
-
-void term_output(const char *s, size_t len, void *user)
-{
- FILE *f = fopen(VTERM_TEST_FILE, "a");
- fprintf(f, "output ");
- for(int i = 0; i < len; i++) {
- fprintf(f, "%x%s", (unsigned char)s[i], i < len-1 ? "," : "\n");
- }
- fclose(f);
-}
-
-#endif
diff --git a/src/vterm/vterm.h b/src/vterm/vterm.h
deleted file mode 100644
index df6878f744..0000000000
--- a/src/vterm/vterm.h
+++ /dev/null
@@ -1,676 +0,0 @@
-#ifndef __VTERM_H__
-#define __VTERM_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
-#include "nvim/macros_defs.h"
-#include "vterm_keycodes.h"
-
-#define VTERM_VERSION_MAJOR 0
-#define VTERM_VERSION_MINOR 3
-#define VTERM_VERSION_PATCH 3
-
-#define VTERM_CHECK_VERSION \
- vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
-
-/* Any cell can contain at most one basic printing character and 5 combining
- * characters. This number could be changed but will be ABI-incompatible if
- * you do */
-enum{ VTERM_MAX_CHARS_PER_CELL=6};
-
-typedef struct VTerm VTerm;
-typedef struct VTermState VTermState;
-typedef struct VTermScreen VTermScreen;
-
-typedef struct {
- int row;
- int col;
-} VTermPos;
-
-/* some small utility functions; we can just keep these static here */
-
-/* order points by on-screen flow order */
-static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
-{
- return (a.row == b.row) ? a.col - b.col : a.row - b.row;
-}
-
-typedef struct {
- int start_row;
- int end_row;
- int start_col;
- int end_col;
-} VTermRect;
-
-/* true if the rect contains the point */
-static inline int vterm_rect_contains(VTermRect r, VTermPos p)
-{
- return p.row >= r.start_row && p.row < r.end_row &&
- p.col >= r.start_col && p.col < r.end_col;
-}
-
-/* move a rect */
-static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
-{
- rect->start_row += row_delta; rect->end_row += row_delta;
- rect->start_col += col_delta; rect->end_col += col_delta;
-}
-
-/**
- * Bit-field describing the content of the tagged union `VTermColor`.
- */
-typedef enum {
- /**
- * If the lower bit of `type` is not set, the colour is 24-bit RGB.
- */
- VTERM_COLOR_RGB = 0x00,
-
- /**
- * The colour is an index into a palette of 256 colours.
- */
- VTERM_COLOR_INDEXED = 0x01,
-
- /**
- * Mask that can be used to extract the RGB/Indexed bit.
- */
- VTERM_COLOR_TYPE_MASK = 0x01,
-
- /**
- * If set, indicates that this colour should be the default foreground
- * color, i.e. there was no SGR request for another colour. When
- * rendering this colour it is possible to ignore "idx" and just use a
- * colour that is not in the palette.
- */
- VTERM_COLOR_DEFAULT_FG = 0x02,
-
- /**
- * If set, indicates that this colour should be the default background
- * color, i.e. there was no SGR request for another colour. A common
- * option when rendering this colour is to not render a background at
- * all, for example by rendering the window transparently at this spot.
- */
- VTERM_COLOR_DEFAULT_BG = 0x04,
-
- /**
- * Mask that can be used to extract the default foreground/background bit.
- */
- VTERM_COLOR_DEFAULT_MASK = 0x06
-} VTermColorType;
-
-/**
- * Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
- * given VTermColor instance is an indexed colour.
- */
-#define VTERM_COLOR_IS_INDEXED(col) \
- (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
-
-/**
- * Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
- * the given VTermColor instance is an rgb colour.
- */
-#define VTERM_COLOR_IS_RGB(col) \
- (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
-
-/**
- * Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
- * that the given VTermColor instance corresponds to the default foreground
- * color.
- */
-#define VTERM_COLOR_IS_DEFAULT_FG(col) \
- (!!((col)->type & VTERM_COLOR_DEFAULT_FG))
-
-/**
- * Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
- * that the given VTermColor instance corresponds to the default background
- * color.
- */
-#define VTERM_COLOR_IS_DEFAULT_BG(col) \
- (!!((col)->type & VTERM_COLOR_DEFAULT_BG))
-
-/**
- * Tagged union storing either an RGB color or an index into a colour palette.
- * In order to convert indexed colours to RGB, you may use the
- * vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb()
- * functions which lookup the RGB colour from the palette maintained by a
- * VTermState or VTermScreen instance.
- */
-typedef union {
- /**
- * Tag indicating which union member is actually valid. This variable
- * coincides with the `type` member of the `rgb` and the `indexed` struct
- * in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether
- * a particular type flag is set.
- */
- uint8_t type;
-
- /**
- * Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
- */
- struct {
- /**
- * Same as the top-level `type` member stored in VTermColor.
- */
- uint8_t type;
-
- /**
- * The actual 8-bit red, green, blue colour values.
- */
- uint8_t red, green, blue;
- } rgb;
-
- /**
- * If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into
- * the colour palette.
- */
- struct {
- /**
- * Same as the top-level `type` member stored in VTermColor.
- */
- uint8_t type;
-
- /**
- * Index into the colour map.
- */
- uint8_t idx;
- } indexed;
-} VTermColor;
-
-/**
- * Constructs a new VTermColor instance representing the given RGB values.
- */
-static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green,
- uint8_t blue)
-{
- col->type = VTERM_COLOR_RGB;
- col->rgb.red = red;
- col->rgb.green = green;
- col->rgb.blue = blue;
-}
-
-/**
- * Construct a new VTermColor instance representing an indexed color with the
- * given index.
- */
-static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
-{
- col->type = VTERM_COLOR_INDEXED;
- col->indexed.idx = idx;
-}
-
-/**
- * Compares two colours. Returns true if the colors are equal, false otherwise.
- */
-int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
-
-typedef enum {
- /* VTERM_VALUETYPE_NONE = 0 */
- VTERM_VALUETYPE_BOOL = 1,
- VTERM_VALUETYPE_INT,
- VTERM_VALUETYPE_STRING,
- VTERM_VALUETYPE_COLOR,
-
- VTERM_N_VALUETYPES
-} VTermValueType;
-
-typedef struct {
- const char *str;
- size_t len : 30;
- bool initial : 1;
- bool final : 1;
-} VTermStringFragment;
-
-typedef union {
- int boolean;
- int number;
- VTermStringFragment string;
- VTermColor color;
-} VTermValue;
-
-typedef enum {
- /* VTERM_ATTR_NONE = 0 */
- VTERM_ATTR_BOLD = 1, // bool: 1, 22
- VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
- VTERM_ATTR_ITALIC, // bool: 3, 23
- VTERM_ATTR_BLINK, // bool: 5, 25
- VTERM_ATTR_REVERSE, // bool: 7, 27
- VTERM_ATTR_CONCEAL, // bool: 8, 28
- VTERM_ATTR_STRIKE, // bool: 9, 29
- VTERM_ATTR_FONT, // number: 10-19
- VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
- VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
- VTERM_ATTR_SMALL, // bool: 73, 74, 75
- VTERM_ATTR_BASELINE, // number: 73, 74, 75
- VTERM_ATTR_URI, // number
-
- VTERM_N_ATTRS
-} VTermAttr;
-
-typedef enum {
- /* VTERM_PROP_NONE = 0 */
- VTERM_PROP_CURSORVISIBLE = 1, // bool
- VTERM_PROP_CURSORBLINK, // bool
- VTERM_PROP_ALTSCREEN, // bool
- VTERM_PROP_TITLE, // string
- VTERM_PROP_ICONNAME, // string
- VTERM_PROP_REVERSE, // bool
- VTERM_PROP_CURSORSHAPE, // number
- VTERM_PROP_MOUSE, // number
- VTERM_PROP_FOCUSREPORT, // bool
-
- VTERM_N_PROPS
-} VTermProp;
-
-enum {
- VTERM_PROP_CURSORSHAPE_BLOCK = 1,
- VTERM_PROP_CURSORSHAPE_UNDERLINE,
- VTERM_PROP_CURSORSHAPE_BAR_LEFT,
-
- VTERM_N_PROP_CURSORSHAPES
-};
-
-enum {
- VTERM_PROP_MOUSE_NONE = 0,
- VTERM_PROP_MOUSE_CLICK,
- VTERM_PROP_MOUSE_DRAG,
- VTERM_PROP_MOUSE_MOVE,
-
- VTERM_N_PROP_MOUSES
-};
-
-typedef enum {
- VTERM_SELECTION_CLIPBOARD = (1<<0),
- VTERM_SELECTION_PRIMARY = (1<<1),
- VTERM_SELECTION_SECONDARY = (1<<2),
- VTERM_SELECTION_SELECT = (1<<3),
- VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */
-} VTermSelectionMask;
-
-typedef struct {
- const uint32_t *chars;
- int width;
- unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
- unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
- unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
-} VTermGlyphInfo;
-
-typedef struct {
- unsigned int doublewidth:1; /* DECDWL or DECDHL line */
- unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
- unsigned int continuation:1; /* Line is a flow continuation of the previous */
-} VTermLineInfo;
-
-/* Copies of VTermState fields that the 'resize' callback might have reason to
- * edit. 'resize' callback gets total control of these fields and may
- * free-and-reallocate them if required. They will be copied back from the
- * struct after the callback has returned.
- */
-typedef struct {
- VTermPos pos; /* current cursor position */
- VTermLineInfo *lineinfos[2]; /* [1] may be NULL */
-} VTermStateFields;
-
-typedef struct {
- /* libvterm relies on this memory to be zeroed out before it is returned
- * by the allocator. */
- void *(*malloc)(size_t size, void *allocdata);
- void (*free)(void *ptr, void *allocdata);
-} VTermAllocatorFunctions;
-
-void vterm_check_version(int major, int minor);
-
-struct VTermBuilder {
- int ver; /* currently unused but reserved for some sort of ABI version flag */
-
- int rows, cols;
-
- const VTermAllocatorFunctions *allocator;
- void *allocdata;
-
- /* Override default sizes for various structures */
- size_t outbuffer_len; /* default: 4096 */
- size_t tmpbuffer_len; /* default: 4096 */
-};
-
-VTerm *vterm_build(const struct VTermBuilder *builder);
-
-/* A convenient shortcut for default cases */
-VTerm *vterm_new(int rows, int cols);
-/* This shortcuts are generally discouraged in favour of just using vterm_build() */
-VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
-
-void vterm_free(VTerm* vt);
-
-void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
-void vterm_set_size(VTerm *vt, int rows, int cols);
-
-int vterm_get_utf8(const VTerm *vt);
-void vterm_set_utf8(VTerm *vt, int is_utf8);
-
-size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
-
-/* Setting output callback will override the buffer logic */
-typedef void VTermOutputCallback(const char *s, size_t len, void *user);
-void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user);
-
-/* These buffer functions only work if output callback is NOT set
- * These are deprecated and will be removed in a later version */
-size_t vterm_output_get_buffer_size(const VTerm *vt);
-size_t vterm_output_get_buffer_current(const VTerm *vt);
-size_t vterm_output_get_buffer_remaining(const VTerm *vt);
-
-/* This too */
-size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
-
-void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
-void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
-
-void vterm_keyboard_start_paste(VTerm *vt);
-void vterm_keyboard_end_paste(VTerm *vt);
-
-void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
-void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
-
-// ------------
-// Parser layer
-// ------------
-
-/* Flag to indicate non-final subparameters in a single CSI parameter.
- * Consider
- * CSI 1;2:3:4;5a
- * 1 4 and 5 are final.
- * 2 and 3 are non-final and will have this bit set
- *
- * Don't confuse this with the final byte of the CSI escape; 'a' in this case.
- */
-#define CSI_ARG_FLAG_MORE (1U<<31)
-#define CSI_ARG_MASK (~(1U<<31))
-
-#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
-#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
-
-/* Can't use -1 to indicate a missing argument; use this instead */
-#define CSI_ARG_MISSING ((1UL<<31)-1)
-
-#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
-#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
-#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
-
-typedef struct {
- int (*text)(const char *bytes, size_t len, void *user);
- int (*control)(unsigned char control, void *user);
- int (*escape)(const char *bytes, size_t len, void *user);
- int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
- int (*osc)(int command, VTermStringFragment frag, void *user);
- int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
- int (*apc)(VTermStringFragment frag, void *user);
- int (*pm)(VTermStringFragment frag, void *user);
- int (*sos)(VTermStringFragment frag, void *user);
- int (*resize)(int rows, int cols, void *user);
-} VTermParserCallbacks;
-
-void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
-void *vterm_parser_get_cbdata(VTerm *vt);
-
-/* Normally NUL, CAN, SUB and DEL are ignored. Setting this true causes them
- * to be emitted by the 'control' callback
- */
-void vterm_parser_set_emit_nul(VTerm *vt, bool emit);
-
-// -----------
-// State layer
-// -----------
-
-typedef struct {
- int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
- int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
- int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
- int (*moverect)(VTermRect dest, VTermRect src, void *user);
- int (*erase)(VTermRect rect, int selective, void *user);
- int (*initpen)(void *user);
- int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
- int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
- int (*bell)(void *user);
- int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
- int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
- int (*sb_clear)(void *user);
-} VTermStateCallbacks;
-
-typedef struct {
- int (*control)(unsigned char control, void *user);
- int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
- int (*osc)(int command, VTermStringFragment frag, void *user);
- int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
- int (*apc)(VTermStringFragment frag, void *user);
- int (*pm)(VTermStringFragment frag, void *user);
- int (*sos)(VTermStringFragment frag, void *user);
-} VTermStateFallbacks;
-
-typedef struct {
- int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
- int (*query)(VTermSelectionMask mask, void *user);
-} VTermSelectionCallbacks;
-
-VTermState *vterm_obtain_state(VTerm *vt);
-
-void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
-void *vterm_state_get_cbdata(VTermState *state);
-
-void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
-void *vterm_state_get_unrecognised_fbdata(VTermState *state);
-
-void vterm_state_reset(VTermState *state, int hard);
-void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
-void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
-void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
-void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
-void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
-void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
-int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
-int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val);
-int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
-void vterm_state_focus_in(VTermState *state);
-void vterm_state_focus_out(VTermState *state);
-const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
-
-/**
- * Makes sure that the given color `col` is indeed an RGB colour. After this
- * function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
- * flags stored in `col->type` will have been reset.
- *
- * @param state is the VTermState instance from which the colour palette should
- * be extracted.
- * @param col is a pointer at the VTermColor instance that should be converted
- * to an RGB colour.
- */
-void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
-
-void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
- char *buffer, size_t buflen);
-
-void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag);
-
-// ------------
-// Screen layer
-// ------------
-
-typedef struct {
- unsigned int bold : 1;
- unsigned int underline : 2;
- unsigned int italic : 1;
- unsigned int blink : 1;
- unsigned int reverse : 1;
- unsigned int conceal : 1;
- unsigned int strike : 1;
- unsigned int font : 4; /* 0 to 9 */
- unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
- unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
- unsigned int small : 1;
- unsigned int baseline : 2;
-} VTermScreenCellAttrs;
-
-enum {
- VTERM_UNDERLINE_OFF,
- VTERM_UNDERLINE_SINGLE,
- VTERM_UNDERLINE_DOUBLE,
- VTERM_UNDERLINE_CURLY,
-};
-
-enum {
- VTERM_BASELINE_NORMAL,
- VTERM_BASELINE_RAISE,
- VTERM_BASELINE_LOWER,
-};
-
-typedef struct {
- uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
- char width;
- VTermScreenCellAttrs attrs;
- VTermColor fg, bg;
- int uri;
-} VTermScreenCell;
-
-typedef struct {
- int (*damage)(VTermRect rect, void *user);
- int (*moverect)(VTermRect dest, VTermRect src, void *user);
- int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
- int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
- int (*bell)(void *user);
- int (*resize)(int rows, int cols, void *user);
- int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
- int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
- int (*sb_clear)(void* user);
-} VTermScreenCallbacks;
-
-VTermScreen *vterm_obtain_screen(VTerm *vt);
-
-void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
-void *vterm_screen_get_cbdata(VTermScreen *screen);
-
-void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
-void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
-
-void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow);
-
-// Back-compat alias for the brief time it was in 0.3-RC1
-#define vterm_screen_set_reflow vterm_screen_enable_reflow
-
-void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
-
-typedef enum {
- VTERM_DAMAGE_CELL, /* every cell */
- VTERM_DAMAGE_ROW, /* entire rows */
- VTERM_DAMAGE_SCREEN, /* entire screen */
- VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */
-
- VTERM_N_DAMAGES
-} VTermDamageSize;
-
-void vterm_screen_flush_damage(VTermScreen *screen);
-void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
-
-void vterm_screen_reset(VTermScreen *screen, int hard);
-
-/* Neither of these functions NUL-terminate the buffer */
-size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
-size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
-
-typedef enum {
- VTERM_ATTR_BOLD_MASK = 1 << 0,
- VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
- VTERM_ATTR_ITALIC_MASK = 1 << 2,
- VTERM_ATTR_BLINK_MASK = 1 << 3,
- VTERM_ATTR_REVERSE_MASK = 1 << 4,
- VTERM_ATTR_STRIKE_MASK = 1 << 5,
- VTERM_ATTR_FONT_MASK = 1 << 6,
- VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
- VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
- VTERM_ATTR_CONCEAL_MASK = 1 << 9,
- VTERM_ATTR_SMALL_MASK = 1 << 10,
- VTERM_ATTR_BASELINE_MASK = 1 << 11,
- VTERM_ATTR_URI_MASK = 1 << 12,
-
- VTERM_ALL_ATTRS_MASK = (1 << 13) - 1
-} VTermAttrMask;
-
-int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
-
-int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
-
-int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
-
-/**
- * Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
- * instance.
- */
-void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
-
-/**
- * Similar to vterm_state_set_default_colors(), but also resets colours in the
- * screen buffer(s)
- */
-void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg);
-
-// ---------
-// Utilities
-// ---------
-
-VTermValueType vterm_get_attr_type(VTermAttr attr);
-VTermValueType vterm_get_prop_type(VTermProp prop);
-
-void vterm_scroll_rect(VTermRect rect,
- int downward,
- int rightward,
- int (*moverect)(VTermRect src, VTermRect dest, void *user),
- int (*eraserect)(VTermRect rect, int selective, void *user),
- void *user);
-
-void vterm_copy_cells(VTermRect dest,
- VTermRect src,
- void (*copycell)(VTermPos dest, VTermPos src, void *user),
- void *user);
-
-#ifndef NDEBUG
-int parser_text(const char bytes[], size_t len, void *user);
-int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
-int parser_osc(int command, VTermStringFragment frag, void *user);
-int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
-int parser_apc(VTermStringFragment frag, void *user);
-int parser_pm(VTermStringFragment frag, void *user);
-int parser_sos(VTermStringFragment frag, void *user);
-int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user);
-int selection_query(VTermSelectionMask mask, void *user);
-int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user);
-int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
-int state_scrollrect(VTermRect rect, int downward, int rightward, void *user);
-int state_moverect(VTermRect dest, VTermRect src, void *user);
-int state_settermprop(VTermProp prop, VTermValue *val, void *user);
-int state_erase(VTermRect rect, int selective, void *user);
-int state_setpenattr(VTermAttr attr, VTermValue *val, void *user);
-int state_sb_clear(void *user);
-void print_color(const VTermColor *col);
-int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user);
-int screen_sb_popline(int cols, VTermScreenCell *cells, void *user);
-int screen_sb_clear(void *user);
-void term_output(const char *s, size_t len, void *user);
-EXTERN VTermPos state_pos;
-EXTERN bool want_state_putglyph INIT (=false);
-EXTERN bool want_state_movecursor INIT(= false);
-EXTERN bool want_state_erase INIT(= false);
-EXTERN bool want_state_scrollrect INIT(= false);
-EXTERN bool want_state_moverect INIT(= false);
-EXTERN bool want_state_settermprop INIT(= false);
-EXTERN bool want_state_scrollback INIT(= false);
-EXTERN bool want_screen_scrollback INIT(= false);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/vterm/vterm_internal.h b/src/vterm/vterm_internal.h
deleted file mode 100644
index 53f9b5e100..0000000000
--- a/src/vterm/vterm_internal.h
+++ /dev/null
@@ -1,298 +0,0 @@
-#ifndef __VTERM_INTERNAL_H__
-#define __VTERM_INTERNAL_H__
-
-#include "vterm.h"
-
-#include <stdarg.h>
-
-#if defined(__GNUC__)
-# define INTERNAL __attribute__((visibility("internal")))
-#else
-# define INTERNAL
-#endif
-
-#ifdef DEBUG
-# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
-#else
-# define DEBUG_LOG(...)
-#endif
-
-#define ESC_S "\x1b"
-
-#define INTERMED_MAX 16
-
-#define CSI_ARGS_MAX 16
-#define CSI_LEADER_MAX 16
-
-#define BUFIDX_PRIMARY 0
-#define BUFIDX_ALTSCREEN 1
-
-typedef struct VTermEncoding VTermEncoding;
-
-typedef struct {
- VTermEncoding *enc;
-
- // This size should be increased if required by other stateful encodings
- char data[4*sizeof(uint32_t)];
-} VTermEncodingInstance;
-
-struct VTermPen
-{
- VTermColor fg;
- VTermColor bg;
- int uri;
- unsigned int bold:1;
- unsigned int underline:2;
- unsigned int italic:1;
- unsigned int blink:1;
- unsigned int reverse:1;
- unsigned int conceal:1;
- unsigned int strike:1;
- unsigned int font:4; /* To store 0-9 */
- unsigned int small:1;
- unsigned int baseline:2;
-};
-
-struct VTermState
-{
- VTerm *vt;
-
- const VTermStateCallbacks *callbacks;
- void *cbdata;
-
- const VTermStateFallbacks *fallbacks;
- void *fbdata;
-
- int rows;
- int cols;
-
- /* Current cursor position */
- VTermPos pos;
-
- int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
-
- int scrollregion_top;
- int scrollregion_bottom; /* -1 means unbounded */
-#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
- int scrollregion_left;
-#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
- int scrollregion_right; /* -1 means unbounded */
-#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
-
- /* Bitvector of tab stops */
- unsigned char *tabstops;
-
- /* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */
- VTermLineInfo *lineinfos[2];
-
- /* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */
- VTermLineInfo *lineinfo;
-#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
-#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
-
- /* Mouse state */
- int mouse_col, mouse_row;
- int mouse_buttons;
- int mouse_flags;
-#define MOUSE_WANT_CLICK 0x01
-#define MOUSE_WANT_DRAG 0x02
-#define MOUSE_WANT_MOVE 0x04
-
- enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
-
- /* Last glyph output, for Unicode recombining purposes */
- uint32_t *combine_chars;
- size_t combine_chars_size; // Number of ELEMENTS in the above
- int combine_width; // The width of the glyph above
- VTermPos combine_pos; // Position before movement
-
- struct {
- unsigned int keypad:1;
- unsigned int cursor:1;
- unsigned int autowrap:1;
- unsigned int insert:1;
- unsigned int newline:1;
- unsigned int cursor_visible:1;
- unsigned int cursor_blink:1;
- unsigned int cursor_shape:2;
- unsigned int alt_screen:1;
- unsigned int origin:1;
- unsigned int screen:1;
- unsigned int leftrightmargin:1;
- unsigned int bracketpaste:1;
- unsigned int report_focus:1;
- } mode;
-
- VTermEncodingInstance encoding[4], encoding_utf8;
- int gl_set, gr_set, gsingle_set;
-
- struct VTermPen pen;
-
- VTermColor default_fg;
- VTermColor default_bg;
- VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
-
- int bold_is_highbright;
-
- unsigned int protected_cell : 1;
-
- /* Saved state under DEC mode 1048/1049 */
- struct {
- VTermPos pos;
- struct VTermPen pen;
-
- struct {
- unsigned int cursor_visible:1;
- unsigned int cursor_blink:1;
- unsigned int cursor_shape:2;
- } mode;
- } saved;
-
- /* Temporary state for DECRQSS parsing */
- union {
- char decrqss[4];
- struct {
- uint16_t mask;
- enum {
- SELECTION_INITIAL,
- SELECTION_SELECTED,
- SELECTION_QUERY,
- SELECTION_SET_INITIAL,
- SELECTION_SET,
- SELECTION_INVALID,
- } state : 8;
- uint32_t recvpartial;
- uint32_t sendpartial;
- } selection;
- } tmp;
-
- struct {
- const VTermSelectionCallbacks *callbacks;
- void *user;
- char *buffer;
- size_t buflen;
- } selection;
-};
-
-struct VTerm
-{
- const VTermAllocatorFunctions *allocator;
- void *allocdata;
-
- int rows;
- int cols;
-
- struct {
- unsigned int utf8:1;
- unsigned int ctrl8bit:1;
- } mode;
-
- struct {
- enum VTermParserState {
- NORMAL,
- CSI_LEADER,
- CSI_ARGS,
- CSI_INTERMED,
- DCS_COMMAND,
- /* below here are the "string states" */
- OSC_COMMAND,
- OSC,
- DCS,
- APC,
- PM,
- SOS,
- } state;
-
- bool in_esc : 1;
-
- int intermedlen;
- char intermed[INTERMED_MAX];
-
- union {
- struct {
- int leaderlen;
- char leader[CSI_LEADER_MAX];
-
- int argi;
- long args[CSI_ARGS_MAX];
- } csi;
- struct {
- int command;
- } osc;
- struct {
- int commandlen;
- char command[CSI_LEADER_MAX];
- } dcs;
- } v;
-
- const VTermParserCallbacks *callbacks;
- void *cbdata;
-
- bool string_initial;
-
- bool emit_nul;
- } parser;
-
- /* len == malloc()ed size; cur == number of valid bytes */
-
- VTermOutputCallback *outfunc;
- void *outdata;
-
- char *outbuffer;
- size_t outbuffer_len;
- size_t outbuffer_cur;
-
- char *tmpbuffer;
- size_t tmpbuffer_len;
-
- VTermState *state;
- VTermScreen *screen;
-};
-
-struct VTermEncoding {
- void (*init) (VTermEncoding *enc, void *data);
- void (*decode)(VTermEncoding *enc, void *data,
- uint32_t cp[], int *cpi, int cplen,
- const char bytes[], size_t *pos, size_t len);
-};
-
-typedef enum {
- ENC_UTF8,
- ENC_SINGLE_94
-} VTermEncodingType;
-
-void *vterm_allocator_malloc(VTerm *vt, size_t size);
-void vterm_allocator_free(VTerm *vt, void *ptr);
-
-void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
-void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
-void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
-void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
-void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...);
-
-void vterm_state_free(VTermState *state);
-
-void vterm_state_newpen(VTermState *state);
-void vterm_state_resetpen(VTermState *state);
-void vterm_state_setpen(VTermState *state, const long args[], int argcount);
-int vterm_state_getpen(VTermState *state, long args[], int argcount);
-void vterm_state_savepen(VTermState *state, int save);
-
-enum {
- C1_SS3 = 0x8f,
- C1_DCS = 0x90,
- C1_CSI = 0x9b,
- C1_ST = 0x9c,
- C1_OSC = 0x9d,
-};
-
-void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
-
-void vterm_screen_free(VTermScreen *screen);
-
-VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
-
-int vterm_unicode_width(uint32_t codepoint);
-int vterm_unicode_is_combining(uint32_t codepoint);
-
-#endif
diff --git a/test/README.md b/test/README.md
index d1b053fca3..5b225980a2 100644
--- a/test/README.md
+++ b/test/README.md
@@ -138,6 +138,163 @@ Debugging tests
Then put `screen:snapshot_util()` anywhere in your test. See the comments in
`test/functional/ui/screen.lua` for more info.
+Debugging Lua test code
+-----------------------
+
+Debugging Lua test code is a bit involved. Get your shopping list ready, you'll
+need to install and configure:
+
+1. [nvim-dap](https://github.com/mfussenegger/nvim-dap)
+2. [local-lua-debugger-vscode](https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#local-lua-debugger-vscode)
+3. [nlua](https://github.com/mfussenegger/nlua)
+4. [one-small-step-for-vimkind](https://github.com/jbyuki/one-small-step-for-vimkind) (called `osv`)
+5. A `nbusted` command in `$PATH`. This command can be a copy of `busted` with
+ `exec '/usr/bin/lua5.1'"` replaced with `"exec '/usr/bin/nlua'"` (or the
+ path to your `nlua`)
+
+
+The setup roughly looks like this:
+
+```
+ ┌─────────────────────────┐
+ │ nvim used for debugging │◄────┐
+ └─────────────────────────┘ │
+ │ │
+ ▼ │
+ ┌─────────────────┐ │
+ │ local-lua-debug │ │
+ └─────────────────┘ │
+ │ │
+ ▼ │
+ ┌─────────┐ │
+ │ nbusted │ │
+ └─────────┘ │
+ │ │
+ ▼ │
+ ┌───────────┐ │
+ │ test-case │ │
+ └───────────┘ │
+ │ │
+ ▼ │
+ ┌────────────────────┐ │
+ │ nvim test-instance │ │
+ └────────────────────┘ │
+ │ ┌─────┐ │
+ └──►│ osv │─────────────────┘
+ └─────┘
+```
+
+
+With these installed you can use a configuration like this:
+
+
+```lua
+local dap = require("dap")
+
+
+local function free_port()
+ local tcp = vim.loop.new_tcp()
+ assert(tcp)
+ tcp:bind('127.0.0.1', 0)
+ local port = tcp:getsockname().port
+ tcp:shutdown()
+ tcp:close()
+ return port
+end
+
+
+local name = "nvim-test-case" -- arbitrary name
+local config = {
+ name = name,
+
+ -- value of type must match the key used in `dap.adapters["local-lua"] = ...` from step 2)
+ type = "local-lua",
+
+ request = "launch",
+ cwd = "${workspaceFolder}",
+ program = {
+ command = "nbusted",
+ },
+ args = {
+ "--ignore-lua",
+ "--lazy",
+ "--helper=test/functional/preload.lua",
+ "--lpath=build/?.lua",
+ "--lpath=?.lua",
+
+ -- path to file to debug, could be replaced with a hardcoded string
+ function()
+ return vim.api.nvim_buf_get_name(0)
+ end,
+
+ -- You can filter to specific test-case by adding:
+ -- '--filter="' .. test_case_name .. '"',
+ },
+ env = {
+ OSV_PORT = free_port
+ }
+}
+
+-- Whenever the config is used it needs to launch a second debug session that attaches to `osv`
+-- This makes it possible to step into `exec_lua` code blocks
+setmetatable(config, {
+
+ __call = function(c)
+ ---@param session dap.Session
+ dap.listeners.after.event_initialized["nvim_debug"] = function(session)
+ if session.config.name ~= name then
+ return
+ end
+ dap.listeners.after.event_initialized["nvim_debug"] = nil
+ vim.defer_fn(function()
+ dap.run({
+ name = "attach-osv",
+ type = "nlua", -- value must match the `dap.adapters` definition key for osv
+ request = "attach",
+ port = session.config.env.OSV_PORT,
+ })
+ end, 500)
+ end
+
+ return c
+ end,
+})
+
+```
+
+You can either add this configuration to your `dap.configurations.lua` list as
+described in `:help dap-configuration` or create it dynamically in a
+user-command or function and call it directly via `dap.run(config)`. The latter
+is useful if you use tree-sitter to find the test case around a cursor location
+with a query like the following and set the `--filter` property to it.
+
+```query
+(function_call
+ name: (identifier) @name (#any-of? @name "describe" "it")
+ arguments: (arguments
+ (string) @str
+ )
+)
+```
+
+Limitations:
+
+- You need to add the following boilerplate to each spec file where you want to
+ be able to stop at breakpoints within the test-case code:
+
+```
+if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
+ require("lldebugger").start()
+end
+```
+
+This is a [local-lua-debugger
+limitation](https://github.com/tomblind/local-lua-debugger-vscode?tab=readme-ov-file#busted)
+
+- You cannot step into code of files which get baked into the nvim binary like
+ the `shared.lua`.
+
+
Filtering Tests
---------------
@@ -379,3 +536,6 @@ Number; !must be defined to function properly):
- `NVIM_TEST_MAXTRACE` (U) (N): specifies maximum number of trace lines to
keep. Default is 1024.
+
+- `OSV_PORT`: (F): launches `osv` listening on the given port within nvim test
+ instances.
diff --git a/test/benchmark/decor_spec.lua b/test/benchmark/decor_spec.lua
new file mode 100644
index 0000000000..1b7e763a09
--- /dev/null
+++ b/test/benchmark/decor_spec.lua
@@ -0,0 +1,140 @@
+local n = require('test.functional.testnvim')()
+local Screen = require('test.functional.ui.screen')
+local exec_lua = n.exec_lua
+
+describe('decor perf', function()
+ before_each(n.clear)
+
+ it('can handle long lines', function()
+ Screen.new(100, 101)
+
+ local result = exec_lua [==[
+ local ephemeral_pattern = {
+ { 0, 4, 'Comment', 11 },
+ { 0, 3, 'Keyword', 12 },
+ { 1, 2, 'Label', 12 },
+ { 0, 1, 'String', 21 },
+ { 1, 3, 'Function', 21 },
+ { 2, 10, 'Label', 8 },
+ }
+
+ local regular_pattern = {
+ { 4, 5, 'String', 12 },
+ { 1, 4, 'Function', 2 },
+ }
+
+ for _, list in ipairs({ ephemeral_pattern, regular_pattern }) do
+ for _, p in ipairs(list) do
+ p[3] = vim.api.nvim_get_hl_id_by_name(p[3])
+ end
+ end
+
+ local text = ('abcdefghijklmnopqrstuvwxyz0123'):rep(333)
+ local line_len = #text
+ vim.api.nvim_buf_set_lines(0, 0, 0, false, { text })
+
+ local ns = vim.api.nvim_create_namespace('decor_spec.lua')
+ vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
+ vim.api.nvim_win_set_cursor(0, { 1, 0 })
+
+ local ps, pe
+ local function add_pattern(pattern, ephemeral)
+ ps = vim.uv.hrtime()
+ local i = 0
+ while i < line_len - 10 do
+ for _, p in ipairs(pattern) do
+ vim.api.nvim_buf_set_extmark(0, ns, 0, i + p[1], {
+ end_row = 0,
+ end_col = i + p[2],
+ hl_group = p[3],
+ priority = p[4],
+ ephemeral = ephemeral,
+ })
+ end
+ i = i + 5
+ end
+ pe = vim.uv.hrtime()
+ end
+
+ vim.api.nvim_set_decoration_provider(ns, {
+ on_win = function()
+ return true
+ end,
+ on_line = function()
+ add_pattern(ephemeral_pattern, true)
+ end,
+ })
+
+ add_pattern(regular_pattern, false)
+
+ local total = {}
+ local provider = {}
+ for i = 1, 100 do
+ local tic = vim.uv.hrtime()
+ vim.cmd'redraw!'
+ local toc = vim.uv.hrtime()
+ table.insert(total, toc - tic)
+ table.insert(provider, pe - ps)
+ end
+
+ return { total, provider }
+ ]==]
+
+ local total, provider = unpack(result)
+ table.sort(total)
+ table.sort(provider)
+
+ local ms = 1 / 1000000
+ local function fmt(stats)
+ return string.format(
+ 'min, 25%%, median, 75%%, max:\n\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms',
+ stats[1] * ms,
+ stats[1 + math.floor(#stats * 0.25)] * ms,
+ stats[1 + math.floor(#stats * 0.5)] * ms,
+ stats[1 + math.floor(#stats * 0.75)] * ms,
+ stats[#stats] * ms
+ )
+ end
+
+ print('\nTotal ' .. fmt(total) .. '\nDecoration provider: ' .. fmt(provider))
+ end)
+
+ it('can handle full screen of highlighting', function()
+ Screen.new(100, 51)
+
+ local result = exec_lua(function()
+ local long_line = 'local a={' .. ('a=5,'):rep(22) .. '}'
+ local lines = {}
+ for _ = 1, 50 do
+ table.insert(lines, long_line)
+ end
+ vim.api.nvim_buf_set_lines(0, 0, 0, false, lines)
+ vim.api.nvim_win_set_cursor(0, { 1, 0 })
+ vim.treesitter.start(0, 'lua')
+
+ local total = {}
+ for _ = 1, 100 do
+ local tic = vim.uv.hrtime()
+ vim.cmd 'redraw!'
+ local toc = vim.uv.hrtime()
+ table.insert(total, toc - tic)
+ end
+
+ return { total }
+ end)
+
+ local total = unpack(result)
+ table.sort(total)
+
+ local ms = 1 / 1000000
+ local res = string.format(
+ 'min, 25%%, median, 75%%, max:\n\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms,\t%0.1fms',
+ total[1] * ms,
+ total[1 + math.floor(#total * 0.25)] * ms,
+ total[1 + math.floor(#total * 0.5)] * ms,
+ total[1 + math.floor(#total * 0.75)] * ms,
+ total[#total] * ms
+ )
+ print('\nTotal ' .. res)
+ end)
+end)
diff --git a/test/benchmark/text_spec.lua b/test/benchmark/text_spec.lua
new file mode 100644
index 0000000000..9cfeaf765b
--- /dev/null
+++ b/test/benchmark/text_spec.lua
@@ -0,0 +1,52 @@
+describe('vim.text', function()
+ --- @param t number[]
+ local function mean(t)
+ assert(#t > 0)
+ local sum = 0
+ for _, v in ipairs(t) do
+ sum = sum + v
+ end
+ return sum / #t
+ end
+
+ --- @param t number[]
+ local function median(t)
+ local len = #t
+ if len % 2 == 0 then
+ return t[len / 2]
+ end
+ return t[(len + 1) / 2]
+ end
+
+ --- @param f fun(t: number[]): table<number, number|string|table>
+ local function measure(f, input, N)
+ local stats = {} ---@type number[]
+ for _ = 1, N do
+ local tic = vim.uv.hrtime()
+ f(input)
+ local toc = vim.uv.hrtime()
+ stats[#stats + 1] = (toc - tic) / 1000000
+ end
+ table.sort(stats)
+ print(
+ string.format(
+ '\nN: %d, Min: %0.6f ms, Max: %0.6f ms, Median: %0.6f ms, Mean: %0.6f ms',
+ N,
+ math.min(unpack(stats)),
+ math.max(unpack(stats)),
+ median(stats),
+ mean(stats)
+ )
+ )
+ end
+
+ local input, output = string.rep('😂', 2 ^ 16), string.rep('F09F9882', 2 ^ 16)
+
+ it('hexencode', function()
+ measure(vim.text.hexencode, input, 100)
+ end)
+
+ it('hexdecode', function()
+ measure(vim.text.hexdecode, output, 100)
+ end)
+end)
diff --git a/test/client/msgpack_rpc_stream.lua b/test/client/rpc_stream.lua
index 7131940a58..9f2672bcf9 100644
--- a/test/client/msgpack_rpc_stream.lua
+++ b/test/client/rpc_stream.lua
@@ -1,34 +1,41 @@
+---
+--- Reading/writing of msgpack over any of the stream types from `uv_stream.lua`.
+--- Does not implement the RPC protocol, see `session.lua` for that.
+---
+
local mpack = vim.mpack
local Response = {}
Response.__index = Response
-function Response.new(msgpack_rpc_stream, request_id)
+function Response.new(rpc_stream, request_id)
return setmetatable({
- _msgpack_rpc_stream = msgpack_rpc_stream,
+ _rpc_stream = rpc_stream,
_request_id = request_id,
}, Response)
end
function Response:send(value, is_error)
- local data = self._msgpack_rpc_stream._session:reply(self._request_id)
+ local data = self._rpc_stream._session:reply(self._request_id)
if is_error then
- data = data .. self._msgpack_rpc_stream._pack(value)
- data = data .. self._msgpack_rpc_stream._pack(mpack.NIL)
+ data = data .. self._rpc_stream._pack(value)
+ data = data .. self._rpc_stream._pack(mpack.NIL)
else
- data = data .. self._msgpack_rpc_stream._pack(mpack.NIL)
- data = data .. self._msgpack_rpc_stream._pack(value)
+ data = data .. self._rpc_stream._pack(mpack.NIL)
+ data = data .. self._rpc_stream._pack(value)
end
- self._msgpack_rpc_stream._stream:write(data)
+ self._rpc_stream._stream:write(data)
end
---- @class test.MsgpackRpcStream
+--- Nvim msgpack RPC stream.
+---
+--- @class test.RpcStream
--- @field private _stream test.Stream
--- @field private __pack table
-local MsgpackRpcStream = {}
-MsgpackRpcStream.__index = MsgpackRpcStream
+local RpcStream = {}
+RpcStream.__index = RpcStream
-function MsgpackRpcStream.new(stream)
+function RpcStream.new(stream)
return setmetatable({
_stream = stream,
_pack = mpack.Packer(),
@@ -50,10 +57,10 @@ function MsgpackRpcStream.new(stream)
},
}),
}),
- }, MsgpackRpcStream)
+ }, RpcStream)
end
-function MsgpackRpcStream:write(method, args, response_cb)
+function RpcStream:write(method, args, response_cb)
local data
if response_cb then
assert(type(response_cb) == 'function')
@@ -66,10 +73,10 @@ function MsgpackRpcStream:write(method, args, response_cb)
self._stream:write(data)
end
-function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
+function RpcStream:read_start(on_request, on_notification, on_eof)
self._stream:read_start(function(data)
if not data then
- return eof_cb()
+ return on_eof()
end
local type, id_or_cb, method_or_error, args_or_result
local pos = 1
@@ -78,9 +85,9 @@ function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
type, id_or_cb, method_or_error, args_or_result, pos = self._session:receive(data, pos)
if type == 'request' or type == 'notification' then
if type == 'request' then
- request_cb(method_or_error, args_or_result, Response.new(self, id_or_cb))
+ on_request(method_or_error, args_or_result, Response.new(self, id_or_cb))
else
- notification_cb(method_or_error, args_or_result)
+ on_notification(method_or_error, args_or_result)
end
elseif type == 'response' then
if method_or_error == mpack.NIL then
@@ -94,12 +101,12 @@ function MsgpackRpcStream:read_start(request_cb, notification_cb, eof_cb)
end)
end
-function MsgpackRpcStream:read_stop()
+function RpcStream:read_stop()
self._stream:read_stop()
end
-function MsgpackRpcStream:close(signal)
+function RpcStream:close(signal)
self._stream:close(signal)
end
-return MsgpackRpcStream
+return RpcStream
diff --git a/test/client/session.lua b/test/client/session.lua
index f1f46c5efe..a5839e012a 100644
--- a/test/client/session.lua
+++ b/test/client/session.lua
@@ -1,12 +1,18 @@
+---
+--- Nvim msgpack-RPC protocol session. Manages requests/notifications/responses.
+---
+
local uv = vim.uv
-local MsgpackRpcStream = require('test.client.msgpack_rpc_stream')
+local RpcStream = require('test.client.rpc_stream')
+--- Nvim msgpack-RPC protocol session. Manages requests/notifications/responses.
+---
--- @class test.Session
---- @field private _pending_messages string[]
---- @field private _msgpack_rpc_stream test.MsgpackRpcStream
+--- @field private _pending_messages string[] Requests/notifications received from the remote end.
+--- @field private _rpc_stream test.RpcStream
--- @field private _prepare uv.uv_prepare_t
--- @field private _timer uv.uv_timer_t
---- @field private _is_running boolean
+--- @field private _is_running boolean true during `Session:run()` scope.
--- @field exec_lua_setup boolean
local Session = {}
Session.__index = Session
@@ -51,9 +57,10 @@ local function coroutine_exec(func, ...)
end))
end
+--- Creates a new msgpack-RPC session.
function Session.new(stream)
return setmetatable({
- _msgpack_rpc_stream = MsgpackRpcStream.new(stream),
+ _rpc_stream = RpcStream.new(stream),
_pending_messages = {},
_prepare = uv.new_prepare(),
_timer = uv.new_timer(),
@@ -91,10 +98,13 @@ function Session:next_message(timeout)
return table.remove(self._pending_messages, 1)
end
+--- Sends a notification to the RPC endpoint.
function Session:notify(method, ...)
- self._msgpack_rpc_stream:write(method, { ... })
+ self._rpc_stream:write(method, { ... })
end
+--- Sends a request to the RPC endpoint.
+---
--- @param method string
--- @param ... any
--- @return boolean, table
@@ -114,8 +124,16 @@ function Session:request(method, ...)
return true, result
end
---- Runs the event loop.
+--- Processes incoming RPC requests/notifications until exhausted.
+---
+--- TODO(justinmk): luaclient2 avoids this via uvutil.cb_wait() + uvutil.add_idle_call()?
+---
+--- @param request_cb function Handles requests from the sever to the local end.
+--- @param notification_cb function Handles notifications from the sever to the local end.
+--- @param setup_cb function
+--- @param timeout number
function Session:run(request_cb, notification_cb, setup_cb, timeout)
+ --- Handles an incoming request.
local function on_request(method, args, response)
coroutine_exec(request_cb, method, args, function(status, result, flag)
if status then
@@ -126,6 +144,7 @@ function Session:run(request_cb, notification_cb, setup_cb, timeout)
end)
end
+ --- Handles an incoming notification.
local function on_notification(method, args)
coroutine_exec(notification_cb, method, args)
end
@@ -160,39 +179,45 @@ function Session:close(signal)
if not self._prepare:is_closing() then
self._prepare:close()
end
- self._msgpack_rpc_stream:close(signal)
+ self._rpc_stream:close(signal)
self.closed = true
end
+--- Sends a request to the RPC endpoint, without blocking (schedules a coroutine).
function Session:_yielding_request(method, args)
return coroutine.yield(function(co)
- self._msgpack_rpc_stream:write(method, args, function(err, result)
+ self._rpc_stream:write(method, args, function(err, result)
resume(co, err, result)
end)
end)
end
+--- Sends a request to the RPC endpoint, and blocks (polls event loop) until a response is received.
function Session:_blocking_request(method, args)
local err, result
+ -- Invoked when a request is received from the remote end.
local function on_request(method_, args_, response)
table.insert(self._pending_messages, { 'request', method_, args_, response })
end
+ -- Invoked when a notification is received from the remote end.
local function on_notification(method_, args_)
table.insert(self._pending_messages, { 'notification', method_, args_ })
end
- self._msgpack_rpc_stream:write(method, args, function(e, r)
+ self._rpc_stream:write(method, args, function(e, r)
err = e
result = r
uv.stop()
end)
+ -- Poll for incoming requests/notifications received from the remote end.
self:_run(on_request, on_notification)
return (err or self.eof_err), result
end
+--- Polls for incoming requests/notifications received from the remote end.
function Session:_run(request_cb, notification_cb, timeout)
if type(timeout) == 'number' then
self._prepare:start(function()
@@ -202,14 +227,21 @@ function Session:_run(request_cb, notification_cb, timeout)
self._prepare:stop()
end)
end
- self._msgpack_rpc_stream:read_start(request_cb, notification_cb, function()
+ self._rpc_stream:read_start(request_cb, notification_cb, function()
uv.stop()
- self.eof_err = { 1, 'EOF was received from Nvim. Likely the Nvim process crashed.' }
+
+ --- @diagnostic disable-next-line: invisible
+ local stderr = self._rpc_stream._stream.stderr --[[@as string?]]
+ -- See if `ProcStream.stderr` has anything useful.
+ stderr = '' ~= ((stderr or ''):match('^%s*(.*%S)') or '') and ' stderr:\n' .. stderr or ''
+
+ self.eof_err = { 1, 'EOF was received from Nvim. Likely the Nvim process crashed.' .. stderr }
end)
uv.run()
self._prepare:stop()
self._timer:stop()
- self._msgpack_rpc_stream:read_stop()
+ self._rpc_stream:read_stop()
end
+--- Nvim msgpack-RPC session.
return Session
diff --git a/test/client/uv_stream.lua b/test/client/uv_stream.lua
index adf002ba1e..6e1a6995be 100644
--- a/test/client/uv_stream.lua
+++ b/test/client/uv_stream.lua
@@ -1,3 +1,8 @@
+---
+--- Basic stream types.
+--- See `rpc_stream.lua` for the msgpack layer.
+---
+
local uv = vim.uv
--- @class test.Stream
@@ -6,6 +11,8 @@ local uv = vim.uv
--- @field read_stop fun(self)
--- @field close fun(self, signal?: string)
+--- Stream over given pipes.
+---
--- @class vim.StdioStream : test.Stream
--- @field private _in uv.uv_pipe_t
--- @field private _out uv.uv_pipe_t
@@ -45,6 +52,8 @@ function StdioStream:close()
self._out:close()
end
+--- Stream over a named pipe or TCP socket.
+---
--- @class test.SocketStream : test.Stream
--- @field package _stream_error? string
--- @field package _socket uv.uv_pipe_t
@@ -109,26 +118,54 @@ function SocketStream:close()
uv.close(self._socket)
end
---- @class test.ChildProcessStream : test.Stream
+--- Stream over child process stdio.
+---
+--- @class test.ProcStream : test.Stream
--- @field private _proc uv.uv_process_t
--- @field private _pid integer
--- @field private _child_stdin uv.uv_pipe_t
--- @field private _child_stdout uv.uv_pipe_t
+--- @field private _child_stderr uv.uv_pipe_t
+--- Collects stdout (if `collect_text=true`). Treats data as text (CRLF converted to LF).
+--- @field stdout string
+--- Collects stderr as raw data.
+--- @field stderr string
+--- Gets stderr+stdout as text (CRLF converted to LF).
+--- @field output fun(): string
+--- @field stdout_eof boolean
+--- @field stderr_eof boolean
+--- Collects text into the `stdout` field.
+--- @field collect_text boolean
+--- Exit code
--- @field status integer
--- @field signal integer
-local ChildProcessStream = {}
-ChildProcessStream.__index = ChildProcessStream
+local ProcStream = {}
+ProcStream.__index = ProcStream
+--- Starts child process specified by `argv`.
+---
--- @param argv string[]
--- @param env string[]?
--- @param io_extra uv.uv_pipe_t?
---- @return test.ChildProcessStream
-function ChildProcessStream.spawn(argv, env, io_extra)
+--- @return test.ProcStream
+function ProcStream.spawn(argv, env, io_extra)
local self = setmetatable({
- _child_stdin = uv.new_pipe(false),
- _child_stdout = uv.new_pipe(false),
+ collect_text = false,
+ output = function(self)
+ if not self.collect_text then
+ error('set collect_text=true')
+ end
+ return (self.stderr .. self.stdout):gsub('\r\n', '\n')
+ end,
+ stdout = '',
+ stderr = '',
+ stdout_eof = false,
+ stderr_eof = false,
+ _child_stdin = assert(uv.new_pipe(false)),
+ _child_stdout = assert(uv.new_pipe(false)),
+ _child_stderr = assert(uv.new_pipe(false)),
_exiting = false,
- }, ChildProcessStream)
+ }, ProcStream)
local prog = argv[1]
local args = {} --- @type string[]
for i = 2, #argv do
@@ -136,13 +173,14 @@ function ChildProcessStream.spawn(argv, env, io_extra)
end
--- @diagnostic disable-next-line:missing-fields
self._proc, self._pid = uv.spawn(prog, {
- stdio = { self._child_stdin, self._child_stdout, 1, io_extra },
+ stdio = { self._child_stdin, self._child_stdout, self._child_stderr, io_extra },
args = args,
--- @diagnostic disable-next-line:assign-type-mismatch
env = env,
}, function(status, signal)
- self.status = status
self.signal = signal
+ -- "Abort" exit may not set status; force to nonzero in that case.
+ self.status = (0 ~= (status or 0) or 0 == (signal or 0)) and status or (128 + (signal or 0))
end)
if not self._proc then
@@ -153,24 +191,54 @@ function ChildProcessStream.spawn(argv, env, io_extra)
return self
end
-function ChildProcessStream:write(data)
+function ProcStream:write(data)
self._child_stdin:write(data)
end
-function ChildProcessStream:read_start(cb)
- self._child_stdout:read_start(function(err, chunk)
- if err then
- error(err)
+function ProcStream:on_read(stream, cb, err, chunk)
+ if err then
+ error(err) -- stream read failed?
+ elseif chunk then
+ -- Always collect stderr, in case it gives useful info on failure.
+ if stream == 'stderr' then
+ self.stderr = self.stderr .. chunk --[[@as string]]
+ elseif stream == 'stdout' and self.collect_text then
+ -- Set `stdout` and convert CRLF => LF.
+ self.stdout = (self.stdout .. chunk):gsub('\r\n', '\n')
end
+ else
+ -- stderr_eof/stdout_eof
+ self[stream .. '_eof'] = true ---@type boolean
+ end
+
+ -- Handler provided by the caller.
+ if cb then
cb(chunk)
+ end
+end
+
+--- Collects output until the process exits.
+function ProcStream:wait()
+ while not (self.stdout_eof and self.stderr_eof and (self.status or self.signal)) do
+ uv.run('once')
+ end
+end
+
+function ProcStream:read_start(on_stdout, on_stderr)
+ self._child_stdout:read_start(function(err, chunk)
+ self:on_read('stdout', on_stdout, err, chunk)
+ end)
+ self._child_stderr:read_start(function(err, chunk)
+ self:on_read('stderr', on_stderr, err, chunk)
end)
end
-function ChildProcessStream:read_stop()
+function ProcStream:read_stop()
self._child_stdout:read_stop()
+ self._child_stderr:read_stop()
end
-function ChildProcessStream:close(signal)
+function ProcStream:close(signal)
if self._closed then
return
end
@@ -178,6 +246,7 @@ function ChildProcessStream:close(signal)
self:read_stop()
self._child_stdin:close()
self._child_stdout:close()
+ self._child_stderr:close()
if type(signal) == 'string' then
self._proc:kill('sig' .. signal)
end
@@ -189,6 +258,6 @@ end
return {
StdioStream = StdioStream,
- ChildProcessStream = ChildProcessStream,
+ ProcStream = ProcStream,
SocketStream = SocketStream,
}
diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua
index 3f9883f43f..64b28e7bf7 100644
--- a/test/functional/api/autocmd_spec.lua
+++ b/test/functional/api/autocmd_spec.lua
@@ -273,54 +273,72 @@ describe('autocmd api', function()
eq({}, api.nvim_get_autocmds({ event = 'User', pattern = 'Test' }))
end)
- it('receives an args table', function()
+ local function test_autocmd_args(event)
+ local function get_amatch(pat)
+ return event == 'User' and pat or vim.fs.normalize(n.fn.fnamemodify(pat, ':p'))
+ end
+
local group_id = api.nvim_create_augroup('TestGroup', {})
-- Having an existing autocmd calling expand("<afile>") shouldn't change args #18964
- api.nvim_create_autocmd('User', {
+ api.nvim_create_autocmd(event, {
group = 'TestGroup',
pattern = 'Te*',
command = 'call expand("<afile>")',
})
- local autocmd_id = exec_lua [[
- return vim.api.nvim_create_autocmd("User", {
+ local autocmd_id = exec_lua(([[
+ return vim.api.nvim_create_autocmd(%q, {
group = "TestGroup",
pattern = "Te*",
callback = function(args)
vim.g.autocmd_args = args
end,
})
- ]]
+ ]]):format(event))
- api.nvim_exec_autocmds('User', { pattern = 'Test pattern' })
+ local exec_pat = 'Test pattern'
+ local amatch = get_amatch(exec_pat)
+ api.nvim_exec_autocmds(event, { pattern = exec_pat })
eq({
id = autocmd_id,
group = group_id,
- event = 'User',
- match = 'Test pattern',
- file = 'Test pattern',
+ event = event,
+ match = amatch,
+ file = exec_pat,
buf = 1,
}, api.nvim_get_var('autocmd_args'))
-- Test without a group
- autocmd_id = exec_lua [[
- return vim.api.nvim_create_autocmd("User", {
+ autocmd_id = exec_lua(([[
+ return vim.api.nvim_create_autocmd(%q, {
pattern = "*",
callback = function(args)
vim.g.autocmd_args = args
end,
})
- ]]
+ ]]):format(event))
- api.nvim_exec_autocmds('User', { pattern = 'some_pat' })
+ exec_pat = 'some_pat'
+ amatch = get_amatch(exec_pat)
+ api.nvim_exec_autocmds(event, { pattern = exec_pat })
eq({
id = autocmd_id,
group = nil,
- event = 'User',
- match = 'some_pat',
- file = 'some_pat',
+ event = event,
+ match = amatch,
+ file = exec_pat,
buf = 1,
}, api.nvim_get_var('autocmd_args'))
+ end
+
+ describe('receives correct args table', function()
+ it('for event that takes non-file pattern', function()
+ test_autocmd_args('User')
+ end)
+
+ it('for event that takes file pattern', function()
+ test_autocmd_args('BufEnter')
+ end)
end)
it('can receive arbitrary data', function()
@@ -881,6 +899,89 @@ describe('autocmd api', function()
eq([[:echo "Buffer"]], normalized_aus[1].command)
end)
end)
+
+ describe('id', function()
+ it('gets events by ID', function()
+ local id = api.nvim_create_autocmd('BufEnter', {
+ command = 'echo "hello"',
+ })
+ eq({
+ {
+ buflocal = false,
+ command = 'echo "hello"',
+ event = 'BufEnter',
+ id = id,
+ once = false,
+ pattern = '*',
+ },
+ }, api.nvim_get_autocmds({ id = id }))
+ end)
+
+ it('gets events by ID by other filters', function()
+ local group_name = 'NVIM_GET_AUTOCMDS_ID'
+ local group = api.nvim_create_augroup(group_name, { clear = true })
+ local id = api.nvim_create_autocmd('BufEnter', {
+ command = 'set number',
+ group = group,
+ })
+ api.nvim_create_autocmd('WinEnter', {
+ group = group,
+ command = 'set cot&',
+ })
+ eq({
+ {
+ buflocal = false,
+ command = 'set number',
+ event = 'BufEnter',
+ group = group,
+ group_name = group_name,
+ id = id,
+ once = false,
+ pattern = '*',
+ },
+ }, api.nvim_get_autocmds({ id = id, group = group }))
+ end)
+
+ it('gets events by ID and a specific event', function()
+ local id = api.nvim_create_autocmd('InsertEnter', { command = 'set number' })
+ api.nvim_create_autocmd('InsertEnter', { command = 'set wrap' })
+ eq({
+ {
+ buflocal = false,
+ command = 'set number',
+ event = 'InsertEnter',
+ id = id,
+ once = false,
+ pattern = '*',
+ },
+ }, api.nvim_get_autocmds({ id = id, event = 'InsertEnter' }))
+ end)
+
+ it('gets events by ID and a specific pattern', function()
+ local id = api.nvim_create_autocmd('InsertEnter', {
+ pattern = '*.c',
+ command = 'set number',
+ })
+ api.nvim_create_autocmd('InsertEnter', {
+ pattern = '*.c',
+ command = 'set wrap',
+ })
+ eq({
+ {
+ buflocal = false,
+ command = 'set number',
+ event = 'InsertEnter',
+ id = id,
+ once = false,
+ pattern = '*.c',
+ },
+ }, api.nvim_get_autocmds({ id = id, pattern = '*.c' }))
+ end)
+
+ it('empty result when id does not found', function()
+ eq({}, api.nvim_get_autocmds({ id = 255 }))
+ end)
+ end)
end)
describe('nvim_exec_autocmds', function()
diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua
index 527394bfd1..489f601d31 100644
--- a/test/functional/api/buffer_updates_spec.lua
+++ b/test/functional/api/buffer_updates_spec.lua
@@ -879,7 +879,8 @@ describe('API: buffer events:', function()
it('when :terminal lines change', function()
local buffer_lines = {}
local expected_lines = {}
- fn.termopen({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '-n', '-c', 'set shortmess+=A' }, {
+ fn.jobstart({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '-n', '-c', 'set shortmess+=A' }, {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
index a16c6a88e3..fabd9be6d6 100644
--- a/test/functional/api/command_spec.lua
+++ b/test/functional/api/command_spec.lua
@@ -651,6 +651,11 @@ describe('nvim_create_user_command', function()
api.nvim_set_current_buf(bufnr)
command('Hello')
assert_alive()
+ eq(
+ 'Invalid buffer id: 1234',
+ pcall_err(api.nvim_buf_create_user_command, 1234, 'Hello', '', {})
+ )
+ assert_alive()
end)
it('can use a Lua complete function', function()
@@ -771,5 +776,9 @@ describe('nvim_del_user_command', function()
command('Hello')
api.nvim_buf_del_user_command(0, 'Hello')
matches('Not an editor command: Hello', pcall_err(command, 'Hello'))
+ eq('Invalid command (not found): Hello', pcall_err(api.nvim_buf_del_user_command, 0, 'Hello'))
+ eq('Invalid command (not found): Bye', pcall_err(api.nvim_buf_del_user_command, 0, 'Bye'))
+ eq('Invalid buffer id: 1234', pcall_err(api.nvim_buf_del_user_command, 1234, 'Hello'))
+ assert_alive()
end)
end)
diff --git a/test/functional/api/deprecated_spec.lua b/test/functional/api/deprecated_spec.lua
new file mode 100644
index 0000000000..2efcfda873
--- /dev/null
+++ b/test/functional/api/deprecated_spec.lua
@@ -0,0 +1,21 @@
+-- Island of misfit toys.
+--- @diagnostic disable: deprecated
+
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+
+describe('deprecated', function()
+ before_each(n.clear)
+
+ describe('nvim_notify', function()
+ it('can notify a info message', function()
+ n.api.nvim_notify('hello world', 2, {})
+ end)
+
+ it('can be overridden', function()
+ n.command('lua vim.notify = function(...) return 42 end')
+ t.eq(42, n.api.nvim_exec_lua("return vim.notify('Hello world')", {}))
+ n.api.nvim_notify('hello world', 4, {})
+ end)
+ end)
+end)
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index 43be0c0e43..8a4aea1efe 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -249,7 +249,7 @@ describe('API/extmarks', function()
set_extmark(ns, 2, 1, 0, { right_gravity = false })
eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
feed('u')
- eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
+ eq({ { 1, 0, 0 }, { 2, 0, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 }))
api.nvim_buf_clear_namespace(0, ns, 0, -1)
end)
@@ -1731,7 +1731,7 @@ describe('API/extmarks', function()
-- mark with invalidate is removed
command('d2')
screen:expect([[
- S2^aaa bbb ccc |
+ {7:S2}^aaa bbb ccc |
{7: }aaa bbb ccc |*3
{7: } |
|
@@ -1739,9 +1739,9 @@ describe('API/extmarks', function()
-- mark is restored with undo_restore == true
command('silent undo')
screen:expect([[
- S1{7: }^aaa bbb ccc |
- S2S1aaa bbb ccc |
- S2{7: }aaa bbb ccc |
+ {7:S1 }^aaa bbb ccc |
+ {7:S2S1}aaa bbb ccc |
+ {7:S2 }aaa bbb ccc |
{7: }aaa bbb ccc |*2
|
]])
@@ -1794,6 +1794,16 @@ describe('API/extmarks', function()
eq({}, get_extmark_by_id(ns, 4, {}))
end)
+ it('no crash checking invalidated flag of sign pair end key #31856', function()
+ api.nvim_buf_set_lines(0, 0, 1, false, { '', '' })
+ api.nvim_set_option_value('signcolumn', 'auto:2', {})
+ set_extmark(ns, 1, 0, 0, { sign_text = 'S1', invalidate = true, end_row = 0 })
+ set_extmark(ns, 2, 1, 0, { sign_text = 'S2', end_row = 1 })
+ command('d')
+ api.nvim_buf_clear_namespace(0, ns, 0, -1)
+ n.assert_alive()
+ end)
+
it('can set a URL', function()
local url1 = 'https://example.com'
local url2 = 'http://127.0.0.1'
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index dd0611f184..8f98ae3650 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -309,6 +309,15 @@ describe('API: set highlight', function()
eq({ underdotted = true }, api.nvim_get_hl_by_name('Test_hl', true))
end)
+ it('can set all underline cterm attributes #31385', function()
+ local ns = get_ns()
+ local attrs = { 'underline', 'undercurl', 'underdouble', 'underdotted', 'underdashed' }
+ for _, attr in ipairs(attrs) do
+ api.nvim_set_hl(ns, 'Test_' .. attr, { cterm = { [attr] = true } })
+ eq({ [attr] = true }, api.nvim_get_hl_by_name('Test_' .. attr, false))
+ end
+ end)
+
it('can set a highlight in the global namespace', function()
api.nvim_set_hl(0, 'Test_hl', highlight2_config)
eq(
@@ -710,4 +719,18 @@ describe('API: set/get highlight namespace', function()
api.nvim_win_set_hl_ns(0, ns)
eq(ns, api.nvim_get_hl_ns({ winid = 0 }))
end)
+
+ it('setting namespace takes priority over &winhighlight', function()
+ command('set winhighlight=Visual:Search')
+ n.insert('foobar')
+ local ns = api.nvim_create_namespace('')
+ api.nvim_win_set_hl_ns(0, ns)
+ eq(ns, api.nvim_get_hl_ns({ winid = 0 }))
+ command('enew') -- switching buffer keeps namespace #30904
+ eq(ns, api.nvim_get_hl_ns({ winid = 0 }))
+ command('set winhighlight=')
+ eq(ns, api.nvim_get_hl_ns({ winid = 0 }))
+ command('set winhighlight=Visual:Search')
+ eq(ns, api.nvim_get_hl_ns({ winid = 0 }))
+ end)
end)
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index bdd340f6c6..c022ba28de 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -9,7 +9,6 @@ local nvim_prog, command, fn = n.nvim_prog, n.command, n.fn
local source, next_msg = n.source, n.next_msg
local ok = t.ok
local api = n.api
-local spawn, merge_args = n.spawn, n.merge_args
local set_session = n.set_session
local pcall_err = t.pcall_err
local assert_alive = n.assert_alive
@@ -281,10 +280,9 @@ describe('server -> client', function()
end)
describe('connecting to another (peer) nvim', function()
- local nvim_argv = merge_args(n.nvim_argv, { '--headless' })
local function connect_test(server, mode, address)
local serverpid = fn.getpid()
- local client = spawn(nvim_argv, false, nil, true)
+ local client = n.new_session(true)
set_session(client)
local clientpid = fn.getpid()
@@ -312,7 +310,7 @@ describe('server -> client', function()
end
it('via named pipe', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local address = fn.serverlist()[1]
local first = string.sub(address, 1, 1)
@@ -321,7 +319,7 @@ describe('server -> client', function()
end)
it('via ipv4 address', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local status, address = pcall(fn.serverstart, '127.0.0.1:')
if not status then
@@ -332,7 +330,7 @@ describe('server -> client', function()
end)
it('via ipv6 address', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local status, address = pcall(fn.serverstart, '::1:')
if not status then
@@ -343,7 +341,7 @@ describe('server -> client', function()
end)
it('via hostname', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local address = fn.serverstart('localhost:')
eq('localhost:', string.sub(address, 1, 10))
@@ -351,10 +349,10 @@ describe('server -> client', function()
end)
it('does not crash on receiving UI events', function()
- local server = spawn(nvim_argv)
+ local server = n.new_session(false)
set_session(server)
local address = fn.serverlist()[1]
- local client = spawn(nvim_argv, false, nil, true)
+ local client = n.new_session(true)
set_session(client)
local id = fn.sockconnect('pipe', address, { rpc = true })
diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua
index 617786eb2d..68c4ef7503 100644
--- a/test/functional/api/version_spec.lua
+++ b/test/functional/api/version_spec.lua
@@ -43,7 +43,7 @@ describe("api_info()['version']", function()
eq(0, fn.has('nvim-' .. major .. '.' .. minor .. '.' .. (patch + 1)))
eq(0, fn.has('nvim-' .. major .. '.' .. (minor + 1) .. '.' .. patch))
eq(0, fn.has('nvim-' .. (major + 1) .. '.' .. minor .. '.' .. patch))
- assert(build == nil or type(build) == 'string')
+ assert(build == vim.NIL or type(build) == 'string')
end)
end)
@@ -93,28 +93,28 @@ describe('api metadata', function()
local function clean_level_0(metadata)
for _, f in ipairs(metadata.functions) do
f.can_fail = nil
- f.async = nil
+ f.async = nil -- XXX: renamed to "fast".
f.receives_channel_id = nil
f.since = 0
end
end
- local api_info, compat, stable, api_level
+ local api_info --[[@type table]]
+ local compat --[[@type integer]]
+ local stable --[[@type integer]]
+ local api_level --[[@type integer]]
local old_api = {}
setup(function()
clear() -- Ensure a session before requesting api_info.
+ --[[@type { version: {api_compatible: integer, api_level: integer, api_prerelease: boolean} }]]
api_info = api.nvim_get_api_info()[2]
compat = api_info.version.api_compatible
api_level = api_info.version.api_level
- if api_info.version.api_prerelease then
- stable = api_level - 1
- else
- stable = api_level
- end
+ stable = api_info.version.api_prerelease and api_level - 1 or api_level
for level = compat, stable do
local path = ('test/functional/fixtures/api_level_' .. tostring(level) .. '.mpack')
- old_api[level] = read_mpack_file(path)
+ old_api[level] = read_mpack_file(path) --[[@type table]]
if old_api[level] == nil then
local errstr = 'missing metadata fixture for stable level ' .. level .. '. '
if level == api_level and not api_info.version.api_prerelease then
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 3f1e378bc1..3aa9ba49d5 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -96,12 +96,19 @@ describe('API', function()
assert_alive()
end)
- it('input is processed first when followed immediately by non-fast events', function()
+ it('input is processed first if followed immediately by non-fast events', function()
api.nvim_set_current_line('ab')
async_meths.nvim_input('x')
async_meths.nvim_exec_lua('_G.res1 = vim.api.nvim_get_current_line()', {})
async_meths.nvim_exec_lua('_G.res2 = vim.api.nvim_get_current_line()', {})
eq({ 'b', 'b' }, exec_lua('return { _G.res1, _G.res2 }'))
+ -- Also test with getchar()
+ async_meths.nvim_command('let g:getchar = 1 | call getchar() | let g:getchar = 0')
+ eq(1, api.nvim_get_var('getchar'))
+ async_meths.nvim_input('x')
+ async_meths.nvim_exec_lua('_G.res1 = vim.g.getchar', {})
+ async_meths.nvim_exec_lua('_G.res2 = vim.g.getchar', {})
+ eq({ 0, 0 }, exec_lua('return { _G.res1, _G.res2 }'))
end)
it('does not set CA_COMMAND_BUSY #7254', function()
@@ -695,7 +702,7 @@ describe('API', function()
pcall_err(request, 'nvim_call_dict_function', 42, 'f', { 1, 2 })
)
eq(
- 'Failed to evaluate dict expression',
+ 'Vim:E121: Undefined variable: foo',
pcall_err(request, 'nvim_call_dict_function', 'foo', 'f', { 1, 2 })
)
eq('dict not found', pcall_err(request, 'nvim_call_dict_function', '42', 'f', { 1, 2 }))
@@ -781,18 +788,6 @@ describe('API', function()
end)
end)
- describe('nvim_notify', function()
- it('can notify a info message', function()
- api.nvim_notify('hello world', 2, {})
- end)
-
- it('can be overridden', function()
- command('lua vim.notify = function(...) return 42 end')
- eq(42, api.nvim_exec_lua("return vim.notify('Hello world')", {}))
- api.nvim_notify('hello world', 4, {})
- end)
- end)
-
describe('nvim_input', function()
it('Vimscript error: does NOT fail, updates v:errmsg', function()
local status, _ = pcall(api.nvim_input, ':call bogus_fn()<CR>')
@@ -1770,6 +1765,11 @@ describe('API', function()
end)
it('validation', function()
+ eq("Unknown option 'foobar'", pcall_err(api.nvim_set_option_value, 'foobar', 'baz', {}))
+ eq(
+ "Unknown option 'foobar'",
+ pcall_err(api.nvim_set_option_value, 'foobar', 'baz', { win = api.nvim_get_current_win() })
+ )
eq(
"Invalid 'scope': expected 'local' or 'global'",
pcall_err(api.nvim_get_option_value, 'scrolloff', { scope = 'bogus' })
@@ -1952,6 +1952,16 @@ describe('API', function()
api.nvim_set_current_win(api.nvim_list_wins()[2])
eq(api.nvim_list_wins()[2], api.nvim_get_current_win())
end)
+
+ it('failure modes', function()
+ n.command('split')
+
+ eq('Invalid window id: 9999', pcall_err(api.nvim_set_current_win, 9999))
+
+ -- XXX: force nvim_set_current_win to fail somehow.
+ n.command("au WinLeave * throw 'foo'")
+ eq('WinLeave Autocommands for "*": foo', pcall_err(api.nvim_set_current_win, 1000))
+ end)
end)
describe('nvim_{get,set}_current_tabpage, nvim_list_tabpages', function()
@@ -1971,6 +1981,16 @@ describe('API', function()
eq(api.nvim_list_tabpages()[2], api.nvim_get_current_tabpage())
eq(api.nvim_list_wins()[2], api.nvim_get_current_win())
end)
+
+ it('failure modes', function()
+ n.command('tabnew')
+
+ eq('Invalid tabpage id: 999', pcall_err(api.nvim_set_current_tabpage, 999))
+
+ -- XXX: force nvim_set_current_tabpage to fail somehow.
+ n.command("au TabLeave * throw 'foo'")
+ eq('TabLeave Autocommands for "*": foo', pcall_err(api.nvim_set_current_tabpage, 1))
+ end)
end)
describe('nvim_get_mode', function()
@@ -2679,7 +2699,8 @@ describe('API', function()
-- :terminal with args + running process.
command('enew')
local progpath_esc = eval('shellescape(v:progpath)')
- fn.termopen(('%s -u NONE -i NONE'):format(progpath_esc), {
+ fn.jobstart(('%s -u NONE -i NONE'):format(progpath_esc), {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running?
@@ -3654,6 +3675,30 @@ describe('API', function()
async_meths.nvim_echo({ { 'msg\nmsg' }, { 'msg' } }, false, {})
eq('', exec_capture('messages'))
end)
+
+ it('can print error message', function()
+ async_meths.nvim_echo({ { 'Error\nMessage' } }, false, { err = true })
+ screen:expect([[
+ |
+ {1:~ }|*3
+ {3: }|
+ {9:Error} |
+ {9:Message} |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed(':messages<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*6
+ |
+ ]])
+ async_meths.nvim_echo({ { 'Error' }, { 'Message', 'Special' } }, false, { err = true })
+ screen:expect([[
+ ^ |
+ {1:~ }|*6
+ {9:Error}{16:Message} |
+ ]])
+ end)
end)
describe('nvim_open_term', function()
@@ -3760,7 +3805,7 @@ describe('API', function()
screen:expect {
grid = [[
|
- {1:~}{102: }{4: }{1: }|
+ {1:~}{4:^ }{1: }|
{1:~}{4: }{1: }|*4
{1:~ }|*3
{5:-- TERMINAL --} |
@@ -3776,7 +3821,7 @@ describe('API', function()
screen:expect {
grid = [[
|
- {1:~}{4:herrejösses!}{102: }{4: }{1: }|
+ {1:~}{4:herrejösses!^ }{1: }|
{1:~}{4: }{1: }|*4
{1:~ }|*3
{5:-- TERMINAL --} |
@@ -3955,8 +4000,8 @@ describe('API', function()
str = 'TextWithWarningHighlightTextWithUserHighlight',
width = 45,
highlights = {
- { start = 0, group = 'WarningMsg' },
- { start = 24, group = 'User1' },
+ { start = 0, group = 'WarningMsg', groups = { 'StatusLine', 'WarningMsg' } },
+ { start = 24, group = 'User1', groups = { 'StatusLine', 'User1' } },
},
},
api.nvim_eval_statusline(
@@ -3971,7 +4016,7 @@ describe('API', function()
str = 'TextWithNoHighlight',
width = 19,
highlights = {
- { start = 0, group = 'StatusLine' },
+ { start = 0, group = 'StatusLine', groups = { 'StatusLine' } },
},
}, api.nvim_eval_statusline('TextWithNoHighlight', { highlights = true }))
end)
@@ -3983,8 +4028,8 @@ describe('API', function()
str = 'TextWithNoHighlightTextWithWarningHighlight',
width = 43,
highlights = {
- { start = 0, group = 'StatusLineNC' },
- { start = 19, group = 'WarningMsg' },
+ { start = 0, group = 'StatusLineNC', groups = { 'StatusLineNC' } },
+ { start = 19, group = 'WarningMsg', groups = { 'StatusLineNC', 'WarningMsg' } },
},
},
api.nvim_eval_statusline(
@@ -4000,8 +4045,8 @@ describe('API', function()
str = 'TextWithNoHighlightTextWithWarningHighlight',
width = 43,
highlights = {
- { start = 0, group = 'TabLineFill' },
- { start = 19, group = 'WarningMsg' },
+ { start = 0, group = 'TabLineFill', groups = { 'TabLineFill' } },
+ { start = 19, group = 'WarningMsg', groups = { 'TabLineFill', 'WarningMsg' } },
},
},
api.nvim_eval_statusline(
@@ -4017,8 +4062,8 @@ describe('API', function()
str = 'TextWithNoHighlightTextWithWarningHighlight',
width = 43,
highlights = {
- { start = 0, group = 'WinBar' },
- { start = 19, group = 'WarningMsg' },
+ { start = 0, group = 'WinBar', groups = { 'WinBar' } },
+ { start = 19, group = 'WarningMsg', groups = { 'WinBar', 'WarningMsg' } },
},
},
api.nvim_eval_statusline(
@@ -4045,11 +4090,11 @@ describe('API', function()
str = '││bbaa 4 ',
width = 9,
highlights = {
- { group = 'CursorLineFold', start = 0 },
- { group = 'Normal', start = 6 },
- { group = 'ErrorMsg', start = 6 },
- { group = 'IncSearch', start = 8 },
- { group = 'Normal', start = 10 },
+ { group = 'CursorLineFold', start = 0, groups = { 'CursorLineFold' } },
+ { group = 'Normal', start = 6, groups = { 'Normal' } },
+ { group = 'ErrorMsg', start = 6, groups = { 'CursorLineSign', 'ErrorMsg' } },
+ { group = 'IncSearch', start = 8, groups = { 'CursorLineSign', 'IncSearch' } },
+ { group = 'Normal', start = 10, groups = { 'Normal' } },
},
}, api.nvim_eval_statusline(
'%C%s%=%l ',
@@ -4060,8 +4105,8 @@ describe('API', function()
str = ' 3 ',
width = 9,
highlights = {
- { group = 'LineNr', start = 0 },
- { group = 'ErrorMsg', start = 8 },
+ { group = 'LineNr', start = 0, groups = { 'LineNr' } },
+ { group = 'ErrorMsg', start = 8, groups = { 'LineNr', 'ErrorMsg' } },
},
},
api.nvim_eval_statusline('%l%#ErrorMsg# ', { use_statuscol_lnum = 3, highlights = true })
@@ -5359,8 +5404,53 @@ describe('API', function()
13 |
]],
})
- -- takes buffer line count from correct buffer with "win" and {0, -1} "range"
- api.nvim__redraw({ win = 0, range = { 0, -1 } })
+ end)
+
+ it('nvim__redraw range parameter', function()
+ Screen.new(10, 5)
+ fn.setline(1, fn.range(4))
+
+ exec_lua([[
+ _G.lines_list = {}
+ ns = vim.api.nvim_create_namespace('')
+ vim.api.nvim_set_decoration_provider(ns, {
+ on_win = function()
+ end,
+ on_line = function(_, _, _, line)
+ table.insert(_G.lines_list, line)
+ end,
+ })
+ function _G.get_lines()
+ local lines = _G.lines_list
+ _G.lines_list = {}
+ return lines
+ end
+ ]])
+
+ api.nvim__redraw({ flush = true, valid = false })
+ exec_lua('_G.get_lines()')
+
+ local actual_lines = {}
+ local function test(range)
+ api.nvim__redraw({ win = 0, range = range })
+ table.insert(actual_lines, exec_lua('return _G.get_lines()'))
+ end
+
+ test({ 0, -1 })
+ test({ 2, 2 ^ 31 })
+ test({ 2, 2 ^ 32 })
+ test({ 2 ^ 31 - 1, 2 })
+ test({ 2 ^ 32 - 1, 2 })
+
+ local expected_lines = {
+ { 0, 1, 2, 3 },
+ { 2, 3 },
+ { 2, 3 },
+ {},
+ {},
+ }
+ eq(expected_lines, actual_lines)
+
n.assert_alive()
end)
end)
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 92999f383a..028f0beb38 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -359,6 +359,15 @@ describe('API/win', function()
eq(2, api.nvim_win_get_height(api.nvim_list_wins()[2]))
end)
+ it('failure modes', function()
+ command('split')
+ eq('Invalid window id: 999999', pcall_err(api.nvim_win_set_height, 999999, 10))
+ eq(
+ 'Wrong type for argument 2 when calling nvim_win_set_height, expecting Integer',
+ pcall_err(api.nvim_win_set_height, 0, 0.9)
+ )
+ end)
+
it('correctly handles height=1', function()
command('split')
api.nvim_set_current_win(api.nvim_list_wins()[1])
@@ -409,6 +418,15 @@ describe('API/win', function()
eq(2, api.nvim_win_get_width(api.nvim_list_wins()[2]))
end)
+ it('failure modes', function()
+ command('vsplit')
+ eq('Invalid window id: 999999', pcall_err(api.nvim_win_set_width, 999999, 10))
+ eq(
+ 'Wrong type for argument 2 when calling nvim_win_set_width, expecting Integer',
+ pcall_err(api.nvim_win_set_width, 0, 0.9)
+ )
+ end)
+
it('do not cause ml_get errors with foldmethod=expr #19989', function()
insert([[
aaaaa
@@ -488,6 +506,48 @@ describe('API/win', function()
assert_alive()
end)
+ describe('after closing', function()
+ local buf, win0, win1, win2
+
+ before_each(function()
+ win0 = api.nvim_get_current_win()
+ command('new')
+ buf = api.nvim_get_current_buf()
+ win1 = api.nvim_get_current_win()
+ command('set numberwidth=10')
+ command('split')
+ win2 = api.nvim_get_current_win()
+ command('set numberwidth=15')
+ command('enew')
+ api.nvim_set_current_win(win1)
+ command('normal ix')
+ command('enew')
+ api.nvim_set_current_win(win0)
+ eq(4, api.nvim_get_option_value('numberwidth', {}))
+ end)
+
+ -- at this point buffer `buf` is current in no windows. Closing shouldn't affect its defaults
+ it('0 windows', function()
+ api.nvim_set_current_buf(buf)
+ eq(10, api.nvim_get_option_value('numberwidth', {}))
+ end)
+
+ it('1 window', function()
+ api.nvim_win_close(win1, false)
+
+ api.nvim_set_current_buf(buf)
+ eq(10, api.nvim_get_option_value('numberwidth', {}))
+ end)
+
+ it('2 windows', function()
+ api.nvim_win_close(win1, false)
+ api.nvim_win_close(win2, false)
+
+ api.nvim_set_current_buf(buf)
+ eq(10, api.nvim_get_option_value('numberwidth', {}))
+ end)
+ end)
+
it('returns values for unset local options', function()
eq(-1, api.nvim_get_option_value('scrolloff', { win = 0, scope = 'local' }))
end)
@@ -1664,7 +1724,7 @@ describe('API/win', function()
autocmd BufWinEnter * ++once let fired = v:true
]])
eq(
- 'Failed to set buffer 2',
+ 'Vim:E37: No write since last change (add ! to override)',
pcall_err(api.nvim_open_win, api.nvim_create_buf(true, true), false, { split = 'left' })
)
eq(false, eval('fired'))
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index c62e4752e0..1b7275ebf6 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -160,7 +160,7 @@ describe('autocmd', function()
it('++once', function() -- :help autocmd-once
--
- -- ":autocmd ... ++once" executes its handler once, then removes the handler.
+ -- ":autocmd … ++once" executes its handler once, then removes the handler.
--
local expected = {
'Many1',
@@ -206,7 +206,7 @@ describe('autocmd', function()
)
--
- -- ":autocmd ... ++once" handlers can be deleted.
+ -- ":autocmd … ++once" handlers can be deleted.
--
expected = {}
command('let g:foo = []')
@@ -216,7 +216,7 @@ describe('autocmd', function()
eq(expected, eval('g:foo'))
--
- -- ":autocmd ... <buffer> ++once ++nested"
+ -- ":autocmd … <buffer> ++once ++nested"
--
expected = {
'OptionSet-Once',
@@ -250,6 +250,24 @@ describe('autocmd', function()
--- Autocommands ---]]),
fn.execute('autocmd Tabnew')
)
+
+ --
+ -- :autocmd does not recursively call ++once Lua handlers.
+ --
+ exec_lua [[vim.g.count = 0]]
+ eq(0, eval('g:count'))
+ exec_lua [[
+ vim.api.nvim_create_autocmd('User', {
+ once = true,
+ pattern = nil,
+ callback = function()
+ vim.g.count = vim.g.count + 1
+ vim.api.nvim_exec_autocmds('User', { pattern = nil })
+ end,
+ })
+ vim.api.nvim_exec_autocmds('User', { pattern = nil })
+ ]]
+ eq(1, eval('g:count'))
end)
it('internal `aucmd_win` window', function()
diff --git a/test/functional/autocmd/completedone_spec.lua b/test/functional/autocmd/completedone_spec.lua
index 33beb16db2..36dc73842d 100644
--- a/test/functional/autocmd/completedone_spec.lua
+++ b/test/functional/autocmd/completedone_spec.lua
@@ -32,7 +32,7 @@ describe('CompleteDone', function()
feed('<Esc>')
eq('cancel', eval('g:donereason'))
end)
- it('when overriden by another complete()', function()
+ it('when overridden by another complete()', function()
call('complete', call('col', '.'), { 'bar', 'baz' })
eq('cancel', eval('g:donereason'))
end)
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
index 1cde0e0552..9b572df568 100644
--- a/test/functional/autocmd/dirchanged_spec.lua
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -351,11 +351,10 @@ describe('autocmd DirChanged and DirChangedPre', function()
eq(2, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
- local status, err = pcall(function()
- request('nvim_set_current_dir', '/doesnotexist')
- end)
- eq(false, status)
- eq('Failed to change directory', string.match(err, ': (.*)'))
+ eq(
+ 'Vim:E344: Can\'t find directory "/doesnotexist" in cdpath',
+ t.pcall_err(request, 'nvim_set_current_dir', '/doesnotexist')
+ )
eq({ directory = '/doesnotexist', scope = 'global', changed_window = false }, eval('g:evpre'))
eq(3, eval('g:cdprecount'))
eq(2, eval('g:cdcount'))
diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua
index 7f6092bf48..177e8997cf 100644
--- a/test/functional/autocmd/focus_spec.lua
+++ b/test/functional/autocmd/focus_spec.lua
@@ -47,7 +47,7 @@ describe('autoread TUI FocusGained/FocusLost', function()
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -57,7 +57,7 @@ describe('autoread TUI FocusGained/FocusLost', function()
feed_command('edit ' .. path)
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:xtest-foo }|
:edit xtest-foo |
@@ -68,7 +68,7 @@ describe('autoread TUI FocusGained/FocusLost', function()
feed_data('\027[O')
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:xtest-foo }|
:edit xtest-foo |
@@ -83,7 +83,7 @@ describe('autoread TUI FocusGained/FocusLost', function()
screen:expect {
grid = [[
- {1:l}ine 1 |
+ ^line 1 |
line 2 |
line 3 |
line 4 |
diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua
index baf2bb6071..950ef2f58a 100644
--- a/test/functional/autocmd/termxx_spec.lua
+++ b/test/functional/autocmd/termxx_spec.lua
@@ -213,9 +213,11 @@ describe('autocmd TextChangedT', function()
end)
it('cannot delete terminal buffer', function()
- command([[autocmd TextChangedT * call nvim_input('<CR>') | bwipe!]])
+ command('autocmd TextChangedT * bwipe!')
tt.feed_data('a')
screen:expect({ any = 'E937: ' })
+ feed('<CR>')
+ command('autocmd! TextChangedT')
matches(
'^E937: Attempt to delete a buffer that is in use: term://',
api.nvim_get_vvar('errmsg')
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua
index dee13d19ae..7b10eb05ef 100644
--- a/test/functional/core/channels_spec.lua
+++ b/test/functional/core/channels_spec.lua
@@ -5,7 +5,6 @@ local clear, eq, eval, next_msg, ok, source = n.clear, t.eq, n.eval, n.next_msg,
local command, fn, api = n.command, n.fn, n.api
local matches = t.matches
local sleep = vim.uv.sleep
-local spawn, nvim_argv = n.spawn, n.nvim_argv
local get_session, set_session = n.get_session, n.set_session
local nvim_prog = n.nvim_prog
local is_os = t.is_os
@@ -33,10 +32,10 @@ describe('channels', function()
end)
pending('can connect to socket', function()
- local server = spawn(nvim_argv, nil, nil, true)
+ local server = n.new_session(true)
set_session(server)
local address = fn.serverlist()[1]
- local client = spawn(nvim_argv, nil, nil, true)
+ local client = n.new_session(true)
set_session(client)
source(init)
@@ -63,7 +62,7 @@ describe('channels', function()
it('dont crash due to garbage in rpc #23781', function()
local client = get_session()
- local server = spawn(nvim_argv, nil, nil, true)
+ local server = n.new_session(true)
set_session(server)
local address = fn.serverlist()[1]
set_session(client)
diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua
index 34c3eedbd2..65f6bc28a6 100644
--- a/test/functional/core/exit_spec.lua
+++ b/test/functional/core/exit_spec.lua
@@ -8,8 +8,6 @@ local feed = n.feed
local eval = n.eval
local eq = t.eq
local run = n.run
-local fn = n.fn
-local nvim_prog = n.nvim_prog
local pcall_err = t.pcall_err
local exec_capture = n.exec_capture
local poke_eventloop = n.poke_eventloop
@@ -69,8 +67,8 @@ describe(':cquit', function()
poke_eventloop()
assert_alive()
else
- fn.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline })
- eq(exit_code, eval('v:shell_error'))
+ local p = n.spawn_wait('--cmd', cmdline)
+ eq(exit_code, p.status)
end
end
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
index cf9715f848..b1a8e21762 100644
--- a/test/functional/core/fileio_spec.lua
+++ b/test/functional/core/fileio_spec.lua
@@ -31,7 +31,6 @@ local feed_command = n.feed_command
local skip = t.skip
local is_os = t.is_os
local is_ci = t.is_ci
-local spawn = n.spawn
local set_session = n.set_session
describe('fileio', function()
@@ -51,12 +50,11 @@ describe('fileio', function()
rmdir('Xtest_backupdir with spaces')
end)
- local args = { nvim_prog, '--clean', '--cmd', 'set nofsync directory=Xtest_startup_swapdir' }
+ local args = { '--clean', '--cmd', 'set nofsync directory=Xtest_startup_swapdir' }
--- Starts a new nvim session and returns an attached screen.
- local function startup(extra_args)
- extra_args = extra_args or {}
- local argv = vim.iter({ args, '--embed', extra_args }):flatten():totable()
- local screen_nvim = spawn(argv)
+ local function startup()
+ local argv = vim.iter({ args, '--embed' }):flatten():totable()
+ local screen_nvim = n.new_session(false, { args = argv, merge = false })
set_session(screen_nvim)
local screen = Screen.new(70, 10)
screen:set_default_attr_ids({
@@ -100,7 +98,8 @@ describe('fileio', function()
eq('foozubbaz', trim(read_file('Xtest_startup_file1')))
-- 4. Exit caused by deadly signal (+ 'swapfile').
- local j = fn.jobstart(vim.iter({ args, '--embed' }):flatten():totable(), { rpc = true })
+ local j =
+ fn.jobstart(vim.iter({ nvim_prog, args, '--embed' }):flatten():totable(), { rpc = true })
fn.rpcrequest(
j,
'nvim_exec2',
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index 618c294566..e833b5127d 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -65,6 +65,39 @@ describe('jobs', function()
]])
end)
+ it('validation', function()
+ matches(
+ "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set",
+ pcall_err(command, "call jobstart(['cat', '-'], { 'pty': v:true, 'rpc': v:true })")
+ )
+ matches(
+ 'E475: Invalid argument: expected valid directory',
+ pcall_err(command, "call jobstart(['cat', '-'], { 'cwd': 9313843 })")
+ )
+ matches(
+ 'E475: Invalid argument: expected valid directory',
+ pcall_err(command, "call jobstart(['cat', '-'], { 'cwd': 'bogusssssss/bogus' })")
+ )
+ matches(
+ "E475: Invalid argument: 'term' must be Boolean",
+ pcall_err(command, "call jobstart(['cat', '-'], { 'term': 'bogus' })")
+ )
+ matches(
+ "E475: Invalid argument: 'term' must be Boolean",
+ pcall_err(command, "call jobstart(['cat', '-'], { 'term': 1 })")
+ )
+ command('set modified')
+ matches(
+ vim.pesc('jobstart(...,{term=true}) requires unmodified buffer'),
+ pcall_err(command, "call jobstart(['cat', '-'], { 'term': v:true })")
+ )
+
+ -- Non-failure cases:
+ command('set nomodified')
+ command("call jobstart(['cat', '-'], { 'term': v:true })")
+ command("call jobstart(['cat', '-'], { 'term': v:false })")
+ end)
+
it('must specify env option as a dict', function()
command('let g:job_opts.env = v:true')
local _, err = pcall(function()
@@ -940,6 +973,39 @@ describe('jobs', function()
feed('<CR>')
fn.jobstop(api.nvim_get_var('id'))
end)
+
+ it('does not set UI busy with zero timeout #31712', function()
+ local screen = Screen.new(50, 6)
+ command([[let g:id = jobstart(['sleep', '0.3'])]])
+ local busy = 0
+ screen._handle_busy_start = (function(orig)
+ return function()
+ orig(screen)
+ busy = busy + 1
+ end
+ end)(screen._handle_busy_start)
+ source([[
+ func PrintAndPoll()
+ echon "aaa\nbbb"
+ call jobwait([g:id], 0)
+ echon "\nccc"
+ endfunc
+ ]])
+ feed_command('call PrintAndPoll()')
+ screen:expect {
+ grid = [[
+ |
+ {3: }|
+ aaa |
+ bbb |
+ ccc |
+ {6:Press ENTER or type command to continue}^ |
+ ]],
+ }
+ feed('<CR>')
+ fn.jobstop(api.nvim_get_var('id'))
+ eq(0, busy)
+ end)
end)
pending('exit event follows stdout, stderr', function()
@@ -969,13 +1035,6 @@ describe('jobs', function()
eq({ 'notification', 'exit', { 0, 143 } }, next_msg())
end)
- it('cannot have both rpc and pty options', function()
- command('let g:job_opts.pty = v:true')
- command('let g:job_opts.rpc = v:true')
- local _, err = pcall(command, "let j = jobstart(['cat', '-'], g:job_opts)")
- matches("E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set", err)
- end)
-
it('does not crash when repeatedly failing to start shell', function()
source([[
set shell=nosuchshell
@@ -1198,7 +1257,7 @@ describe('jobs', function()
})
-- Wait for startup to complete, so that all terminal responses are received.
screen:expect([[
- {1: } |
+ ^ |
~ |*3
{1:[No Name] 0,0-1 All}|
|
@@ -1208,7 +1267,7 @@ describe('jobs', function()
feed(':q<CR>')
screen:expect([[
|
- [Process exited 0]{1: } |
+ [Process exited 0]^ |
|*4
{3:-- TERMINAL --} |
]])
@@ -1230,7 +1289,7 @@ describe('pty process teardown', function()
it('does not prevent/delay exit. #4798 #4900', function()
skip(fn.executable('sleep') == 0, 'missing "sleep" command')
-- Use a nested nvim (in :term) to test without --headless.
- fn.termopen({
+ fn.jobstart({
n.nvim_prog,
'-u',
'NONE',
@@ -1243,7 +1302,10 @@ describe('pty process teardown', function()
'+terminal',
'+!(sleep 300 &)',
'+qa',
- }, { env = { VIMRUNTIME = os.getenv('VIMRUNTIME') } })
+ }, {
+ term = true,
+ env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
+ })
-- Exiting should terminate all descendants (PTY, its children, ...).
screen:expect([[
diff --git a/test/functional/core/log_spec.lua b/test/functional/core/log_spec.lua
index 57dfd6364c..571bece833 100644
--- a/test/functional/core/log_spec.lua
+++ b/test/functional/core/log_spec.lua
@@ -46,7 +46,7 @@ describe('log', function()
env = env,
})
screen:expect([[
- {1: } |
+ ^ |
~ |*4
|
{3:-- TERMINAL --} |
diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua
index a445423efc..65a7c556b8 100644
--- a/test/functional/core/main_spec.lua
+++ b/test/functional/core/main_spec.lua
@@ -9,7 +9,6 @@ local feed = n.feed
local eval = n.eval
local clear = n.clear
local fn = n.fn
-local nvim_prog_abs = n.nvim_prog_abs
local write_file = t.write_file
local is_os = t.is_os
local skip = t.skip
@@ -35,7 +34,7 @@ describe('command-line option', function()
it('treats - as stdin', function()
eq(nil, uv.fs_stat(fname))
fn.system({
- nvim_prog_abs(),
+ n.nvim_prog,
'-u',
'NONE',
'-i',
@@ -56,41 +55,39 @@ describe('command-line option', function()
eq(nil, uv.fs_stat(fname))
eq(true, not not dollar_fname:find('%$%w+'))
write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n')
- fn.system({
- nvim_prog_abs(),
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
+ local p = n.spawn_wait(
'--cmd',
'set noswapfile shortmess+=IFW fileformats=unix',
'-s',
dollar_fname,
- fname,
- })
- eq(0, eval('v:shell_error'))
+ fname
+ )
+ eq(0, p.status)
local attrs = uv.fs_stat(fname)
eq(#'100500\n', attrs.size)
end)
- it('does not crash when run completion in ex mode', function()
- fn.system({
- nvim_prog_abs(),
- '--clean',
- '-e',
- '-s',
- '--cmd',
- 'exe "norm! i\\<C-X>\\<C-V>"',
- })
- eq(0, eval('v:shell_error'))
+ it('does not crash when running completion in Ex mode', function()
+ local p =
+ n.spawn_wait('--clean', '-e', '-s', '--cmd', 'exe "norm! i\\<C-X>\\<C-V>"', '--cmd', 'qa!')
+ eq(0, p.status)
+ end)
+
+ it('does not crash when running completion from -l script', function()
+ local lua_fname = 'Xinscompl.lua'
+ write_file(lua_fname, [=[vim.cmd([[exe "norm! i\<C-X>\<C-V>"]])]=])
+ finally(function()
+ os.remove(lua_fname)
+ end)
+ local p = n.spawn_wait('--clean', '-l', lua_fname)
+ eq(0, p.status)
end)
it('does not crash after reading from stdin in non-headless mode', function()
skip(is_os('win'))
local screen = Screen.new(40, 8)
local args = {
- nvim_prog_abs(),
+ n.nvim_prog,
'-u',
'NONE',
'-i',
@@ -103,7 +100,8 @@ describe('command-line option', function()
-- Need to explicitly pipe to stdin so that the embedded Nvim instance doesn't try to read
-- data from the terminal #18181
- fn.termopen(string.format([[echo "" | %s]], table.concat(args, ' ')), {
+ fn.jobstart(string.format([[echo "" | %s]], table.concat(args, ' ')), {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
screen:expect(
@@ -120,7 +118,7 @@ describe('command-line option', function()
feed('i:cq<CR>')
screen:expect([[
|
- [Process exited 1]{2: } |
+ [Process exited 1]^ |
|*5
{5:-- TERMINAL --} |
]])
@@ -137,60 +135,49 @@ describe('command-line option', function()
]=]
end)
- it('errors out when trying to use nonexistent file with -s', function()
+ it('fails when trying to use nonexistent file with -s', function()
+ local p = n.spawn_wait(
+ '--cmd',
+ 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd',
+ 'language C',
+ '-s',
+ nonexistent_fname
+ )
eq(
'Cannot open for reading: "' .. nonexistent_fname .. '": no such file or directory\n',
- fn.system({
- nvim_prog_abs(),
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--cmd',
- 'set noswapfile shortmess+=IFW fileformats=unix',
- '--cmd',
- 'language C',
- '-s',
- nonexistent_fname,
- })
+ --- TODO(justinmk): using `p.output` because Nvim emits CRLF even on non-Win. Emit LF instead?
+ p:output()
)
- eq(2, eval('v:shell_error'))
+ eq(2, p.status)
end)
it('errors out when trying to use -s twice', function()
write_file(fname, ':call setline(1, "1")\n:wqall!\n')
write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n')
- eq(
- 'Attempt to open script file again: "-s ' .. dollar_fname .. '"\n',
- fn.system({
- nvim_prog_abs(),
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--cmd',
- 'set noswapfile shortmess+=IFW fileformats=unix',
- '--cmd',
- 'language C',
- '-s',
- fname,
- '-s',
- dollar_fname,
- fname_2,
- })
+ local p = n.spawn_wait(
+ '--cmd',
+ 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd',
+ 'language C',
+ '-s',
+ fname,
+ '-s',
+ dollar_fname,
+ fname_2
)
- eq(2, eval('v:shell_error'))
+ --- TODO(justinmk): using `p.output` because Nvim emits CRLF even on non-Win. Emit LF instead?
+ eq('Attempt to open script file again: "-s ' .. dollar_fname .. '"\n', p:output())
+ eq(2, p.status)
eq(nil, uv.fs_stat(fname_2))
end)
end)
it('nvim -v, :version', function()
matches('Run ":verbose version"', fn.execute(':version'))
- matches('Compilation: .*Run :checkhealth', fn.execute(':verbose version'))
- matches('Run "nvim %-V1 %-v"', fn.system({ nvim_prog_abs(), '-v' }))
- matches('Compilation: .*Run :checkhealth', fn.system({ nvim_prog_abs(), '-V1', '-v' }))
+ matches('fall%-back for %$VIM: .*Run :checkhealth', fn.execute(':verbose version'))
+ matches('Run "nvim %-V1 %-v"', n.spawn_wait('-v').stdout)
+ matches('fall%-back for %$VIM: .*Run :checkhealth', n.spawn_wait('-V1', '-v').stdout)
end)
if is_os('win') then
@@ -204,7 +191,7 @@ describe('command-line option', function()
eq(
'some text',
fn.system({
- nvim_prog_abs(),
+ n.nvim_prog,
'-es',
'+%print',
'+q',
diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua
index 6cc28ddeef..1cfa0535f6 100644
--- a/test/functional/core/remote_spec.lua
+++ b/test/functional/core/remote_spec.lua
@@ -10,10 +10,8 @@ local expect = n.expect
local fn = n.fn
local insert = n.insert
local nvim_prog = n.nvim_prog
-local new_argv = n.new_argv
local neq = t.neq
local set_session = n.set_session
-local spawn = n.spawn
local tmpname = t.tmpname
local write_file = t.write_file
@@ -32,8 +30,7 @@ describe('Remote', function()
describe('connect to server and', function()
local server
before_each(function()
- server = spawn(new_argv(), true)
- set_session(server)
+ server = n.clear()
end)
after_each(function()
@@ -49,7 +46,7 @@ describe('Remote', function()
-- to wait for the remote instance to exit and calling jobwait blocks
-- the event loop. If the server event loop is blocked, it can't process
-- our incoming --remote calls.
- local client_starter = spawn(new_argv(), false, nil, true)
+ local client_starter = n.new_session(true)
set_session(client_starter)
-- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
eq(
@@ -144,15 +141,8 @@ describe('Remote', function()
describe('exits with error on', function()
local function run_and_check_exit_code(...)
- local bogus_argv = new_argv(...)
-
- -- Create an nvim instance just to run the remote-invoking nvim. We want
- -- to wait for the remote instance to exit and calling jobwait blocks
- -- the event loop. If the server event loop is blocked, it can't process
- -- our incoming --remote calls.
- clear()
- -- 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))
+ local p = n.spawn_wait { args = { ... } }
+ eq(2, p.status)
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 7062211187..8ecd3dca97 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -55,7 +55,10 @@ describe('startup', function()
clear()
local screen
screen = Screen.new(84, 3)
- fn.termopen({ nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' })
+ fn.jobstart(
+ { nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' },
+ { term = true }
+ )
screen:expect([[
^Cannot attach UI of :terminal child to its parent. (Unset $NVIM to skip this check) |
|*2
@@ -74,22 +77,9 @@ describe('startup', function()
end)
it('--startuptime does not crash on error #31125', function()
- eq(
- "E484: Can't open file .",
- fn.system({
- nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--startuptime',
- '.',
- '-c',
- '42cquit',
- })
- )
- eq(42, api.nvim_get_vvar('shell_error'))
+ local p = n.spawn_wait('--startuptime', '.', '-c', '42cquit')
+ eq("E484: Can't open file .", p.stderr)
+ eq(42, p.status)
end)
it('-D does not hang #12647', function()
@@ -98,7 +88,7 @@ describe('startup', function()
screen = Screen.new(60, 7)
-- not the same colors on windows for some reason
screen._default_attr_ids = nil
- local id = fn.termopen({
+ local id = fn.jobstart({
nvim_prog,
'-u',
'NONE',
@@ -108,6 +98,7 @@ describe('startup', function()
'set noruler',
'-D',
}, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -145,13 +136,18 @@ describe('startup', function()
vim.list_extend(args, { '-l', (script or 'test/functional/fixtures/startup.lua') })
vim.list_extend(args, lua_args or {})
local out = fn.system(args, input):gsub('\r\n', '\n')
- return eq(dedent(expected), out)
+ if type(expected) == 'function' then
+ return expected(out)
+ else
+ return eq(dedent(expected), out)
+ end
end
it('failure modes', function()
-- nvim -l <empty>
- matches('nvim%.?e?x?e?: Argument missing after: "%-l"', fn.system({ nvim_prog, '-l' }))
- eq(1, eval('v:shell_error'))
+ local proc = n.spawn_wait('-l')
+ matches('nvim%.?e?x?e?: Argument missing after: "%-l"', proc.stderr)
+ eq(1, proc.status)
end)
it('os.exit() sets Nvim exitcode', function()
@@ -178,13 +174,13 @@ describe('startup', function()
end)
it('Lua-error sets Nvim exitcode', function()
+ local proc = n.spawn_wait('-l', 'test/functional/fixtures/startup-fail.lua')
+ matches('E5113: .* my pearls!!', proc:output())
+ eq(0, proc.signal)
+ eq(1, proc.status)
+
eq(0, eval('v:shell_error'))
matches(
- 'E5113: .* my pearls!!',
- fn.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' })
- )
- eq(1, eval('v:shell_error'))
- matches(
'E5113: .* %[string "error%("whoa"%)"%]:1: whoa',
fn.system({ nvim_prog, '-l', '-' }, 'error("whoa")')
)
@@ -291,14 +287,30 @@ describe('startup', function()
eq(0, eval('v:shell_error'))
end)
- it('disables swapfile/shada/config/plugins', function()
+ it('disables swapfile/shada/config/plugins unless overridden', function()
+ local script = [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format(
+ vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]]
+ finally(function()
+ os.remove('Xtest_shada')
+ end)
+
assert_l_out(
'updatecount=0 shadafile=NONE loadplugins=false scripts=1\n',
nil,
nil,
'-',
- [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format(
- vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]]
+ script
+ )
+
+ -- User can override.
+ assert_l_out(
+ function(out)
+ return matches('updatecount=99 shadafile=Xtest_shada loadplugins=true scripts=2%d\n', out)
+ end,
+ { '+set updatecount=99', '-i', 'Xtest_shada', '+set loadplugins', '-u', 'NORC' },
+ nil,
+ '-',
+ script
)
end)
end)
@@ -343,7 +355,7 @@ describe('startup', function()
local screen = Screen.new(25, 3)
-- Remote UI connected by --embed.
-- TODO: a lot of tests in this file already use the new default color scheme.
- -- once we do the batch update of tests to use it, remove this workarond
+ -- once we do the batch update of tests to use it, remove this workaround
screen._default_attr_ids = nil
command([[echo has('ttyin') has('ttyout')]])
screen:expect([[
@@ -360,7 +372,7 @@ describe('startup', function()
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
- fn.termopen({
+ fn.jobstart({
nvim_prog,
'-u',
'NONE',
@@ -371,6 +383,7 @@ describe('startup', function()
'-c',
'echo has("ttyin") has("ttyout")',
}, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -393,13 +406,14 @@ describe('startup', function()
os.remove('Xtest_startup_ttyout')
end)
-- Running in :terminal
- fn.termopen(
+ fn.jobstart(
(
[["%s" -u NONE -i NONE --cmd "%s"]]
.. [[ -c "call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')"]]
.. [[ -c q | cat -v]]
):format(nvim_prog, nvim_set),
{
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -424,7 +438,7 @@ describe('startup', function()
os.remove('Xtest_startup_ttyout')
end)
-- Running in :terminal
- fn.termopen(
+ fn.jobstart(
(
[[echo foo | ]] -- Input from a pipe.
.. [["%s" -u NONE -i NONE --cmd "%s"]]
@@ -432,6 +446,7 @@ describe('startup', function()
.. [[ -c q -- -]]
):format(nvim_prog, nvim_set),
{
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -454,13 +469,14 @@ describe('startup', function()
command([[set shellcmdflag=/s\ /c shellxquote=\"]])
end
-- Running in :terminal
- fn.termopen(
+ fn.jobstart(
(
[[echo foo | ]]
.. [["%s" -u NONE -i NONE --cmd "%s"]]
.. [[ -c "echo has('ttyin') has('ttyout')"]]
):format(nvim_prog, nvim_set),
{
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -577,19 +593,21 @@ describe('startup', function()
eq(' encoding=utf-8\n', fn.system({ nvim_prog, '-n', '-es' }, { 'set encoding', '' }))
end)
- it('-es/-Es disables swapfile, user config #8540', function()
+ it('-es/-Es disables swapfile/shada/config #8540', function()
for _, arg in ipairs({ '-es', '-Es' }) do
local out = fn.system({
nvim_prog,
arg,
- '+set swapfile? updatecount? shadafile?',
+ '+set updatecount? shadafile? loadplugins?',
'+put =map(getscriptinfo(), {-> v:val.name})',
'+%print',
})
local line1 = string.match(out, '^.-\n')
-- updatecount=0 means swapfile was disabled.
- eq(' swapfile updatecount=0 shadafile=\n', line1)
- -- Standard plugins were loaded, but not user config.
+ eq(' updatecount=0 shadafile=NONE loadplugins\n', line1)
+ -- Standard plugins were loaded, but not user config. #31878
+ local nrlines = #vim.split(out, '\n')
+ ok(nrlines > 20, '>20', nrlines)
ok(string.find(out, 'man.lua') ~= nil)
ok(string.find(out, 'init.vim') == nil)
end
@@ -598,15 +616,15 @@ describe('startup', function()
it('fails on --embed with -es/-Es/-l', function()
matches(
'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
- fn.system({ nvim_prog, '--embed', '-es' })
+ n.spawn_wait('--embed', '-es').stderr
)
matches(
'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
- fn.system({ nvim_prog, '--embed', '-Es' })
+ n.spawn_wait('--embed', '-Es').stderr
)
matches(
'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l',
- fn.system({ nvim_prog, '--embed', '-l', 'foo.lua' })
+ n.spawn_wait('--embed', '-l', 'foo.lua').stderr
)
end)
@@ -614,7 +632,7 @@ describe('startup', function()
local screen
screen = Screen.new(60, 6)
screen._default_attr_ids = nil
- local id = fn.termopen({
+ local id = fn.jobstart({
nvim_prog,
'-u',
'NONE',
@@ -625,6 +643,7 @@ describe('startup', function()
'--cmd',
'let g:foo = g:bar',
}, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -689,20 +708,8 @@ describe('startup', function()
end)
it('get command line arguments from v:argv', function()
- local out = fn.system({
- nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--headless',
- '--cmd',
- nvim_set,
- '-c',
- [[echo v:argv[-1:] len(v:argv) > 1]],
- '+q',
- })
- eq("['+q'] 1", out)
+ local p = n.spawn_wait('--cmd', nvim_set, '-c', [[echo v:argv[-1:] len(v:argv) > 1]], '+q')
+ eq("['+q'] 1", p.stderr)
end)
end)
@@ -1144,7 +1151,8 @@ describe('user config init', function()
local screen = Screen.new(50, 8)
screen._default_attr_ids = nil
- fn.termopen({ nvim_prog }, {
+ fn.jobstart({ nvim_prog }, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -1153,7 +1161,7 @@ describe('user config init', function()
-- `i` to enter Terminal mode, `a` to allow
feed('ia')
screen:expect([[
- |
+ ^ |
~ |*4
[No Name] 0,0-1 All|
|
@@ -1162,7 +1170,7 @@ describe('user config init', function()
feed(':echo g:exrc_file<CR>')
screen:expect(string.format(
[[
- |
+ ^ |
~ |*4
[No Name] 0,0-1 All|
%s%s|
@@ -1418,7 +1426,7 @@ describe('inccommand on ex mode', function()
clear()
local screen
screen = Screen.new(60, 10)
- local id = fn.termopen({
+ local id = fn.jobstart({
nvim_prog,
'-u',
'NONE',
@@ -1429,6 +1437,7 @@ describe('inccommand on ex mode', function()
'-E',
'test/README.md',
}, {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
fn.chansend(id, '%s/N')
diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua
index 030181764d..7c68de272b 100644
--- a/test/functional/editor/completion_spec.lua
+++ b/test/functional/editor/completion_spec.lua
@@ -10,6 +10,7 @@ local fn = n.fn
local command = n.command
local api = n.api
local poke_eventloop = n.poke_eventloop
+local exec_lua = n.exec_lua
describe('completion', function()
local screen
@@ -1023,18 +1024,18 @@ describe('completion', function()
it("'ignorecase' 'infercase' CTRL-X CTRL-N #6451", function()
feed_command('set ignorecase infercase')
- feed_command('edit runtime/doc/backers.txt')
+ feed_command('edit runtime/doc/credits.txt')
feed('oX<C-X><C-N>')
screen:expect {
grid = [[
- *backers.txt* Nvim |
- Xnull^ |
- {12:Xnull }{101: } |
- {4:Xoxomoon }{101: } |
- {4:Xu }{101: } NVIM REFERENCE MANUAL |
- {4:Xpayn }{12: } |
- {4:Xinity }{12: } |
- {5:-- Keyword Local completion (^N^P) }{6:match 1 of 7} |
+ *credits.txt* Nvim |
+ Xvi^ |
+ {12:Xvi }{101: } |
+ {4:Xvim }{101: } |
+ {4:X11 }{12: } NVIM REFERENCE MANUAL |
+ {4:Xnull }{12: } |
+ {4:Xoxomoon }{12: } |
+ {5:-- Keyword Local completion (^N^P) }{6:match 1 of 10} |
]],
}
end)
@@ -1265,7 +1266,7 @@ describe('completion', function()
command([[
call setline(1, ['aaaa'])
let ns_id = nvim_create_namespace('extmark')
- let mark_id = nvim_buf_set_extmark(0, ns_id, 0, 0, { 'end_col':2, 'hl_group':'Error'})
+ let mark_id = nvim_buf_set_extmark(0, ns_id, 0, 0, { 'end_col':2, 'hl_group':'Error' })
let mark = nvim_buf_get_extmark_by_id(0, ns_id, mark_id, { 'details':1 })
inoremap <C-x> <C-r>=Complete()<CR>
function Complete() abort
@@ -1302,5 +1303,53 @@ describe('completion', function()
aaaaa |
{5:-- INSERT --} |
]])
+ -- Also when completion leader is changed #31384
+ feed('<Esc>hi<C-N><C-P>a')
+ screen:expect({
+ grid = [[
+ {9:aa}a^aa |
+ {4:aaaa } |
+ {4:aaaaa } |
+ {5:-- Keyword completion (^N^P) }{19:Back at original} |
+ ]],
+ })
+ -- But still grows with end_right_gravity #31437
+ command(
+ "call nvim_buf_set_extmark(0, ns_id, 1, 0, { 'end_col':2, 'hl_group':'Error', 'end_right_gravity': 1 })"
+ )
+ feed('<Esc>ji<C-N>a')
+ screen:expect({
+ grid = [[
+ {9:aa}aaa |
+ {9:aaa}^aa |
+ aaaaa |
+ {5:-- INSERT --} |
+ ]],
+ })
+ end)
+
+ describe('nvim__complete_set', function()
+ it("fails when 'completeopt' does not include popup", function()
+ exec_lua([[
+ function _G.omni_test(findstart, base)
+ if findstart == 1 then
+ return vim.fn.col('.') - 1
+ end
+ return { { word = 'one' } }
+ end
+ vim.api.nvim_create_autocmd('CompleteChanged', {
+ callback = function()
+ local ok, err = pcall(vim.api.nvim__complete_set, 0, { info = '1info' })
+ if not ok then
+ vim.g.err_msg = err
+ end
+ end,
+ })
+ vim.opt.completeopt = 'menu,menuone'
+ vim.opt.omnifunc = 'v:lua.omni_test'
+ ]])
+ feed('S<C-X><C-O>')
+ eq('completeopt option does not include popup', api.nvim_get_var('err_msg'))
+ end)
end)
end)
diff --git a/test/functional/editor/defaults_spec.lua b/test/functional/editor/defaults_spec.lua
index 82d285cc9a..ee6bfa1be9 100644
--- a/test/functional/editor/defaults_spec.lua
+++ b/test/functional/editor/defaults_spec.lua
@@ -32,7 +32,7 @@ describe('default', function()
describe('popupmenu', function()
it('can be disabled by user', function()
n.clear {
- args = { '+autocmd! nvim_popupmenu', '+aunmenu PopUp' },
+ args = { '+autocmd! nvim.popupmenu', '+aunmenu PopUp' },
}
local screen = Screen.new(40, 8)
n.insert([[
@@ -94,8 +94,103 @@ describe('default', function()
end)
describe('key mappings', function()
+ describe('Visual mode search mappings', function()
+ it('handle various chars properly', function()
+ n.clear({ args_rm = { '--cmd' } })
+ local screen = Screen.new(60, 8)
+ screen:set_default_attr_ids({
+ [1] = { foreground = Screen.colors.NvimDarkGray4 },
+ [2] = {
+ foreground = Screen.colors.NvimDarkGray3,
+ background = Screen.colors.NvimLightGray3,
+ },
+ [3] = {
+ foreground = Screen.colors.NvimLightGrey1,
+ background = Screen.colors.NvimDarkYellow,
+ },
+ [4] = {
+ foreground = Screen.colors.NvimDarkGrey1,
+ background = Screen.colors.NvimLightYellow,
+ },
+ })
+ n.api.nvim_buf_set_lines(0, 0, -1, true, {
+ [[testing <CR> /?\!1]],
+ [[testing <CR> /?\!2]],
+ [[testing <CR> /?\!3]],
+ [[testing <CR> /?\!4]],
+ })
+ n.feed('gg0vf!o*')
+ screen:expect([[
+ {3:testing <CR> /?\!}1 |
+ {4:^testing <CR> /?\!}2 |
+ {3:testing <CR> /?\!}3 |
+ {3:testing <CR> /?\!}4 |
+ {1:~ }|*2
+ {2:[No Name] [+] 2,1 All}|
+ /\Vtesting <CR> \/?\\! [2/4] |
+ ]])
+ n.feed('n')
+ screen:expect([[
+ {3:testing <CR> /?\!}1 |
+ {3:testing <CR> /?\!}2 |
+ {4:^testing <CR> /?\!}3 |
+ {3:testing <CR> /?\!}4 |
+ {1:~ }|*2
+ {2:[No Name] [+] 3,1 All}|
+ /\Vtesting <CR> \/?\\! [3/4] |
+ ]])
+ n.feed('G0vf!o#')
+ screen:expect([[
+ {3:testing <CR> /?\!}1 |
+ {3:testing <CR> /?\!}2 |
+ {4:^testing <CR> /?\!}3 |
+ {3:testing <CR> /?\!}4 |
+ {1:~ }|*2
+ {2:[No Name] [+] 3,1 All}|
+ ?\Vtesting <CR> /?\\! [3/4] |
+ ]])
+ n.feed('n')
+ screen:expect([[
+ {3:testing <CR> /?\!}1 |
+ {4:^testing <CR> /?\!}2 |
+ {3:testing <CR> /?\!}3 |
+ {3:testing <CR> /?\!}4 |
+ {1:~ }|*2
+ {2:[No Name] [+] 2,1 All}|
+ ?\Vtesting <CR> /?\\! [2/4] |
+ ]])
+ end)
+ end)
+
describe('unimpaired-style mappings', function()
- it('do not show a full stack trace #30625', function()
+ it('show the command ouptut when successful', function()
+ n.clear({ args_rm = { '--cmd' } })
+ local screen = Screen.new(40, 8)
+ n.fn.setqflist({
+ { filename = 'file1', text = 'item1' },
+ { filename = 'file2', text = 'item2' },
+ })
+
+ n.feed(']q')
+
+ screen:set_default_attr_ids({
+ [1] = { foreground = Screen.colors.NvimDarkGrey4 },
+ [2] = {
+ background = Screen.colors.NvimLightGray3,
+ foreground = Screen.colors.NvimDarkGrey3,
+ },
+ })
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*5
+ {2:file2 0,0-1 All}|
+ (2 of 2): item2 |
+ ]],
+ })
+ end)
+
+ it('do not show a full stack trace when unsuccessful #30625', function()
n.clear({ args_rm = { '--cmd' } })
local screen = Screen.new(40, 8)
screen:set_default_attr_ids({
diff --git a/test/functional/editor/mode_normal_spec.lua b/test/functional/editor/mode_normal_spec.lua
index 5237f51237..353a261edb 100644
--- a/test/functional/editor/mode_normal_spec.lua
+++ b/test/functional/editor/mode_normal_spec.lua
@@ -26,10 +26,10 @@ describe('Normal mode', function()
it('&showcmd does not crash with :startinsert #28419', function()
local screen = Screen.new(60, 17)
- fn.termopen(
- { n.nvim_prog, '--clean', '--cmd', 'startinsert' },
- { env = { VIMRUNTIME = os.getenv('VIMRUNTIME') } }
- )
+ fn.jobstart({ n.nvim_prog, '--clean', '--cmd', 'startinsert' }, {
+ term = true,
+ env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
+ })
screen:expect({
grid = [[
^ |
diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
index 2820cc9663..f84fc140d2 100644
--- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
+++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua
@@ -12,12 +12,10 @@ local fn = n.fn
local nvim_prog = n.nvim_prog
local ok = t.ok
local rmdir = n.rmdir
-local new_argv = n.new_argv
local new_pipename = n.new_pipename
local pesc = vim.pesc
local os_kill = n.os_kill
local set_session = n.set_session
-local spawn = n.spawn
local async_meths = n.async_meths
local expect_msg_seq = n.expect_msg_seq
local pcall_err = t.pcall_err
@@ -56,7 +54,7 @@ describe("preserve and (R)ecover with custom 'directory'", function()
local nvim0
before_each(function()
- nvim0 = spawn(new_argv())
+ nvim0 = n.new_session(false)
set_session(nvim0)
rmdir(swapdir)
mkdir(swapdir)
@@ -76,7 +74,8 @@ describe("preserve and (R)ecover with custom 'directory'", function()
local function test_recover(swappath1)
-- Start another Nvim instance.
- local nvim2 = spawn({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed' }, true)
+ local nvim2 =
+ n.new_session(false, { args = { '-u', 'NONE', '-i', 'NONE', '--embed' }, merge = false })
set_session(nvim2)
exec(init)
@@ -115,7 +114,8 @@ describe("preserve and (R)ecover with custom 'directory'", function()
t.skip(t.is_os('win'))
local screen0 = Screen.new()
local child_server = new_pipename()
- fn.termopen({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--listen', child_server }, {
+ fn.jobstart({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--listen', child_server }, {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
screen0:expect({ any = pesc('[No Name]') }) -- Wait for the child process to start.
@@ -140,7 +140,7 @@ describe('swapfile detection', function()
set swapfile fileformat=unix nomodified undolevels=-1 nohidden
]]
before_each(function()
- nvim0 = spawn(new_argv())
+ nvim0 = n.new_session(false)
set_session(nvim0)
rmdir(swapdir)
mkdir(swapdir)
@@ -167,12 +167,13 @@ describe('swapfile detection', function()
command('preserve')
-- Start another Nvim instance.
- local nvim2 = spawn({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed' }, true, nil, true)
+ local nvim2 =
+ n.new_session(true, { args = { '-u', 'NONE', '-i', 'NONE', '--embed' }, merge = false })
set_session(nvim2)
local screen2 = Screen.new(256, 40)
screen2._default_attr_ids = nil
exec(init)
- command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
+ command('autocmd! nvim.swapfile') -- Delete the default handler (which skips the dialog).
-- With shortmess+=F
command('set shortmess+=F')
@@ -219,6 +220,7 @@ describe('swapfile detection', function()
.. [[%.swp"]],
}
feed('e') -- Chose "Edit" at the swap dialog.
+ screen2:expect({ any = pesc('E5555: API call: Vim(edit):E325: ATTENTION') })
feed('<c-c>')
screen2:expect(expected_no_dialog)
@@ -249,7 +251,7 @@ describe('swapfile detection', function()
command('preserve') -- Make sure the swap file exists.
local nvimpid = fn.getpid()
- local nvim1 = spawn(new_argv(), true, nil, true)
+ local nvim1 = n.new_session(true)
set_session(nvim1)
local screen = Screen.new(75, 18)
exec(init)
@@ -271,11 +273,11 @@ describe('swapfile detection', function()
[1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg
})
- local nvim1 = spawn(new_argv(), true, nil, true)
+ local nvim1 = n.new_session(true)
set_session(nvim1)
screen:attach()
exec(init)
- command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
+ command('autocmd! nvim.swapfile') -- Delete the default handler (which skips the dialog).
feed(':split Xfile1\n')
-- The default SwapExists handler does _not_ skip this prompt.
screen:expect({
@@ -290,11 +292,11 @@ describe('swapfile detection', function()
]])
nvim1:close()
- local nvim2 = spawn(new_argv(), true, nil, true)
+ local nvim2 = n.new_session(true)
set_session(nvim2)
screen:attach()
exec(init)
- command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
+ command('autocmd! nvim.swapfile') -- Delete the default handler (which skips the dialog).
command('set more')
command('au bufadd * let foo_w = wincol()')
feed(':e Xfile1<CR>')
@@ -325,7 +327,7 @@ describe('swapfile detection', function()
exec(init)
if not swapexists then
- command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
+ command('autocmd! nvim.swapfile') -- Delete the default handler (which skips the dialog).
end
command('set nohidden')
@@ -446,9 +448,10 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
end)
it('(Q)uit at first file argument', function()
- local chan = fn.termopen(
+ local chan = fn.jobstart(
{ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', init_dir, '--cmd', init_set, testfile },
{
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
}
)
@@ -468,7 +471,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
end)
it('(A)bort at second file argument with -p', function()
- local chan = fn.termopen({
+ local chan = fn.jobstart({
nvim_prog,
'-u',
'NONE',
@@ -482,6 +485,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
otherfile,
testfile,
}, {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
retry(nil, nil, function()
@@ -508,7 +512,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
second %s /^ \zssecond$/
third %s /^ \zsthird$/]]):format(testfile, testfile, testfile)
)
- local chan = fn.termopen({
+ local chan = fn.jobstart({
nvim_prog,
'-u',
'NONE',
@@ -522,6 +526,7 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
'set tags=' .. otherfile,
'-tsecond',
}, {
+ term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
})
retry(nil, nil, function()
@@ -532,10 +537,6 @@ describe('quitting swapfile dialog on startup stops TUI properly', function()
end)
api.nvim_chan_send(chan, 'q')
retry(nil, nil, function()
- eq('Press ENTER or type command to continue', eval("getline('$')->trim(' ', 2)"))
- end)
- api.nvim_chan_send(chan, '\r')
- retry(nil, nil, function()
eq(
{ '', '[Process exited 1]', '' },
eval("[1, 2, '$']->map({_, lnum -> getline(lnum)->trim(' ', 2)})")
diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua
index 2299f33f06..9b81c6d06d 100644
--- a/test/functional/ex_cmds/wundo_spec.lua
+++ b/test/functional/ex_cmds/wundo_spec.lua
@@ -5,8 +5,6 @@ local n = require('test.functional.testnvim')()
local command = n.command
local clear = n.clear
local eval = n.eval
-local spawn = n.spawn
-local nvim_prog = n.nvim_prog
local set_session = n.set_session
describe(':wundo', function()
@@ -24,15 +22,11 @@ end)
describe('u_* functions', function()
it('safely fail on new, non-empty buffer', function()
- local session = spawn({
- nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
- '--embed',
- '-c',
- 'set undodir=. undofile',
+ local session = n.new_session(false, {
+ args = {
+ '-c',
+ 'set undodir=. undofile',
+ },
})
set_session(session)
command('echo "True"') -- Should not error out due to crashed Neovim
diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt
index 150407fe46..a388f9cb33 100644
--- a/test/functional/fixtures/CMakeLists.txt
+++ b/test/functional/fixtures/CMakeLists.txt
@@ -1,7 +1,4 @@
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()
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index f813927f77..5d7ab2ad12 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -386,6 +386,21 @@ function tests.check_forward_content_modified()
}
end
+function tests.check_forward_server_cancelled()
+ skeleton {
+ on_init = function()
+ return { capabilities = {} }
+ end,
+ body = function()
+ expect_request('error_code_test', function()
+ return { code = -32802 }, nil, { method = 'error_code_test', client_id = 1 }
+ end)
+ expect_notification('finish')
+ notify('finish')
+ end,
+ }
+end
+
function tests.check_pending_request_tracked()
skeleton {
on_init = function(_)
diff --git a/test/functional/fixtures/printargs-test.c b/test/functional/fixtures/printargs-test.c
index 2c25cf8447..a1d3fdf76e 100644
--- a/test/functional/fixtures/printargs-test.c
+++ b/test/functional/fixtures/printargs-test.c
@@ -2,7 +2,7 @@
int main(int argc, char **argv)
{
- for (int i=1; i<argc; i++) {
+ for (int i = 1; i < argc; i++) {
printf("arg%d=%s;", i, argv[i]);
}
return 0;
diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c
index bd71e7d11b..f3e94a28da 100644
--- a/test/functional/fixtures/shell-test.c
+++ b/test/functional/fixtures/shell-test.c
@@ -1,11 +1,12 @@
+#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
-#include <stdint.h>
#ifdef _MSC_VER
-#include <Windows.h>
-#define usleep(usecs) Sleep(usecs/1000)
+# include <Windows.h>
+# define usleep(usecs) Sleep(usecs/1000)
#else
-#include <unistd.h>
+# include <unistd.h>
#endif
static void flush_wait(void)
@@ -56,7 +57,7 @@ int main(int argc, char **argv)
if (argc >= 2) {
if (strcmp(argv[1], "-t") == 0) {
if (argc < 3) {
- fprintf(stderr,"Missing prompt text for -t option\n");
+ fprintf(stderr, "Missing prompt text for -t option\n");
return 5;
} else {
fprintf(stderr, "%s $ ", argv[2]);
@@ -107,18 +108,18 @@ int main(int argc, char **argv)
char cmd[100];
int arg;
- while (1) {
+ while (true) {
fprintf(stderr, "interact $ ");
if (fgets(input, sizeof(input), stdin) == NULL) {
break; // EOF
}
- if(1 == sscanf(input, "%99s %d", cmd, &arg)) {
+ if (1 == sscanf(input, "%99s %d", cmd, &arg)) {
arg = 0;
}
if (strcmp(cmd, "exit") == 0) {
- return arg;
+ return arg;
} else {
fprintf(stderr, "command not found: %s\n", cmd);
}
diff --git a/test/functional/fixtures/streams-test.c b/test/functional/fixtures/streams-test.c
index 5a59abb33b..68e668d5fa 100644
--- a/test/functional/fixtures/streams-test.c
+++ b/test/functional/fixtures/streams-test.c
@@ -1,6 +1,5 @@
/// Helper program to exit and keep stdout open (like "xclip -i -loops 1").
#include <stdio.h>
-
#include <uv.h>
int main(int argc, char **argv)
@@ -8,7 +7,7 @@ int main(int argc, char **argv)
uv_loop_t *loop = uv_default_loop();
uv_process_t child_req;
- char * args[3];
+ char *args[3];
args[0] = "sleep";
args[1] = "10";
args[2] = NULL;
diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua
index bf146e1322..819b40323a 100644
--- a/test/functional/legacy/cmdline_spec.lua
+++ b/test/functional/legacy/cmdline_spec.lua
@@ -159,6 +159,50 @@ describe('cmdline', function()
endfunc
]])
+ feed(':resize -3<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*2
+ {3:[No Name] }|
+ |*4
+ ]])
+
+ -- :resize now also changes 'cmdheight' accordingly
+ feed(':set cmdheight+=1<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {3:[No Name] }|
+ |*5
+ ]])
+
+ -- using more space moves the status line up
+ feed(':set cmdheight+=1<CR>')
+ screen:expect([[
+ ^ |
+ {3:[No Name] }|
+ |*6
+ ]])
+
+ -- reducing cmdheight moves status line down
+ feed(':set cmdheight-=3<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*3
+ {3:[No Name] }|
+ |*3
+ ]])
+
+ -- reducing window size and then setting cmdheight
+ feed(':resize -1<CR>')
+ feed(':set cmdheight=1<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:[No Name] }|
+ |
+ ]])
+
-- setting 'cmdheight' works after outputting two messages
feed(':call EchoTwo()')
screen:expect([[
@@ -185,6 +229,16 @@ describe('cmdline', function()
bar |
{6:Press ENTER or type command to continue}^ |
]])
+
+ -- window commands do not reduce 'cmdheight' to value lower than :set by user
+ feed('<CR>:wincmd _<CR>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*4
+ {3:[No Name] }|
+ :wincmd _ |
+ |
+ ]])
end)
-- oldtest: Test_cmdheight_tabline()
@@ -216,6 +270,22 @@ describe('cmdline', function()
longish |
]]
end)
+
+ -- oldtest: Test_rulerformat_function()
+ it("'rulerformat' can use %!", function()
+ local screen = Screen.new(40, 2)
+ exec([[
+ func TestRulerFn()
+ return '10,20%=30%%'
+ endfunc
+ ]])
+ api.nvim_set_option_value('ruler', true, {})
+ api.nvim_set_option_value('rulerformat', '%!TestRulerFn()', {})
+ screen:expect([[
+ ^ |
+ 10,20 30% |
+ ]])
+ end)
end)
describe('cmdwin', function()
diff --git a/test/functional/legacy/highlight_spec.lua b/test/functional/legacy/highlight_spec.lua
index e663931bd7..24d6abcc6b 100644
--- a/test/functional/legacy/highlight_spec.lua
+++ b/test/functional/legacy/highlight_spec.lua
@@ -29,8 +29,8 @@ describe(':highlight', function()
|
TermCursor {2:xxx} {18:cterm=}reverse |
{18:gui=}reverse |
- TermCursorNC xxx cleared |
NonText {1:xxx} {18:ctermfg=}12 |
+ {18:gui=}bold |
{6:-- More --}^ |
]])
feed('q')
diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua
index bc58551a34..db5e3f6857 100644
--- a/test/functional/legacy/messages_spec.lua
+++ b/test/functional/legacy/messages_spec.lua
@@ -82,7 +82,7 @@ describe('messages', function()
NoSuchFil^e |
three |
{1:~ }|*5
- from DebugSilent visual |
+ |
{9:E447: Can't find file "NoSuchFile" in path} |
]])
end)
@@ -689,4 +689,43 @@ describe('messages', function()
]])
os.remove('b.txt')
end)
+
+ -- oldtest: Test_messagesopt_wait()
+ it('&messagesopt "wait"', function()
+ screen = Screen.new(45, 6)
+ command('set cmdheight=1')
+
+ -- Check hit-enter prompt
+ command('set messagesopt=hit-enter,history:500')
+ feed(":echo 'foo' | echo 'bar' | echo 'baz'\n")
+ screen:expect([[
+ |
+ {3: }|
+ foo |
+ bar |
+ baz |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<CR>')
+
+ -- Check no hit-enter prompt when "wait:" is set
+ command('set messagesopt=wait:500,history:500')
+ feed(":echo 'foo' | echo 'bar' | echo 'baz'\n")
+ screen:expect({
+ grid = [[
+ |
+ {1:~ }|
+ {3: }|
+ foo |
+ bar |
+ baz |
+ ]],
+ timeout = 500,
+ })
+ screen:expect([[
+ ^ |
+ {1:~ }|*4
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/legacy/signs_spec.lua b/test/functional/legacy/signs_spec.lua
index ac7c8cd0bc..af355f27e6 100644
--- a/test/functional/legacy/signs_spec.lua
+++ b/test/functional/legacy/signs_spec.lua
@@ -26,6 +26,9 @@ describe('signs', function()
-- oldtest: Test_sign_cursor_position()
it('are drawn correctly', function()
local screen = Screen.new(75, 6)
+ screen:add_extra_attr_ids({
+ [100] = { foreground = Screen.colors.Blue4, background = Screen.colors.Yellow },
+ })
exec([[
call setline(1, [repeat('x', 75), 'mmmm', 'yyyy'])
call cursor(2,1)
@@ -37,7 +40,7 @@ describe('signs', function()
screen:expect([[
{7: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
{7: }xx |
- {10:=>}^mmmm |
+ {100:=>}^mmmm |
{7: }yyyy |
{1:~ }|
|
@@ -48,7 +51,7 @@ describe('signs', function()
screen:expect([[
{7: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
{7: }xx |
- {10:-)}^mmmm |
+ {100:-)}^mmmm |
{7: }yyyy |
{1:~ }|
|
@@ -59,7 +62,7 @@ describe('signs', function()
screen:expect([[
{7: }xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|
{7: }xx |
- {10:-)}{4:^mmmm }|
+ {100:-)}{4:^mmmm }|
{7: }yyyy |
{1:~ }|
|
diff --git a/test/functional/legacy/substitute_spec.lua b/test/functional/legacy/substitute_spec.lua
index 0081371f7f..30ff13140f 100644
--- a/test/functional/legacy/substitute_spec.lua
+++ b/test/functional/legacy/substitute_spec.lua
@@ -220,8 +220,10 @@ describe(':substitute', function()
{2:o}ne |
two |
three |
- {1:~ }|*4
- {6:replace with (y/n/a/q/l/^E/^Y)?}^ |
+ {1:~ }|*2
+ {3: }|
+ {6:replace with ? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^}|
+ {6:E)/down(^Y)}^ |
]])
end)
end)
diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua
index d68c780f49..fac982354c 100644
--- a/test/functional/legacy/window_cmd_spec.lua
+++ b/test/functional/legacy/window_cmd_spec.lua
@@ -120,7 +120,7 @@ describe('splitkeep', function()
row = 0,
col = 0,
}))
- vim.cmd("call termopen([&sh, &shcf, 'true'], { 'on_exit': 'C2' })")
+ vim.cmd("call jobstart([&sh, &shcf, 'true'], { 'term': v:true, 'on_exit': 'C2' })")
end
})]])
feed('j')
@@ -299,7 +299,7 @@ describe('splitkeep', function()
c |
{1:~ }|
{3:[No Name] }|
- |
+ :call win_move_statusline(win, 1) |
]])
end)
diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua
index eb1ac3e6a1..a19f558ef2 100644
--- a/test/functional/lua/diagnostic_spec.lua
+++ b/test/functional/lua/diagnostic_spec.lua
@@ -5,6 +5,7 @@ local command = n.command
local clear = n.clear
local exec_lua = n.exec_lua
local eq = t.eq
+local neq = t.neq
local matches = t.matches
local api = n.api
local pcall_err = t.pcall_err
@@ -112,6 +113,18 @@ describe('vim.diagnostic', function()
)
end
+ function _G.get_virt_lines_extmarks(ns)
+ ns = vim.diagnostic.get_namespace(ns)
+ local virt_lines_ns = ns.user_data.virt_lines_ns
+ return vim.api.nvim_buf_get_extmarks(
+ _G.diagnostic_bufnr,
+ virt_lines_ns,
+ 0,
+ -1,
+ { details = true }
+ )
+ end
+
---@param ns integer
function _G.get_underline_extmarks(ns)
---@type integer
@@ -160,6 +173,11 @@ describe('vim.diagnostic', function()
'DiagnosticUnderlineOk',
'DiagnosticUnderlineWarn',
'DiagnosticUnnecessary',
+ 'DiagnosticVirtualLinesError',
+ 'DiagnosticVirtualLinesHint',
+ 'DiagnosticVirtualLinesInfo',
+ 'DiagnosticVirtualLinesOk',
+ 'DiagnosticVirtualLinesWarn',
'DiagnosticVirtualTextError',
'DiagnosticVirtualTextHint',
'DiagnosticVirtualTextInfo',
@@ -368,6 +386,9 @@ describe('vim.diagnostic', function()
end)
it('handles one namespace clearing highlights while the other still has highlights', function()
+ exec_lua(function()
+ vim.diagnostic.config({ virtual_text = true })
+ end)
-- 1 Error (1)
-- 1 Warning (2)
-- 1 Warning (2) + 1 Warning (1)
@@ -442,6 +463,10 @@ describe('vim.diagnostic', function()
end)
it('does not display diagnostics when disabled', function()
+ exec_lua(function()
+ vim.diagnostic.config({ virtual_text = true })
+ end)
+
eq(
{ 0, 2 },
exec_lua(function()
@@ -574,7 +599,7 @@ describe('vim.diagnostic', function()
vim.diagnostic.set(
_G.diagnostic_ns,
_G.diagnostic_bufnr,
- { { lnum = 0, end_lnum = 0, col = 0, end_col = 0 } }
+ { { message = '', lnum = 0, end_lnum = 0, col = 0, end_col = 0 } }
)
vim.cmd('bwipeout! ' .. _G.diagnostic_bufnr)
@@ -915,6 +940,10 @@ describe('vim.diagnostic', function()
describe('reset()', function()
it('diagnostic count is 0 and displayed diagnostics are 0 after call', function()
+ exec_lua(function()
+ vim.diagnostic.config({ virtual_text = true })
+ end)
+
-- 1 Error (1)
-- 1 Warning (2)
-- 1 Warning (2) + 1 Warning (1)
@@ -1005,7 +1034,7 @@ describe('vim.diagnostic', function()
vim.diagnostic.set(
_G.diagnostic_ns,
_G.diagnostic_bufnr,
- { { lnum = 0, end_lnum = 0, col = 0, end_col = 0 } }
+ { { message = '', lnum = 0, end_lnum = 0, col = 0, end_col = 0 } }
)
vim.cmd('bwipeout! ' .. _G.diagnostic_bufnr)
@@ -2105,6 +2134,139 @@ describe('vim.diagnostic', function()
end)
)
end)
+
+ it('can filter diagnostics by returning nil when formatting', function()
+ local result = exec_lua(function()
+ vim.diagnostic.config {
+ virtual_text = {
+ format = function(diagnostic)
+ if diagnostic.code == 'foo_err' then
+ return nil
+ end
+ return diagnostic.message
+ end,
+ },
+ }
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('An error here!', 0, 0, 0, 0, 'foo_server', 'foo_err'),
+ _G.make_error('An error there!', 1, 1, 1, 1, 'bar_server', 'bar_err'),
+ })
+
+ local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ eq(1, #result)
+ eq(' An error there!', result[1][4].virt_text[3][1])
+ end)
+
+ it('can only show virtual_text for the current line', function()
+ local result = exec_lua(function()
+ vim.api.nvim_win_set_cursor(0, { 1, 0 })
+
+ vim.diagnostic.config({ virtual_text = { current_line = true } })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
+ _G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_text_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ eq(1, #result)
+ eq(' Error here!', result[1][4].virt_text[3][1])
+ end)
+ end)
+
+ describe('handlers.virtual_lines', function()
+ it('includes diagnostic code and message', function()
+ local result = exec_lua(function()
+ vim.diagnostic.config({ virtual_lines = true })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Missed symbol `,`', 0, 0, 0, 0, 'lua_ls', 'miss-symbol'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks[1][4].virt_lines
+ end)
+
+ eq('miss-symbol: Missed symbol `,`', result[1][3][1])
+ end)
+
+ it('adds space to the left of the diagnostic', function()
+ local error_offset = 5
+ local result = exec_lua(function()
+ vim.diagnostic.config({ virtual_lines = true })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!', 0, error_offset, 0, error_offset, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks[1][4].virt_lines
+ end)
+
+ eq(error_offset, result[1][1][1]:len())
+ end)
+
+ it('highlights diagnostics in multiple lines by default', function()
+ local result = exec_lua(function()
+ vim.diagnostic.config({ virtual_lines = true })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
+ _G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ eq(2, #result)
+ eq('Error here!', result[1][4].virt_lines[1][3][1])
+ eq('Another error there!', result[2][4].virt_lines[1][3][1])
+ end)
+
+ it('can highlight diagnostics only in the current line', function()
+ local result = exec_lua(function()
+ vim.api.nvim_win_set_cursor(0, { 1, 0 })
+
+ vim.diagnostic.config({ virtual_lines = { current_line = true } })
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error here!', 0, 0, 0, 0, 'foo_server'),
+ _G.make_error('Another error there!', 1, 0, 1, 0, 'foo_server'),
+ })
+
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks
+ end)
+
+ eq(1, #result)
+ eq('Error here!', result[1][4].virt_lines[1][3][1])
+ end)
+
+ it('supports a format function for diagnostic messages', function()
+ local result = exec_lua(function()
+ vim.diagnostic.config({
+ virtual_lines = {
+ format = function()
+ return 'Error here!'
+ end,
+ },
+ })
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Invalid syntax', 0, 0, 0, 0),
+ })
+ local extmarks = _G.get_virt_lines_extmarks(_G.diagnostic_ns)
+ return extmarks[1][4].virt_lines
+ end)
+ eq('Error here!', result[1][3][1])
+ end)
end)
describe('set()', function()
@@ -2116,7 +2278,11 @@ describe('vim.diagnostic', function()
end)
it('can perform updates after insert_leave', function()
- exec_lua [[vim.api.nvim_set_current_buf( _G.diagnostic_bufnr)]]
+ exec_lua(function()
+ vim.diagnostic.config({ virtual_text = true })
+ vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
+ end)
+
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
@@ -2257,7 +2423,10 @@ describe('vim.diagnostic', function()
end)
it('can perform updates while in insert mode, if desired', function()
- exec_lua [[vim.api.nvim_set_current_buf( _G.diagnostic_bufnr)]]
+ exec_lua(function()
+ vim.diagnostic.config({ virtual_text = true })
+ vim.api.nvim_set_current_buf(_G.diagnostic_bufnr)
+ end)
api.nvim_input('o')
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
@@ -2291,6 +2460,10 @@ describe('vim.diagnostic', function()
end)
it('can set diagnostics without displaying them', function()
+ exec_lua(function()
+ vim.diagnostic.config({ virtual_text = true })
+ end)
+
eq(
0,
exec_lua(function()
@@ -3212,6 +3385,74 @@ describe('vim.diagnostic', function()
end)
end)
+ describe('setqflist()', function()
+ it('updates existing diagnostics quickfix if one already exists', function()
+ local result = exec_lua(function()
+ vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
+
+ vim.fn.setqflist({}, ' ', { title = 'Diagnostics' })
+ local diagnostics_qf_id = vim.fn.getqflist({ id = 0 }).id
+
+ vim.diagnostic.setqflist({ title = 'Diagnostics' })
+ local qf_id = vim.fn.getqflist({ id = 0, nr = '$' }).id
+
+ return { diagnostics_qf_id, qf_id }
+ end)
+
+ eq(result[1], result[2])
+ end)
+
+ it('navigates to existing diagnostics quickfix if one already exists and open=true', function()
+ local result = exec_lua(function()
+ vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
+
+ vim.fn.setqflist({}, ' ', { title = 'Diagnostics' })
+ local diagnostics_qf_id = vim.fn.getqflist({ id = 0 }).id
+
+ vim.fn.setqflist({}, ' ', { title = 'Other' })
+
+ vim.diagnostic.setqflist({ title = 'Diagnostics', open = true })
+ local qf_id = vim.fn.getqflist({ id = 0 }).id
+
+ return { diagnostics_qf_id, qf_id }
+ end)
+
+ eq(result[1], result[2])
+ end)
+
+ it('sets new diagnostics quickfix as active when open=true', function()
+ local result = exec_lua(function()
+ vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
+
+ vim.fn.setqflist({}, ' ', { title = 'Other' })
+ local other_qf_id = vim.fn.getqflist({ id = 0 }).id
+
+ vim.diagnostic.setqflist({ title = 'Diagnostics', open = true })
+ local qf_id = vim.fn.getqflist({ id = 0 }).id
+
+ return { other_qf_id, qf_id }
+ end)
+
+ neq(result[1], result[2])
+ end)
+
+ it('opens quickfix window when open=true', function()
+ local qf_winid = exec_lua(function()
+ vim.api.nvim_win_set_buf(0, _G.diagnostic_bufnr)
+
+ vim.diagnostic.set(_G.diagnostic_ns, _G.diagnostic_bufnr, {
+ _G.make_error('Error', 1, 1, 1, 1),
+ })
+
+ vim.diagnostic.setqflist({ open = true })
+
+ return vim.fn.getqflist({ winid = 0 }).winid
+ end)
+
+ neq(0, qf_winid)
+ end)
+ end)
+
describe('match()', function()
it('matches a string', function()
local msg = 'ERROR: george.txt:19:84:Two plus two equals five'
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
index b6011d5268..b75ff75b05 100644
--- a/test/functional/lua/filetype_spec.lua
+++ b/test/functional/lua/filetype_spec.lua
@@ -199,7 +199,19 @@ describe('filetype.lua', function()
finally(function()
uv.fs_unlink('Xfiletype/.config/git')
end)
- clear({ args = { '--clean', 'Xfiletype/.config/git/config' } })
+ local args = { '--clean', 'Xfiletype/.config/git/config' }
+ clear({ args = args })
eq('gitconfig', api.nvim_get_option_value('filetype', {}))
+ table.insert(args, 2, '--cmd')
+ table.insert(args, 3, "autocmd BufRead * call expand('<afile>')")
+ clear({ args = args })
+ eq('gitconfig', api.nvim_get_option_value('filetype', {}))
+ end)
+
+ it('works with :doautocmd BufRead #31306', function()
+ clear({ args = { '--clean' } })
+ eq('', api.nvim_get_option_value('filetype', {}))
+ command('doautocmd BufRead README.md')
+ eq('markdown', api.nvim_get_option_value('filetype', {}))
end)
end)
diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua
index f0d49205e7..33af25f629 100644
--- a/test/functional/lua/fs_spec.lua
+++ b/test/functional/lua/fs_spec.lua
@@ -17,6 +17,8 @@ local mkdir = t.mkdir
local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim'
+local link_limit = is_os('win') and 64 or (is_os('mac') or is_os('bsd')) and 33 or 41
+
local test_basename_dirname_eq = {
'~/foo/',
'~/foo',
@@ -152,7 +154,7 @@ describe('vim.fs', function()
)
end)
- it('works with opts.depth and opts.skip', function()
+ it('works with opts.depth, opts.skip and opts.follow', function()
io.open('testd/a1', 'w'):close()
io.open('testd/b1', 'w'):close()
io.open('testd/c1', 'w'):close()
@@ -166,10 +168,10 @@ describe('vim.fs', function()
io.open('testd/a/b/c/b4', 'w'):close()
io.open('testd/a/b/c/c4', 'w'):close()
- local function run(dir, depth, skip)
- return exec_lua(function()
- local r = {}
- local skip_f
+ local function run(dir, depth, skip, follow)
+ return exec_lua(function(follow_)
+ local r = {} --- @type table<string, string>
+ local skip_f --- @type function
if skip then
skip_f = function(n0)
if vim.tbl_contains(skip or {}, n0) then
@@ -177,11 +179,11 @@ describe('vim.fs', function()
end
end
end
- for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f }) do
+ for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f, follow = follow_ }) do
r[name] = type_
end
return r
- end)
+ end, follow)
end
local exp = {}
@@ -197,6 +199,7 @@ describe('vim.fs', function()
exp['a/b2'] = 'file'
exp['a/c2'] = 'file'
exp['a/b'] = 'directory'
+ local lexp = vim.deepcopy(exp)
eq(exp, run('testd', 2))
@@ -213,6 +216,29 @@ describe('vim.fs', function()
exp['a/b/c/c4'] = 'file'
eq(exp, run('testd', 999))
+
+ vim.uv.fs_symlink(vim.uv.fs_realpath('testd/a'), 'testd/l', { junction = true, dir = true })
+ lexp['l'] = 'link'
+ eq(lexp, run('testd', 2, nil, false))
+
+ lexp['l/a2'] = 'file'
+ lexp['l/b2'] = 'file'
+ lexp['l/c2'] = 'file'
+ lexp['l/b'] = 'directory'
+ eq(lexp, run('testd', 2, nil, true))
+ end)
+
+ it('follow=true handles symlink loop', function()
+ local cwd = 'testd/a/b/c'
+ local symlink = cwd .. '/link_loop' ---@type string
+ vim.uv.fs_symlink(vim.uv.fs_realpath(cwd), symlink, { junction = true, dir = true })
+
+ eq(
+ link_limit,
+ exec_lua(function()
+ return #vim.iter(vim.fs.dir(cwd, { depth = math.huge, follow = true })):totable()
+ end)
+ )
end)
end)
@@ -228,6 +254,53 @@ describe('vim.fs', function()
eq({ nvim_dir }, vim.fs.find(name, { path = parent, upward = true, type = 'directory' }))
end)
+ it('follows symlinks', function()
+ local build_dir = test_source_path .. '/build' ---@type string
+ local symlink = test_source_path .. '/build_link' ---@type string
+ vim.uv.fs_symlink(build_dir, symlink, { junction = true, dir = true })
+
+ finally(function()
+ vim.uv.fs_unlink(symlink)
+ end)
+
+ eq(
+ { nvim_prog, symlink .. '/bin/' .. nvim_prog_basename },
+ vim.fs.find(nvim_prog_basename, {
+ path = test_source_path,
+ type = 'file',
+ limit = 2,
+ follow = true,
+ })
+ )
+
+ eq(
+ { nvim_prog },
+ vim.fs.find(nvim_prog_basename, {
+ path = test_source_path,
+ type = 'file',
+ limit = 2,
+ follow = false,
+ })
+ )
+ end)
+
+ it('follow=true handles symlink loop', function()
+ local cwd = test_source_path ---@type string
+ local symlink = test_source_path .. '/loop_link' ---@type string
+ vim.uv.fs_symlink(cwd, symlink, { junction = true, dir = true })
+
+ finally(function()
+ vim.uv.fs_unlink(symlink)
+ end)
+
+ eq(link_limit, #vim.fs.find(nvim_prog_basename, {
+ path = test_source_path,
+ type = 'file',
+ limit = math.huge,
+ follow = true,
+ }))
+ end)
+
it('accepts predicate as names', function()
local opts = { path = nvim_dir, upward = true, type = 'directory' }
eq(
@@ -273,14 +346,14 @@ describe('vim.fs', function()
end)
it('works with a single marker', function()
- eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]]))
+ eq(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
end)
it('works with multiple markers', function()
local bufnr = api.nvim_get_current_buf()
eq(
vim.fs.joinpath(test_source_path, 'test/functional/fixtures'),
- exec_lua([[return vim.fs.root(..., {'CMakeLists.txt', '.git'})]], bufnr)
+ exec_lua([[return vim.fs.root(..., {'CMakeLists.txt', 'CMakePresets.json'})]], bufnr)
)
end)
@@ -295,26 +368,26 @@ describe('vim.fs', function()
end)
it('works with a filename argument', function()
- eq(test_source_path, exec_lua([[return vim.fs.root(..., '.git')]], nvim_prog))
+ eq(test_source_path, exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], nvim_prog))
end)
it('works with a relative path', function()
eq(
test_source_path,
- exec_lua([[return vim.fs.root(..., '.git')]], vim.fs.basename(nvim_prog))
+ exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], vim.fs.basename(nvim_prog))
)
end)
it('uses cwd for unnamed buffers', function()
command('new')
- eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]]))
+ eq(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
end)
it("uses cwd for buffers with non-empty 'buftype'", function()
command('new')
command('set buftype=nofile')
command('file lua://')
- eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]]))
+ eq(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]]))
end)
end)
@@ -323,6 +396,20 @@ describe('vim.fs', function()
eq('foo/bar/baz', vim.fs.joinpath('foo', 'bar', 'baz'))
eq('foo/bar/baz', vim.fs.joinpath('foo', '/bar/', '/baz'))
end)
+ it('rewrites backslashes on Windows', function()
+ if is_os('win') then
+ eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo]], [[\\bar\\\\baz]], [[zub\]]))
+ else
+ eq([[foo/\\bar\\\\baz/zub\]], vim.fs.joinpath([[foo]], [[\\bar\\\\baz]], [[zub\]]))
+ end
+ end)
+ it('strips redundant slashes', function()
+ if is_os('win') then
+ eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo//]], [[\\bar\\\\baz]], [[zub\]]))
+ else
+ eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo]], [[//bar////baz]], [[zub/]]))
+ end
+ end)
end)
describe('normalize()', function()
@@ -347,8 +434,8 @@ describe('vim.fs', function()
end)
-- Opts required for testing posix paths and win paths
- local posix_opts = is_os('win') and { win = false } or {}
- local win_opts = is_os('win') and {} or { win = true }
+ local posix_opts = { win = false }
+ local win_opts = { win = true }
it('preserves leading double slashes in POSIX paths', function()
eq('//foo', vim.fs.normalize('//foo', posix_opts))
@@ -359,6 +446,29 @@ describe('vim.fs', function()
eq('/foo/bar', vim.fs.normalize('/foo//bar////', posix_opts))
end)
+ it('normalizes drive letter', function()
+ eq('C:/', vim.fs.normalize('C:/', win_opts))
+ eq('C:/', vim.fs.normalize('c:/', win_opts))
+ eq('D:/', vim.fs.normalize('d:/', win_opts))
+ eq('C:', vim.fs.normalize('C:', win_opts))
+ eq('C:', vim.fs.normalize('c:', win_opts))
+ eq('D:', vim.fs.normalize('d:', win_opts))
+ eq('C:/foo/test', vim.fs.normalize('C:/foo/test/', win_opts))
+ eq('C:/foo/test', vim.fs.normalize('c:/foo/test/', win_opts))
+ eq('D:foo/test', vim.fs.normalize('D:foo/test/', win_opts))
+ eq('D:foo/test', vim.fs.normalize('d:foo/test/', win_opts))
+ end)
+
+ it('always treats paths as case-sensitive #31833', function()
+ eq('TEST', vim.fs.normalize('TEST', win_opts))
+ eq('test', vim.fs.normalize('test', win_opts))
+ eq('C:/FOO/test', vim.fs.normalize('C:/FOO/test', win_opts))
+ eq('C:/foo/test', vim.fs.normalize('C:/foo/test', win_opts))
+ eq('//SERVER/SHARE/FOO/BAR', vim.fs.normalize('//SERVER/SHARE/FOO/BAR', win_opts))
+ eq('//server/share/foo/bar', vim.fs.normalize('//server/share/foo/bar', win_opts))
+ eq('C:/FOO/test', vim.fs.normalize('c:/FOO/test', win_opts))
+ end)
+
it('allows backslashes on unix-based os', function()
eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world', posix_opts))
end)
@@ -454,4 +564,92 @@ describe('vim.fs', function()
end)
end)
end)
+
+ describe('abspath()', function()
+ local cwd = assert(t.fix_slashes(assert(vim.uv.cwd())))
+ local home = t.fix_slashes(assert(vim.uv.os_homedir()))
+
+ it('works', function()
+ eq(cwd .. '/foo', vim.fs.abspath('foo'))
+ eq(cwd .. '/././foo', vim.fs.abspath('././foo'))
+ eq(cwd .. '/.././../foo', vim.fs.abspath('.././../foo'))
+ end)
+
+ it('works with absolute paths', function()
+ if is_os('win') then
+ eq([[C:/foo]], vim.fs.abspath([[C:\foo]]))
+ eq([[C:/foo/../.]], vim.fs.abspath([[C:\foo\..\.]]))
+ eq('//foo/bar', vim.fs.abspath('\\\\foo\\bar'))
+ else
+ eq('/foo/../.', vim.fs.abspath('/foo/../.'))
+ eq('/foo/bar', vim.fs.abspath('/foo/bar'))
+ end
+ end)
+
+ it('expands ~', function()
+ eq(home .. '/foo', vim.fs.abspath('~/foo'))
+ eq(home .. '/./.././foo', vim.fs.abspath('~/./.././foo'))
+ end)
+
+ if is_os('win') then
+ it('works with drive-specific cwd on Windows', function()
+ local cwd_drive = cwd:match('^%w:')
+
+ eq(cwd .. '/foo', vim.fs.abspath(cwd_drive .. 'foo'))
+ end)
+ end
+ end)
+
+ describe('relpath()', function()
+ it('works', function()
+ local cwd = assert(t.fix_slashes(assert(vim.uv.cwd())))
+ local my_dir = vim.fs.joinpath(cwd, 'foo')
+
+ eq(nil, vim.fs.relpath('/var/lib', '/var'))
+ eq(nil, vim.fs.relpath('/var/lib', '/bin'))
+ eq(nil, vim.fs.relpath(my_dir, 'bin'))
+ eq(nil, vim.fs.relpath(my_dir, './bin'))
+ eq(nil, vim.fs.relpath(my_dir, '././'))
+ eq(nil, vim.fs.relpath(my_dir, '../'))
+ eq(nil, vim.fs.relpath('/var/lib', '/'))
+ eq(nil, vim.fs.relpath('/var/lib', '//'))
+ eq(nil, vim.fs.relpath(' ', '/var'))
+ eq(nil, vim.fs.relpath(' ', '/var'))
+ eq('.', vim.fs.relpath('/var/lib', '/var/lib'))
+ eq('lib', vim.fs.relpath('/var/', '/var/lib'))
+ eq('var/lib', vim.fs.relpath('/', '/var/lib'))
+ eq('bar/package.json', vim.fs.relpath('/foo/test', '/foo/test/bar/package.json'))
+ eq('foo/bar', vim.fs.relpath(cwd, 'foo/bar'))
+ eq('foo/bar', vim.fs.relpath('.', vim.fs.joinpath(cwd, 'foo/bar')))
+ eq('bar', vim.fs.relpath('foo', 'foo/bar'))
+ eq(nil, vim.fs.relpath('/var/lib', '/var/library/foo'))
+
+ if is_os('win') then
+ eq(nil, vim.fs.relpath('/', ' '))
+ eq(nil, vim.fs.relpath('/', 'var'))
+ else
+ local cwd_rel_root = cwd:sub(2)
+ eq(cwd_rel_root .. '/ ', vim.fs.relpath('/', ' '))
+ eq(cwd_rel_root .. '/var', vim.fs.relpath('/', 'var'))
+ end
+
+ if is_os('win') then
+ eq(nil, vim.fs.relpath('c:/aaaa/', '/aaaa/cccc'))
+ eq(nil, vim.fs.relpath('c:/aaaa/', './aaaa/cccc'))
+ eq(nil, vim.fs.relpath('c:/aaaa/', 'aaaa/cccc'))
+ eq(nil, vim.fs.relpath('c:/blah\\blah', 'd:/games'))
+ eq(nil, vim.fs.relpath('c:/games', 'd:/games'))
+ eq(nil, vim.fs.relpath('c:/games', 'd:/games/foo'))
+ eq(nil, vim.fs.relpath('c:/aaaa/bbbb', 'c:/aaaa'))
+ eq('cccc', vim.fs.relpath('c:/aaaa/', 'c:/aaaa/cccc'))
+ eq('aaaa/bbbb', vim.fs.relpath('C:/', 'c:\\aaaa\\bbbb'))
+ eq('bar/package.json', vim.fs.relpath('C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json'))
+ eq('baz', vim.fs.relpath('\\\\foo\\bar', '\\\\foo\\bar\\baz'))
+ eq(nil, vim.fs.relpath('a/b/c', 'a\\b'))
+ eq('d', vim.fs.relpath('a/b/c', 'a\\b\\c\\d'))
+ eq('.', vim.fs.relpath('\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'))
+ eq(nil, vim.fs.relpath('C:\\foo\\test', 'C:\\foo\\Test\\bar\\package.json'))
+ end
+ end)
+ end)
end)
diff --git a/test/functional/lua/func_memoize_spec.lua b/test/functional/lua/func_memoize_spec.lua
new file mode 100644
index 0000000000..ca518ab88d
--- /dev/null
+++ b/test/functional/lua/func_memoize_spec.lua
@@ -0,0 +1,142 @@
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+local clear = n.clear
+local exec_lua = n.exec_lua
+local eq = t.eq
+
+describe('vim.func._memoize', function()
+ before_each(clear)
+
+ it('caches function results based on their parameters', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ collectgarbage('stop')
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ collectgarbage('restart')
+ ]])
+
+ eq(1, exec_lua([[return _G.count]]))
+ end)
+
+ it('caches function results using a weak table by default', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat-2', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ ]])
+
+ eq(3, exec_lua([[return _G.count]]))
+ end)
+
+ it('can cache using a strong table', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat-2', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end, false)
+
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ collectgarbage()
+ adder(3, -4)
+ ]])
+
+ eq(1, exec_lua([[return _G.count]]))
+ end)
+
+ it('can clear a single cache entry', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize(function(arg1, arg2)
+ return tostring(arg1) .. '%%' .. tostring(arg2)
+ end, function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ collectgarbage('stop')
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder(3, -4)
+ adder:clear(3, -4)
+ adder(3, -4)
+ collectgarbage('restart')
+ ]])
+
+ eq(2, exec_lua([[return _G.count]]))
+ end)
+
+ it('can clear the entire cache', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize(function(arg1, arg2)
+ return tostring(arg1) .. '%%' .. tostring(arg2)
+ end, function(arg1, arg2)
+ _G.count = _G.count + 1
+ return arg1 + arg2
+ end)
+
+ collectgarbage('stop')
+ adder(1, 2)
+ adder(3, -4)
+ adder(1, 2)
+ adder(3, -4)
+ adder(1, 2)
+ adder(3, -4)
+ adder:clear()
+ adder(1, 2)
+ adder(3, -4)
+ collectgarbage('restart')
+ ]])
+
+ eq(4, exec_lua([[return _G.count]]))
+ end)
+
+ it('can cache functions that return nil', function()
+ exec_lua([[
+ _G.count = 0
+
+ local adder = vim.func._memoize('concat', function(arg1, arg2)
+ _G.count = _G.count + 1
+ return nil
+ end)
+
+ collectgarbage('stop')
+ adder(1, 2)
+ adder(1, 2)
+ adder(1, 2)
+ adder(1, 2)
+ adder:clear()
+ adder(1, 2)
+ collectgarbage('restart')
+ ]])
+
+ eq(2, exec_lua([[return _G.count]]))
+ end)
+end)
diff --git a/test/functional/lua/hl_spec.lua b/test/functional/lua/hl_spec.lua
index 89881973bf..512f6be48f 100644
--- a/test/functional/lua/hl_spec.lua
+++ b/test/functional/lua/hl_spec.lua
@@ -104,6 +104,33 @@ describe('vim.hl.range', function()
|
]])
end)
+
+ it('removes highlight after given `timeout`', function()
+ local timeout = 100
+ exec_lua(function()
+ local ns = vim.api.nvim_create_namespace('')
+ vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { timeout = timeout })
+ end)
+ screen:expect({
+ grid = [[
+ {10:^asdfghjkl}{100:$} |
+ {10:«口=口»}{100:$} |
+ {10:qwertyuiop}{100:$} |
+ {10:口口=口口}{1:$} |
+ zxcvbnm{1:$} |
+ |
+ ]],
+ timeout = timeout,
+ })
+ screen:expect([[
+ ^asdfghjkl{1:$} |
+ «口=口»{1:$} |
+ qwertyuiop{1:$} |
+ 口口=口口{1:$} |
+ zxcvbnm{1:$} |
+ |
+ ]])
+ end)
end)
describe('vim.hl.on_yank', function()
@@ -144,7 +171,7 @@ describe('vim.hl.on_yank', function()
vim.api.nvim_buf_set_mark(0, ']', 1, 1, {})
vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } })
end)
- local ns = api.nvim_create_namespace('hlyank')
+ local ns = api.nvim_create_namespace('nvim.hlyank')
local win = api.nvim_get_current_win()
eq({ win }, api.nvim__ns_get(ns).wins)
command('wincmd w')
@@ -158,7 +185,7 @@ describe('vim.hl.on_yank', function()
vim.api.nvim_buf_set_mark(0, ']', 1, 1, {})
vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } })
end)
- local ns = api.nvim_create_namespace('hlyank')
+ local ns = api.nvim_create_namespace('nvim.hlyank')
eq(api.nvim_get_current_win(), api.nvim__ns_get(ns).wins[1])
command('wincmd w')
exec_lua(function()
diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua
index a6e814d739..e4a1df1d4c 100644
--- a/test/functional/lua/json_spec.lua
+++ b/test/functional/lua/json_spec.lua
@@ -152,6 +152,45 @@ describe('vim.json.encode()', function()
clear()
end)
+ it('escape_slash', function()
+ -- With slash
+ eq('"Test\\/"', exec_lua([[return vim.json.encode('Test/', { escape_slash = true })]]))
+ eq(
+ 'Test/',
+ exec_lua([[return vim.json.decode(vim.json.encode('Test/', { escape_slash = true }))]])
+ )
+
+ -- Without slash
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/')]]))
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/', {})]]))
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/', { _invalid = true })]]))
+ eq('"Test/"', exec_lua([[return vim.json.encode('Test/', { escape_slash = false })]]))
+ eq(
+ '"Test/"',
+ exec_lua([[return vim.json.encode('Test/', { _invalid = true, escape_slash = false })]])
+ )
+ eq(
+ 'Test/',
+ exec_lua([[return vim.json.decode(vim.json.encode('Test/', { escape_slash = false }))]])
+ )
+
+ -- Checks for for global side-effects
+ eq(
+ '"Test/"',
+ exec_lua([[
+ vim.json.encode('Test/', { escape_slash = true })
+ return vim.json.encode('Test/')
+ ]])
+ )
+ eq(
+ '"Test\\/"',
+ exec_lua([[
+ vim.json.encode('Test/', { escape_slash = false })
+ return vim.json.encode('Test/', { escape_slash = true })
+ ]])
+ )
+ end)
+
it('dumps strings', function()
eq('"Test"', exec_lua([[return vim.json.encode('Test')]]))
eq('""', exec_lua([[return vim.json.encode('')]]))
diff --git a/test/functional/lua/loader_spec.lua b/test/functional/lua/loader_spec.lua
index 8508f2aa14..20d3a940b2 100644
--- a/test/functional/lua/loader_spec.lua
+++ b/test/functional/lua/loader_spec.lua
@@ -10,7 +10,17 @@ local eq = t.eq
describe('vim.loader', function()
before_each(clear)
- it('can work in compatibility with --luamod-dev #27413', function()
+ it('can be disabled', function()
+ exec_lua(function()
+ local orig_loader = _G.loadfile
+ vim.loader.enable()
+ assert(orig_loader ~= _G.loadfile)
+ vim.loader.enable(false)
+ assert(orig_loader == _G.loadfile)
+ end)
+ end)
+
+ it('works with --luamod-dev #27413', function()
clear({ args = { '--luamod-dev' } })
exec_lua(function()
vim.loader.enable()
@@ -31,7 +41,7 @@ describe('vim.loader', function()
end)
end)
- it('handles changing files (#23027)', function()
+ it('handles changing files #23027', function()
exec_lua(function()
vim.loader.enable()
end)
@@ -63,7 +73,7 @@ describe('vim.loader', function()
)
end)
- it('handles % signs in modpath (#24491)', function()
+ it('handles % signs in modpath #24491', function()
exec_lua [[
vim.loader.enable()
]]
@@ -82,7 +92,7 @@ describe('vim.loader', function()
eq(2, exec_lua('return loadfile(...)()', tmp2))
end)
- it('correct indent on error message (#29809)', function()
+ it('indents error message #29809', function()
local errmsg = exec_lua [[
vim.loader.enable()
local _, errmsg = pcall(require, 'non_existent_module')
diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua
index afbada007d..6c320376c9 100644
--- a/test/functional/lua/system_spec.lua
+++ b/test/functional/lua/system_spec.lua
@@ -18,8 +18,7 @@ local function system_sync(cmd, opts)
local res = obj:wait()
-- Check the process is no longer running
- local proc = vim.api.nvim_get_proc(obj.pid)
- assert(not proc, 'process still exists')
+ assert(not vim.api.nvim_get_proc(obj.pid), 'process still exists')
return res
end)
@@ -27,23 +26,23 @@ end
local function system_async(cmd, opts)
return exec_lua(function()
- _G.done = false
+ local done = false
+ local res --- @type vim.SystemCompleted?
local obj = vim.system(cmd, opts, function(obj)
- _G.done = true
- _G.ret = obj
+ done = true
+ res = obj
end)
local ok = vim.wait(10000, function()
- return _G.done
+ return done
end)
assert(ok, 'process did not exit')
-- Check the process is no longer running
- local proc = vim.api.nvim_get_proc(obj.pid)
- assert(not proc, 'process still exists')
+ assert(not vim.api.nvim_get_proc(obj.pid), 'process still exists')
- return _G.ret
+ return res
end)
end
@@ -114,10 +113,39 @@ describe('vim.system', function()
end)
if t.is_os('win') then
- it('can resolve windows command extentions.', function()
+ it('can resolve windows command extensions', function()
t.write_file('test.bat', 'echo hello world')
system_sync({ 'chmod', '+x', 'test.bat' })
system_sync({ './test' })
end)
end
+
+ it('always captures all content of stdout/stderr #30846', function()
+ t.skip(n.fn.executable('git') == 0, 'missing "git" command')
+ t.skip(n.fn.isdirectory('.git') == 0, 'missing ".git" directory')
+ eq(
+ 0,
+ exec_lua(function()
+ local done = 0
+ local fail = 0
+ for _ = 1, 200 do
+ vim.system(
+ { 'git', 'show', ':0:test/functional/plugin/lsp_spec.lua' },
+ { text = true },
+ function(o)
+ if o.code ~= 0 or #o.stdout == 0 then
+ fail = fail + 1
+ end
+ done = done + 1
+ end
+ )
+ end
+
+ local ok = vim.wait(10000, function()
+ return done == 200
+ end, 200)
+ return fail + (ok and 0 or 1)
+ end)
+ )
+ end)
end)
diff --git a/test/functional/lua/text_spec.lua b/test/functional/lua/text_spec.lua
index be471bfd62..dd08a6ec04 100644
--- a/test/functional/lua/text_spec.lua
+++ b/test/functional/lua/text_spec.lua
@@ -26,5 +26,21 @@ describe('vim.text', function()
eq(output, vim.text.hexencode(input))
eq(input, vim.text.hexdecode(output))
end)
+
+ it('errors on invalid input', function()
+ -- Odd number of hex characters
+ do
+ local res, err = vim.text.hexdecode('ABC')
+ eq(nil, res)
+ eq('string must have an even number of hex characters', err)
+ end
+
+ -- Non-hexadecimal input
+ do
+ local res, err = vim.text.hexdecode('nothex')
+ eq(nil, res)
+ eq('string must contain only hex characters', err)
+ end
+ end)
end)
end)
diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua
index 310705fd97..8ca4bdc4f5 100644
--- a/test/functional/lua/thread_spec.lua
+++ b/test/functional/lua/thread_spec.lua
@@ -19,6 +19,26 @@ describe('thread', function()
screen = Screen.new(50, 10)
end)
+ it('handle non-string error', function()
+ exec_lua [[
+ local thread = vim.uv.new_thread(function()
+ error()
+ end)
+ vim.uv.thread_join(thread)
+ ]]
+
+ screen:expect([[
+ |
+ {1:~ }|*5
+ {3: }|
+ {9:Error in luv thread:} |
+ {9:[NULL]} |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ assert_alive()
+ end)
+
it('entry func is executed in protected mode', function()
exec_lua [[
local thread = vim.uv.new_thread(function()
diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua
index c8616e3e11..ddb10127e4 100644
--- a/test/functional/lua/ui_event_spec.lua
+++ b/test/functional/lua/ui_event_spec.lua
@@ -106,20 +106,15 @@ describe('vim.ui_attach', function()
end)
it('does not crash on exit', function()
- fn.system({
- n.nvim_prog,
- '-u',
- 'NONE',
- '-i',
- 'NONE',
+ local p = n.spawn_wait(
'--cmd',
[[ lua ns = vim.api.nvim_create_namespace 'testspace' ]],
'--cmd',
[[ lua vim.ui_attach(ns, {ext_popupmenu=true}, function() end) ]],
'--cmd',
- 'quitall!',
- })
- eq(0, n.eval('v:shell_error'))
+ 'quitall!'
+ )
+ eq(0, p.status)
end)
it('can receive accurate message kinds even if they are history', function()
@@ -173,18 +168,67 @@ describe('vim.ui_attach', function()
vim.ui_attach(ns, { ext_messages = true }, function(ev)
if ev == 'msg_show' then
vim.schedule(function() vim.cmd.redraw() end)
- else
- vim.cmd.redraw()
+ elseif ev:find('cmdline') then
+ _G.cmdline = _G.cmdline + (ev == 'cmdline_show' and 1 or 0)
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, { tostring(_G.cmdline) })
+ vim.cmd('redraw')
end
- _G.cmdline = _G.cmdline + (ev == 'cmdline_show' and 1 or 0)
end
)]])
+ screen:expect([[
+ ^ |
+ {1:~ }|*4
+ ]])
feed(':')
- n.assert_alive()
- eq(2, exec_lua('return _G.cmdline'))
- n.assert_alive()
+ screen:expect({
+ grid = [[
+ ^1 |
+ {1:~ }|*4
+ ]],
+ cmdline = { {
+ content = { { '' } },
+ firstc = ':',
+ pos = 0,
+ } },
+ })
feed('version<CR><CR>v<Esc>')
- n.assert_alive()
+ screen:expect({
+ grid = [[
+ ^2 |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
+ feed([[:call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
+ screen:expect({
+ grid = [[
+ ^4 |
+ {1:~ }|*4
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 10,
+ pos = 0,
+ prompt = '[Y]es, (N)o, (C)ancel: ',
+ },
+ },
+ messages = {
+ {
+ content = { { '\nSave changes?\n', 6, 10 } },
+ history = false,
+ kind = 'confirm',
+ },
+ },
+ })
+ feed('n')
+ screen:expect({
+ grid = [[
+ ^4 |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
end)
it("preserved 'incsearch/command' screen state after :redraw from ext_cmdline", function()
@@ -261,30 +305,15 @@ describe('vim.ui_attach', function()
lled in a fast event context |
{1:~ }|
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'E122: Function Foo already exists, add ! to replace it', 9, 7 } },
+ content = { { 'E122: Function Foo already exists, add ! to replace it', 9, 6 } },
+ history = true,
kind = 'emsg',
},
},
})
- -- No fast context for prompt message kinds
- feed(':%s/Function/Replacement/c<cr>')
- screen:expect({
- grid = [[
- ^E122: {10:Function} Foo already exists, add !|
- to replace it |
- replace with Replacement (y/n/a/q/l/^E/^|
- Y)? |
- {1:~ }|
- ]],
- messages = {
- {
- content = { { 'replace with Replacement (y/n/a/q/l/^E/^Y)?', 6, 19 } },
- kind = 'confirm_sub',
- },
- },
- })
end)
end)
@@ -316,30 +345,36 @@ describe('vim.ui_attach', function()
vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] })
end)
]])
+ local s1 = [[
+ ^ |
+ {1:~ }|*4
+ ]]
+ screen:expect(s1)
+ feed('QQQQQQ<CR>')
screen:expect({
grid = [[
- ^ |
- {1:~ }|*4
- ]],
- })
- feed('ifoo')
- screen:expect({
- grid = [[
- foo^ |
- {1:~ }|*4
- ]],
- showmode = { { '-- INSERT --', 5, 12 } },
- })
- feed('<esc>:1mes clear<cr>:mes<cr>')
- screen:expect({
- grid = [[
- foo |
- {3: }|
- {9:Excessive errors in vim.ui_attach() call}|
- {9:back from ns: 1.} |
+ {9:obal 'err' (a nil value)} |
+ {9:stack traceback:} |
+ {9: [string "<nvim>"]:2: in function}|
+ {9: <[string "<nvim>"]:1>} |
{100:Press ENTER or type command to continue}^ |
]],
+ messages = {
+ {
+ content = { { 'Press ENTER or type command to continue', 100, 18 } },
+ history = true,
+ kind = 'return_prompt',
+ },
+ },
})
+ feed(':1mes clear<CR>:mes<CR>')
+ screen:expect([[
+ |
+ {3: }|
+ {9:Excessive errors in vim.ui_attach() call}|
+ {9:back from ns: 1.} |
+ {100:Press ENTER or type command to continue}^ |
+ ]])
feed('<cr>')
-- Also when scheduled
exec_lua([[
@@ -348,16 +383,17 @@ describe('vim.ui_attach', function()
end)
]])
screen:expect({
- any = 'fo^o',
+ grid = s1,
messages = {
{
content = {
{
'Error executing vim.schedule lua callback: [string "<nvim>"]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:2: in function <[string "<nvim>"]:2>',
9,
- 7,
+ 6,
},
},
+ history = true,
kind = 'lua_error',
},
{
@@ -365,26 +401,35 @@ describe('vim.ui_attach', function()
{
'Error executing vim.schedule lua callback: [string "<nvim>"]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:2: in function <[string "<nvim>"]:2>',
9,
- 7,
+ 6,
},
},
+ history = true,
kind = 'lua_error',
},
{
- content = { { 'Press ENTER or type command to continue', 100, 19 } },
+ content = { { 'Press ENTER or type command to continue', 100, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
})
feed('<esc>:1mes clear<cr>:mes<cr>')
- screen:expect({
- grid = [[
- foo |
- {3: }|
- {9:Excessive errors in vim.ui_attach() call}|
- {9:back from ns: 2.} |
- {100:Press ENTER or type command to continue}^ |
- ]],
- })
+ screen:expect([[
+ |
+ {3: }|
+ {9:Excessive errors in vim.ui_attach() call}|
+ {9:back from ns: 2.} |
+ {100:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+
+ it('sourcing invalid file does not crash #32166', function()
+ exec_lua([[
+ local ns = vim.api.nvim_create_namespace("")
+ vim.ui_attach(ns, { ext_messages = true }, function() end)
+ ]])
+ feed((':luafile %s<CR>'):format(testlog))
+ n.assert_alive()
end)
end)
diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua
index 258b96bc43..d706f8b78b 100644
--- a/test/functional/lua/uri_spec.lua
+++ b/test/functional/lua/uri_spec.lua
@@ -252,4 +252,12 @@ describe('URI methods', function()
end
)
end)
+
+ describe('encode to uri', function()
+ it('rfc2732 including brackets', function()
+ exec_lua("str = '[:]'")
+ exec_lua("rfc = 'rfc2732'")
+ eq('[%3a]', exec_lua('return vim.uri_encode(str, rfc)'))
+ end)
+ end)
end)
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 3cfbfe167a..55e5158596 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3435,7 +3435,6 @@ stack traceback:
end)
it('can discard input', function()
- clear()
-- discard every other normal 'x' command
exec_lua [[
n_key = 0
@@ -3461,7 +3460,6 @@ stack traceback:
end)
it('callback invalid return', function()
- clear()
-- second key produces an error which removes the callback
exec_lua [[
n_call = 0
@@ -3955,6 +3953,17 @@ stack traceback:
eq(win2, val)
end)
+ it('failure modes', function()
+ matches(
+ 'nvim_exec2%(%): Vim:E492: Not an editor command: fooooo',
+ pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() vim.cmd 'fooooo' end)]])
+ )
+ eq(
+ 'Error executing lua: [string "<nvim>"]:0: fooooo',
+ pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() error('fooooo') end)]])
+ )
+ end)
+
it('does not cause ml_get errors with invalid visual selection', function()
-- Add lines to the current buffer and make another window looking into an empty buffer.
exec_lua [[
diff --git a/test/functional/lua/watch_spec.lua b/test/functional/lua/watch_spec.lua
index ad16df8a7c..3b109b70d5 100644
--- a/test/functional/lua/watch_spec.lua
+++ b/test/functional/lua/watch_spec.lua
@@ -91,8 +91,7 @@ describe('vim._watch', function()
skip(is_os('mac'), 'flaky test on mac')
skip(is_os('bsd'), 'Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38')
elseif watchfunc == 'watchdirs' and is_os('mac') then
- -- Bump this (or fix the bug) if CI continues to fail in future versions of macos CI.
- skip(is_ci() and vim.uv.os_uname().release == '24.0.0', 'weird failure for macOS arm 15 CI')
+ skip(true, 'weird failure since macOS 14 CI, see bbf208784ca279178ba0075b60d3e9c80f11da7a')
else
skip(
is_os('bsd'),
diff --git a/test/functional/lua/with_spec.lua b/test/functional/lua/with_spec.lua
index 6127e83619..92e798e7f3 100644
--- a/test/functional/lua/with_spec.lua
+++ b/test/functional/lua/with_spec.lua
@@ -1621,4 +1621,21 @@ describe('vim._with', function()
matches('Invalid buffer', get_error('{ buf = -1 }, function() end'))
matches('Invalid window', get_error('{ win = -1 }, function() end'))
end)
+
+ it('no double-free when called from :filter browse oldfiles #31501', function()
+ exec_lua([=[
+ vim.api.nvim_create_autocmd('BufEnter', {
+ callback = function()
+ vim._with({ lockmarks = true }, function() end)
+ end,
+ })
+ vim.cmd([[
+ let v:oldfiles = ['Xoldfile']
+ call nvim_input('1<CR>')
+ noswapfile filter /Xoldfile/ browse oldfiles
+ ]])
+ ]=])
+ n.assert_alive()
+ eq('Xoldfile', fn.bufname('%'))
+ end)
end)
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index e7f47ef4e9..a82279e775 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -14,7 +14,6 @@ local api = n.api
local command = n.command
local clear = n.clear
local exc_exec = n.exc_exec
-local exec_lua = n.exec_lua
local eval = n.eval
local eq = t.eq
local ok = t.ok
@@ -929,17 +928,12 @@ describe('stdpath()', function()
assert_alive() -- Check for crash. #8393
-- Check that Nvim rejects invalid APPNAMEs
- -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
local function test_appname(testAppname, expected_exitcode)
- local lua_code = string.format(
- [[
- local child = vim.fn.jobstart({ vim.v.progpath, '--clean', '--headless', '--listen', 'x', '+qall!' }, { env = { NVIM_APPNAME = %q } })
- return vim.fn.jobwait({ child }, %d)[1]
- ]],
- testAppname,
- 3000
- )
- eq(expected_exitcode, exec_lua(lua_code))
+ local p = n.spawn_wait({
+ args = { '--listen', 'x', '+qall!' },
+ env = { NVIM_APPNAME = testAppname },
+ })
+ eq(expected_exitcode, p.status)
end
-- Invalid appnames:
test_appname('a/../b', 1)
diff --git a/test/functional/options/winfixbuf_spec.lua b/test/functional/options/winfixbuf_spec.lua
index 124f194b5a..a01650ea71 100644
--- a/test/functional/options/winfixbuf_spec.lua
+++ b/test/functional/options/winfixbuf_spec.lua
@@ -1,55 +1,51 @@
local n = require('test.functional.testnvim')()
+local t = require('test.testutil')
local clear = n.clear
local exec_lua = n.exec_lua
-describe("Nvim API calls with 'winfixbuf'", function()
+describe("'winfixbuf'", function()
before_each(function()
clear()
end)
- it("Calling vim.api.nvim_win_set_buf with 'winfixbuf'", function()
- local results = exec_lua([[
- local function _setup_two_buffers()
- local buffer = vim.api.nvim_create_buf(true, true)
-
- vim.api.nvim_create_buf(true, true) -- Make another buffer
-
- local current_window = 0
- vim.api.nvim_set_option_value("winfixbuf", true, {win=current_window})
-
- return buffer
- end
-
- local other_buffer = _setup_two_buffers()
- local current_window = 0
- local results, _ = pcall(vim.api.nvim_win_set_buf, current_window, other_buffer)
-
- return results
+ ---@return integer
+ local function setup_winfixbuf()
+ return exec_lua([[
+ local buffer = vim.api.nvim_create_buf(true, true)
+ vim.api.nvim_create_buf(true, true) -- Make another buffer
+ vim.wo.winfixbuf = true
+ return buffer
]])
-
- assert(results == false)
+ end
+
+ it('nvim_win_set_buf on non-current buffer', function()
+ local other_buf = setup_winfixbuf()
+ t.eq(
+ "Vim:E1513: Cannot switch buffer. 'winfixbuf' is enabled",
+ t.pcall_err(n.api.nvim_win_set_buf, 0, other_buf)
+ )
end)
- it("Calling vim.api.nvim_set_current_buf with 'winfixbuf'", function()
- local results = exec_lua([[
- local function _setup_two_buffers()
- local buffer = vim.api.nvim_create_buf(true, true)
-
- vim.api.nvim_create_buf(true, true) -- Make another buffer
-
- local current_window = 0
- vim.api.nvim_set_option_value("winfixbuf", true, {win=current_window})
-
- return buffer
- end
-
- local other_buffer = _setup_two_buffers()
- local results, _ = pcall(vim.api.nvim_set_current_buf, other_buffer)
+ it('nvim_set_current_buf on non-current buffer', function()
+ local other_buf = setup_winfixbuf()
+ t.eq(
+ "Vim:E1513: Cannot switch buffer. 'winfixbuf' is enabled",
+ t.pcall_err(n.api.nvim_set_current_buf, other_buf)
+ )
+ end)
- return results
- ]])
+ it('nvim_win_set_buf on current buffer', function()
+ setup_winfixbuf()
+ local curbuf = n.api.nvim_get_current_buf()
+ n.api.nvim_win_set_buf(0, curbuf)
+ t.eq(curbuf, n.api.nvim_get_current_buf())
+ end)
- assert(results == false)
+ it('nvim_set_current_buf on current buffer', function()
+ setup_winfixbuf()
+ local curbuf = n.api.nvim_get_current_buf()
+ n.api.nvim_set_current_buf(curbuf)
+ t.eq(curbuf, n.api.nvim_get_current_buf())
end)
end)
diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua
index 753da64522..406b5c3c16 100644
--- a/test/functional/plugin/health_spec.lua
+++ b/test/functional/plugin/health_spec.lua
@@ -66,6 +66,18 @@ describe(':checkhealth', function()
eq({}, getcompletion('', 'checkhealth'))
assert_alive()
end)
+
+ it('vim.g.health', function()
+ clear()
+ command("let g:health = {'style':'float'}")
+ command('checkhealth lsp')
+ eq(
+ 'editor',
+ exec_lua([[
+ return vim.api.nvim_win_get_config(0).relative
+ ]])
+ )
+ end)
end)
describe('vim.health', function()
diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua
index 39b6ddc105..4e90c2fd1b 100644
--- a/test/functional/plugin/lsp/completion_spec.lua
+++ b/test/functional/plugin/lsp/completion_spec.lua
@@ -216,6 +216,43 @@ describe('vim.lsp.completion: item conversion', function()
})
end)
+ it('uses filterText as word if label/newText would not match', function()
+ local items = {
+ {
+ filterText = '<module',
+ insertTextFormat = 2,
+ kind = 10,
+ label = 'module',
+ sortText = 'module',
+ textEdit = {
+ newText = '<module>$1</module>$0',
+ range = {
+ start = {
+ character = 0,
+ line = 0,
+ },
+ ['end'] = {
+ character = 0,
+ line = 0,
+ },
+ },
+ },
+ },
+ }
+ assert_completion_matches('<mo', items, {
+ {
+ abbr = 'module',
+ word = '<module',
+ },
+ })
+ assert_completion_matches('', items, {
+ {
+ abbr = 'module',
+ word = 'module',
+ },
+ })
+ end)
+
it('fuzzy matches on label when filterText is missing', function()
assert_completion_matches('fo', {
{ label = 'foo' },
@@ -731,9 +768,10 @@ describe('vim.lsp.completion: item conversion', function()
)
end)
+--- @param name string
--- @param completion_result lsp.CompletionList
--- @return integer
-local function create_server(completion_result)
+local function create_server(name, completion_result)
return exec_lua(function()
local server = _G._create_server({
capabilities = {
@@ -751,7 +789,7 @@ local function create_server(completion_result)
local bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_win_set_buf(0, bufnr)
return vim.lsp.start({
- name = 'dummy',
+ name = name,
cmd = server.cmd,
on_attach = function(client, bufnr0)
vim.lsp.completion.enable(true, client.id, bufnr0, {
@@ -800,7 +838,7 @@ describe('vim.lsp.completion: protocol', function()
end
it('fetches completions and shows them using complete on trigger', function()
- create_server({
+ create_server('dummy', {
isIncomplete = false,
items = {
{
@@ -892,7 +930,7 @@ describe('vim.lsp.completion: protocol', function()
end)
it('merges results from multiple clients', function()
- create_server({
+ create_server('dummy1', {
isIncomplete = false,
items = {
{
@@ -900,7 +938,7 @@ describe('vim.lsp.completion: protocol', function()
},
},
})
- create_server({
+ create_server('dummy2', {
isIncomplete = false,
items = {
{
@@ -933,7 +971,7 @@ describe('vim.lsp.completion: protocol', function()
},
},
}
- local client_id = create_server(completion_list)
+ local client_id = create_server('dummy', completion_list)
exec_lua(function()
_G.called = false
@@ -970,7 +1008,7 @@ describe('vim.lsp.completion: protocol', function()
end)
it('enable(…,{convert=fn}) custom word/abbr format', function()
- create_server({
+ create_server('dummy', {
isIncomplete = false,
items = {
{
@@ -1012,7 +1050,7 @@ describe('vim.lsp.completion: integration', function()
exec_lua(function()
vim.o.completeopt = 'menuone,noselect'
end)
- create_server(completion_list)
+ create_server('dummy', completion_list)
feed('i world<esc>0ih<c-x><c-o>')
retry(nil, nil, function()
eq(
diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua
index 5afbe22793..4ecb056d01 100644
--- a/test/functional/plugin/lsp/diagnostic_spec.lua
+++ b/test/functional/plugin/lsp/diagnostic_spec.lua
@@ -89,7 +89,7 @@ describe('vim.lsp.diagnostic', function()
return extmarks
end
- client_id = assert(vim.lsp.start_client {
+ client_id = assert(vim.lsp.start({
cmd_env = {
NVIM_LUA_NOTRACK = '1',
},
@@ -101,7 +101,7 @@ describe('vim.lsp.diagnostic', function()
'--headless',
},
offset_encoding = 'utf-16',
- })
+ }, { attach = false }))
end)
fake_uri = 'file:///fake/uri'
@@ -209,10 +209,16 @@ describe('vim.lsp.diagnostic', function()
before_each(function()
exec_lua(create_server_definition)
exec_lua(function()
+ _G.requests = 0
_G.server = _G._create_server({
capabilities = {
diagnosticProvider = {},
},
+ handlers = {
+ [vim.lsp.protocol.Methods.textDocument_diagnostic] = function()
+ _G.requests = _G.requests + 1
+ end,
+ },
})
function _G.get_extmarks(bufnr, client_id0)
@@ -373,5 +379,56 @@ describe('vim.lsp.diagnostic', function()
end)
)
end)
+
+ it('handles server cancellation', function()
+ eq(
+ 1,
+ exec_lua(function()
+ vim.lsp.diagnostic.on_diagnostic({
+ code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
+ -- Empty data defaults to retriggering request
+ data = {},
+ message = '',
+ }, {}, {
+ method = vim.lsp.protocol.Methods.textDocument_diagnostic,
+ client_id = client_id,
+ })
+
+ return _G.requests
+ end)
+ )
+
+ eq(
+ 2,
+ exec_lua(function()
+ vim.lsp.diagnostic.on_diagnostic({
+ code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
+ data = { retriggerRequest = true },
+ message = '',
+ }, {}, {
+ method = vim.lsp.protocol.Methods.textDocument_diagnostic,
+ client_id = client_id,
+ })
+
+ return _G.requests
+ end)
+ )
+
+ eq(
+ 2,
+ exec_lua(function()
+ vim.lsp.diagnostic.on_diagnostic({
+ code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
+ data = { retriggerRequest = false },
+ message = '',
+ }, {}, {
+ method = vim.lsp.protocol.Methods.textDocument_diagnostic,
+ client_id = client_id,
+ })
+
+ return _G.requests
+ end)
+ )
+ end)
end)
end)
diff --git a/test/functional/plugin/lsp/folding_range_spec.lua b/test/functional/plugin/lsp/folding_range_spec.lua
new file mode 100644
index 0000000000..7e68a598d2
--- /dev/null
+++ b/test/functional/plugin/lsp/folding_range_spec.lua
@@ -0,0 +1,647 @@
+local t = require('test.testutil')
+local n = require('test.functional.testnvim')()
+local Screen = require('test.functional.ui.screen')
+local t_lsp = require('test.functional.plugin.lsp.testutil')
+
+local eq = t.eq
+local tempname = t.tmpname
+
+local clear_notrace = t_lsp.clear_notrace
+local create_server_definition = t_lsp.create_server_definition
+
+local api = n.api
+local exec_lua = n.exec_lua
+local insert = n.insert
+local command = n.command
+local feed = n.feed
+
+describe('vim.lsp.folding_range', function()
+ local text = [[// foldLevel() {{{2
+/// @return fold level at line number "lnum" in the current window.
+static int foldLevel(linenr_T lnum)
+{
+ // While updating the folds lines between invalid_top and invalid_bot have
+ // an undefined fold level. Otherwise update the folds first.
+ if (invalid_top == 0) {
+ checkupdate(curwin);
+ } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) {
+ return prev_lnum_lvl;
+ } else if (lnum >= invalid_top && lnum <= invalid_bot) {
+ return -1;
+ }
+
+ // Return quickly when there is no folding at all in this window.
+ if (!hasAnyFolding(curwin)) {
+ return 0;
+ }
+
+ return foldLevelWin(curwin, lnum);
+}]]
+
+ local result = {
+ {
+ endLine = 19,
+ kind = 'region',
+ startCharacter = 1,
+ startLine = 3,
+ },
+ {
+ endCharacter = 2,
+ endLine = 7,
+ kind = 'region',
+ startCharacter = 25,
+ startLine = 6,
+ },
+ {
+ endCharacter = 2,
+ endLine = 9,
+ kind = 'region',
+ startCharacter = 55,
+ startLine = 8,
+ },
+ {
+ endCharacter = 2,
+ endLine = 11,
+ kind = 'region',
+ startCharacter = 58,
+ startLine = 10,
+ },
+ {
+ endCharacter = 2,
+ endLine = 16,
+ kind = 'region',
+ startCharacter = 31,
+ startLine = 15,
+ },
+ {
+ endCharacter = 68,
+ endLine = 1,
+ kind = 'comment',
+ startCharacter = 2,
+ startLine = 0,
+ },
+ {
+ endCharacter = 64,
+ endLine = 5,
+ kind = 'comment',
+ startCharacter = 4,
+ startLine = 4,
+ },
+ }
+
+ local bufnr ---@type integer
+ local client_id ---@type integer
+
+ clear_notrace()
+ before_each(function()
+ clear_notrace()
+
+ exec_lua(create_server_definition)
+ bufnr = n.api.nvim_get_current_buf()
+ client_id = exec_lua(function()
+ _G.server = _G._create_server({
+ capabilities = {
+ foldingRangeProvider = true,
+ },
+ handlers = {
+ ['textDocument/foldingRange'] = function(_, _, callback)
+ callback(nil, result)
+ end,
+ },
+ })
+
+ vim.api.nvim_win_set_buf(0, bufnr)
+
+ return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
+ end)
+ command('set foldmethod=expr foldcolumn=1 foldlevel=999')
+ insert(text)
+ end)
+ after_each(function()
+ api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
+ end)
+
+ describe('setup()', function()
+ ---@type integer
+ local bufnr_set_expr
+ ---@type integer
+ local bufnr_never_set_expr
+
+ local function buf_autocmd_num(bufnr_to_check)
+ return exec_lua(function()
+ return #vim.api.nvim_get_autocmds({ buffer = bufnr_to_check, event = 'LspNotify' })
+ end)
+ end
+
+ before_each(function()
+ command([[setlocal foldexpr=v:lua.vim.lsp.foldexpr()]])
+ exec_lua(function()
+ bufnr_set_expr = vim.api.nvim_create_buf(true, false)
+ vim.api.nvim_set_current_buf(bufnr_set_expr)
+ end)
+ insert(text)
+ command('write ' .. tempname(false))
+ command([[setlocal foldexpr=v:lua.vim.lsp.foldexpr()]])
+ exec_lua(function()
+ bufnr_never_set_expr = vim.api.nvim_create_buf(true, false)
+ vim.api.nvim_set_current_buf(bufnr_never_set_expr)
+ end)
+ insert(text)
+ api.nvim_win_set_buf(0, bufnr_set_expr)
+ end)
+
+ it('only create event hooks where foldexpr has been set', function()
+ eq(1, buf_autocmd_num(bufnr))
+ eq(1, buf_autocmd_num(bufnr_set_expr))
+ eq(0, buf_autocmd_num(bufnr_never_set_expr))
+ end)
+
+ it('does not create duplicate event hooks after reloaded', function()
+ command('edit')
+ eq(1, buf_autocmd_num(bufnr_set_expr))
+ end)
+
+ it('cleans up event hooks when buffer is unloaded', function()
+ command('bdelete')
+ eq(0, buf_autocmd_num(bufnr_set_expr))
+ end)
+ end)
+
+ describe('expr()', function()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ screen = Screen.new(80, 45)
+ screen:set_default_attr_ids({
+ [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
+ [2] = { bold = true, foreground = Screen.colors.Blue1 },
+ [3] = { bold = true, reverse = true },
+ [4] = { reverse = true },
+ })
+ command([[set foldexpr=v:lua.vim.lsp.foldexpr()]])
+ command([[split]])
+ end)
+
+ it('can compute fold levels', function()
+ ---@type table<integer, string>
+ local foldlevels = {}
+ for i = 1, 21 do
+ foldlevels[i] = exec_lua('return vim.lsp.foldexpr(' .. i .. ')')
+ end
+ eq({
+ [1] = '>1',
+ [2] = '<1',
+ [3] = '0',
+ [4] = '>1',
+ [5] = '>2',
+ [6] = '<2',
+ [7] = '>2',
+ [8] = '<2',
+ [9] = '>2',
+ [10] = '<2',
+ [11] = '>2',
+ [12] = '<2',
+ [13] = '1',
+ [14] = '1',
+ [15] = '1',
+ [16] = '>2',
+ [17] = '<2',
+ [18] = '1',
+ [19] = '1',
+ [20] = '<1',
+ [21] = '0',
+ }, foldlevels)
+ end)
+
+ it('updates folds in all windows', function()
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+
+ it('persists wherever foldexpr is set', function()
+ command([[setlocal foldexpr=]])
+ feed('<C-w><C-w>zx')
+ screen:expect({
+ grid = [[
+{1: }// foldLevel() {{{2 |
+{1: }/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1: }{ |
+{1: } // While updating the folds lines between invalid_top and invalid_bot have |
+{1: } // an undefined fold level. Otherwise update the folds first. |
+{1: } if (invalid_top == 0) { |
+{1: } checkupdate(curwin); |
+{1: } } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1: } return prev_lnum_lvl; |
+{1: } } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1: } return -1; |
+{1: } } |
+{1: } |
+{1: } // Return quickly when there is no folding at all in this window. |
+{1: } if (!hasAnyFolding(curwin)) { |
+{1: } return 0; |
+{1: } } |
+{1: } |
+{1: } return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+
+ it('synchronizes changed rows with their previous foldlevels', function()
+ command('1,2d')
+ screen:expect({
+ grid = [[
+{1: }^static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{2:~ }|*2
+{3:[No Name] [+] }|
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{2:~ }|*2
+{4:[No Name] [+] }|
+ |
+]],
+ })
+ end)
+
+ it('clears folds when sole client detaches', function()
+ exec_lua(function()
+ vim.lsp.buf_detach_client(bufnr, client_id)
+ end)
+ screen:expect({
+ grid = [[
+{1: }// foldLevel() {{{2 |
+{1: }/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1: }{ |
+{1: } // While updating the folds lines between invalid_top and invalid_bot have |
+{1: } // an undefined fold level. Otherwise update the folds first. |
+{1: } if (invalid_top == 0) { |
+{1: } checkupdate(curwin); |
+{1: } } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1: } return prev_lnum_lvl; |
+{1: } } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1: } return -1; |
+{1: } } |
+{1: } |
+{1: } // Return quickly when there is no folding at all in this window. |
+{1: } if (!hasAnyFolding(curwin)) { |
+{1: } return 0; |
+{1: } } |
+{1: } |
+{1: } return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+{1: }// foldLevel() {{{2 |
+{1: }/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1: }{ |
+{1: } // While updating the folds lines between invalid_top and invalid_bot have |
+{1: } // an undefined fold level. Otherwise update the folds first. |
+{1: } if (invalid_top == 0) { |
+{1: } checkupdate(curwin); |
+{1: } } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1: } return prev_lnum_lvl; |
+{1: } } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1: } return -1; |
+{1: } } |
+{1: } |
+{1: } // Return quickly when there is no folding at all in this window. |
+{1: } if (!hasAnyFolding(curwin)) { |
+{1: } return 0; |
+{1: } } |
+{1: } |
+{1: } return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+
+ it('remains valid after the client re-attaches.', function()
+ exec_lua(function()
+ vim.lsp.buf_detach_client(bufnr, client_id)
+ vim.lsp.buf_attach_client(bufnr, client_id)
+ end)
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:[No Name] [+] }|
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }} |
+{4:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
+ end)
+
+ describe('foldtext()', function()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ screen = Screen.new(80, 23)
+ screen:set_default_attr_ids({
+ [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
+ [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey },
+ [3] = { bold = true, foreground = Screen.colors.Blue1 },
+ [4] = { bold = true, reverse = true },
+ [5] = { reverse = true },
+ })
+ command(
+ [[set foldexpr=v:lua.vim.lsp.foldexpr() foldtext=v:lua.vim.lsp.foldtext() foldlevel=1]]
+ )
+ end)
+
+ it('shows the first folded line if `collapsedText` does not exist', function()
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:+}{2: // While updating the folds lines between invalid_top and invalid_bot have···}|
+{1:+}{2: if (invalid_top == 0) {······················································}|
+{1:+}{2: } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) {························}|
+{1:+}{2: } else if (lnum >= invalid_top && lnum <= invalid_bot) {·····················}|
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:+}{2: if (!hasAnyFolding(curwin)) {················································}|
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*6
+ |
+ ]],
+ })
+ end)
+ end)
+
+ describe('foldclose()', function()
+ --- @type test.functional.ui.screen
+ local screen
+ before_each(function()
+ screen = Screen.new(80, 23)
+ screen:set_default_attr_ids({
+ [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue },
+ [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey },
+ [3] = { bold = true, foreground = Screen.colors.Blue1 },
+ [4] = { bold = true, reverse = true },
+ [5] = { reverse = true },
+ })
+ command([[set foldexpr=v:lua.vim.lsp.foldexpr()]])
+ end)
+
+ it('closes all folds of one kind immediately', function()
+ exec_lua(function()
+ vim.lsp.foldclose('comment')
+ end)
+ screen:expect({
+ grid = [[
+{1:+}{2:+-- 2 lines: foldLevel()······················································}|
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:+}{2:+--- 2 lines: While updating the folds lines between invalid_top and invalid_b}|
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*3
+ |
+ ]],
+ })
+ end)
+
+ it('closes the smallest fold first', function()
+ exec_lua(function()
+ vim.lsp.foldclose('region')
+ end)
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:+}{2:+-- 17 lines: {································································}|
+{1: }^} |
+{3:~ }|*17
+ |
+ ]],
+ })
+ command('4foldopen')
+ screen:expect({
+ grid = [[
+{1:-}// foldLevel() {{{2 |
+{1:│}/// @return fold level at line number "lnum" in the current window. |
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:-} // While updating the folds lines between invalid_top and invalid_bot have |
+{1:2} // an undefined fold level. Otherwise update the folds first. |
+{1:+}{2:+--- 2 lines: if (invalid_top == 0) {·········································}|
+{1:+}{2:+--- 2 lines: } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) {···········}|
+{1:+}{2:+--- 2 lines: } else if (lnum >= invalid_top && lnum <= invalid_bot) {········}|
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:+}{2:+--- 2 lines: if (!hasAnyFolding(curwin)) {···································}|
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*5
+ |
+ ]],
+ })
+ end)
+
+ it('is defered when the buffer is not up-to-date', function()
+ exec_lua(function()
+ vim.lsp.foldclose('comment')
+ vim.lsp.util.buf_versions[bufnr] = 0
+ end)
+ screen:expect({
+ grid = [[
+{1:+}{2:+-- 2 lines: foldLevel()······················································}|
+{1: }static int foldLevel(linenr_T lnum) |
+{1:-}{ |
+{1:+}{2:+--- 2 lines: While updating the folds lines between invalid_top and invalid_b}|
+{1:-} if (invalid_top == 0) { |
+{1:2} checkupdate(curwin); |
+{1:-} } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { |
+{1:2} return prev_lnum_lvl; |
+{1:-} } else if (lnum >= invalid_top && lnum <= invalid_bot) { |
+{1:2} return -1; |
+{1:│} } |
+{1:│} |
+{1:│} // Return quickly when there is no folding at all in this window. |
+{1:-} if (!hasAnyFolding(curwin)) { |
+{1:2} return 0; |
+{1:│} } |
+{1:│} |
+{1:│} return foldLevelWin(curwin, lnum); |
+{1: }^} |
+{3:~ }|*3
+ |
+ ]],
+ })
+ end)
+ end)
+end)
diff --git a/test/functional/plugin/lsp/handler_spec.lua b/test/functional/plugin/lsp/handler_spec.lua
deleted file mode 100644
index 4b05b676a8..0000000000
--- a/test/functional/plugin/lsp/handler_spec.lua
+++ /dev/null
@@ -1,42 +0,0 @@
-local t = require('test.testutil')
-local n = require('test.functional.testnvim')()
-
-local eq = t.eq
-local exec_lua = n.exec_lua
-local pcall_err = t.pcall_err
-local matches = t.matches
-
-describe('lsp-handlers', function()
- describe('vim.lsp._with_extend', function()
- it('should return a table with the default keys', function()
- eq(
- { hello = 'world' },
- exec_lua(function()
- return vim.lsp._with_extend('test', { hello = 'world' })
- end)
- )
- end)
-
- it('should override with config keys', function()
- eq(
- { hello = 'universe', other = true },
- exec_lua(function()
- return vim.lsp._with_extend(
- 'test',
- { other = true, hello = 'world' },
- { hello = 'universe' }
- )
- end)
- )
- end)
-
- it('should not allow invalid keys', function()
- matches(
- '.*Invalid option for `test`.*',
- pcall_err(exec_lua, function()
- return vim.lsp._with_extend('test', { hello = 'world' }, { invalid = true })
- end)
- )
- end)
- end)
-end)
diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua
index f60e159d64..0bca1fa4ca 100644
--- a/test/functional/plugin/lsp/incremental_sync_spec.lua
+++ b/test/functional/plugin/lsp/incremental_sync_spec.lua
@@ -23,7 +23,7 @@ before_each(function()
-- local line_ending = format_line_ending[vim.api.nvim_get_option_value('fileformat', {})]
--- @diagnostic disable-next-line:duplicate-set-field
- function _G.test_register(bufnr, id, offset_encoding, line_ending)
+ function _G.test_register(bufnr, id, position_encoding, line_ending)
local prev_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
local function callback(_, bufnr0, _changedtick, firstline, lastline, new_lastline)
@@ -38,7 +38,7 @@ before_each(function()
firstline,
lastline,
new_lastline,
- offset_encoding,
+ position_encoding,
line_ending
)
@@ -63,15 +63,15 @@ local function test_edit(
prev_buffer,
edit_operations,
expected_text_changes,
- offset_encoding,
+ position_encoding,
line_ending
)
- offset_encoding = offset_encoding or 'utf-16'
+ position_encoding = position_encoding or 'utf-16'
line_ending = line_ending or '\n'
api.nvim_buf_set_lines(0, 0, -1, true, prev_buffer)
exec_lua(function()
- return _G.test_register(0, 'test1', offset_encoding, line_ending)
+ return _G.test_register(0, 'test1', position_encoding, line_ending)
end)
for _, edit in ipairs(edit_operations) do
diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua
index 280bd27207..9912bf2063 100644
--- a/test/functional/plugin/lsp/semantic_tokens_spec.lua
+++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua
@@ -456,7 +456,7 @@ describe('semantic token highlighting', function()
vim.notify = function(...)
table.insert(_G.notifications, 1, { ... })
end
- return vim.lsp.start_client({ name = 'dummy', cmd = _G.server.cmd })
+ return vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }, { attach = false })
end)
eq(false, exec_lua('return vim.lsp.buf_is_attached(0, ...)', client_id))
diff --git a/test/functional/plugin/lsp/testutil.lua b/test/functional/plugin/lsp/testutil.lua
index a36cbac568..95fc22b96b 100644
--- a/test/functional/plugin/lsp/testutil.lua
+++ b/test/functional/plugin/lsp/testutil.lua
@@ -182,16 +182,17 @@ function M.test_rpc_server(config)
)
end
local client = setmetatable({}, {
- __index = function(_, name)
+ __index = function(t, 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 function(arg1, ...)
+ local ismethod = arg1 == t
return exec_lua(function(...)
- if type(_G.TEST_RPC_CLIENT[name]) == 'function' then
- return _G.TEST_RPC_CLIENT[name](...)
- else
- return _G.TEST_RPC_CLIENT[name]
+ local client = _G.TEST_RPC_CLIENT
+ if type(client[name]) == 'function' then
+ return client[name](ismethod and client or arg1, ...)
end
+ return client[name]
end, ...)
end
end,
diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua
index 813b8de812..1e3e759e0b 100644
--- a/test/functional/plugin/lsp/utils_spec.lua
+++ b/test/functional/plugin/lsp/utils_spec.lua
@@ -5,6 +5,8 @@ local Screen = require('test.functional.ui.screen')
local feed = n.feed
local eq = t.eq
local exec_lua = n.exec_lua
+local command, api = n.command, n.api
+local pcall_err = t.pcall_err
describe('vim.lsp.util', function()
before_each(n.clear)
@@ -265,6 +267,66 @@ describe('vim.lsp.util', function()
eq(56, opts.height)
end)
+
+ describe('vim.lsp.util.open_floating_preview', function()
+ local var_name = 'lsp_floating_preview'
+ local curbuf = api.nvim_get_current_buf()
+
+ it('clean bufvar after fclose', function()
+ exec_lua(function()
+ vim.lsp.util.open_floating_preview({ 'test' }, '', { height = 5, width = 2 })
+ end)
+ eq(true, api.nvim_win_is_valid(api.nvim_buf_get_var(curbuf, var_name)))
+ command('fclose')
+ eq(
+ 'Key not found: lsp_floating_preview',
+ pcall_err(api.nvim_buf_get_var, curbuf, var_name)
+ )
+ end)
+
+ it('clean bufvar after CursorMoved', function()
+ local result = exec_lua(function()
+ vim.lsp.util.open_floating_preview({ 'test' }, '', { height = 5, width = 2 })
+ local winnr = vim.b[vim.api.nvim_get_current_buf()].lsp_floating_preview
+ local result = vim.api.nvim_win_is_valid(winnr)
+ vim.api.nvim_feedkeys(vim.keycode('G'), 'txn', false)
+ return result
+ end)
+ eq(true, result)
+ eq(
+ 'Key not found: lsp_floating_preview',
+ pcall_err(api.nvim_buf_get_var, curbuf, var_name)
+ )
+ end)
+ end)
+ end)
+ end)
+
+ it('open_floating_preview zindex greater than current window', function()
+ local screen = Screen.new()
+ exec_lua(function()
+ vim.api.nvim_open_win(0, true, {
+ relative = 'editor',
+ border = 'single',
+ height = 11,
+ width = 51,
+ row = 2,
+ col = 2,
+ })
+ vim.keymap.set('n', 'K', function()
+ vim.lsp.util.open_floating_preview({ 'foo' }, '', { border = 'single' })
+ end, {})
end)
+ feed('K')
+ screen:expect([[
+ ┌───────────────────────────────────────────────────┐|
+ │{4:^ }│|
+ │┌───┐{11: }│|
+ ││{4:foo}│{11: }│|
+ │└───┘{11: }│|
+ │{11:~ }│|*7
+ └───────────────────────────────────────────────────┘|
+ |
+ ]])
end)
end)
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 5222216faf..db3ab8ed98 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -95,7 +95,7 @@ describe('LSP', function()
exec_lua(function()
_G.lsp = require('vim.lsp')
function _G.test__start_client()
- return vim.lsp.start_client {
+ return vim.lsp.start({
cmd_env = {
NVIM_LOG_FILE = fake_lsp_logfile,
NVIM_APPNAME = 'nvim_lsp_test',
@@ -112,7 +112,7 @@ describe('LSP', function()
name = 'test_folder',
},
},
- }
+ }, { attach = false })
end
_G.TEST_CLIENT1 = _G.test__start_client()
end)
@@ -232,7 +232,7 @@ describe('LSP', function()
-- client is a dummy object which will queue up commands to be run
-- once the server initializes. It can't accept lua callbacks or
-- other types that may be unserializable for now.
- client.stop()
+ client:stop()
end,
-- If the program timed out, then code will be nil.
on_exit = function(code, signal)
@@ -254,8 +254,8 @@ describe('LSP', function()
test_rpc_server {
test_name = 'basic_init',
on_init = function(client)
- client.notify('test')
- client.stop()
+ client:notify('test')
+ client:stop()
end,
on_exit = function(code, signal)
eq(101, code, 'exit code') -- See fake-lsp-server.lua
@@ -275,7 +275,7 @@ describe('LSP', function()
test_rpc_server({
test_name = 'basic_init_did_change_configuration',
on_init = function(client, _)
- client.stop()
+ client:stop()
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -333,9 +333,9 @@ describe('LSP', function()
test_name = 'basic_init',
on_init = function(client)
eq(0, client.server_capabilities().textDocumentSync.change)
- client.request('shutdown')
- client.notify('exit')
- client.stop()
+ client:request('shutdown')
+ client:notify('exit')
+ client:stop()
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -377,7 +377,7 @@ describe('LSP', function()
end,
on_init = function(_client)
client = _client
- client.notify('finish')
+ client:notify('finish')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -395,7 +395,7 @@ describe('LSP', function()
return vim.lsp.buf_is_attached(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)
end)
)
- client.stop()
+ client:stop()
end
end,
}
@@ -430,7 +430,7 @@ describe('LSP', function()
return vim.lsp.buf_attach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)
end)
)
- client.notify('finish')
+ client:notify('finish')
end,
on_handler = function(_, _, ctx)
if ctx.method == 'finish' then
@@ -439,7 +439,7 @@ describe('LSP', function()
return vim.lsp.buf_detach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID)
end)
eq('basic_init', api.nvim_get_var('lsp_detached'))
- client.stop()
+ client:stop()
end
end,
}
@@ -466,13 +466,20 @@ describe('LSP', function()
true,
exec_lua(function()
local keymap --- @type table<string,any>
+ local called = false
+ local origin = vim.lsp.buf.hover
+ vim.lsp.buf.hover = function()
+ called = true
+ end
vim._with({ buf = _G.BUFFER }, function()
keymap = vim.fn.maparg('K', 'n', false, true)
end)
- return keymap.callback == vim.lsp.buf.hover
+ keymap.callback()
+ vim.lsp.buf.hover = origin
+ return called
end)
)
- client.stop()
+ client:stop()
end
end,
on_exit = function(_, _)
@@ -480,13 +487,13 @@ describe('LSP', function()
eq('', get_buf_option('omnifunc'))
eq('', get_buf_option('formatexpr'))
eq(
- '',
+ true,
exec_lua(function()
local keymap --- @type string
vim._with({ buf = _G.BUFFER }, function()
keymap = vim.fn.maparg('K', 'n', false, false)
end)
- return keymap
+ return keymap:match('<Lua %d+: .+/runtime/lua/vim/lsp%.lua:%d+>') ~= nil
end)
)
end,
@@ -524,7 +531,7 @@ describe('LSP', function()
eq('v:lua.vim.lsp.tagfunc', get_buf_option('tagfunc', BUFFER_1))
eq('v:lua.vim.lsp.omnifunc', get_buf_option('omnifunc', BUFFER_2))
eq('v:lua.vim.lsp.formatexpr()', get_buf_option('formatexpr', BUFFER_2))
- client.stop()
+ client:stop()
end
end,
on_exit = function(_, _)
@@ -554,7 +561,7 @@ describe('LSP', function()
eq('tfu', get_buf_option('tagfunc'))
eq('ofu', get_buf_option('omnifunc'))
eq('fex', get_buf_option('formatexpr'))
- client.stop()
+ client:stop()
end
end,
on_exit = function(_, _)
@@ -711,10 +718,10 @@ describe('LSP', function()
ctx.method,
result
)
- client.notify('workspace/configuration', server_result)
+ client:notify('workspace/configuration', server_result)
end
if ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -756,7 +763,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'basic_check_capabilities',
on_init = function(client)
- client.stop()
+ client:stop()
local full_kind = exec_lua(function()
return require 'vim.lsp.protocol'.TextDocumentSyncKind.Full
end)
@@ -798,7 +805,7 @@ describe('LSP', function()
vim.api.nvim_exec_autocmds('BufWritePost', { buffer = _G.BUFFER, modeline = false })
end)
else
- client.stop()
+ client:stop()
end
end,
}
@@ -898,7 +905,7 @@ describe('LSP', function()
end)
end)
else
- client.stop()
+ client:stop()
end
end,
})
@@ -929,20 +936,20 @@ describe('LSP', function()
vim.api.nvim_exec_autocmds('BufWritePost', { buffer = _G.BUFFER, modeline = false })
end)
else
- client.stop()
+ client:stop()
end
end,
}
end)
- it('client.supports_methods() should validate capabilities', function()
+ it('client:supports_methods() should validate capabilities', function()
local expected_handlers = {
{ NIL, {}, { method = 'shutdown', client_id = 1 } },
}
test_rpc_server {
test_name = 'capabilities_for_client_supports_method',
on_init = function(client)
- client.stop()
+ client:stop()
local expected_sync_capabilities = {
change = 1,
openClose = true,
@@ -958,11 +965,11 @@ describe('LSP', function()
eq(true, client.server_capabilities().codeLensProvider.resolveProvider)
-- known methods for resolved capabilities
- eq(true, client.supports_method('textDocument/hover'))
- eq(false, client.supports_method('textDocument/definition'))
+ eq(true, client:supports_method('textDocument/hover'))
+ eq(false, client:supports_method('textDocument/definition'))
-- unknown methods are assumed to be supported.
- eq(true, client.supports_method('unknown-method'))
+ eq(true, client:supports_method('unknown-method'))
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -989,7 +996,7 @@ describe('LSP', function()
end)
end,
on_init = function(client)
- client.stop()
+ client:stop()
exec_lua(function()
vim.lsp.buf.type_definition()
end)
@@ -1018,7 +1025,7 @@ describe('LSP', function()
end)
end,
on_init = function(client)
- client.stop()
+ client:stop()
exec_lua(function()
vim.lsp.buf.type_definition()
end)
@@ -1042,7 +1049,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'check_forward_request_cancelled',
on_init = function(_client)
- _client.request('error_code_test')
+ _client:request('error_code_test')
client = _client
end,
on_exit = function(code, signal)
@@ -1053,7 +1060,40 @@ describe('LSP', function()
on_handler = function(err, _, ctx)
eq(table.remove(expected_handlers), { err, {}, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
+ end
+ end,
+ }
+ end)
+
+ it('should forward ServerCancelled to callback', function()
+ local expected_handlers = {
+ { NIL, {}, { method = 'finish', client_id = 1 } },
+ {
+ { code = -32802 },
+ NIL,
+ { method = 'error_code_test', bufnr = 1, client_id = 1, version = 0 },
+ },
+ }
+ local client --- @type vim.lsp.Client
+ test_rpc_server {
+ test_name = 'check_forward_server_cancelled',
+ on_init = function(_client)
+ _client:request('error_code_test')
+ client = _client
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, 'exit code')
+ eq(0, signal, 'exit signal')
+ eq(0, #expected_handlers, 'did not call expected handler')
+ end,
+ on_handler = function(err, _, ctx)
+ eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler')
+ if ctx.method ~= 'finish' then
+ client:notify('finish')
+ end
+ if ctx.method == 'finish' then
+ client:stop()
end
end,
}
@@ -1072,7 +1112,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'check_forward_content_modified',
on_init = function(_client)
- _client.request('error_code_test')
+ _client:request('error_code_test')
client = _client
end,
on_exit = function(code, signal)
@@ -1082,12 +1122,11 @@ describe('LSP', function()
end,
on_handler = function(err, _, ctx)
eq(table.remove(expected_handlers), { err, _, ctx }, 'expected handler')
- -- if ctx.method == 'error_code_test' then client.notify("finish") end
if ctx.method ~= 'finish' then
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1103,13 +1142,13 @@ describe('LSP', function()
test_name = 'check_pending_request_tracked',
on_init = function(_client)
client = _client
- client.request('slow_request')
+ client:request('slow_request')
local request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('pending', request.type)
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1123,10 +1162,10 @@ describe('LSP', function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq(nil, request)
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1141,14 +1180,14 @@ describe('LSP', function()
test_name = 'check_cancel_request_tracked',
on_init = function(_client)
client = _client
- client.request('slow_request')
- client.cancel_request(2)
+ client:request('slow_request')
+ client:cancel_request(2)
local request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('cancel', request.type)
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1162,7 +1201,7 @@ describe('LSP', function()
end)
eq(nil, request)
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1178,19 +1217,19 @@ describe('LSP', function()
test_name = 'check_tracked_requests_cleared',
on_init = function(_client)
client = _client
- client.request('slow_request')
+ client:request('slow_request')
local request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('pending', request.type)
- client.cancel_request(2)
+ client:cancel_request(2)
request = exec_lua(function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq('slow_request', request.method)
eq('cancel', request.type)
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1204,10 +1243,10 @@ describe('LSP', function()
return _G.TEST_RPC_CLIENT.requests[2]
end)
eq(nil, request)
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1225,11 +1264,11 @@ describe('LSP', function()
command('let g:requests = 0')
command('autocmd LspRequest * let g:requests+=1')
client = _client
- client.request('slow_request')
+ client:request('slow_request')
eq(1, eval('g:requests'))
- client.cancel_request(2)
+ client:cancel_request(2)
eq(2, eval('g:requests'))
- client.notify('release')
+ client:notify('release')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1240,10 +1279,10 @@ describe('LSP', function()
on_handler = function(err, _, ctx)
eq(table.remove(expected_handlers), { err, {}, ctx }, 'expected handler')
if ctx.method == 'slow_request' then
- client.notify('finish')
+ client:notify('finish')
end
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1277,7 +1316,7 @@ describe('LSP', function()
end)
eq(full_kind, client.server_capabilities().textDocumentSync.change)
eq(true, client.server_capabilities().textDocumentSync.openClose)
- client.notify('finish')
+ client:notify('finish')
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1286,7 +1325,7 @@ describe('LSP', function()
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1333,11 +1372,11 @@ describe('LSP', function()
end,
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1378,11 +1417,11 @@ describe('LSP', function()
end,
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1428,11 +1467,11 @@ describe('LSP', function()
'boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1479,11 +1518,11 @@ describe('LSP', function()
'boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1529,7 +1568,7 @@ describe('LSP', function()
end,
on_init = function(_client)
client = _client
- eq(true, client.supports_method('textDocument/inlayHint'))
+ eq(true, client:supports_method('textDocument/inlayHint'))
exec_lua(function()
assert(vim.lsp.buf_attach_client(_G.BUFFER, _G.TEST_RPC_CLIENT_ID))
end)
@@ -1545,11 +1584,11 @@ describe('LSP', function()
end)
end
if ctx.method == 'textDocument/inlayHint' then
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1598,11 +1637,11 @@ describe('LSP', function()
'123boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1652,11 +1691,11 @@ describe('LSP', function()
'123boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1699,11 +1738,11 @@ describe('LSP', function()
on_handler = function(err, result, ctx)
if ctx.method == 'start' then
n.command('normal! 1Go')
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1752,11 +1791,11 @@ describe('LSP', function()
'boop',
})
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -1806,15 +1845,29 @@ describe('LSP', function()
})
vim.api.nvim_command(_G.BUFFER .. 'bwipeout')
end)
- client.notify('finish')
+ client:notify('finish')
end
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
end)
+
+ it('vim.lsp.start when existing client has no workspace_folders', function()
+ exec_lua(create_server_definition)
+ eq(
+ { 2, 'foo', 'foo' },
+ exec_lua(function()
+ local server = _G._create_server()
+ vim.lsp.start { cmd = server.cmd, name = 'foo' }
+ vim.lsp.start { cmd = server.cmd, name = 'foo', root_dir = 'bar' }
+ local foos = vim.lsp.get_clients()
+ return { #foos, foos[1].name, foos[2].name }
+ end)
+ )
+ end)
end)
describe('parsing tests', function()
@@ -1830,7 +1883,7 @@ describe('LSP', function()
on_setup = function() end,
on_init = function(_client)
client = _client
- client.stop(true)
+ client:stop(true)
end,
on_exit = function(code, signal)
eq(0, code, 'exit code')
@@ -1882,7 +1935,7 @@ describe('LSP', function()
on_handler = function(err, result, ctx)
eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler')
if ctx.method == 'finish' then
- client.stop()
+ client:stop()
end
end,
}
@@ -2348,7 +2401,7 @@ describe('LSP', function()
test_rpc_server {
test_name = 'basic_init',
on_init = function(client, _)
- client.stop()
+ client:stop()
end,
-- If the program timed out, then code will be nil.
on_exit = function(code, signal)
@@ -3448,6 +3501,19 @@ describe('LSP', function()
end)
)
end)
+ it('handles empty line', function()
+ exec_lua(function()
+ _G.contents = {
+ '',
+ }
+ end)
+ eq(
+ { 20, 1 },
+ exec_lua(function()
+ return { vim.lsp.util._make_floating_popup_size(_G.contents, { width = 20 }) }
+ end)
+ )
+ end)
end)
describe('lsp.util.trim.trim_empty_lines', function()
@@ -3499,7 +3565,7 @@ describe('LSP', function()
}
return vim.lsp.util.convert_signature_help_to_markdown_lines(signature_help, 'zig', { '(' })
end)
- -- Note that although the higlight positions below are 0-indexed, the 2nd parameter
+ -- Note that although the highlight positions below are 0-indexed, the 2nd parameter
-- corresponds to the 3rd line because the first line is the ``` from the
-- Markdown block.
local expected = { 3, 4, 3, 11 }
@@ -4284,7 +4350,7 @@ describe('LSP', function()
end)
)
end
- client.stop()
+ client:stop()
end
end,
}
@@ -4331,7 +4397,7 @@ describe('LSP', function()
return type(vim.lsp.commands['dummy2'])
end)
)
- client.stop()
+ client:stop()
end
end,
}
@@ -4372,7 +4438,7 @@ describe('LSP', function()
vim.lsp.buf.code_action()
end)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
})
@@ -4447,7 +4513,7 @@ describe('LSP', function()
return type(vim.lsp.commands['executed_type_annotate'])
end)
)
- client.stop()
+ client:stop()
end
end,
}
@@ -4564,7 +4630,7 @@ describe('LSP', function()
end)
eq({ command = 'Dummy', title = 'Lens1' }, cmd)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4653,7 +4719,7 @@ describe('LSP', function()
end)
eq({ command = 'Dummy', title = 'Lens2' }, response)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4762,7 +4828,7 @@ describe('LSP', function()
return notify_msg
end)
eq('[LSP] Format request failed, no matching language servers.', notify_msg)
- client.stop()
+ client:stop()
end,
}
end)
@@ -4795,7 +4861,7 @@ describe('LSP', function()
end)
eq(nil, notify_msg)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4836,7 +4902,7 @@ describe('LSP', function()
end)
eq(nil, notify_msg)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4883,7 +4949,7 @@ describe('LSP', function()
end)
eq(nil, notify_msg)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -4930,7 +4996,7 @@ describe('LSP', function()
end)
eq({ handler_called = true }, result)
elseif ctx.method == 'shutdown' then
- client.stop()
+ client:stop()
end
end,
}
@@ -5145,8 +5211,8 @@ describe('LSP', function()
local win = vim.api.nvim_get_current_win()
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { 'local x = 10', '', 'print(x)' })
vim.api.nvim_win_set_cursor(win, { 3, 6 })
- local client_id1 = assert(vim.lsp.start({ name = 'dummy', cmd = server1.cmd }))
- local client_id2 = assert(vim.lsp.start({ name = 'dummy', cmd = server2.cmd }))
+ local client_id1 = assert(vim.lsp.start({ name = 'dummy1', cmd = server1.cmd }))
+ local client_id2 = assert(vim.lsp.start({ name = 'dummy2', cmd = server2.cmd }))
local response
vim.lsp.buf.definition({
on_list = function(r)
@@ -5477,7 +5543,7 @@ describe('LSP', function()
result[#result + 1] = {
method = method,
fname = fname,
- supported = client.supports_method(method, { bufnr = bufnr }),
+ supported = client:supports_method(method, { bufnr = bufnr }),
}
end
@@ -6059,15 +6125,6 @@ describe('LSP', function()
end
eq(is_os('mac') or is_os('win'), check_registered(nil)) -- start{_client}() defaults to make_client_capabilities().
- eq(false, check_registered(vim.empty_dict()))
- eq(
- false,
- check_registered({
- workspace = {
- ignoreMe = true,
- },
- })
- )
eq(
false,
check_registered({
@@ -6090,4 +6147,154 @@ describe('LSP', function()
)
end)
end)
+
+ describe('vim.lsp.config() and vim.lsp.enable()', function()
+ it('can merge settings from "*"', function()
+ eq(
+ {
+ name = 'foo',
+ cmd = { 'foo' },
+ root_markers = { '.git' },
+ },
+ exec_lua(function()
+ vim.lsp.config('*', { root_markers = { '.git' } })
+ vim.lsp.config('foo', { cmd = { 'foo' } })
+
+ return vim.lsp.config['foo']
+ end)
+ )
+ end)
+
+ it('sets up an autocmd', function()
+ eq(
+ 1,
+ exec_lua(function()
+ vim.lsp.config('foo', {
+ cmd = { 'foo' },
+ root_markers = { '.foorc' },
+ })
+ vim.lsp.enable('foo')
+ return #vim.api.nvim_get_autocmds({
+ group = 'nvim.lsp.enable',
+ event = 'FileType',
+ })
+ end)
+ )
+ end)
+
+ it('attaches to buffers', function()
+ exec_lua(create_server_definition)
+
+ local tmp1 = t.tmpname(true)
+ local tmp2 = t.tmpname(true)
+
+ exec_lua(function()
+ local server = _G._create_server({
+ handlers = {
+ initialize = function(_, _, callback)
+ callback(nil, { capabilities = {} })
+ end,
+ },
+ })
+
+ vim.lsp.config('foo', {
+ cmd = server.cmd,
+ filetypes = { 'foo' },
+ root_markers = { '.foorc' },
+ })
+
+ vim.lsp.config('bar', {
+ cmd = server.cmd,
+ filetypes = { 'bar' },
+ root_markers = { '.foorc' },
+ })
+
+ vim.lsp.enable('foo')
+ vim.lsp.enable('bar')
+
+ vim.cmd.edit(tmp1)
+ vim.bo.filetype = 'foo'
+ _G.foo_buf = vim.api.nvim_get_current_buf()
+
+ vim.cmd.edit(tmp2)
+ vim.bo.filetype = 'bar'
+ _G.bar_buf = vim.api.nvim_get_current_buf()
+ end)
+
+ eq(
+ { 1, 'foo', 1, 'bar' },
+ exec_lua(function()
+ local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
+ local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
+ return { #foos, foos[1].name, #bars, bars[1].name }
+ end)
+ )
+ end)
+
+ it('does not attach to buffers more than once if no root_dir', function()
+ exec_lua(create_server_definition)
+
+ local tmp1 = t.tmpname(true)
+
+ eq(
+ 1,
+ exec_lua(function()
+ local server = _G._create_server({
+ handlers = {
+ initialize = function(_, _, callback)
+ callback(nil, { capabilities = {} })
+ end,
+ },
+ })
+
+ vim.lsp.config('foo', { cmd = server.cmd, filetypes = { 'foo' } })
+ vim.lsp.enable('foo')
+
+ vim.cmd.edit(assert(tmp1))
+ vim.bo.filetype = 'foo'
+ vim.bo.filetype = 'foo'
+
+ return #vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf() })
+ end)
+ )
+ end)
+
+ it('supports async function for root_dir', function()
+ exec_lua(create_server_definition)
+
+ local tmp1 = t.tmpname(true)
+ exec_lua(function()
+ local server = _G._create_server({
+ handlers = {
+ initialize = function(_, _, callback)
+ callback(nil, { capabilities = {} })
+ end,
+ },
+ })
+
+ vim.lsp.config('foo', {
+ cmd = server.cmd,
+ filetypes = { 'foo' },
+ root_dir = function(cb)
+ vim.system({ 'sleep', '0' }, {}, function()
+ cb('some_dir')
+ end)
+ end,
+ })
+ vim.lsp.enable('foo')
+
+ vim.cmd.edit(assert(tmp1))
+ vim.bo.filetype = 'foo'
+ end)
+
+ retry(nil, 1000, function()
+ eq(
+ 'some_dir',
+ exec_lua(function()
+ return vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf() })[1].root_dir
+ end)
+ )
+ end)
+ end)
+ end)
end)
diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua
index 8906e60dce..c1dbc6dac3 100644
--- a/test/functional/plugin/man_spec.lua
+++ b/test/functional/plugin/man_spec.lua
@@ -21,13 +21,12 @@ local function get_search_history(name)
local man = require('man')
local res = {}
--- @diagnostic disable-next-line:duplicate-set-field
- man.find_path = function(sect, name0)
+ man._find_path = function(name0, sect)
table.insert(res, { sect, name0 })
return nil
end
- local ok, rv = pcall(man.open_page, -1, { tab = 0 }, args)
- assert(not ok)
- assert(rv and rv:match('no manual entry'))
+ local err = man.open_page(-1, { tab = 0 }, args)
+ assert(err and err:match('no manual entry'))
return res
end)
end
@@ -225,7 +224,7 @@ describe(':Man', function()
matches('^/.+', 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(
+ ('Error detected while processing command line:\r\n' .. 'man.lua: no manual entry for %s'):format(
pesc(actual_file)
),
fn.system(args, { '' })
@@ -235,8 +234,8 @@ describe(':Man', function()
it('tries variants with spaces, underscores #22503', function()
eq({
- { '', 'NAME WITH SPACES' },
- { '', 'NAME_WITH_SPACES' },
+ { vim.NIL, 'NAME WITH SPACES' },
+ { vim.NIL, 'NAME_WITH_SPACES' },
}, get_search_history('NAME WITH SPACES'))
eq({
{ '3', 'some other man' },
@@ -255,12 +254,21 @@ describe(':Man', function()
{ 'n', 'some_other_man' },
}, get_search_history('n some other man'))
eq({
- { '', '123some other man' },
- { '', '123some_other_man' },
+ { vim.NIL, '123some other man' },
+ { vim.NIL, '123some_other_man' },
}, get_search_history('123some other man'))
eq({
{ '1', 'other_man' },
{ '1', 'other_man' },
}, get_search_history('other_man(1)'))
end)
+
+ it('can complete', function()
+ eq(
+ true,
+ exec_lua(function()
+ return #require('man').man_complete('f', 'Man f') > 0
+ end)
+ )
+ end)
end)
diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua
index 722442acbd..2b54ea93e0 100644
--- a/test/functional/provider/clipboard_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -544,7 +544,7 @@ describe('clipboard (with fake clipboard.vim)', function()
]])
feed('gg^<C-v>') -- Goto start of top line enter visual block mode
feed('3ljy^k') -- yank 4x2 block & goto initial location
- feed('P') -- Paste it infront
+ feed('P') -- Paste it before cursor
expect([[
aabbaabbcc
ddeeddeeff
diff --git a/test/functional/script/luacats_grammar_spec.lua b/test/functional/script/luacats_grammar_spec.lua
index 8bc55879d4..6e73f6894b 100644
--- a/test/functional/script/luacats_grammar_spec.lua
+++ b/test/functional/script/luacats_grammar_spec.lua
@@ -118,9 +118,9 @@ describe('luacats grammar', function()
type = '"rfc2396" | "rfc2732" | "rfc3986" | nil',
})
- test('@param offset_encoding "utf-8" | "utf-16" | "utf-32" | nil', {
+ test('@param position_encoding "utf-8" | "utf-16" | "utf-32" | nil', {
kind = 'param',
- name = 'offset_encoding',
+ name = 'position_encoding',
type = '"utf-8" | "utf-16" | "utf-32" | nil',
})
diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua
index d680f0b83b..837978dee1 100644
--- a/test/functional/shada/marks_spec.lua
+++ b/test/functional/shada/marks_spec.lua
@@ -218,7 +218,7 @@ describe('ShaDa support code', function()
-- -c temporary sets lnum to zero to make `+/pat` work, so calling setpcmark()
-- during -c used to add item with zero lnum to jump list.
it('does not create incorrect file for non-existent buffers when writing from -c', function()
- local argv = n.new_argv {
+ local p = n.spawn_wait {
args_rm = {
'-i',
'--embed', -- no --embed
@@ -232,12 +232,13 @@ describe('ShaDa support code', function()
'qall',
},
}
- eq('', fn.system(argv))
+ eq('', p:output())
+ eq(0, p.status)
eq(0, exc_exec('rshada'))
end)
it('does not create incorrect file for non-existent buffers opened from -c', function()
- local argv = n.new_argv {
+ local p = n.spawn_wait {
args_rm = {
'-i',
'--embed', -- no --embed
@@ -251,7 +252,8 @@ describe('ShaDa support code', function()
'autocmd VimEnter * qall',
},
}
- eq('', fn.system(argv))
+ eq('', p:output())
+ eq(0, p.status)
eq(0, exc_exec('rshada'))
end)
diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua
index 5debdc6c77..78fe19b984 100644
--- a/test/functional/shada/shada_spec.lua
+++ b/test/functional/shada/shada_spec.lua
@@ -6,8 +6,7 @@ local uv = vim.uv
local paths = t.paths
local api, nvim_command, fn, eq = n.api, n.command, n.fn, t.eq
-local write_file, spawn, set_session, nvim_prog, exc_exec =
- t.write_file, n.spawn, n.set_session, n.nvim_prog, n.exc_exec
+local write_file, set_session, exc_exec = t.write_file, n.set_session, n.exc_exec
local is_os = t.is_os
local skip = t.skip
@@ -254,7 +253,7 @@ describe('ShaDa support code', function()
it('does not crash when ShaDa file directory is not writable', function()
skip(is_os('win'))
- fn.mkdir(dirname, '', 0)
+ fn.mkdir(dirname, '', '0')
eq(0, fn.filewritable(dirname))
reset { shadafile = dirshada, args = { '--cmd', 'set shada=' } }
api.nvim_set_option_value('shada', "'10", {})
@@ -270,10 +269,10 @@ end)
describe('ShaDa support code', function()
it('does not write NONE file', function()
- local session = spawn(
- { nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '--headless', '--cmd', 'qall' },
- true
- )
+ local session = n.new_session(false, {
+ merge = false,
+ args = { '-u', 'NONE', '-i', 'NONE', '--embed', '--headless', '--cmd', 'qall' },
+ })
session:close()
eq(nil, uv.fs_stat('NONE'))
eq(nil, uv.fs_stat('NONE.tmp.a'))
@@ -281,7 +280,10 @@ describe('ShaDa support code', function()
it('does not read NONE file', function()
write_file('NONE', '\005\001\015\131\161na\162rX\194\162rc\145\196\001-')
- local session = spawn({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed', '--headless' }, true)
+ local session = n.new_session(
+ false,
+ { merge = false, args = { '-u', 'NONE', '-i', 'NONE', '--embed', '--headless' } }
+ )
set_session(session)
eq('', fn.getreg('a'))
session:close()
diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua
index 4a61e0203d..839e37f541 100644
--- a/test/functional/terminal/altscreen_spec.lua
+++ b/test/functional/terminal/altscreen_spec.lua
@@ -35,13 +35,13 @@ describe(':terminal altscreen', function()
line6 |
line7 |
line8 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
enter_altscreen()
screen:expect([[
|*5
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
eq(10, api.nvim_buf_line_count(0))
@@ -68,7 +68,7 @@ describe(':terminal altscreen', function()
line6 |
line7 |
line8 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
feed('<c-\\><c-n>gg')
@@ -103,7 +103,7 @@ describe(':terminal altscreen', function()
line14 |
line15 |
line16 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -132,7 +132,7 @@ describe(':terminal altscreen', function()
screen:expect([[
|*2
rows: 4, cols: 50 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end
@@ -160,7 +160,7 @@ describe(':terminal altscreen', function()
line5 |
line6 |
line7 |
- line8 |
+ ^line8 |
{3:-- TERMINAL --} |
]])
end)
diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua
index b550df80c3..a8e5367176 100644
--- a/test/functional/terminal/api_spec.lua
+++ b/test/functional/terminal/api_spec.lua
@@ -33,7 +33,7 @@ describe('api', function()
it('qa! RPC request during insert-mode', function()
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*4
|
{3:-- TERMINAL --} |
@@ -45,7 +45,7 @@ describe('api', function()
-- Wait for socket creation.
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*4
]] .. socket_name .. [[ |
{3:-- TERMINAL --} |
@@ -57,7 +57,7 @@ describe('api', function()
tt.feed_data('i[tui] insert-mode')
-- Wait for stdin to be processed.
screen:expect([[
- [tui] insert-mode{1: } |
+ [tui] insert-mode^ |
{4:~ }|*4
{3:-- INSERT --} |
{3:-- TERMINAL --} |
@@ -73,7 +73,7 @@ describe('api', function()
[tui] insert-mode |
[socket 1] this is more t |
han 25 columns |
- [socket 2] input{1: } |
+ [socket 2] input^ |
{4:~ } |
{3:-- INSERT --} |
{3:-- TERMINAL --} |
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 05258a9e50..4635259e33 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -89,7 +89,7 @@ describe(':terminal buffer', function()
feed('<c-\\><c-n>')
screen:expect([[
tty ready |
- {2:^ } |
+ ^ |
|*5
]])
end)
@@ -109,7 +109,7 @@ describe(':terminal buffer', function()
feed('<c-\\><c-n>dd')
screen:expect([[
tty ready |
- {2:^ } |
+ ^ |
|*4
{8:E21: Cannot make changes, 'modifiable' is off} |
]])
@@ -122,7 +122,7 @@ describe(':terminal buffer', function()
screen:expect([[
^tty ready |
appended tty ready |*2
- {2: } |
+ |
|*2
:let @a = "appended " . @a |
]])
@@ -142,7 +142,7 @@ describe(':terminal buffer', function()
screen:expect([[
^tty ready |
appended tty ready |
- {2: } |
+ |
|*3
:put a |
]])
@@ -151,7 +151,7 @@ describe(':terminal buffer', function()
screen:expect([[
tty ready |
appended tty ready |*2
- {2: } |
+ |
|
^ |
:6put a |
@@ -198,7 +198,7 @@ describe(':terminal buffer', function()
{4:~ }|
{5:========== }|
rows: 2, cols: 50 |
- {2: } |
+ |
{18:========== }|
|
]])
@@ -234,7 +234,7 @@ describe(':terminal buffer', function()
command('set rightleft')
screen:expect([[
ydaer ytt|
- {1:a}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
|*4
{3:-- TERMINAL --} |
]])
@@ -277,7 +277,7 @@ describe(':terminal buffer', function()
screen:expect {
grid = [[
tty ready |
- {2:^ } |
+ ^ |
|*4
{3:-- (terminal) --} |
]],
@@ -288,7 +288,7 @@ describe(':terminal buffer', function()
screen:expect {
grid = [[
tty ready |
- {2: } |
+ |
|*4
:let g:x = 17^ |
]],
@@ -298,7 +298,7 @@ describe(':terminal buffer', function()
screen:expect {
grid = [[
tty ready |
- {1: } |
+ ^ |
|*4
{3:-- TERMINAL --} |
]],
@@ -351,7 +351,7 @@ describe(':terminal buffer', function()
end)
it('TermRequest synchronization #27572', function()
- command('autocmd! nvim_terminal TermRequest')
+ command('autocmd! nvim.terminal TermRequest')
local term = exec_lua([[
_G.input = {}
local term = vim.api.nvim_open_term(0, {
@@ -378,7 +378,7 @@ describe(':terminal buffer', function()
}, exec_lua('return _G.input'))
end)
- it('no heap-buffer-overflow when using termopen(echo) #3161', function()
+ it('no heap-buffer-overflow when using jobstart("echo",{term=true}) #3161', function()
local testfilename = 'Xtestfile-functional-terminal-buffers_spec'
write_file(testfilename, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa')
finally(function()
@@ -387,8 +387,8 @@ describe(':terminal buffer', function()
feed_command('edit ' .. testfilename)
-- Move cursor away from the beginning of the line
feed('$')
- -- Let termopen() modify the buffer
- feed_command('call termopen("echo")')
+ -- Let jobstart(…,{term=true}) modify the buffer
+ feed_command([[call jobstart("echo", {'term':v:true})]])
assert_alive()
feed_command('bdelete!')
end)
@@ -400,18 +400,31 @@ describe(':terminal buffer', function()
assert_alive()
end)
- it('truncates number of composing characters to 5', function()
+ it('truncates the size of grapheme clusters', function()
local chan = api.nvim_open_term(0, {})
local composing = ('a̳'):sub(2)
- api.nvim_chan_send(chan, 'a' .. composing:rep(8))
+ api.nvim_chan_send(chan, 'a' .. composing:rep(20))
retry(nil, nil, function()
- eq('a' .. composing:rep(5), api.nvim_get_current_line())
+ eq('a' .. composing:rep(14), api.nvim_get_current_line())
end)
end)
+ it('handles extended grapheme clusters', function()
+ local screen = Screen.new(50, 7)
+ feed 'i'
+ local chan = api.nvim_open_term(0, {})
+ api.nvim_chan_send(chan, '🏴‍☠️ yarrr')
+ screen:expect([[
+ 🏴‍☠️ yarrr^ |
+ |*5
+ {5:-- TERMINAL --} |
+ ]])
+ eq('🏴‍☠️ yarrr', api.nvim_get_current_line())
+ end)
+
it('handles split UTF-8 sequences #16245', function()
local screen = Screen.new(50, 7)
- fn.termopen({ testprg('shell-test'), 'UTF-8' })
+ fn.jobstart({ testprg('shell-test'), 'UTF-8' }, { term = true })
screen:expect([[
^å |
ref: å̲ |
@@ -422,6 +435,19 @@ describe(':terminal buffer', function()
]])
end)
+ it('handles unprintable chars', function()
+ local screen = Screen.new(50, 7)
+ feed 'i'
+ local chan = api.nvim_open_term(0, {})
+ api.nvim_chan_send(chan, '\239\187\191') -- '\xef\xbb\xbf'
+ screen:expect([[
+ {18:<feff>}^ |
+ |*5
+ {5:-- TERMINAL --} |
+ ]])
+ eq('\239\187\191', api.nvim_get_current_line())
+ end)
+
it("handles bell respecting 'belloff' and 'visualbell'", function()
local screen = Screen.new(50, 7)
local chan = api.nvim_open_term(0, {})
@@ -531,16 +557,19 @@ describe('terminal input', function()
'--cmd',
'set notermguicolors',
'-c',
- 'while 1 | redraw | echo keytrans(getcharstr()) | endwhile',
+ 'while 1 | redraw | echo keytrans(getcharstr(-1, #{simplify: 0})) | endwhile',
})
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] 0,0-1 All}|
|
{3:-- TERMINAL --} |
]])
- for _, key in ipairs({
+ local keys = {
+ '<Tab>',
+ '<CR>',
+ '<Esc>',
'<M-Tab>',
'<M-CR>',
'<M-Esc>',
@@ -568,18 +597,36 @@ describe('terminal input', function()
'<S-End>',
'<C-End>',
'<End>',
- '<C-LeftMouse>',
- '<C-LeftRelease>',
- '<2-LeftMouse>',
- '<2-LeftRelease>',
- '<S-RightMouse>',
- '<S-RightRelease>',
- '<2-RightMouse>',
- '<2-RightRelease>',
- '<M-MiddleMouse>',
- '<M-MiddleRelease>',
- '<2-MiddleMouse>',
- '<2-MiddleRelease>',
+ '<C-LeftMouse><0,0>',
+ '<C-LeftDrag><0,1>',
+ '<C-LeftRelease><0,1>',
+ '<2-LeftMouse><0,1>',
+ '<2-LeftDrag><0,0>',
+ '<2-LeftRelease><0,0>',
+ '<M-MiddleMouse><0,0>',
+ '<M-MiddleDrag><0,1>',
+ '<M-MiddleRelease><0,1>',
+ '<2-MiddleMouse><0,1>',
+ '<2-MiddleDrag><0,0>',
+ '<2-MiddleRelease><0,0>',
+ '<S-RightMouse><0,0>',
+ '<S-RightDrag><0,1>',
+ '<S-RightRelease><0,1>',
+ '<2-RightMouse><0,1>',
+ '<2-RightDrag><0,0>',
+ '<2-RightRelease><0,0>',
+ '<S-X1Mouse><0,0>',
+ '<S-X1Drag><0,1>',
+ '<S-X1Release><0,1>',
+ '<2-X1Mouse><0,1>',
+ '<2-X1Drag><0,0>',
+ '<2-X1Release><0,0>',
+ '<S-X2Mouse><0,0>',
+ '<S-X2Drag><0,1>',
+ '<S-X2Release><0,1>',
+ '<2-X2Mouse><0,1>',
+ '<2-X2Drag><0,0>',
+ '<2-X2Release><0,0>',
'<S-ScrollWheelUp>',
'<S-ScrollWheelDown>',
'<ScrollWheelUp>',
@@ -588,15 +635,22 @@ describe('terminal input', function()
'<S-ScrollWheelRight>',
'<ScrollWheelLeft>',
'<ScrollWheelRight>',
- }) do
+ }
+ -- FIXME: The escape sequence to enable kitty keyboard mode doesn't work on Windows
+ if not is_os('win') then
+ table.insert(keys, '<C-I>')
+ table.insert(keys, '<C-M>')
+ table.insert(keys, '<C-[>')
+ end
+ for _, key in ipairs(keys) do
feed(key)
screen:expect(([[
|
{4:~ }|*3
{5:[No Name] 0,0-1 All}|
- %s{1: }{MATCH: *}|
+ %s^ {MATCH: *}|
{3:-- TERMINAL --} |
- ]]):format(key))
+ ]]):format(key:gsub('<%d+,%d+>$', '')))
end
end)
end)
@@ -624,7 +678,7 @@ if is_os('win') then
> :: appended :: tty ready |
> :: tty ready |
> :: appended :: tty ready |
- ^> {2: } |
+ ^> |
:let @a = @a . "\n:: appended " . @a . "\n\n" |
]])
-- operator count is also taken into consideration
@@ -635,7 +689,7 @@ if is_os('win') then
> :: appended :: tty ready |
> :: tty ready |
> :: appended :: tty ready |
- ^> {2: } |
+ ^> |
:let @a = @a . "\n:: appended " . @a . "\n\n" |
]])
end)
@@ -649,7 +703,7 @@ if is_os('win') then
|
> :: tty ready |
> :: appended :: tty ready |
- > {2: } |
+ > |
|
^ |
:put a |
@@ -662,14 +716,14 @@ if is_os('win') then
> :: appended :: tty ready |
> :: tty ready |
> :: appended :: tty ready |
- ^> {2: } |
+ ^> |
:6put a |
]])
end)
end)
end
-describe('termopen()', function()
+describe('termopen() (deprecated alias to `jobstart(…,{term=true})`)', function()
before_each(clear)
it('disallowed when textlocked and in cmdwin buffer', function()
diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua
index 9912c1ff7b..bb97411f43 100644
--- a/test/functional/terminal/channel_spec.lua
+++ b/test/functional/terminal/channel_spec.lua
@@ -75,8 +75,8 @@ describe('terminal channel is closed and later released if', function()
eq(chans - 1, eval('len(nvim_list_chans())'))
end)
- it('opened by termopen(), exited, and deleted by pressing a key', function()
- command([[let id = termopen('echo')]])
+ it('opened by jobstart(…,{term=true}), exited, and deleted by pressing a key', function()
+ command([[let id = jobstart('echo',{'term':v:true})]])
local chans = eval('len(nvim_list_chans())')
-- wait for process to exit
screen:expect({ any = '%[Process exited 0%]' })
@@ -96,8 +96,8 @@ describe('terminal channel is closed and later released if', function()
end)
-- This indirectly covers #16264
- it('opened by termopen(), exited, and deleted by :bdelete', function()
- command([[let id = termopen('echo')]])
+ it('opened by jobstart(…,{term=true}), exited, and deleted by :bdelete', function()
+ command([[let id = jobstart('echo', {'term':v:true})]])
local chans = eval('len(nvim_list_chans())')
-- wait for process to exit
screen:expect({ any = '%[Process exited 0%]' })
@@ -124,7 +124,7 @@ it('chansend sends lines to terminal channel in proper order', function()
screen._default_attr_ids = nil
local shells = is_os('win') and { 'cmd.exe', 'pwsh.exe -nop', 'powershell.exe -nop' } or { 'sh' }
for _, sh in ipairs(shells) do
- command([[let id = termopen(']] .. sh .. [[')]])
+ command([[let id = jobstart(']] .. sh .. [[', {'term':v:true})]])
command([[call chansend(id, ['echo "hello"', 'echo "world"', ''])]])
screen:expect {
any = [[echo "hello".*echo "world"]],
@@ -149,7 +149,7 @@ describe('no crash when TermOpen autocommand', function()
})
end)
- it('processes job exit event when using termopen()', function()
+ it('processes job exit event when using jobstart(…,{term=true})', function()
command([[autocmd TermOpen * call input('')]])
async_meths.nvim_command('terminal foobar')
screen:expect {
@@ -179,7 +179,7 @@ describe('no crash when TermOpen autocommand', function()
assert_alive()
end)
- it('wipes buffer and processes events when using termopen()', function()
+ it('wipes buffer and processes events when using jobstart(…,{term=true})', function()
command([[autocmd TermOpen * bwipe! | call input('')]])
async_meths.nvim_command('terminal foobar')
screen:expect {
diff --git a/test/functional/terminal/clipboard_spec.lua b/test/functional/terminal/clipboard_spec.lua
index 4a1a0e29fd..f0ce407eaa 100644
--- a/test/functional/terminal/clipboard_spec.lua
+++ b/test/functional/terminal/clipboard_spec.lua
@@ -56,7 +56,7 @@ describe(':terminal', function()
return string.format('\027]52;;%s\027\\', arg)
end
- fn.termopen({ testprg('shell-test'), '-t', osc52(encoded) })
+ fn.jobstart({ testprg('shell-test'), '-t', osc52(encoded) }, { term = true })
retry(nil, 1000, function()
eq(text, exec_lua([[ return vim.g.clipboard_data ]]))
diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua
index f223cdd417..83408e41b3 100644
--- a/test/functional/terminal/cursor_spec.lua
+++ b/test/functional/terminal/cursor_spec.lua
@@ -1,13 +1,12 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
-local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.testterm')
local feed, clear = n.feed, n.clear
local testprg, command = n.testprg, n.command
local eq, eval = t.eq, n.eval
local matches = t.matches
-local poke_eventloop = n.poke_eventloop
+local call = n.call
local hide_cursor = tt.hide_cursor
local show_cursor = tt.show_cursor
local is_os = t.is_os
@@ -16,16 +15,27 @@ local skip = t.skip
describe(':terminal cursor', function()
local screen
+ local terminal_mode_idx ---@type number
+
before_each(function()
clear()
screen = tt.setup_screen()
+
+ if terminal_mode_idx == nil then
+ for i, v in ipairs(screen._mode_info) do
+ if v.name == 'terminal' then
+ terminal_mode_idx = i
+ end
+ end
+ assert(terminal_mode_idx)
+ end
end)
it('moves the screen cursor when focused', function()
tt.feed_data('testing cursor')
screen:expect([[
tty ready |
- testing cursor{1: } |
+ testing cursor^ |
|*4
{3:-- TERMINAL --} |
]])
@@ -35,7 +45,7 @@ describe(':terminal cursor', function()
feed('<c-\\><c-n>')
screen:expect([[
tty ready |
- {2:^ } |
+ ^ |
|*5
]])
end)
@@ -49,7 +59,7 @@ describe(':terminal cursor', function()
screen:expect([[
{7: 1 }tty ready |
{7: 2 }^rows: 6, cols: 46 |
- {7: 3 }{2: } |
+ {7: 3 } |
{7: 4 } |
{7: 5 } |
{7: 6 } |
@@ -61,7 +71,7 @@ describe(':terminal cursor', function()
screen:expect([[
{7: 1 }tty ready |
{7: 2 }^rows: 6, cols: 46 |
- {7: 3 }{2: } |
+ {7: 3 } |
{7: 4 } |
{7: 5 } |
{7: 6 } |
@@ -72,7 +82,7 @@ describe(':terminal cursor', function()
screen:expect([[
{7: 1 }tty ready |
{7: 2 }rows: 6, cols: 46 |
- {7: 3 }{1: } |
+ {7: 3 }^ |
{7: 4 } |
{7: 5 } |
{7: 6 } |
@@ -82,8 +92,8 @@ describe(':terminal cursor', function()
end)
describe('when invisible', function()
- it('is not highlighted and is detached from screen cursor', function()
- skip(is_os('win'))
+ it('is not highlighted', function()
+ skip(is_os('win'), '#31587')
hide_cursor()
screen:expect([[
tty ready |
@@ -93,59 +103,259 @@ describe(':terminal cursor', function()
show_cursor()
screen:expect([[
tty ready |
- {1: } |
+ ^ |
|*4
{3:-- TERMINAL --} |
]])
-- same for when the terminal is unfocused
feed('<c-\\><c-n>')
hide_cursor()
+ screen:expect({
+ grid = [[
+ tty ready |
+ ^ |
+ |*5
+ ]],
+ unchanged = true,
+ })
+ show_cursor()
+ screen:expect({
+ grid = [[
+ tty ready |
+ ^ |
+ |*5
+ ]],
+ unchanged = true,
+ })
+ end)
+
+ it('becomes visible when exiting Terminal mode', function()
+ skip(is_os('win'), '#31587')
+ hide_cursor()
+ screen:expect([[
+ tty ready |
+ |*5
+ {3:-- TERMINAL --} |
+ ]])
+ feed('<c-\\><c-n>')
screen:expect([[
tty ready |
^ |
|*5
]])
- show_cursor()
+ feed('i')
screen:expect([[
tty ready |
- {2:^ } |
|*5
+ {3:-- TERMINAL --} |
]])
end)
end)
-end)
-describe('cursor with customized highlighting', function()
- local screen
+ it('can be modified by application #3681 #31685', function()
+ skip(is_os('win'), '#31587')
- before_each(function()
- clear()
- command('highlight TermCursor ctermfg=45 ctermbg=46 cterm=NONE')
- command('highlight TermCursorNC ctermfg=55 ctermbg=56 cterm=NONE')
- screen = Screen.new(50, 7, { rgb = false })
- screen:set_default_attr_ids({
- [1] = { foreground = 45, background = 46 },
- [2] = { foreground = 55, background = 56 },
- [3] = { bold = true },
+ local states = {
+ [1] = { blink = true, shape = 'block' },
+ [2] = { blink = false, shape = 'block' },
+ [3] = { blink = true, shape = 'horizontal' },
+ [4] = { blink = false, shape = 'horizontal' },
+ [5] = { blink = true, shape = 'vertical' },
+ [6] = { blink = false, shape = 'vertical' },
+ }
+
+ for k, v in pairs(states) do
+ tt.feed_csi(('%d q'):format(k))
+ screen:expect({
+ grid = [[
+ tty ready |
+ ^ |
+ |*4
+ {3:-- TERMINAL --} |
+ ]],
+ condition = function()
+ if v.blink then
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
+ else
+ eq(0, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
+ end
+
+ eq(v.shape, screen._mode_info[terminal_mode_idx].cursor_shape)
+
+ -- Cell percentages are hard coded for each shape in terminal.c
+ if v.shape == 'horizontal' then
+ eq(20, screen._mode_info[terminal_mode_idx].cell_percentage)
+ elseif v.shape == 'vertical' then
+ eq(25, screen._mode_info[terminal_mode_idx].cell_percentage)
+ end
+ end,
+ })
+ end
+
+ feed([[<C-\><C-N>]])
+
+ screen:expect([[
+ tty ready |
+ ^ |
+ |*5
+ ]])
+
+ -- Cursor returns to default on TermLeave
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
+ eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
+ end)
+
+ it('can be modified per terminal', function()
+ skip(is_os('win'), '#31587')
+
+ -- Set cursor to vertical bar with blink
+ tt.feed_csi('5 q')
+ screen:expect({
+ grid = [[
+ tty ready |
+ ^ |
+ |*4
+ {3:-- TERMINAL --} |
+ ]],
+ condition = function()
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
+ eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
+ end,
+ })
+
+ tt.hide_cursor()
+ screen:expect({
+ grid = [[
+ tty ready |
+ |
+ |*4
+ {3:-- TERMINAL --} |
+ ]],
+ condition = function()
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
+ eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
+ end,
})
- command('call termopen(["' .. testprg('tty-test') .. '"])')
+
+ -- Exit terminal mode to reset terminal cursor settings to default and
+ -- create a new terminal window
+ feed([[<C-\><C-N>]])
+ command('set statusline=~~~')
+ command('new')
+ call('jobstart', { testprg('tty-test') }, { term = true })
feed('i')
- poke_eventloop()
+ screen:expect({
+ grid = [[
+ tty ready |
+ ^ |
+ {17:~~~ }|
+ rows: 2, cols: 50 |
+ |
+ {18:~~~ }|
+ {3:-- TERMINAL --} |
+ ]],
+ condition = function()
+ -- New terminal, cursor resets to defaults
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
+ eq('block', screen._mode_info[terminal_mode_idx].cursor_shape)
+ end,
+ })
+
+ -- Set cursor to underline, no blink
+ tt.feed_csi('4 q')
+ screen:expect({
+ grid = [[
+ tty ready |
+ ^ |
+ {17:~~~ }|
+ rows: 2, cols: 50 |
+ |
+ {18:~~~ }|
+ {3:-- TERMINAL --} |
+ ]],
+ condition = function()
+ eq(0, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(0, screen._mode_info[terminal_mode_idx].blinkoff)
+ eq('horizontal', screen._mode_info[terminal_mode_idx].cursor_shape)
+ end,
+ })
+
+ -- Switch back to first terminal, cursor should still be hidden
+ command('wincmd p')
+ screen:expect({
+ grid = [[
+ tty ready |
+ |
+ {18:~~~ }|
+ rows: 2, cols: 50 |
+ |
+ {17:~~~ }|
+ {3:-- TERMINAL --} |
+ ]],
+ condition = function()
+ eq(500, screen._mode_info[terminal_mode_idx].blinkon)
+ eq(500, screen._mode_info[terminal_mode_idx].blinkoff)
+ eq('vertical', screen._mode_info[terminal_mode_idx].cursor_shape)
+ end,
+ })
end)
- it('overrides the default highlighting', function()
+ it('can be positioned arbitrarily', function()
+ clear()
+ screen = tt.setup_child_nvim({
+ '-u',
+ 'NONE',
+ '-i',
+ 'NONE',
+ '--cmd',
+ n.nvim_set .. ' noshowmode',
+ })
+ screen:expect([[
+ ^ |
+ ~ |*4
+ |
+ {3:-- TERMINAL --} |
+ ]])
+
+ feed('i<Tab>')
screen:expect([[
- tty ready |
- {1: } |
- |*4
+ ^ |
+ ~ |*4
+ |
{3:-- TERMINAL --} |
]])
- feed('<c-\\><c-n>')
+ end)
+
+ it('preserves guicursor value on TermLeave #31612', function()
+ eq(3, screen._mode_info[terminal_mode_idx].hl_id)
+
+ -- Change 'guicursor' while terminal mode is active
+ command('set guicursor+=t:Error')
+
+ local error_hl_id = call('hlID', 'Error')
+
+ screen:expect({
+ condition = function()
+ eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
+ end,
+ })
+
+ -- Exit terminal mode
+ feed([[<C-\><C-N>]])
+
screen:expect([[
tty ready |
- {2:^ } |
+ ^ |
|*5
]])
+
+ eq(error_hl_id, screen._mode_info[terminal_mode_idx].hl_id)
end)
end)
@@ -171,19 +381,10 @@ describe('buffer cursor position is correct in terminal without number column',
}, {
cols = 70,
})
- screen:set_default_attr_ids({
- [1] = { foreground = 253, background = 11 },
- [2] = { reverse = true },
- [3] = { bold = true },
- [4] = { background = 11 },
- })
- -- Also check for real cursor position, as it is used for stuff like input methods
- screen._handle_busy_start = function() end
- screen._handle_busy_stop = function() end
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :{2:^ } |
+ :^ |
{3:-- TERMINAL --} |
]])
end
@@ -200,7 +401,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :aaaaaaaa{2:^ } |
+ :aaaaaaaa^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 9 }, eval('nvim_win_get_cursor(0)'))
@@ -208,7 +409,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :aaaaaaa^a{4: } |
+ :aaaaaaa^a |
|
]])
eq({ 6, 8 }, eval('nvim_win_get_cursor(0)'))
@@ -219,7 +420,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :aaaaaa{2:^a}a |
+ :aaaaaa^aa |
{3:-- TERMINAL --} |
]])
eq({ 6, 7 }, eval('nvim_win_get_cursor(0)'))
@@ -227,7 +428,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :aaaaa^a{4:a}a |
+ :aaaaa^aaa |
|
]])
eq({ 6, 6 }, eval('nvim_win_get_cursor(0)'))
@@ -238,7 +439,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :a{2:^a}aaaaaa |
+ :a^aaaaaaa |
{3:-- TERMINAL --} |
]])
eq({ 6, 2 }, eval('nvim_win_get_cursor(0)'))
@@ -246,7 +447,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :^a{4:a}aaaaaa |
+ :^aaaaaaaa |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -263,7 +464,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µµµµµµµµ{2:^ } |
+ :µµµµµµµµ^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 17 }, eval('nvim_win_get_cursor(0)'))
@@ -271,7 +472,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µµµµµµµ^µ{4: } |
+ :µµµµµµµ^µ |
|
]])
eq({ 6, 15 }, eval('nvim_win_get_cursor(0)'))
@@ -282,7 +483,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µµµµµµ{2:^µ}µ |
+ :µµµµµµ^µµ |
{3:-- TERMINAL --} |
]])
eq({ 6, 13 }, eval('nvim_win_get_cursor(0)'))
@@ -290,7 +491,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µµµµµ^µ{4:µ}µ |
+ :µµµµµ^µµµ |
|
]])
eq({ 6, 11 }, eval('nvim_win_get_cursor(0)'))
@@ -301,7 +502,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µ{2:^µ}µµµµµµ |
+ :µ^µµµµµµµ |
{3:-- TERMINAL --} |
]])
eq({ 6, 3 }, eval('nvim_win_get_cursor(0)'))
@@ -309,7 +510,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :^µ{4:µ}µµµµµµ |
+ :^µµµµµµµµ |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -326,7 +527,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{2:^ } |
+ :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 33 }, eval('nvim_win_get_cursor(0)'))
@@ -334,7 +535,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{4: } |
+ :µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳ |
|
]])
eq({ 6, 29 }, eval('nvim_win_get_cursor(0)'))
@@ -346,7 +547,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µ̳µ̳µ̳µ̳µ̳µ̳{2:^µ̳}µ̳ |
+ :µ̳µ̳µ̳µ̳µ̳µ̳^µ̳µ̳ |
{3:-- TERMINAL --} |
]])
eq({ 6, 25 }, eval('nvim_win_get_cursor(0)'))
@@ -354,7 +555,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µ̳µ̳µ̳µ̳µ̳^µ̳{4:µ̳}µ̳ |
+ :µ̳µ̳µ̳µ̳µ̳^µ̳µ̳µ̳ |
|
]])
eq({ 6, 21 }, eval('nvim_win_get_cursor(0)'))
@@ -366,7 +567,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :µ̳{2:^µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ |
+ :µ̳^µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
{3:-- TERMINAL --} |
]])
eq({ 6, 5 }, eval('nvim_win_get_cursor(0)'))
@@ -374,7 +575,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :^µ̳{4:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ |
+ :^µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -391,7 +592,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :哦哦哦哦哦哦哦哦{2:^ } |
+ :哦哦哦哦哦哦哦哦^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 25 }, eval('nvim_win_get_cursor(0)'))
@@ -399,7 +600,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :哦哦哦哦哦哦哦^哦{4: } |
+ :哦哦哦哦哦哦哦^哦 |
|
]])
eq({ 6, 22 }, eval('nvim_win_get_cursor(0)'))
@@ -410,7 +611,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :哦哦哦哦哦哦{2:^哦}哦 |
+ :哦哦哦哦哦哦^哦哦 |
{3:-- TERMINAL --} |
]])
eq({ 6, 19 }, eval('nvim_win_get_cursor(0)'))
@@ -418,7 +619,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :哦哦哦哦哦^哦{4:哦}哦 |
+ :哦哦哦哦哦^哦哦哦 |
|
]])
eq({ 6, 16 }, eval('nvim_win_get_cursor(0)'))
@@ -429,7 +630,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :哦{2:^哦}哦哦哦哦哦哦 |
+ :哦^哦哦哦哦哦哦哦 |
{3:-- TERMINAL --} |
]])
eq({ 6, 4 }, eval('nvim_win_get_cursor(0)'))
@@ -437,7 +638,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :^哦{4:哦}哦哦哦哦哦哦 |
+ :^哦哦哦哦哦哦哦哦 |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -450,7 +651,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :aaaaaaaa {2:^ } |
+ :aaaaaaaa ^ |
{3:-- TERMINAL --} |
]])
matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
@@ -459,7 +660,7 @@ describe('buffer cursor position is correct in terminal without number column',
screen:expect([[
|*4
Entering Ex mode. Type "visual" to go to Normal mode. |
- :aaaaaaaa ^ {4: } |
+ :aaaaaaaa ^ |
|
]])
eq({ 6, 12 }, eval('nvim_win_get_cursor(0)'))
@@ -488,30 +689,20 @@ describe('buffer cursor position is correct in terminal with number column', fun
}, {
cols = 70,
})
- screen:set_default_attr_ids({
- [1] = { foreground = 253, background = 11 },
- [2] = { reverse = true },
- [3] = { bold = true },
- [4] = { background = 11 },
- [7] = { foreground = 130 },
- })
- -- Also check for real cursor position, as it is used for stuff like input methods
- screen._handle_busy_start = function() end
- screen._handle_busy_stop = function() end
screen:expect([[
{7: 1 } |
{7: 2 } |
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:{2:^ } |
+ {7: 6 }:^ |
{3:-- TERMINAL --} |
]])
end
before_each(function()
clear()
- command('set number')
+ command('au TermOpen * set number')
end)
describe('in a line with no multibyte chars or trailing spaces,', function()
@@ -527,7 +718,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:aaaaaaaa{2:^ } |
+ {7: 6 }:aaaaaaaa^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 9 }, eval('nvim_win_get_cursor(0)'))
@@ -538,7 +729,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:aaaaaaa^a{4: } |
+ {7: 6 }:aaaaaaa^a |
|
]])
eq({ 6, 8 }, eval('nvim_win_get_cursor(0)'))
@@ -552,7 +743,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:aaaaaa{2:^a}a |
+ {7: 6 }:aaaaaa^aa |
{3:-- TERMINAL --} |
]])
eq({ 6, 7 }, eval('nvim_win_get_cursor(0)'))
@@ -563,7 +754,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:aaaaa^a{4:a}a |
+ {7: 6 }:aaaaa^aaa |
|
]])
eq({ 6, 6 }, eval('nvim_win_get_cursor(0)'))
@@ -577,7 +768,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:a{2:^a}aaaaaa |
+ {7: 6 }:a^aaaaaaa |
{3:-- TERMINAL --} |
]])
eq({ 6, 2 }, eval('nvim_win_get_cursor(0)'))
@@ -588,7 +779,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:^a{4:a}aaaaaa |
+ {7: 6 }:^aaaaaaaa |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -608,7 +799,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µµµµµµµµ{2:^ } |
+ {7: 6 }:µµµµµµµµ^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 17 }, eval('nvim_win_get_cursor(0)'))
@@ -619,7 +810,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µµµµµµµ^µ{4: } |
+ {7: 6 }:µµµµµµµ^µ |
|
]])
eq({ 6, 15 }, eval('nvim_win_get_cursor(0)'))
@@ -633,7 +824,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µµµµµµ{2:^µ}µ |
+ {7: 6 }:µµµµµµ^µµ |
{3:-- TERMINAL --} |
]])
eq({ 6, 13 }, eval('nvim_win_get_cursor(0)'))
@@ -644,7 +835,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µµµµµ^µ{4:µ}µ |
+ {7: 6 }:µµµµµ^µµµ |
|
]])
eq({ 6, 11 }, eval('nvim_win_get_cursor(0)'))
@@ -658,7 +849,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µ{2:^µ}µµµµµµ |
+ {7: 6 }:µ^µµµµµµµ |
{3:-- TERMINAL --} |
]])
eq({ 6, 3 }, eval('nvim_win_get_cursor(0)'))
@@ -669,7 +860,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:^µ{4:µ}µµµµµµ |
+ {7: 6 }:^µµµµµµµµ |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -689,7 +880,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{2:^ } |
+ {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 33 }, eval('nvim_win_get_cursor(0)'))
@@ -700,7 +891,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{4: } |
+ {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳ |
|
]])
eq({ 6, 29 }, eval('nvim_win_get_cursor(0)'))
@@ -715,7 +906,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳{2:^µ̳}µ̳ |
+ {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳^µ̳µ̳ |
{3:-- TERMINAL --} |
]])
eq({ 6, 25 }, eval('nvim_win_get_cursor(0)'))
@@ -726,7 +917,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳{4:µ̳}µ̳ |
+ {7: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳µ̳µ̳ |
|
]])
eq({ 6, 21 }, eval('nvim_win_get_cursor(0)'))
@@ -741,7 +932,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:µ̳{2:^µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ |
+ {7: 6 }:µ̳^µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
{3:-- TERMINAL --} |
]])
eq({ 6, 5 }, eval('nvim_win_get_cursor(0)'))
@@ -752,7 +943,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:^µ̳{4:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ |
+ {7: 6 }:^µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -772,7 +963,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:哦哦哦哦哦哦哦哦{2:^ } |
+ {7: 6 }:哦哦哦哦哦哦哦哦^ |
{3:-- TERMINAL --} |
]])
eq({ 6, 25 }, eval('nvim_win_get_cursor(0)'))
@@ -783,7 +974,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:哦哦哦哦哦哦哦^哦{4: } |
+ {7: 6 }:哦哦哦哦哦哦哦^哦 |
|
]])
eq({ 6, 22 }, eval('nvim_win_get_cursor(0)'))
@@ -797,7 +988,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:哦哦哦哦哦哦{2:^哦}哦 |
+ {7: 6 }:哦哦哦哦哦哦^哦哦 |
{3:-- TERMINAL --} |
]])
eq({ 6, 19 }, eval('nvim_win_get_cursor(0)'))
@@ -808,7 +999,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:哦哦哦哦哦^哦{4:哦}哦 |
+ {7: 6 }:哦哦哦哦哦^哦哦哦 |
|
]])
eq({ 6, 16 }, eval('nvim_win_get_cursor(0)'))
@@ -822,7 +1013,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:哦{2:^哦}哦哦哦哦哦哦 |
+ {7: 6 }:哦^哦哦哦哦哦哦哦 |
{3:-- TERMINAL --} |
]])
eq({ 6, 4 }, eval('nvim_win_get_cursor(0)'))
@@ -833,7 +1024,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:^哦{4:哦}哦哦哦哦哦哦 |
+ {7: 6 }:^哦哦哦哦哦哦哦哦 |
|
]])
eq({ 6, 1 }, eval('nvim_win_get_cursor(0)'))
@@ -849,7 +1040,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:aaaaaaaa {2:^ } |
+ {7: 6 }:aaaaaaaa ^ |
{3:-- TERMINAL --} |
]])
matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
@@ -861,7 +1052,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
{7: 3 } |
{7: 4 } |
{7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
- {7: 6 }:aaaaaaaa ^ {4: } |
+ {7: 6 }:aaaaaaaa ^ |
|
]])
eq({ 6, 12 }, eval('nvim_win_get_cursor(0)'))
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 5ebe7bd4fc..c29a1e9cd4 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -175,8 +175,8 @@ local function test_terminal_with_fake_shell(backslash)
api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes
end)
- it('with no argument, acts like termopen()', function()
- command('autocmd! nvim_terminal TermClose')
+ it('with no argument, acts like jobstart(…,{term=true})', function()
+ command('autocmd! nvim.terminal TermClose')
feed_command('terminal')
screen:expect([[
^ready $ |
@@ -196,7 +196,7 @@ local function test_terminal_with_fake_shell(backslash)
]])
end)
- it("with no argument, but 'shell' has arguments, acts like termopen()", function()
+ it("with no argument, but 'shell' has arguments, acts like jobstart(…,{term=true})", function()
api.nvim_set_option_value('shell', shell_path .. ' INTERACT', {})
feed_command('terminal')
screen:expect([[
@@ -246,7 +246,7 @@ local function test_terminal_with_fake_shell(backslash)
end)
it('ignores writes if the backing stream closes', function()
- command('autocmd! nvim_terminal TermClose')
+ command('autocmd! nvim.terminal TermClose')
feed_command('terminal')
feed('iiXXXXXXX')
poke_eventloop()
@@ -258,14 +258,14 @@ local function test_terminal_with_fake_shell(backslash)
end)
it('works with findfile()', function()
- command('autocmd! nvim_terminal TermClose')
+ command('autocmd! nvim.terminal TermClose')
feed_command('terminal')
eq('term://', string.match(eval('bufname("%")'), '^term://'))
eq('scripts/shadacat.py', eval('findfile("scripts/shadacat.py", ".")'))
end)
it('works with :find', function()
- command('autocmd! nvim_terminal TermClose')
+ command('autocmd! nvim.terminal TermClose')
feed_command('terminal')
screen:expect([[
^ready $ |
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
index 7822a27b93..0afbd010f7 100644
--- a/test/functional/terminal/highlight_spec.lua
+++ b/test/functional/terminal/highlight_spec.lua
@@ -6,7 +6,6 @@ local tt = require('test.functional.testterm')
local feed, clear = n.feed, n.clear
local api = n.api
local testprg, command = n.testprg, n.command
-local nvim_prog_abs = n.nvim_prog_abs
local fn = n.fn
local nvim_set = n.nvim_set
local is_os = t.is_os
@@ -33,11 +32,11 @@ describe(':terminal highlight', function()
[12] = { bold = true, underdouble = true },
[13] = { italic = true, undercurl = true },
})
- command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
+ command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
feed('i')
screen:expect([[
tty ready |
- {10: } |
+ ^ |
|*4
{5:-- TERMINAL --} |
]])
@@ -61,7 +60,7 @@ describe(':terminal highlight', function()
skip(is_os('win'))
screen:expect(sub([[
tty ready |
- {NUM:text}text{10: } |
+ {NUM:text}text^ |
|*4
{5:-- TERMINAL --} |
]]))
@@ -84,7 +83,7 @@ describe(':terminal highlight', function()
line6 |
line7 |
line8 |
- {10: } |
+ ^ |
{5:-- TERMINAL --} |
]])
feed('<c-\\><c-n>gg')
@@ -150,8 +149,8 @@ it(':terminal highlight has lower precedence than editor #9964', function()
},
})
-- Child nvim process in :terminal (with cterm colors).
- fn.termopen({
- nvim_prog_abs(),
+ fn.jobstart({
+ n.nvim_prog,
'-n',
'-u',
'NORC',
@@ -163,6 +162,7 @@ it(':terminal highlight has lower precedence than editor #9964', function()
'+norm! ichild nvim',
'+norm! oline 2',
}, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -195,12 +195,12 @@ it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', functi
local screen = Screen.new(50, 7)
screen:set_default_attr_ids({
[1] = { background = Screen.colors.Grey90 }, -- CursorLine, CursorColumn
- [2] = { reverse = true }, -- TermCursor
+ [2] = { reverse = true },
[3] = { bold = true }, -- ModeMsg
[4] = { background = Screen.colors.Grey90, reverse = true },
[5] = { background = Screen.colors.Red },
})
- command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
+ command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
screen:expect([[
^tty ready |
|*6
@@ -234,7 +234,7 @@ it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', functi
foobar foobar foobar foobar foobar foobar foobar f|
oobar foobar foobar foobar foobar foobar foobar fo|
obar foobar foobar foobar foobar foobar foobar foo|
- bar foobar{2: } |
+ bar foobar^ |
{3:-- TERMINAL --} |
]])
-- Leaving terminal mode restores old values.
@@ -248,46 +248,60 @@ it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', functi
{1:bar fooba^r }|
|
]])
- -- CursorLine and CursorColumn are combined with TermCursorNC.
- command('highlight TermCursorNC gui=reverse')
+
+ -- Skip the rest of these tests on Windows #31587
+ if is_os('win') then
+ return
+ end
+
+ -- CursorLine and CursorColumn are combined with terminal colors.
+ tt.set_reverse()
+ tt.feed_data(' foobar')
+ tt.clear_attrs()
screen:expect([[
tty ready{1: } |
foobar f{1:o}obar foobar foobar foobar foobar foobar |
foobar fo{1:o}bar foobar foobar foobar foobar foobar f|
oobar foo{1:b}ar foobar foobar foobar foobar foobar fo|
obar foob{1:a}r foobar foobar foobar foobar foobar foo|
- {1:bar fooba^r}{4: }{1: }|
+ {1:bar fooba^r}{4: foobar}{1: }|
|
]])
- feed('2gg11|')
+ feed('2gg15|')
screen:expect([[
- tty ready {1: } |
- {1: foobar fo^obar foobar foobar foobar foobar foobar }|
- foobar foo{1:b}ar foobar foobar foobar foobar foobar f|
- oobar foob{1:a}r foobar foobar foobar foobar foobar fo|
- obar fooba{1:r} foobar foobar foobar foobar foobar foo|
- bar foobar{4: } |
+ tty ready {1: } |
+ {1: foobar foobar^ foobar foobar foobar foobar foobar }|
+ foobar foobar {1:f}oobar foobar foobar foobar foobar f|
+ oobar foobar f{1:o}obar foobar foobar foobar foobar fo|
+ obar foobar fo{1:o}bar foobar foobar foobar foobar foo|
+ bar foobar{2: foo}{4:b}{2:ar} |
|
]])
- -- TermCursorNC has higher precedence.
- command('highlight TermCursorNC gui=NONE guibg=Red')
+
+ -- Set bg color to red
+ tt.feed_csi('48;2;255:0:0m')
+ tt.feed_data(' foobar')
+ tt.clear_attrs()
+ feed('2gg20|')
+
+ -- Terminal color has higher precedence
screen:expect([[
- tty ready {1: } |
- {1: foobar fo^obar foobar foobar foobar foobar foobar }|
- foobar foo{1:b}ar foobar foobar foobar foobar foobar f|
- oobar foob{1:a}r foobar foobar foobar foobar foobar fo|
- obar fooba{1:r} foobar foobar foobar foobar foobar foo|
- bar foobar{5: } |
+ tty ready {1: } |
+ {1: foobar foobar foob^ar foobar foobar foobar foobar }|
+ foobar foobar fooba{1:r} foobar foobar foobar foobar f|
+ oobar foobar foobar{1: }foobar foobar foobar foobar fo|
+ obar foobar foobar {1:f}oobar foobar foobar foobar foo|
+ bar foobar{2: foobar}{5: foobar} |
|
]])
feed('G$')
screen:expect([[
- tty ready{1: } |
- foobar f{1:o}obar foobar foobar foobar foobar foobar |
- foobar fo{1:o}bar foobar foobar foobar foobar foobar f|
- oobar foo{1:b}ar foobar foobar foobar foobar foobar fo|
- obar foob{1:a}r foobar foobar foobar foobar foobar foo|
- {1:bar fooba^r}{5: }{1: }|
+ tty ready {1: } |
+ foobar foobar foobar f{1:o}obar foobar foobar foobar |
+ foobar foobar foobar fo{1:o}bar foobar foobar foobar f|
+ oobar foobar foobar foo{1:b}ar foobar foobar foobar fo|
+ obar foobar foobar foob{1:a}r foobar foobar foobar foo|
+ {1:bar foobar}{4: foobar}{5: fooba^r}{1: }|
|
]])
end)
@@ -300,18 +314,17 @@ describe(':terminal highlight forwarding', function()
screen = Screen.new(50, 7)
screen:set_rgb_cterm(true)
screen:set_default_attr_ids({
- [1] = { { reverse = true }, { reverse = true } },
- [2] = { { bold = true }, { bold = true } },
- [3] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } },
- [4] = { { foreground = tonumber('0xff8000') }, {} },
+ [1] = { { bold = true }, { bold = true } },
+ [2] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } },
+ [3] = { { foreground = tonumber('0xff8000') }, {} },
})
- command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
+ command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
feed('i')
screen:expect([[
tty ready |
- {1: } |
+ ^ |
|*4
- {2:-- TERMINAL --} |
+ {1:-- TERMINAL --} |
]])
end)
@@ -326,9 +339,9 @@ describe(':terminal highlight forwarding', function()
screen:expect {
grid = [[
tty ready |
- {3:text}{4:color}text{1: } |
+ {2:text}{3:color}text^ |
|*4
- {2:-- TERMINAL --} |
+ {1:-- TERMINAL --} |
]],
}
end)
@@ -351,11 +364,11 @@ describe(':terminal highlight with custom palette', function()
[9] = { bold = true },
})
api.nvim_set_var('terminal_color_3', '#123456')
- command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
+ command(("enew | call jobstart(['%s'], {'term':v:true})"):format(testprg('tty-test')))
feed('i')
screen:expect([[
tty ready |
- {7: } |
+ ^ |
|*4
{9:-- TERMINAL --} |
]])
@@ -369,7 +382,7 @@ describe(':terminal highlight with custom palette', function()
tt.feed_data('text')
screen:expect([[
tty ready |
- {1:text}text{7: } |
+ {1:text}text^ |
|*4
{9:-- TERMINAL --} |
]])
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index 38d6b83417..5898484449 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -32,7 +32,7 @@ describe(':terminal mouse', function()
line28 |
line29 |
line30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -107,7 +107,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -121,7 +121,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- "#{1: } |
+ "#^ |
{3:-- TERMINAL --} |
]])
feed('<LeftDrag><2,2>')
@@ -131,7 +131,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- @##{1: } |
+ @##^ |
{3:-- TERMINAL --} |
]])
feed('<LeftDrag><3,2>')
@@ -141,7 +141,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- @$#{1: } |
+ @$#^ |
{3:-- TERMINAL --} |
]])
feed('<LeftRelease><3,2>')
@@ -151,7 +151,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- #$#{1: } |
+ #$#^ |
{3:-- TERMINAL --} |
]])
end)
@@ -165,7 +165,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- `!!{1: } |
+ `!!^ |
{3:-- TERMINAL --} |
]])
end)
@@ -179,7 +179,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- "#{1: } |
+ "#^ |
{3:-- TERMINAL --} |
]])
feed('<ScrollWheelUp><1,2>')
@@ -189,7 +189,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- `"#{1: } |
+ `"#^ |
{3:-- TERMINAL --} |
]])
feed('<LeftDrag><2,2>')
@@ -199,7 +199,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- @##{1: } |
+ @##^ |
{3:-- TERMINAL --} |
]])
feed('<ScrollWheelUp><2,2>')
@@ -209,7 +209,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- `##{1: } |
+ `##^ |
{3:-- TERMINAL --} |
]])
feed('<LeftRelease><2,2>')
@@ -219,7 +219,7 @@ describe(':terminal mouse', function()
line29 |
line30 |
mouse enabled |
- ###{1: } |
+ ###^ |
{3:-- TERMINAL --} |
]])
end)
@@ -237,7 +237,7 @@ describe(':terminal mouse', function()
{7: 13 }line30 |
{7: 14 }mouse enabled |
{7: 15 }rows: 6, cols: 46 |
- {7: 16 }{2: } |
+ {7: 16 } |
|
]])
-- If click on the coordinate (0,1) of the region of the terminal
@@ -249,7 +249,7 @@ describe(':terminal mouse', function()
{7: 13 }line30 |
{7: 14 }mouse enabled |
{7: 15 }rows: 6, cols: 46 |
- {7: 16 } !"{1: } |
+ {7: 16 } !"^ |
{3:-- TERMINAL --} |
]])
end)
@@ -261,7 +261,7 @@ describe(':terminal mouse', function()
line30 |
mouse enabled |
rows: 5, cols: 50 |
- {1: } |
+ ^ |
========== |
{3:-- TERMINAL --} |
]])
@@ -271,7 +271,7 @@ describe(':terminal mouse', function()
line30 |
mouse enabled |
rows: 5, cols: 50 |
- {2:^ } |
+ ^ |
========== |
|
]])
@@ -280,7 +280,7 @@ describe(':terminal mouse', function()
mouse enabled |
rows: 5, cols: 50 |
rows: 4, cols: 50 |
- {2:^ } |
+ ^ |
========== |
|*2
]])
@@ -293,7 +293,7 @@ describe(':terminal mouse', function()
line30 │{4:~ }|
mouse enabled │{4:~ }|
rows: 5, cols: 24 │{4:~ }|
- {1: } │{4:~ }|
+ ^ │{4:~ }|
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -303,7 +303,7 @@ describe(':terminal mouse', function()
line30 │{4:~ }|
mouse enabled │{4:~ }|
rows: 5, cols: 24 │{4:~ }|
- {2:^ } │{4:~ }|
+ ^ │{4:~ }|
========== ========== |
|
]])
@@ -313,7 +313,7 @@ describe(':terminal mouse', function()
mouse enabled │{4:~ }|
rows: 5, cols: 24 │{4:~ }|
rows: 5, cols: 23 │{4:~ }|
- {2:^ } │{4:~ }|
+ ^ │{4:~ }|
========== ========== |
|
]])
@@ -327,7 +327,7 @@ describe(':terminal mouse', function()
line30 |
mouse enabled |
rows: 5, cols: 50 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
feed('<LeftMouse><0,0>')
@@ -337,7 +337,7 @@ describe(':terminal mouse', function()
line30 |
mouse enabled |
rows: 5, cols: 50 |
- {2:^ } |
+ ^ |
|
]])
command('set showtabline=2 tabline=TABLINE | startinsert')
@@ -347,7 +347,7 @@ describe(':terminal mouse', function()
mouse enabled |
rows: 5, cols: 50 |
rows: 4, cols: 50 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
feed('<LeftMouse><0,0>')
@@ -357,7 +357,7 @@ describe(':terminal mouse', function()
mouse enabled |
rows: 5, cols: 50 |
rows: 4, cols: 50 |
- {2:^ } |
+ ^ |
|
]])
command('setlocal winbar= | startinsert')
@@ -367,7 +367,7 @@ describe(':terminal mouse', function()
rows: 5, cols: 50 |
rows: 4, cols: 50 |
rows: 5, cols: 50 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
feed('<LeftMouse><0,0>')
@@ -377,7 +377,7 @@ describe(':terminal mouse', function()
rows: 5, cols: 50 |
rows: 4, cols: 50 |
rows: 5, cols: 50 |
- {2:^ } |
+ ^ |
|
]])
end)
@@ -391,7 +391,7 @@ describe(':terminal mouse', function()
line29 │line29 |
line30 │line30 |
rows: 5, cols: 25 │rows: 5, cols: 25 |
- {2:^ } │{2: } |
+ ^ │ |
========== ========== |
:vsp |
]])
@@ -401,7 +401,7 @@ describe(':terminal mouse', function()
{4:~ }│line30 |
{4:~ }│rows: 5, cols: 25 |
{4:~ }│rows: 5, cols: 24 |
- {4:~ }│{2: } |
+ {4:~ }│ |
========== ========== |
:enew | set number |
]])
@@ -411,7 +411,7 @@ describe(':terminal mouse', function()
{7: 28 }line │line30 |
{7: 29 }line │rows: 5, cols: 25 |
{7: 30 }line │rows: 5, cols: 24 |
- {7: 31 }^ │{2: } |
+ {7: 31 }^ │ |
========== ========== |
|
]])
@@ -421,7 +421,7 @@ describe(':terminal mouse', function()
{7: 28 }line │line30 |
{7: 29 }line │rows: 5, cols: 25 |
{7: 30 }line │rows: 5, cols: 24 |
- {7: 31 } │{1: } |
+ {7: 31 } │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -434,7 +434,7 @@ describe(':terminal mouse', function()
{7: 28 }line │rows: 5, cols: 25 |
{7: 29 }line │rows: 5, cols: 24 |
{7: 30 }line │mouse enabled |
- {7: 31 } │{1: } |
+ {7: 31 } │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -447,7 +447,7 @@ describe(':terminal mouse', function()
{7: 22 }line │rows: 5, cols: 25 |
{7: 23 }line │rows: 5, cols: 24 |
{7: 24 }line │mouse enabled |
- {7: 25 }line │{1: } |
+ {7: 25 }line │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -457,7 +457,7 @@ describe(':terminal mouse', function()
{7: 27 }line │rows: 5, cols: 25 |
{7: 28 }line │rows: 5, cols: 24 |
{7: 29 }line │mouse enabled |
- {7: 30 }line │{1: } |
+ {7: 30 }line │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -468,7 +468,7 @@ describe(':terminal mouse', function()
{7: 17 }line │rows: 5, cols: 25 |
{7: 18 }line │rows: 5, cols: 24 |
{7: 19 }line │mouse enabled |
- {7: 20 }line │{1: } |
+ {7: 20 }line │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -483,7 +483,7 @@ describe(':terminal mouse', function()
{7: 2 }linelinelinelineline │rows: 5, cols: 25 |
{7: 3 }linelinelinelineline │rows: 5, cols: 24 |
{7: 4 }linelinelinelineline │mouse enabled |
- {7: 5 }linelinelinelineline │{1: } |
+ {7: 5 }linelinelinelineline │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -493,7 +493,7 @@ describe(':terminal mouse', function()
{7: 2 }nelinelineline │rows: 5, cols: 25 |
{7: 3 }nelinelineline │rows: 5, cols: 24 |
{7: 4 }nelinelineline │mouse enabled |
- {7: 5 }nelinelineline │{1: } |
+ {7: 5 }nelinelineline │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -504,7 +504,7 @@ describe(':terminal mouse', function()
{7: 2 }nelinelinelineline │rows: 5, cols: 25 |
{7: 3 }nelinelinelineline │rows: 5, cols: 24 |
{7: 4 }nelinelinelineline │mouse enabled |
- {7: 5 }nelinelinelineline │{1: } |
+ {7: 5 }nelinelinelineline │^ |
========== ========== |
{3:-- TERMINAL --} |
]])
@@ -517,7 +517,7 @@ describe(':terminal mouse', function()
{7: 28 }l^ine │rows: 5, cols: 25 |
{7: 29 }line │rows: 5, cols: 24 |
{7: 30 }line │mouse enabled |
- {7: 31 } │{2: } |
+ {7: 31 } │ |
========== ========== |
|
]])
@@ -531,7 +531,7 @@ describe(':terminal mouse', function()
{7: 28 }line │rows: 5, cols: 25 |
{7: 29 }line │rows: 5, cols: 24 |
{7: 30 }line │mouse enabled |
- {7: 31 }^ │{2: } |
+ {7: 31 }^ │ |
========== ========== |
|
]])
@@ -541,7 +541,7 @@ describe(':terminal mouse', function()
rows: 5, cols: 24 │rows: 5, cols: 24 |
mouse enabled │mouse enabled |
rows: 5, cols: 25 │rows: 5, cols: 25 |
- {2:^ } │{2: } |
+ ^ │ |
========== ========== |
:bn |
]])
@@ -551,7 +551,7 @@ describe(':terminal mouse', function()
{7: 28 }line │mouse enabled |
{7: 29 }line │rows: 5, cols: 25 |
{7: 30 }line │rows: 5, cols: 24 |
- {7: 31 }^ │{2: } |
+ {7: 31 }^ │ |
========== ========== |
:bn |
]])
diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua
index 1751db1aa9..804c5367eb 100644
--- a/test/functional/terminal/scrollback_spec.lua
+++ b/test/functional/terminal/scrollback_spec.lua
@@ -39,7 +39,7 @@ describe(':terminal scrollback', function()
line28 |
line29 |
line30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -67,7 +67,7 @@ describe(':terminal scrollback', function()
line2 |
line3 |
line4 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -84,7 +84,7 @@ describe(':terminal scrollback', function()
line3 |
line4 |
line5 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
eq(7, api.nvim_buf_line_count(0))
@@ -102,7 +102,7 @@ describe(':terminal scrollback', function()
line5 |
line6 |
line7 |
- line8{1: } |
+ line8^ |
{3:-- TERMINAL --} |
]])
@@ -135,7 +135,7 @@ describe(':terminal scrollback', function()
line5 |
line6 |
line7 |
- ^line8{2: } |
+ ^line8 |
|
]])
end)
@@ -151,7 +151,7 @@ describe(':terminal scrollback', function()
line3 |
line4 |
rows: 5, cols: 28 |
- {2:^ } |
+ ^ |
|
]])
end
@@ -168,7 +168,7 @@ describe(':terminal scrollback', function()
screen:expect([[
rows: 5, cols: 28 |
rows: 3, cols: 26 |
- {2:^ } |
+ ^ |
|
]])
eq(8, api.nvim_buf_line_count(0))
@@ -201,7 +201,7 @@ describe(':terminal scrollback', function()
screen:expect([[
tty ready |
rows: 4, cols: 30 |
- {1: } |
+ ^ |
|
{3:-- TERMINAL --} |
]])
@@ -220,7 +220,7 @@ describe(':terminal scrollback', function()
screen:expect([[
rows: 4, cols: 30 |
rows: 3, cols: 30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
eq(4, api.nvim_buf_line_count(0))
@@ -235,7 +235,7 @@ describe(':terminal scrollback', function()
screen:expect([[
rows: 4, cols: 30 |
rows: 3, cols: 30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -252,14 +252,14 @@ describe(':terminal scrollback', function()
line2 |
line3 |
line4 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
screen:try_resize(screen._width, screen._height - 3)
screen:expect([[
line4 |
rows: 3, cols: 30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
eq(7, api.nvim_buf_line_count(0))
@@ -278,7 +278,7 @@ describe(':terminal scrollback', function()
line4 |
rows: 3, cols: 30 |
rows: 4, cols: 30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end
@@ -300,7 +300,7 @@ describe(':terminal scrollback', function()
rows: 3, cols: 30 |
rows: 4, cols: 30 |
rows: 7, cols: 30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
eq(9, api.nvim_buf_line_count(0))
@@ -337,7 +337,7 @@ describe(':terminal scrollback', function()
rows: 4, cols: 30 |
rows: 7, cols: 30 |
rows: 11, cols: 30 |
- {1: } |
+ ^ |
|
{3:-- TERMINAL --} |
]])
@@ -355,14 +355,16 @@ describe(':terminal prints more lines than the screen height and exits', functio
it('will push extra lines to scrollback', function()
clear()
local screen = Screen.new(30, 7, { rgb = false })
- command(("call termopen(['%s', '10']) | startinsert"):format(testprg('tty-test')))
+ command(
+ ("call jobstart(['%s', '10'], {'term':v:true}) | startinsert"):format(testprg('tty-test'))
+ )
screen:expect([[
line6 |
line7 |
line8 |
line9 |
|
- [Process exited 0]{2: } |
+ [Process exited 0]^ |
{5:-- TERMINAL --} |
]])
feed('<cr>')
@@ -454,7 +456,7 @@ describe("'scrollback' option", function()
39: line |
40: line |
|
- ${1: } |
+ $^ |
{3:-- TERMINAL --} |
]],
}
@@ -493,7 +495,7 @@ describe("'scrollback' option", function()
line28 |
line29 |
line30 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
local term_height = 6 -- Actual terminal screen height, not the scrollback
@@ -623,7 +625,7 @@ describe('pending scrollback line handling', function()
local bufnr = vim.api.nvim_create_buf(false, true)
local args = ...
vim.api.nvim_buf_call(bufnr, function()
- vim.fn.termopen(args)
+ vim.fn.jobstart(args, { term = true })
end)
vim.api.nvim_win_set_buf(0, bufnr)
vim.cmd('startinsert')
@@ -634,7 +636,7 @@ describe('pending scrollback line handling', function()
screen:expect [[
hi |*4
|
- [Process exited 0]{2: } |
+ [Process exited 0]^ |
{3:-- TERMINAL --} |
]]
assert_alive()
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index ded0cd99d3..a2dc3c500f 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -26,7 +26,6 @@ local api = n.api
local is_ci = t.is_ci
local is_os = t.is_os
local new_pipename = n.new_pipename
-local spawn_argv = n.spawn_argv
local set_session = n.set_session
local write_file = t.write_file
local eval = n.eval
@@ -59,7 +58,7 @@ describe('TUI', function()
'colorscheme vim',
})
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -105,7 +104,7 @@ describe('TUI', function()
-- Need buffer rows to provoke the behavior.
feed_data(':edit test/functional/fixtures/bigfile.txt\n')
screen:expect([[
- {1:0}000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
+ ^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;;;; |
@@ -155,7 +154,7 @@ describe('TUI', function()
{8:FAIL 0} |
{8:FAIL 1} |
{8:FAIL 2} |
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -170,7 +169,7 @@ describe('TUI', function()
{8:FAIL 1} |
{8:FAIL 2} |
|*2
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -186,7 +185,7 @@ describe('TUI', function()
{8:FAIL 3} |
{8:FAIL 4} |
{8:FAIL 5} |
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -199,7 +198,7 @@ describe('TUI', function()
{8:FAIL 3} |
{8:FAIL 4} |
{8:FAIL 5} |
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -210,7 +209,7 @@ describe('TUI', function()
{8:FAIL 3} |
{8:FAIL 4} |
{8:FAIL 5} |
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -221,7 +220,7 @@ describe('TUI', function()
:call ManyErr() |
{8:Error detected while processing function ManyErr:} |
{11:line 2:} |
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -237,7 +236,7 @@ describe('TUI', function()
{8:FAIL 2} |
{8:FAIL 3} |
{8:FAIL 4} |
- {10:-- More --}{1: } |
+ {10:-- More --}^ |
{3:-- TERMINAL --} |
]],
}
@@ -245,7 +244,7 @@ describe('TUI', function()
feed_data('\003')
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*6
{5:[No Name] }|
|
@@ -259,7 +258,7 @@ describe('TUI', function()
screen:expect([[
abc |
test1 |
- test2{1: } |
+ test2^ |
{4:~ }|
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -269,7 +268,7 @@ describe('TUI', function()
screen:expect([[
abc |
test1 |
- test{1:2} |
+ test^2 |
{4:~ }|
{5:[No Name] [+] }|
|
@@ -287,14 +286,14 @@ describe('TUI', function()
alt-j |
alt-k |
alt-l |
- {1: } |
+ ^ |
{5:[No Name] [+] }|
|
{3:-- TERMINAL --} |
]])
feed_data('gg')
screen:expect([[
- {1:a}lt-d |
+ ^alt-d |
alt-f |
alt-g |
alt-h |
@@ -309,7 +308,7 @@ describe('TUI', function()
-- ALT+j inserts "ê". Nvim does not (#3982).
feed_data('i\022\027j')
screen:expect([[
- <M-j>{1: } |
+ <M-j>^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -329,7 +328,7 @@ describe('TUI', function()
)
feed_data('\027[27u;')
screen:expect([[
- ESCsemicolo{1:n} |
+ ESCsemicolo^n |
{4:~ }|*3
{5:[No Name] [+] }|
|
@@ -343,7 +342,7 @@ describe('TUI', function()
it('interprets <Esc><Nul> as <M-C-Space> #17198', function()
feed_data('i\022\027\000')
screen:expect([[
- <M-C-Space>{1: } |
+ <M-C-Space>^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -357,7 +356,7 @@ describe('TUI', function()
feed_data('\022\022') -- ctrl+v
feed_data('\022\013') -- ctrl+m
screen:expect([[
- {6:^G^V^M}{1: } |
+ {6:^G^V^M}^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -376,7 +375,7 @@ describe('TUI', function()
{}
)
screen:expect([[
- {11: 1 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----|
+ {11: 1 }^0----1----2----3----4│{11: 1 }0----1----2----3----|
{11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----|
{11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
{11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----|
@@ -391,7 +390,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'down', '', 0, 0, 7)
end
screen:expect([[
- {11: 2 }{1:0}----1----2----3----4│{11: 1 }0----1----2----3----|
+ {11: 2 }^0----1----2----3----4│{11: 1 }0----1----2----3----|
{11: 3 }0----1----2----3----4│{11: 2 }0----1----2----3----|
{11: 4 }0----1----2----3----4│{11: 3 }0----1----2----3----|
{11: 5 }0----1----2----3----4│{11: 4 }0----1----2----3----|
@@ -406,7 +405,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'down', '', 0, 0, 47)
end
screen:expect([[
- {11: 2 }{1:0}----1----2----3----4│{11: 2 }0----1----2----3----|
+ {11: 2 }^0----1----2----3----4│{11: 2 }0----1----2----3----|
{11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
{11: 4 }0----1----2----3----4│{11: 4 }0----1----2----3----|
{11: 5 }0----1----2----3----4│{11: 5 }0----1----2----3----|
@@ -421,7 +420,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'right', '', 0, 0, 7)
end
screen:expect([[
- {11: 2 }{1:-}---1----2----3----4-│{11: 2 }0----1----2----3----|
+ {11: 2 }^----1----2----3----4-│{11: 2 }0----1----2----3----|
{11: 3 }----1----2----3----4-│{11: 3 }0----1----2----3----|
{11: 4 }----1----2----3----4-│{11: 4 }0----1----2----3----|
{11: 5 }----1----2----3----4-│{11: 5 }0----1----2----3----|
@@ -436,7 +435,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'right', '', 0, 0, 47)
end
screen:expect([[
- {11: 2 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4|
+ {11: 2 }^----1----2----3----4-│{11: 2 }----1----2----3----4|
{11: 3 }----1----2----3----4-│{11: 3 }----1----2----3----4|
{11: 4 }----1----2----3----4-│{11: 4 }----1----2----3----4|
{11: 5 }----1----2----3----4-│{11: 5 }----1----2----3----4|
@@ -451,7 +450,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'down', 'S', 0, 0, 7)
end
screen:expect([[
- {11: 5 }{1:-}---1----2----3----4-│{11: 2 }----1----2----3----4|
+ {11: 5 }^----1----2----3----4-│{11: 2 }----1----2----3----4|
{11: 6 }----1----2----3----4-│{11: 3 }----1----2----3----4|
{11: 7 }----1----2----3----4-│{11: 4 }----1----2----3----4|
{11: 8 }----1----2----3----4-│{11: 5 }----1----2----3----4|
@@ -466,7 +465,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'down', 'S', 0, 0, 47)
end
screen:expect([[
- {11: 5 }{1:-}---1----2----3----4-│{11: 5 }----1----2----3----4|
+ {11: 5 }^----1----2----3----4-│{11: 5 }----1----2----3----4|
{11: 6 }----1----2----3----4-│{11: 6 }----1----2----3----4|
{11: 7 }----1----2----3----4-│{11: 7 }----1----2----3----4|
{11: 8 }----1----2----3----4-│{11: 8 }----1----2----3----4|
@@ -481,7 +480,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'right', 'S', 0, 0, 7)
end
screen:expect([[
- {11: 5 }{1:-}---6----7----8----9 │{11: 5 }----1----2----3----4|
+ {11: 5 }^----6----7----8----9 │{11: 5 }----1----2----3----4|
{11: 6 }----6----7----8----9 │{11: 6 }----1----2----3----4|
{11: 7 }----6----7----8----9 │{11: 7 }----1----2----3----4|
{11: 8 }----6----7----8----9 │{11: 8 }----1----2----3----4|
@@ -496,7 +495,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'right', 'S', 0, 0, 47)
end
screen:expect([[
- {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----|
+ {11: 5 }^----6----7----8----9 │{11: 5 }5----6----7----8----|
{11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----|
{11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----|
{11: 8 }----6----7----8----9 │{11: 8 }5----6----7----8----|
@@ -512,7 +511,7 @@ describe('TUI', function()
end
screen:expect([[
{11: 4 }----6----7----8----9 │{11: 5 }5----6----7----8----|
- {11: 5 }{1:-}---6----7----8----9 │{11: 6 }5----6----7----8----|
+ {11: 5 }^----6----7----8----9 │{11: 6 }5----6----7----8----|
{11: 6 }----6----7----8----9 │{11: 7 }5----6----7----8----|
{11: 7 }----6----7----8----9 │{11: 8 }5----6----7----8----|
{5:[No Name] [+] }{1:[No Name] [+] }|
@@ -527,7 +526,7 @@ describe('TUI', function()
end
screen:expect([[
{11: 4 }----6----7----8----9 │{11: 4 }5----6----7----8----|
- {11: 5 }{1:-}---6----7----8----9 │{11: 5 }5----6----7----8----|
+ {11: 5 }^----6----7----8----9 │{11: 5 }5----6----7----8----|
{11: 6 }----6----7----8----9 │{11: 6 }5----6----7----8----|
{11: 7 }----6----7----8----9 │{11: 7 }5----6----7----8----|
{5:[No Name] [+] }{1:[No Name] [+] }|
@@ -542,7 +541,7 @@ describe('TUI', function()
end
screen:expect([[
{11: 4 }5----6----7----8----9│{11: 4 }5----6----7----8----|
- {11: 5 }5{1:-}---6----7----8----9│{11: 5 }5----6----7----8----|
+ {11: 5 }5^----6----7----8----9│{11: 5 }5----6----7----8----|
{11: 6 }5----6----7----8----9│{11: 6 }5----6----7----8----|
{11: 7 }5----6----7----8----9│{11: 7 }5----6----7----8----|
{5:[No Name] [+] }{1:[No Name] [+] }|
@@ -557,7 +556,7 @@ describe('TUI', function()
end
screen:expect([[
{11: 4 }5----6----7----8----9│{11: 4 }-5----6----7----8---|
- {11: 5 }5{1:-}---6----7----8----9│{11: 5 }-5----6----7----8---|
+ {11: 5 }5^----6----7----8----9│{11: 5 }-5----6----7----8---|
{11: 6 }5----6----7----8----9│{11: 6 }-5----6----7----8---|
{11: 7 }5----6----7----8----9│{11: 7 }-5----6----7----8---|
{5:[No Name] [+] }{1:[No Name] [+] }|
@@ -574,7 +573,7 @@ describe('TUI', function()
{11: 1 }5----6----7----8----9│{11: 4 }-5----6----7----8---|
{11: 2 }5----6----7----8----9│{11: 5 }-5----6----7----8---|
{11: 3 }5----6----7----8----9│{11: 6 }-5----6----7----8---|
- {11: 4 }5{1:-}---6----7----8----9│{11: 7 }-5----6----7----8---|
+ {11: 4 }5^----6----7----8----9│{11: 7 }-5----6----7----8---|
{5:[No Name] [+] }{1:[No Name] [+] }|
|
{3:-- TERMINAL --} |
@@ -589,7 +588,7 @@ describe('TUI', function()
{11: 1 }5----6----7----8----9│{11: 1 }-5----6----7----8---|
{11: 2 }5----6----7----8----9│{11: 2 }-5----6----7----8---|
{11: 3 }5----6----7----8----9│{11: 3 }-5----6----7----8---|
- {11: 4 }5{1:-}---6----7----8----9│{11: 4 }-5----6----7----8---|
+ {11: 4 }5^----6----7----8----9│{11: 4 }-5----6----7----8---|
{5:[No Name] [+] }{1:[No Name] [+] }|
|
{3:-- TERMINAL --} |
@@ -604,7 +603,7 @@ describe('TUI', function()
{11: 1 }0----1----2----3----4│{11: 1 }-5----6----7----8---|
{11: 2 }0----1----2----3----4│{11: 2 }-5----6----7----8---|
{11: 3 }0----1----2----3----4│{11: 3 }-5----6----7----8---|
- {11: 4 }0----1----2----3----{1:4}│{11: 4 }-5----6----7----8---|
+ {11: 4 }0----1----2----3----^4│{11: 4 }-5----6----7----8---|
{5:[No Name] [+] }{1:[No Name] [+] }|
|
{3:-- TERMINAL --} |
@@ -619,7 +618,7 @@ describe('TUI', function()
{11: 1 }0----1----2----3----4│{11: 1 }0----1----2----3----|
{11: 2 }0----1----2----3----4│{11: 2 }0----1----2----3----|
{11: 3 }0----1----2----3----4│{11: 3 }0----1----2----3----|
- {11: 4 }0----1----2----3----{1:4}│{11: 4 }0----1----2----3----|
+ {11: 4 }0----1----2----3----^4│{11: 4 }0----1----2----3----|
{5:[No Name] [+] }{1:[No Name] [+] }|
|
{3:-- TERMINAL --} |
@@ -645,7 +644,7 @@ describe('TUI', function()
aunmenu PopUp
" Delete the default MenuPopup event handler.
- autocmd! nvim_popupmenu
+ autocmd! nvim.popupmenu
menu PopUp.foo :let g:menustr = 'foo'<CR>
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
@@ -660,7 +659,7 @@ describe('TUI', function()
api.nvim_input_mouse('right', 'press', '', 0, 0, 4)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }{13: foo }{4: }|
{4:~ }{13: bar }{4: }|
{4:~ }{13: baz }{4: }|
@@ -680,7 +679,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'up', '', 0, 0, 4)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }{14: foo }{4: }|
{4:~ }{13: bar }{4: }|
{4:~ }{13: baz }{4: }|
@@ -694,7 +693,7 @@ describe('TUI', function()
api.nvim_input_mouse('move', '', '', 0, 3, 6)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }{13: foo }{4: }|
{4:~ }{13: bar }{4: }|
{4:~ }{14: baz }{4: }|
@@ -708,7 +707,7 @@ describe('TUI', function()
api.nvim_input_mouse('wheel', 'down', '', 0, 3, 6)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }{13: foo }{4: }|
{4:~ }{14: bar }{4: }|
{4:~ }{13: baz }{4: }|
@@ -722,7 +721,7 @@ describe('TUI', function()
api.nvim_input_mouse('left', 'press', '', 0, 2, 6)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }|*3
{5:[No Name] [+] }|
:let g:menustr = 'bar' |
@@ -740,7 +739,7 @@ describe('TUI', function()
api.nvim_input_mouse('right', 'press', '', 0, 2, 44)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }|*2
{4:~ }{13: foo }{4: }|
{5:[No Name] [+] }{13: bar }{5: }|
@@ -753,7 +752,7 @@ describe('TUI', function()
api.nvim_input_mouse('right', 'drag', '', 0, 5, 47)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }|*2
{4:~ }{13: foo }{4: }|
{5:[No Name] [+] }{13: bar }{5: }|
@@ -766,7 +765,7 @@ describe('TUI', function()
api.nvim_input_mouse('right', 'release', '', 0, 5, 47)
end
screen:expect([[
- {1:p}opup menu test |
+ ^popup menu test |
{4:~ }|*3
{5:[No Name] [+] }|
:let g:menustr = 'baz' |
@@ -805,7 +804,7 @@ describe('TUI', function()
feed_data(fn.nr2char(57415)) -- KP_EQUAL
screen:expect([[
0123456789./*-+ |
- ={1: } |
+ =^ |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -814,7 +813,7 @@ describe('TUI', function()
feed_data(fn.nr2char(57417)) -- KP_LEFT
screen:expect([[
0123456789./*-+ |
- {1:=} |
+ ^= |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -823,7 +822,7 @@ describe('TUI', function()
feed_data(fn.nr2char(57418)) -- KP_RIGHT
screen:expect([[
0123456789./*-+ |
- ={1: } |
+ =^ |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -831,7 +830,7 @@ describe('TUI', function()
]])
feed_data(fn.nr2char(57419)) -- KP_UP
screen:expect([[
- 0{1:1}23456789./*-+ |
+ 0^123456789./*-+ |
= |
{4:~ }|*2
{5:[No Name] [+] }|
@@ -841,7 +840,7 @@ describe('TUI', function()
feed_data(fn.nr2char(57420)) -- KP_DOWN
screen:expect([[
0123456789./*-+ |
- ={1: } |
+ =^ |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -850,7 +849,7 @@ describe('TUI', function()
feed_data(fn.nr2char(57425)) -- KP_INSERT
screen:expect([[
0123456789./*-+ |
- ={1: } |
+ =^ |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- REPLACE --} |
@@ -859,7 +858,7 @@ describe('TUI', function()
feed_data('\027[27u') -- ESC
screen:expect([[
0123456789./*-+ |
- {1:=} |
+ ^= |
{4:~ }|*2
{5:[No Name] [+] }|
|
@@ -867,7 +866,7 @@ describe('TUI', function()
]])
feed_data('\027[57417;5u') -- CTRL + KP_LEFT
screen:expect([[
- {1:0}123456789./*-+ |
+ ^0123456789./*-+ |
= |
{4:~ }|*2
{5:[No Name] [+] }|
@@ -876,7 +875,7 @@ describe('TUI', function()
]])
feed_data('\027[57418;2u') -- SHIFT + KP_RIGHT
screen:expect([[
- 0123456789{1:.}/*-+ |
+ 0123456789^./*-+ |
= |
{4:~ }|*2
{5:[No Name] [+] }|
@@ -885,7 +884,7 @@ describe('TUI', function()
]])
feed_data(fn.nr2char(57426)) -- KP_DELETE
screen:expect([[
- 0123456789{1:/}*-+ |
+ 0123456789^/*-+ |
= |
{4:~ }|*2
{5:[No Name] [+] }|
@@ -894,7 +893,7 @@ describe('TUI', function()
]])
feed_data(fn.nr2char(57423)) -- KP_HOME
screen:expect([[
- {1:0}123456789/*-+ |
+ ^0123456789/*-+ |
= |
{4:~ }|*2
{5:[No Name] [+] }|
@@ -903,7 +902,7 @@ describe('TUI', function()
]])
feed_data(fn.nr2char(57424)) -- KP_END
screen:expect([[
- 0123456789/*-{1:+} |
+ 0123456789/*-^+ |
= |
{4:~ }|*2
{5:[No Name] [+] }|
@@ -921,7 +920,7 @@ describe('TUI', function()
)
screen:expect([[
{12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12:X}|
- {1: } |
+ ^ |
{4:~ }|*2
{5:[No Name] }|
|
@@ -930,7 +929,7 @@ describe('TUI', function()
feed_data('\027[57421;5u') -- CTRL + KP_PAGE_UP
screen:expect([[
{12: + [No Name] }{3: + [No Name] }{12: [No Name] }{1: }{12:X}|
- 0123456789/*-{1:+} |
+ 0123456789/*-^+ |
= |
{4:~ }|
{5:[No Name] [+] }|
@@ -940,7 +939,7 @@ describe('TUI', function()
feed_data('\027[57422;5u') -- CTRL + KP_PAGE_DOWN
screen:expect([[
{12: + [No Name] + [No Name] }{3: [No Name] }{1: }{12:X}|
- {1: } |
+ ^ |
{4:~ }|*2
{5:[No Name] }|
|
@@ -961,7 +960,7 @@ describe('TUI', function()
feed_data('\022\027[57379;48u') -- Shift + Alt + Ctrl + Super + Meta + F16
screen:expect([[
<D-j><T-k><T-D-CR><M-T-C-S-D-BS> |
- <D-F13><T-F14><T-D-F15><M-T-C-S-D-F16>{1: } |
+ <D-F13><T-F14><T-D-F15><M-T-C-S-D-F16>^ |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -973,7 +972,7 @@ describe('TUI', function()
-- "bracketed paste"
feed_data('i""\027i\027[200~')
screen:expect([[
- "{1:"} |
+ "^" |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -982,7 +981,7 @@ describe('TUI', function()
feed_data('pasted from terminal')
expect_child_buf_lines({ '"pasted from terminal"' })
screen:expect([[
- "pasted from terminal{1:"} |
+ "pasted from terminal^" |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -994,7 +993,7 @@ describe('TUI', function()
feed_data('\027[27u') -- ESC: go to Normal mode.
wait_for_mode('n')
screen:expect([[
- "pasted from termina{1:l}" |
+ "pasted from termina^l" |
{4:~ }|*3
{5:[No Name] [+] }|
|
@@ -1005,7 +1004,7 @@ describe('TUI', function()
expect_child_buf_lines({ '"pasted from terminapasted from terminalpasted from terminall"' })
screen:expect([[
"pasted from terminapasted from terminalpasted fro|
- m termina{1:l}l" |
+ m termina^ll" |
{4:~ }|*2
{5:[No Name] [+] }|
|
@@ -1027,7 +1026,7 @@ describe('TUI', function()
this is line 1 |
this is line 2 |
line 3 is here |
- {1: } |
+ ^ |
{5:[No Name] [+] }|
|
{3:-- TERMINAL --} |
@@ -1037,7 +1036,7 @@ describe('TUI', function()
screen:expect([[
this{16: is line 1} |
{16:this is line 2} |
- {16:line}{1: }3 is here |
+ {16:line}^ 3 is here |
|
{5:[No Name] [+] }|
{3:-- SELECT --} |
@@ -1047,7 +1046,7 @@ describe('TUI', function()
feed_data('just paste it™')
feed_data('\027[201~')
screen:expect([[
- thisjust paste it{1:™}3 is here |
+ thisjust paste it^™3 is here |
|
{4:~ }|*2
{5:[No Name] [+] }|
@@ -1084,7 +1083,7 @@ describe('TUI', function()
feed_data('i')
screen:expect([[
tty ready |
- {1: } |
+ ^ |
|*2
{19:^^^^^^^ }|
{3:-- TERMINAL --} |*2
@@ -1094,7 +1093,7 @@ describe('TUI', function()
feed_data('\027[201~')
screen:expect([[
tty ready |
- hallo{1: } |
+ hallo^ |
|*2
{19:^^^^^^^ }|
{3:-- TERMINAL --} |*2
@@ -1111,7 +1110,7 @@ describe('TUI', function()
local expected_grid1 = [[
line 1 |
ESC:{6:^[} / CR: |
- {1:x} |
+ ^x |
{4:~ }|
{5:[No Name] [+] 3,1 All}|
|
@@ -1126,7 +1125,7 @@ describe('TUI', function()
ESC:{6:^[} / CR: |
xline 1 |
ESC:{6:^[} / CR: |
- {1:x} |
+ ^x |
{5:[No Name] [+] 5,1 Bot}|
|
{3:-- TERMINAL --} |
@@ -1165,7 +1164,7 @@ describe('TUI', function()
|
{4:~ }|*2
{5:[No Name] [+] }|
- :"{1:"} |
+ :"^" |
{3:-- TERMINAL --} |
]])
-- "bracketed paste"
@@ -1179,7 +1178,7 @@ describe('TUI', function()
|
{4:~ }|*2
{5:[No Name] [+] }|
- :"line 1{1:"} |
+ :"line 1^" |
{3:-- TERMINAL --} |
]])
-- Dot-repeat/redo.
@@ -1188,7 +1187,7 @@ describe('TUI', function()
feed_data('.')
screen:expect([[
foo |*2
- {1: } |
+ ^ |
{4:~ }|
{5:[No Name] [+] }|
|
@@ -1235,7 +1234,7 @@ describe('TUI', function()
wait_for_mode('n')
screen:expect([[
foo |
- {1: } |
+ ^ |
{4:~ }|*2
{5:[No Name] [+] }|
|
@@ -1249,7 +1248,7 @@ describe('TUI', function()
{5: }|
{8:paste: Error executing lua: [string "<nvim>"]:4: f}|
{8:ake fail} |
- {10:Press ENTER or type command to continue}{1: } |
+ {10:Press ENTER or type command to continue}^ |
{3:-- TERMINAL --} |
]])
-- Remaining chunks are discarded after vim.paste() failure.
@@ -1265,7 +1264,7 @@ describe('TUI', function()
feed_data('.')
screen:expect([[
foo |*2
- {1: } |
+ ^ |
{4:~ }|
{5:[No Name] [+] }|
|
@@ -1275,7 +1274,7 @@ describe('TUI', function()
feed_data('ityped input...\027[27u')
screen:expect([[
foo |*2
- typed input..{1:.} |
+ typed input..^. |
{4:~ }|
{5:[No Name] [+] }|
|
@@ -1288,7 +1287,7 @@ describe('TUI', function()
foo |
typed input...line A |
line B |
- {1: } |
+ ^ |
{5:[No Name] [+] }|
|
{3:-- TERMINAL --} |
@@ -1352,7 +1351,7 @@ describe('TUI', function()
{5: }|
{8:paste: Error executing lua: Vim:E21: Cannot make c}|
{8:hanges, 'modifiable' is off} |
- {10:Press ENTER or type command to continue}{1: } |
+ {10:Press ENTER or type command to continue}^ |
{3:-- TERMINAL --} |
]])
feed_data('\n') -- <Enter> to dismiss hit-enter prompt
@@ -1361,7 +1360,7 @@ describe('TUI', function()
screen:expect([[
success 1 |
success 2 |
- {1: } |
+ ^ |
{4:~ }|
{5:[No Name] [+] }|
|
@@ -1380,7 +1379,7 @@ describe('TUI', function()
expected = expected .. ' end'
screen:expect([[
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz|
- zzzzzzzzzzzzzz end{1: } |
+ zzzzzzzzzzzzzz end^ |
{4:~ }|*2
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -1399,7 +1398,7 @@ describe('TUI', function()
|
{4:~ }|*3
{5:[No Name] }|
- :<{1: } |
+ :<^ |
{3:-- TERMINAL --} |
]])
end)
@@ -1420,7 +1419,7 @@ describe('TUI', function()
item 2997 |
item 2998 |
item 2999 |
- item 3000 end{1: } |
+ item 3000 end^ |
{5:[No Name] [+] 3000,14 Bot}|
{3:-- INSERT --} |
{3:-- TERMINAL --} |
@@ -1433,7 +1432,7 @@ describe('TUI', function()
item 2997 |
item 2998 |
item 2999 |
- item 3000 en{1:d}d |
+ item 3000 en^dd |
{5:[No Name] [+] 5999,13 Bot}|
|
{3:-- TERMINAL --} |
@@ -1457,7 +1456,7 @@ describe('TUI', function()
|
pasted from terminal (1) |
{6:^[}[200~ |
- {1: } |
+ ^ |
{5:[No Name] [+] }|
{3:-- INSERT --} |
{3:-- TERMINAL --} |
@@ -1472,7 +1471,7 @@ describe('TUI', function()
-- Send "stop paste" sequence.
feed_data('\027[201~')
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
{3:-- INSERT --} |
@@ -1487,7 +1486,7 @@ describe('TUI', function()
feed_data('\027[2')
feed_data('00~pasted from terminal\027[201~')
screen:expect([[
- pasted from terminal{1: } |
+ pasted from terminal^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -1502,7 +1501,7 @@ describe('TUI', function()
feed_data('\027[200~pasted from terminal\027[20')
feed_data('1~')
screen:expect([[
- pasted from terminal{1: } |
+ pasted from terminal^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -1524,7 +1523,7 @@ describe('TUI', function()
wait_for_mode('i')
feed_data('\027[200~pasted') -- phase 1
screen:expect([[
- pasted{1: } |
+ pasted^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -1532,7 +1531,7 @@ describe('TUI', function()
]])
feed_data(' from terminal') -- phase 2
screen:expect([[
- pasted from terminal{1: } |
+ pasted from terminal^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -1568,7 +1567,7 @@ describe('TUI', function()
feed_data('\028\014') -- crtl+\ ctrl+N
feed_data(':set termguicolors?\n')
screen:expect([[
- {5:^}{6:G} |
+ {6:^^G} |
{2:~ }|*3
{3:[No Name] [+] }|
notermguicolors |
@@ -1577,7 +1576,7 @@ describe('TUI', function()
feed_data(':set termguicolors\n')
screen:expect([[
- {7:^}{8:G} |
+ {8:^^G} |
{9:~}{10: }|*3
{3:[No Name] [+] }|
:set termguicolors |
@@ -1586,7 +1585,7 @@ describe('TUI', function()
feed_data(':set notermguicolors\n')
screen:expect([[
- {5:^}{6:G} |
+ {6:^^G} |
{2:~ }|*3
{3:[No Name] [+] }|
:set notermguicolors |
@@ -1634,7 +1633,7 @@ describe('TUI', function()
child_exec_lua('vim.cmd.terminal(...)', testprg('tty-test'))
screen:expect {
grid = [[
- {1:t}ty ready |
+ ^tty ready |
|*3
{2:^^^^^^^ }|
|
@@ -1646,7 +1645,7 @@ describe('TUI', function()
)
screen:expect {
grid = [[
- {1:t}ty ready |
+ ^tty ready |
{4:text}{5:color}text |
|*2
{2:^^^^^^^ }|
@@ -1658,7 +1657,7 @@ describe('TUI', function()
feed_data(':set notermguicolors\n')
screen:expect {
grid = [[
- {1:t}ty ready |
+ ^tty ready |
{4:text}colortext |
|*2
{6:^^^^^^^}{7: }|
@@ -1681,7 +1680,7 @@ describe('TUI', function()
child_session:request('nvim_set_hl', 0, 'Visual', { undercurl = true })
feed_data('ifoobar\027V')
screen:expect([[
- {5:fooba}{1:r} |
+ {5:fooba}^r |
{4:~ }|*3
{2:[No Name] [+] }|
{3:-- VISUAL LINE --} |
@@ -1689,7 +1688,7 @@ describe('TUI', function()
]])
child_session:request('nvim_set_hl', 0, 'Visual', { underdouble = true })
screen:expect([[
- {6:fooba}{1:r} |
+ {6:fooba}^r |
{4:~ }|*3
{2:[No Name] [+] }|
{3:-- VISUAL LINE --} |
@@ -1777,7 +1776,7 @@ describe('TUI', function()
child_session:request('nvim_set_option_value', 'listchars', 'eol:$', { win = 0 })
feed_data('gg')
local singlewidth_screen = [[
- {13:℃}{12:℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}|
+ {12:^℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃}|
{12:℃℃℃℃℃℃℃℃℃℃}{15:$}{12: }|
℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃℃|
℃℃℃℃℃℃℃℃℃℃{4:$} |
@@ -1788,7 +1787,7 @@ 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 = [[
- {13:℃}{12: ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
+ {12:^℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
{12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }|
{12:℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ }{15:$}{12: }|
℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ ℃ {4:@@@@}|
@@ -1821,7 +1820,7 @@ describe('TUI', function()
child_session:request('nvim_set_option_value', 'listchars', 'eol:$', { win = 0 })
feed_data('gg')
local singlewidth_screen = [[
- {13:✓}{12:✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓}|
+ {12:^✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓}|
{12:✓✓✓✓✓✓✓✓✓✓}{15:$}{12: }|
✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓|
✓✓✓✓✓✓✓✓✓✓{4:$} |
@@ -1832,7 +1831,7 @@ 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 = [[
- {13:✓}{12: ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }|
+ {12:^✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }|
{12:✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }|
{12:✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }{15:$}{12: }|
✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ {4:@@@@}|
@@ -1870,7 +1869,7 @@ describe('TUI', function()
-- Close the :intro message and redraw the lines.
feed_data('\n')
screen:expect([[
- {13:Ꝩ}{12:ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ}|
+ {12:^ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ}|
{12:ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ}|*310
{12:ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ℃ }|
b |
@@ -1912,7 +1911,7 @@ describe('TUI', function()
-- Close the :intro message and redraw the lines.
feed_data('\n')
screen:expect([[
- {1:Ꝩ}ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ|
+ ^ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ|
ꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨꝨ|*325
{3:-- TERMINAL --} |
]])
@@ -1925,7 +1924,7 @@ describe('TUI', function()
feed_data ':set visualbell\n'
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
:set visualbell |
@@ -1939,7 +1938,7 @@ describe('TUI', function()
feed_data 'i'
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
{3:-- INSERT --} |
@@ -1954,7 +1953,7 @@ describe('TUI', function()
grid = [[
Vim: Caught deadly signal 'SIGTERM' |
|*2
- [Process exited 1]{1: } |
+ [Process exited 1]^ |
|*2
{3:-- TERMINAL --} |
]],
@@ -1981,7 +1980,7 @@ describe('TUI', function()
[5] = { bold = true },
})
screen:expect([[
- {1: } |
+ ^ |
{2:~}{3: }|*3
{4:[No Name] }|
|
@@ -1989,7 +1988,7 @@ describe('TUI', function()
]])
feed_data('i')
screen:expect([[
- {1: } |
+ ^ |
{2:~}{3: }|*3
{4:[No Name] }|
{5:-- INSERT --} |
@@ -2000,7 +1999,7 @@ describe('TUI', function()
it('redraws on SIGWINCH even if terminal size is unchanged #23411', function()
child_session:request('nvim_echo', { { 'foo' } }, false, {})
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
foo |
@@ -2008,7 +2007,7 @@ describe('TUI', function()
]])
exec_lua([[vim.uv.kill(vim.fn.jobpid(vim.bo.channel), 'sigwinch')]])
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -2031,7 +2030,7 @@ describe('TUI', function()
]])
feed_data('\003')
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
Type :qa and press <Enter> to exit Nvim |
@@ -2046,7 +2045,7 @@ describe('TUI', function()
{1:foo} |
{4:~ }|*3
{5:[No Name] [+] }|
- /foo{1: } |
+ /foo^ |
{3:-- TERMINAL --} |
]])
screen:sleep(10)
@@ -2055,7 +2054,7 @@ describe('TUI', function()
foo |
{4:~ }|*3
{5:[No Name] [+] }|
- /foob{1: } |
+ /foob^ |
{3:-- TERMINAL --} |
]])
screen:sleep(10)
@@ -2064,7 +2063,7 @@ describe('TUI', function()
foo |
{4:~ }|*3
{5:[No Name] [+] }|
- /fooba{1: } |
+ /fooba^ |
{3:-- TERMINAL --} |
]])
end)
@@ -2114,7 +2113,7 @@ describe('TUI', function()
[5] = { bold = true, reverse = true },
[6] = { foreground = Screen.colors.White, background = Screen.colors.DarkGreen },
})
- fn.termopen({
+ fn.jobstart({
nvim_prog,
'--clean',
'--cmd',
@@ -2124,6 +2123,7 @@ describe('TUI', function()
'--cmd',
'let start = reltime() | while v:true | if reltimefloat(reltime(start)) > 2 | break | endif | endwhile',
}, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -2146,7 +2146,7 @@ describe('TUI', function()
for _, guicolors in ipairs({ 'notermguicolors', 'termguicolors' }) do
it('has no black flicker when clearing regions during startup with ' .. guicolors, function()
local screen = Screen.new(50, 10)
- fn.termopen({
+ fn.jobstart({
nvim_prog,
'--clean',
'--cmd',
@@ -2154,6 +2154,7 @@ describe('TUI', function()
'--cmd',
'sleep 10',
}, {
+ term = true,
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
},
@@ -2194,7 +2195,7 @@ describe('TUI', function()
local screen = tt.setup_child_nvim({ '--clean', '-l', script_file })
screen:expect {
grid = [[
- {1: } |
+ ^ |
~ |*3
[No Name] 0,0-1 All|
|
@@ -2207,7 +2208,7 @@ describe('TUI', function()
Xargv0nvim |
--embed |
--clean |
- {1:X}argv0nvim |
+ ^Xargv0nvim |
[No Name] [+] 5,1 Bot|
4 more lines |
{3:-- TERMINAL --} |
@@ -2233,7 +2234,7 @@ describe('TUI', function()
:q |
abc |
|
- [Process exited 0]{1: } |
+ [Process exited 0]^ |
|
{3:-- TERMINAL --} |
]])
@@ -2254,7 +2255,7 @@ describe('TUI', function()
})
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -2264,7 +2265,7 @@ describe('TUI', function()
command([[call chansend(b:terminal_job_id, "\<C-h>")]])
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
<C-h> |
@@ -2287,7 +2288,7 @@ describe('TUI', function()
}, { cols = 80 })
screen:expect {
grid = [[
- {1:1}st line |
+ ^1st line |
|*2
2nd line |
{5:[No Name] [+] 1,1 All}|
@@ -2300,7 +2301,7 @@ describe('TUI', function()
grid = [[
1st line |
|
- {1: } |
+ ^ |
2nd line |
{5:[No Name] [+] 1,161 All}|
|
@@ -2320,7 +2321,7 @@ describe('TUI', function()
}, { extra_rows = 10, cols = 66 })
screen:expect {
grid = [[
- |
+ ^ |
aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabb|*12
aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabba@@@|
[No Name] [+] 1,0-1 Top|
@@ -2339,7 +2340,7 @@ describe('TUI', function()
-- 500-cell limit, so the buffer is flushed after these spaces.
screen:expect {
grid = [[
- |
+ ^ |
aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabb|*12
aabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabbaabba@@@|
[No Name] [+] 1,0-1 Top|
@@ -2366,7 +2367,7 @@ describe('TUI', function()
screen:expect {
grid = [[
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
~ |*3
[No Name] [+] 1,1 All|
|
@@ -2378,7 +2379,8 @@ describe('TUI', function()
feed_data(':set columns=12\n')
screen:expect {
grid = [[
- aaaaaaaaaaaa |*4
+ ^aaaaaaaaaaaa |
+ aaaaaaaaaaaa |*3
< [+] 1,1 |
|
-- TERMINAL -- |
@@ -2416,7 +2418,7 @@ describe('TUI UIEnter/UILeave', function()
})
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -2426,7 +2428,7 @@ describe('TUI UIEnter/UILeave', function()
feed_data(':echo g:evs\n')
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
['VimEnter', 'UIEnter'] |
@@ -2457,7 +2459,7 @@ describe('TUI FocusGained/FocusLost', function()
})
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -2479,7 +2481,7 @@ describe('TUI FocusGained/FocusLost', function()
retry(2, 3 * screen.timeout, function()
feed_data('\027[I')
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
gained |
@@ -2488,7 +2490,7 @@ describe('TUI FocusGained/FocusLost', function()
feed_data('\027[O')
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
lost |
@@ -2502,7 +2504,7 @@ describe('TUI FocusGained/FocusLost', function()
feed_data('i')
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
:set noshowmode |
@@ -2512,7 +2514,7 @@ describe('TUI FocusGained/FocusLost', function()
retry(2, 3 * screen.timeout, function()
feed_data('\027[I')
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
gained |
@@ -2520,7 +2522,7 @@ describe('TUI FocusGained/FocusLost', function()
]])
feed_data('\027[O')
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
lost |
@@ -2538,7 +2540,7 @@ describe('TUI FocusGained/FocusLost', function()
|
{4:~ }|*3
{5:[No Name] }|
- :{1: } |
+ :^ |
{3:-- TERMINAL --} |
]])
feed_data('\027[O')
@@ -2547,7 +2549,7 @@ describe('TUI FocusGained/FocusLost', function()
|
{4:~ }|*3
{5:[No Name] }|
- :{1: } |
+ :^ |
{3:-- TERMINAL --} |
]],
unchanged = true,
@@ -2590,7 +2592,7 @@ describe('TUI FocusGained/FocusLost', function()
-- Wait for terminal to be ready.
screen:expect {
grid = [[
- {1:r}eady $ zia |
+ ^ready $ zia |
|
[Process exited 0] |
|*2
@@ -2602,7 +2604,7 @@ describe('TUI FocusGained/FocusLost', function()
feed_data('\027[I')
screen:expect {
grid = [[
- {1:r}eady $ zia |
+ ^ready $ zia |
|
[Process exited 0] |
|*2
@@ -2614,7 +2616,7 @@ describe('TUI FocusGained/FocusLost', function()
feed_data('\027[O')
screen:expect([[
- {1:r}eady $ zia |
+ ^ready $ zia |
|
[Process exited 0] |
|*2
@@ -2634,7 +2636,7 @@ describe('TUI FocusGained/FocusLost', function()
msg3 |
msg4 |
msg5 |
- {10:Press ENTER or type command to continue}{1: } |
+ {10:Press ENTER or type command to continue}^ |
{3:-- TERMINAL --} |
]],
}
@@ -2647,7 +2649,7 @@ describe('TUI FocusGained/FocusLost', function()
msg3 |
msg4 |
msg5 |
- {10:Press ENTER or type command to continue}{1: } |
+ {10:Press ENTER or type command to continue}^ |
{3:-- TERMINAL --} |
]],
unchanged = true,
@@ -2690,7 +2692,7 @@ describe("TUI 't_Co' (terminal colors)", function()
screen:expect(string.format(
[[
- {1: } |
+ ^ |
%s|*4
|
{3:-- TERMINAL --} |
@@ -2701,7 +2703,7 @@ describe("TUI 't_Co' (terminal colors)", function()
feed_data(':echo &t_Co\n')
screen:expect(string.format(
[[
- {1: } |
+ ^ |
%s|*4
%-3s |
{3:-- TERMINAL --} |
@@ -3028,7 +3030,7 @@ describe('TUI', function()
-- Wait for TUI to start.
feed_data('Gitext')
screen:expect([[
- text{1: } |
+ text^ |
{4:~ }|*4
{3:-- INSERT --} |
{3:-- TERMINAL --} |
@@ -3045,7 +3047,7 @@ describe('TUI', function()
nvim_tui()
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*4
|
{3:-- TERMINAL --} |
@@ -3055,7 +3057,7 @@ describe('TUI', function()
screen:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*4
|
{3:-- TERMINAL --} |
@@ -3181,7 +3183,6 @@ describe('TUI', function()
local req = args.data
local payload = req:match('^\027P%+q([%x;]+)$')
if payload and vim.text.hexdecode(payload) == 'Ms' then
- vim.g.xtgettcap = 'Ms'
local resp = string.format('\027P1+r%s=%s\027\\', payload, vim.text.hexencode('\027]52;;\027\\'))
vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
return true
@@ -3199,9 +3200,6 @@ describe('TUI', function()
}, {
env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'),
-
- -- Only queries when SSH_TTY is set
- SSH_TTY = '/dev/pts/1',
},
})
@@ -3209,8 +3207,7 @@ describe('TUI', function()
local child_session = n.connect(child_server)
retry(nil, 1000, function()
- eq('Ms', eval("get(g:, 'xtgettcap', '')"))
- eq({ true, 'OSC 52' }, { child_session:request('nvim_eval', 'g:clipboard.name') })
+ eq({ true, { osc52 = true } }, { child_session:request('nvim_eval', 'g:termfeatures') })
end)
end)
end)
@@ -3305,13 +3302,39 @@ describe('TUI bg color', function()
'autocmd OptionSet background echo "did OptionSet, yay!"',
})
screen:expect([[
- {1: } |
+ ^ |
{3:~} |*3
{5:[No Name] 0,0-1 All}|
did OptionSet, yay! |
{3:-- TERMINAL --} |
]])
end)
+
+ it('sends theme update notifications when background changes #31652', function()
+ command('set background=dark') -- set outer Nvim background
+ local child_server = new_pipename()
+ local screen = tt.setup_child_nvim({
+ '--listen',
+ child_server,
+ '-u',
+ 'NONE',
+ '-i',
+ 'NONE',
+ '--cmd',
+ 'colorscheme vim',
+ '--cmd',
+ 'set noswapfile',
+ })
+ screen:expect({ any = '%[No Name%]' })
+ local child_session = n.connect(child_server)
+ retry(nil, nil, function()
+ eq({ true, 'dark' }, { child_session:request('nvim_eval', '&background') })
+ end)
+ command('set background=light') -- set outer Nvim background
+ retry(nil, nil, function()
+ eq({ true, 'light' }, { child_session:request('nvim_eval', '&background') })
+ end)
+ end)
end)
-- These tests require `tt` because --headless/--embed
@@ -3322,8 +3345,8 @@ describe('TUI as a client', function()
end)
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)
+ local server_super = n.new_session(false)
+ local client_super = n.new_session(true)
set_session(server_super)
local server_pipe = new_pipename()
@@ -3343,7 +3366,7 @@ describe('TUI as a client', function()
feed_data('iHello, World')
screen_server:expect {
grid = [[
- Hello, World{1: } |
+ Hello, World^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -3353,7 +3376,7 @@ describe('TUI as a client', function()
feed_data('\027')
screen_server:expect {
grid = [[
- Hello, Worl{1:d} |
+ Hello, Worl^d |
{4:~ }|*3
{5:[No Name] [+] }|
|
@@ -3370,7 +3393,7 @@ describe('TUI as a client', function()
screen_client:expect {
grid = [[
- Hello, Worl{1:d} |
+ Hello, Worl^d |
{4:~ }|*3
{5:[No Name] [+] }|
|
@@ -3383,7 +3406,7 @@ describe('TUI as a client', function()
feed_data('0:set lines=3\n')
screen_server:expect {
grid = [[
- {1:a}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{5:[No Name] [+] }|
|*4
{3:-- TERMINAL --} |
@@ -3397,8 +3420,8 @@ describe('TUI as a client', function()
end)
it('connects to remote instance (--headless)', function()
- local server = spawn_argv(false) -- equivalent to clear()
- local client_super = spawn_argv(true, { env = { NVIM_LOG_FILE = testlog } })
+ local server = n.new_session(false)
+ local client_super = n.new_session(true, { env = { NVIM_LOG_FILE = testlog } })
set_session(server)
local server_pipe = api.nvim_get_vvar('servername')
@@ -3414,7 +3437,7 @@ describe('TUI as a client', function()
screen_client:expect {
grid = [[
- Halloj{1:!} |
+ Halloj^! |
{4:~ }|*4
|
{3:-- TERMINAL --} |
@@ -3428,7 +3451,7 @@ describe('TUI as a client', function()
grid = [[
Vim: Caught deadly signal 'SIGTERM' |
|*2
- [Process exited 1]{1: } |
+ [Process exited 1]^ |
|*2
{3:-- TERMINAL --} |
]],
@@ -3457,15 +3480,15 @@ describe('TUI as a client', function()
screen:expect([[
Remote ui failed to start: {MATCH:.*}|
|
- [Process exited 1]{1: } |
+ [Process exited 1]^ |
|*3
{3:-- TERMINAL --} |
]])
end)
local function test_remote_tui_quit(status)
- local server_super = spawn_argv(false) -- equivalent to clear()
- local client_super = spawn_argv(true)
+ local server_super = n.new_session(false)
+ local client_super = n.new_session(true)
set_session(server_super)
local server_pipe = new_pipename()
@@ -3483,7 +3506,7 @@ describe('TUI as a client', function()
})
screen_server:expect {
grid = [[
- {1: } |
+ ^ |
{4:~ }|*3
{5:[No Name] }|
|
@@ -3494,7 +3517,7 @@ describe('TUI as a client', function()
feed_data('iHello, World')
screen_server:expect {
grid = [[
- Hello, World{1: } |
+ Hello, World^ |
{4:~ }|*3
{5:[No Name] [+] }|
{3:-- INSERT --} |
@@ -3504,7 +3527,7 @@ describe('TUI as a client', function()
feed_data('\027')
screen_server:expect {
grid = [[
- Hello, Worl{1:d} |
+ Hello, Worl^d |
{4:~ }|*3
{5:[No Name] [+] }|
|
@@ -3521,7 +3544,7 @@ describe('TUI as a client', function()
screen_client:expect {
grid = [[
- Hello, Worl{1:d} |
+ Hello, Worl^d |
{4:~ }|*3
{5:[No Name] [+] }|
|
@@ -3536,7 +3559,7 @@ describe('TUI as a client', function()
screen_server:expect {
grid = [[
|
- [Process exited ]] .. status .. [[]{1: }{MATCH:%s+}|
+ [Process exited ]] .. status .. [[]^ {MATCH:%s+}|
|*4
{3:-- TERMINAL --} |
]],
@@ -3545,7 +3568,7 @@ describe('TUI as a client', function()
screen_client:expect {
grid = [[
|
- [Process exited ]] .. status .. [[]{1: }{MATCH:%s+}|
+ [Process exited ]] .. status .. [[]^ {MATCH:%s+}|
|*4
{3:-- TERMINAL --} |
]],
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
index fdb606e959..a65d18de70 100644
--- a/test/functional/terminal/window_spec.lua
+++ b/test/functional/terminal/window_spec.lua
@@ -62,7 +62,7 @@ describe(':terminal window', function()
screen:expect([[
{7:1 }tty ready |
{7:2 }rows: 6, cols: 48 |
- {7:3 }{1: } |
+ {7:3 }^ |
{7:4 } |
{7:5 } |
{7:6 } |
@@ -73,7 +73,7 @@ describe(':terminal window', function()
{7:1 }tty ready |
{7:2 }rows: 6, cols: 48 |
{7:3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV|
- {7:4 }WXYZ{1: } |
+ {7:4 }WXYZ^ |
{7:5 } |
{7:6 } |
{3:-- TERMINAL --} |
@@ -87,7 +87,7 @@ describe(':terminal window', function()
{7: 2 }rows: 6, cols: 48 |
{7: 3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO|
{7: 4 }PQRSTUVWXYZrows: 6, cols: 41 |
- {7: 5 }{1: } |
+ {7: 5 }^ |
{7: 6 } |
{3:-- TERMINAL --} |
]])
@@ -98,7 +98,7 @@ describe(':terminal window', function()
{7: 3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO|
{7: 4 }PQRSTUVWXYZrows: 6, cols: 41 |
{7: 5 } abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN|
- {7: 6 }OPQRSTUVWXYZ{1: } |
+ {7: 6 }OPQRSTUVWXYZ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -110,7 +110,7 @@ describe(':terminal window', function()
screen:expect([[
{7:++1 }tty ready |
{7:++2 }rows: 6, cols: 45 |
- {7:++3 }{1: } |
+ {7:++3 }^ |
{7:++4 } |
{7:++5 } |
{7:++6 } |
@@ -123,7 +123,7 @@ describe(':terminal window', function()
{7:++6 } |
{7:++7 } |
{7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS|
- {7:++9 }TUVWXYZ{1: } |
+ {7:++9 }TUVWXYZ^ |
{3:-- TERMINAL --} |
]])
feed_data('\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
@@ -133,7 +133,7 @@ describe(':terminal window', function()
{7:++ 9 }STUVWXYZ |
{7:++10 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR|
{7:++11 }STUVWXYZrows: 6, cols: 44 |
- {7:++12 }{1: } |
+ {7:++12 }^ |
{3:-- TERMINAL --} |
]])
end)
@@ -144,7 +144,7 @@ describe(':terminal window', function()
feed([[<C-\><C-N>]])
screen:expect([[
tty ready |
- {2:^ } |
+ ^ |
|*5
]])
feed(':set colorcolumn=20<CR>i')
@@ -153,7 +153,7 @@ describe(':terminal window', function()
it('wont show the color column', function()
screen:expect([[
tty ready |
- {1: } |
+ ^ |
|*4
{3:-- TERMINAL --} |
]])
@@ -170,7 +170,7 @@ describe(':terminal window', function()
line2 |
line3 |
line4 |
- {1: } |
+ ^ |
{3:-- TERMINAL --} |
]])
end)
@@ -184,7 +184,7 @@ describe(':terminal window', function()
line2 |
line3 |
line4 |
- {2: } |
+ |
|
]])
end)
@@ -206,7 +206,7 @@ describe(':terminal with multigrid', function()
[3:--------------------------------------------------]|
## grid 2
tty ready |
- {1: } |
+ ^ |
|*4
## grid 3
{3:-- TERMINAL --} |
@@ -223,7 +223,7 @@ describe(':terminal with multigrid', function()
## grid 2
tty ready |
rows: 10, cols: 20 |
- {1: } |
+ ^ |
|*7
## grid 3
{3:-- TERMINAL --} |
@@ -241,7 +241,7 @@ describe(':terminal with multigrid', function()
## grid 2
rows: 10, cols: 20 |
rows: 3, cols: 70 |
- {1: } |
+ ^ |
## grid 3
{3:-- TERMINAL --} |
]])
@@ -260,7 +260,7 @@ describe(':terminal with multigrid', function()
rows: 10, cols: 20 |
rows: 3, cols: 70 |
rows: 6, cols: 50 |
- {1: } |
+ ^ |
|
## grid 3
{3:-- TERMINAL --} |
diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua
index 272fc513af..dc22c87ca0 100644
--- a/test/functional/terminal/window_split_tab_spec.lua
+++ b/test/functional/terminal/window_split_tab_spec.lua
@@ -49,7 +49,7 @@ describe(':terminal', function()
========== |
tty ready |
rows: 5, cols: 50 |
- {2: } |
+ |
|*2
========== |
:2split |
@@ -61,7 +61,7 @@ describe(':terminal', function()
========== |
^tty ready |
rows: 5, cols: 50 |
- {2: } |
+ |
|*2
========== |
:wincmd p |
@@ -77,7 +77,7 @@ describe(':terminal', function()
command('bprevious')
screen:expect([[
tty ready |
- ^foo{2: } |
+ ^foo |
|*8
]])
end)
@@ -102,7 +102,7 @@ describe(':terminal', function()
screen:expect([[
tty ready |
rows: 7, cols: 47 |
- {2: } |
+ |
|*3
^ |
|
@@ -112,7 +112,7 @@ describe(':terminal', function()
tty ready |
rows: 7, cols: 47 |
rows: 4, cols: 41 |
- {2:^ } |
+ ^ |
|
]])
end)
diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua
index 60b2f872fc..59cb593cf7 100644
--- a/test/functional/testnvim.lua
+++ b/test/functional/testnvim.lua
@@ -4,7 +4,7 @@ local t = require('test.testutil')
local Session = require('test.client.session')
local uv_stream = require('test.client.uv_stream')
local SocketStream = uv_stream.SocketStream
-local ChildProcessStream = uv_stream.ChildProcessStream
+local ProcStream = uv_stream.ProcStream
local check_cores = t.check_cores
local check_logs = t.check_logs
@@ -48,6 +48,16 @@ M.nvim_argv = {
'unlet g:colors_name',
'--embed',
}
+if os.getenv('OSV_PORT') then
+ table.insert(M.nvim_argv, '--cmd')
+ table.insert(
+ M.nvim_argv,
+ string.format(
+ "lua require('osv').launch({ port = %s, blocking = true })",
+ os.getenv('OSV_PORT')
+ )
+ )
+end
-- Directory containing nvim.
M.nvim_dir = M.nvim_prog:gsub('[/\\][^/\\]+$', '')
@@ -308,24 +318,14 @@ function M.stop()
assert(session):stop()
end
-function M.nvim_prog_abs()
- -- system(['build/bin/nvim']) does not work for whatever reason. It must
- -- be executable searched in $PATH or something starting with / or ./.
- if M.nvim_prog:match('[/\\]') then
- return M.request('nvim_call_function', 'fnamemodify', { M.nvim_prog, ':p' })
- else
- return M.nvim_prog
- end
-end
-
-- Use for commands which expect nvim to quit.
-- The first argument can also be a timeout.
function M.expect_exit(fn_or_timeout, ...)
local eof_err_msg = 'EOF was received from Nvim. Likely the Nvim process crashed.'
if type(fn_or_timeout) == 'function' then
- eq(eof_err_msg, t.pcall_err(fn_or_timeout, ...))
+ t.matches(eof_err_msg, t.pcall_err(fn_or_timeout, ...))
else
- eq(
+ t.matches(
eof_err_msg,
t.pcall_err(function(timeout, fn, ...)
fn(...)
@@ -455,22 +455,6 @@ function M.check_close()
session = nil
end
---- @param argv string[]
---- @param merge boolean?
---- @param env string[]?
---- @param keep boolean?
---- @param io_extra uv.uv_pipe_t? used for stdin_fd, see :help ui-option
---- @return test.Session
-function M.spawn(argv, merge, env, keep, io_extra)
- if not keep then
- M.check_close()
- end
-
- local child_stream =
- ChildProcessStream.spawn(merge and M.merge_args(prepend_argv, argv) or argv, env, io_extra)
- return Session.new(child_stream)
-end
-
-- Creates a new Session connected by domain socket (named pipe) or TCP.
function M.connect(file_or_address)
local addr, port = string.match(file_or_address, '(.*):(%d+)')
@@ -479,61 +463,112 @@ function M.connect(file_or_address)
return Session.new(stream)
end
--- Starts (and returns) a new global Nvim session.
---
--- Parameters are interpreted as startup args, OR a map with these keys:
--- args: List: Args appended to the default `nvim_argv` set.
--- args_rm: List: Args removed from the default set. All cases are
--- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
--- (and its value) from the default set.
--- env: Map: Defines the environment of the new session.
---
--- Example:
--- clear('-e')
--- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
+--- Starts a new, global Nvim session and clears the current one.
+---
+--- Note: Use `new_session()` to start a session without replacing the current one.
+---
+--- Parameters are interpreted as startup args, OR a map with these keys:
+--- - args: List: Args appended to the default `nvim_argv` set.
+--- - args_rm: List: Args removed from the default set. All cases are
+--- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
+--- (and its value) from the default set.
+--- - env: Map: Defines the environment of the new session.
+---
+--- Example:
+--- ```
+--- clear('-e')
+--- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
+--- ```
+---
+--- @param ... string Nvim CLI args
+--- @return test.Session
+--- @overload fun(opts: test.session.Opts): test.Session
function M.clear(...)
- M.set_session(M.spawn_argv(false, ...))
+ M.set_session(M.new_session(false, ...))
return M.get_session()
end
---- same params as clear, but does returns the session instead
---- of replacing the default session
+--- Starts a new Nvim process with the given args and returns a msgpack-RPC session.
+---
+--- Does not replace the current global session, unlike `clear()`.
+---
+--- @param keep boolean (default: false) Don't close the current global session.
+--- @param ... string Nvim CLI args (or see overload)
--- @return test.Session
-function M.spawn_argv(keep, ...)
- local argv, env, io_extra = M.new_argv(...)
- return M.spawn(argv, nil, env, keep, io_extra)
+--- @overload fun(keep: boolean, opts: test.session.Opts): test.Session
+function M.new_session(keep, ...)
+ if not keep then
+ M.check_close()
+ end
+
+ local argv, env, io_extra = M._new_argv(...)
+
+ local proc = ProcStream.spawn(argv, env, io_extra)
+ return Session.new(proc)
end
---- @class test.new_argv.Opts
+--- Starts a (non-RPC, `--headless --listen "Tx"`) Nvim process, waits for exit, and returns result.
+---
+--- @param ... string Nvim CLI args, or `test.session.Opts` table.
+--- @return test.ProcStream
+--- @overload fun(opts: test.session.Opts): test.ProcStream
+function M.spawn_wait(...)
+ local opts = type(...) == 'string' and { args = { ... } } or ...
+ opts.args_rm = opts.args_rm and opts.args_rm or {}
+ table.insert(opts.args_rm, '--embed')
+ local argv, env, io_extra = M._new_argv(opts)
+ local proc = ProcStream.spawn(argv, env, io_extra)
+ proc.collect_text = true
+ proc:read_start()
+ proc:wait()
+ proc:close()
+ return proc
+end
+
+--- @class test.session.Opts
+--- Nvim CLI args
--- @field args? string[]
+--- Remove these args from the default `nvim_argv` args set. Ignored if `merge=false`.
--- @field args_rm? string[]
+--- (default: true) Merge `args` with the default set. Else use only the provided `args`.
+--- @field merge? boolean
+--- Environment variables
--- @field env? table<string,string>
+--- Used for stdin_fd, see `:help ui-option`
--- @field io_extra? uv.uv_pipe_t
---- Builds an argument list for use in clear().
+--- @private
---
---- @see clear() for parameters.
---- @param ... string
+--- Builds an argument list for use in `new_session()`, `clear()`, and `spawn_wait()`.
+---
+--- @param ... string Nvim CLI args, or `test.session.Opts` table.
--- @return string[]
--- @return string[]?
--- @return uv.uv_pipe_t?
-function M.new_argv(...)
- local args = { unpack(M.nvim_argv) }
- table.insert(args, '--headless')
- if _G._nvim_test_id then
- -- Set the server name to the test-id for logging. #8519
- table.insert(args, '--listen')
- table.insert(args, _G._nvim_test_id)
+--- @overload fun(opts: test.session.Opts): string[], string[]?, uv.uv_pipe_t?
+function M._new_argv(...)
+ --- @type test.session.Opts|string
+ local opts = select(1, ...)
+ local merge = type(opts) ~= 'table' and true or opts.merge ~= false
+
+ local args = merge and { unpack(M.nvim_argv) } or { M.nvim_prog }
+ if merge then
+ table.insert(args, '--headless')
+ if _G._nvim_test_id then
+ -- Set the server name to the test-id for logging. #8519
+ table.insert(args, '--listen')
+ table.insert(args, _G._nvim_test_id)
+ end
end
+
local new_args --- @type string[]
local io_extra --- @type uv.uv_pipe_t?
- local env --- @type string[]?
- --- @type test.new_argv.Opts|string
- local opts = select(1, ...)
+ local env --- @type string[]? List of "key=value" env vars.
+
if type(opts) ~= 'table' then
new_args = { ... }
else
- args = remove_args(args, opts.args_rm)
+ args = merge and remove_args(args, opts.args_rm) or args
if opts.env then
local env_opt = {} --- @type table<string,string>
for k, v in pairs(opts.env) do
@@ -800,81 +835,6 @@ function M.exec_capture(code)
return M.api.nvim_exec2(code, { output = true }).output
end
---- @param f function
---- @return table<string,any>
-local function get_upvalues(f)
- local i = 1
- local upvalues = {} --- @type table<string,any>
- while true do
- local n, v = debug.getupvalue(f, i)
- if not n then
- break
- end
- upvalues[n] = v
- i = i + 1
- end
- return upvalues
-end
-
---- @param f function
---- @param upvalues table<string,any>
-local function set_upvalues(f, upvalues)
- local i = 1
- while true do
- local n = debug.getupvalue(f, i)
- if not n then
- break
- end
- if upvalues[n] then
- debug.setupvalue(f, i, upvalues[n])
- end
- i = i + 1
- end
-end
-
---- @type fun(f: function): table<string,any>
-_G.__get_upvalues = nil
-
---- @type fun(f: function, upvalues: table<string,any>)
-_G.__set_upvalues = nil
-
---- @param self table<string,function>
---- @param bytecode string
---- @param upvalues table<string,any>
---- @param ... any[]
---- @return any[] result
---- @return table<string,any> upvalues
-local function exec_lua_handler(self, bytecode, upvalues, ...)
- local f = assert(loadstring(bytecode))
- self.set_upvalues(f, upvalues)
- local ret = { f(...) } --- @type any[]
- --- @type table<string,any>
- local new_upvalues = self.get_upvalues(f)
-
- do -- Check return value types for better error messages
- local invalid_types = {
- ['thread'] = true,
- ['function'] = true,
- ['userdata'] = true,
- }
-
- for k, v in pairs(ret) do
- if invalid_types[type(v)] then
- error(
- string.format(
- "Return index %d with value '%s' of type '%s' cannot be serialized over RPC",
- k,
- tostring(v),
- type(v)
- )
- )
- end
- end
- end
-
- return ret, new_upvalues
-end
-
--- Execute Lua code in the wrapped Nvim session.
---
--- When `code` is passed as a function, it is converted into Lua byte code.
@@ -921,52 +881,7 @@ function M.exec_lua(code, ...)
end
assert(session, 'no Nvim session')
-
- if not session.exec_lua_setup then
- assert(
- session:request(
- 'nvim_exec_lua',
- [[
- _G.__test_exec_lua = {
- get_upvalues = loadstring((select(1,...))),
- set_upvalues = loadstring((select(2,...))),
- handler = loadstring((select(3,...)))
- }
- setmetatable(_G.__test_exec_lua, { __index = _G.__test_exec_lua })
- ]],
- { string.dump(get_upvalues), string.dump(set_upvalues), string.dump(exec_lua_handler) }
- )
- )
- session.exec_lua_setup = true
- end
-
- local stat, rv = session:request(
- 'nvim_exec_lua',
- 'return { _G.__test_exec_lua:handler(...) }',
- { string.dump(code), get_upvalues(code), ... }
- )
-
- if not stat then
- error(rv[2])
- end
-
- --- @type any[], table<string,any>
- local ret, upvalues = unpack(rv)
-
- -- Update upvalues
- if next(upvalues) then
- local caller = debug.getinfo(2)
- local f = caller.func
- -- On PUC-Lua, if the function is a tail call, then func will be nil.
- -- In this case we need to use the current function.
- if not f then
- assert(caller.source == '=(tail call)')
- f = debug.getinfo(1).func
- end
- set_upvalues(f, upvalues)
- end
-
- return unpack(ret, 1, table.maxn(ret))
+ return require('test.functional.testnvim.exec_lua')(session, 2, code, ...)
end
function M.get_pathsep()
diff --git a/test/functional/testnvim/exec_lua.lua b/test/functional/testnvim/exec_lua.lua
new file mode 100644
index 0000000000..ddd9905ce7
--- /dev/null
+++ b/test/functional/testnvim/exec_lua.lua
@@ -0,0 +1,148 @@
+--- @param f function
+--- @return table<string,any>
+local function get_upvalues(f)
+ local i = 1
+ local upvalues = {} --- @type table<string,any>
+ while true do
+ local n, v = debug.getupvalue(f, i)
+ if not n then
+ break
+ end
+ upvalues[n] = v
+ i = i + 1
+ end
+ return upvalues
+end
+
+--- @param f function
+--- @param upvalues table<string,any>
+local function set_upvalues(f, upvalues)
+ local i = 1
+ while true do
+ local n = debug.getupvalue(f, i)
+ if not n then
+ break
+ end
+ if upvalues[n] then
+ debug.setupvalue(f, i, upvalues[n])
+ end
+ i = i + 1
+ end
+end
+
+--- @param messages string[]
+--- @param ... ...
+local function add_print(messages, ...)
+ local msg = {} --- @type string[]
+ for i = 1, select('#', ...) do
+ msg[#msg + 1] = tostring(select(i, ...))
+ end
+ table.insert(messages, table.concat(msg, '\t'))
+end
+
+local invalid_types = {
+ ['thread'] = true,
+ ['function'] = true,
+ ['userdata'] = true,
+}
+
+--- @param r any[]
+local function check_returns(r)
+ for k, v in pairs(r) do
+ if invalid_types[type(v)] then
+ error(
+ string.format(
+ "Return index %d with value '%s' of type '%s' cannot be serialized over RPC",
+ k,
+ tostring(v),
+ type(v)
+ ),
+ 2
+ )
+ end
+ end
+end
+
+local M = {}
+
+--- This is run in the context of the remote Nvim instance.
+--- @param bytecode string
+--- @param upvalues table<string,any>
+--- @param ... any[]
+--- @return any[] result
+--- @return table<string,any> upvalues
+--- @return string[] messages
+function M.handler(bytecode, upvalues, ...)
+ local messages = {} --- @type string[]
+ local orig_print = _G.print
+
+ function _G.print(...)
+ add_print(messages, ...)
+ return orig_print(...)
+ end
+
+ local f = assert(loadstring(bytecode))
+
+ set_upvalues(f, upvalues)
+
+ -- Run in pcall so we can return any print messages
+ local ret = { pcall(f, ...) } --- @type any[]
+
+ _G.print = orig_print
+
+ local new_upvalues = get_upvalues(f)
+
+ -- Check return value types for better error messages
+ check_returns(ret)
+
+ return ret, new_upvalues, messages
+end
+
+--- @param session test.Session
+--- @param lvl integer
+--- @param code function
+--- @param ... ...
+local function run(session, lvl, code, ...)
+ local stat, rv = session:request(
+ 'nvim_exec_lua',
+ [[return { require('test.functional.testnvim.exec_lua').handler(...) }]],
+ { string.dump(code), get_upvalues(code), ... }
+ )
+
+ if not stat then
+ error(rv[2], 2)
+ end
+
+ --- @type any[], table<string,any>, string[]
+ local ret, upvalues, messages = unpack(rv)
+
+ for _, m in ipairs(messages) do
+ print(m)
+ end
+
+ if not ret[1] then
+ error(ret[2], 2)
+ end
+
+ -- Update upvalues
+ if next(upvalues) then
+ local caller = debug.getinfo(lvl)
+ local i = 0
+
+ -- On PUC-Lua, if the function is a tail call, then func will be nil.
+ -- In this case we need to use the caller.
+ while not caller.func do
+ i = i + 1
+ caller = debug.getinfo(lvl + i)
+ end
+ set_upvalues(caller.func, upvalues)
+ end
+
+ return unpack(ret, 2, table.maxn(ret))
+end
+
+return setmetatable(M, {
+ __call = function(_, ...)
+ return run(...)
+ end,
+})
diff --git a/test/functional/testterm.lua b/test/functional/testterm.lua
index 3aadcc59a7..17209d947e 100644
--- a/test/functional/testterm.lua
+++ b/test/functional/testterm.lua
@@ -29,6 +29,10 @@ function M.feed_termcode(data)
M.feed_data('\027' .. data)
end
+function M.feed_csi(data)
+ M.feed_termcode('[' .. data)
+end
+
function M.make_lua_executor(session)
return function(code, ...)
local status, rv = session:request('nvim_exec_lua', code, { ... })
@@ -78,6 +82,9 @@ end
function M.set_undercurl()
M.feed_termcode('[4:3m')
end
+function M.set_reverse()
+ M.feed_termcode('[7m')
+end
function M.set_strikethrough()
M.feed_termcode('[9m')
end
@@ -108,7 +115,6 @@ function M.setup_screen(extra_rows, cmd, cols, env, screen_opts)
cols = cols and cols or 50
api.nvim_command('highlight TermCursor cterm=reverse')
- api.nvim_command('highlight TermCursorNC ctermbg=11')
api.nvim_command('highlight StatusLineTerm ctermbg=2 ctermfg=0')
api.nvim_command('highlight StatusLineTermNC ctermbg=2 ctermfg=8')
@@ -135,7 +141,7 @@ function M.setup_screen(extra_rows, cmd, cols, env, screen_opts)
})
api.nvim_command('enew')
- api.nvim_call_function('termopen', { cmd, env and { env = env } or nil })
+ api.nvim_call_function('jobstart', { cmd, { term = true, env = (env and env or nil) } })
api.nvim_input('<CR>')
local vim_errmsg = api.nvim_eval('v:errmsg')
if vim_errmsg and '' ~= vim_errmsg then
@@ -154,7 +160,7 @@ function M.setup_screen(extra_rows, cmd, cols, env, screen_opts)
local empty_line = (' '):rep(cols)
local expected = {
'tty ready' .. (' '):rep(cols - 9),
- '{1: }' .. (' '):rep(cols - 1),
+ '^' .. (' '):rep(cols),
empty_line,
empty_line,
empty_line,
diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua
index e38e58ff92..ac58df4bba 100644
--- a/test/functional/treesitter/fold_spec.lua
+++ b/test/functional/treesitter/fold_spec.lua
@@ -5,6 +5,7 @@ local Screen = require('test.functional.ui.screen')
local clear = n.clear
local eq = t.eq
local insert = n.insert
+local write_file = t.write_file
local exec_lua = n.exec_lua
local command = n.command
local feed = n.feed
@@ -767,4 +768,79 @@ t2]])
]],
}
end)
+
+ it("doesn't call get_parser too often when parser is not available", function()
+ -- spy on vim.treesitter.get_parser() to keep track of how many times it is called
+ exec_lua(function()
+ _G.count = 0
+ vim.treesitter.get_parser = (function(wrapped)
+ return function(...)
+ _G.count = _G.count + 1
+ return wrapped(...)
+ end
+ end)(vim.treesitter.get_parser)
+ end)
+
+ insert(test_text)
+ command [[
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ eq(
+ 1,
+ exec_lua [[ return _G.count ]],
+ 'count should not be as high as the # of lines; actually only once for the buffer.'
+ )
+ end)
+
+ it('can detect a new parser and refresh folds accordingly', function()
+ local name = t.tmpname()
+ write_file(name, test_text)
+ command('edit ' .. name)
+ command [[
+ set filetype=some_filetype_without_treesitter_parser
+ set foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldcolumn=1 foldlevel=0
+ ]]
+
+ -- foldexpr will return '0' for all lines
+ local levels = get_fold_levels() ---@type integer[]
+ eq(19, #levels)
+ for lnum, level in ipairs(levels) do
+ eq('0', level, string.format("foldlevel[%d] == %s; expected '0'", lnum, level))
+ end
+
+ -- reload buffer as c filetype to simulate new parser being found
+ feed('GA// vim: ft=c<Esc>')
+ command([[write | edit]])
+
+ eq({
+ [1] = '>1',
+ [2] = '1',
+ [3] = '1',
+ [4] = '1',
+ [5] = '>2',
+ [6] = '2',
+ [7] = '2',
+ [8] = '1',
+ [9] = '1',
+ [10] = '>2',
+ [11] = '2',
+ [12] = '2',
+ [13] = '2',
+ [14] = '2',
+ [15] = '>3',
+ [16] = '3',
+ [17] = '3',
+ [18] = '2',
+ [19] = '1',
+ }, get_fold_levels())
+ end)
end)
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 5c6be869c6..7f0a3cb342 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -12,6 +12,7 @@ local fn = n.fn
local eq = t.eq
local hl_query_c = [[
+ ; query
(ERROR) @error
"if" @keyword
@@ -65,40 +66,40 @@ static int nlua_schedule(lua_State *const lstate)
}]]
local hl_grid_legacy_c = [[
- {2:^/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} nlua_schedule(lua_State *{3:const} lstate) |
+ {18:^/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} nlua_schedule(lua_State *{6:const} lstate) |
{ |
- {4:if} (lua_type(lstate, {5:1}) != LUA_TFUNCTION |
+ {15:if} (lua_type(lstate, {26:1}) != LUA_TFUNCTION |
|| lstate != lstate) { |
- lua_pushliteral(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} lua_error(lstate); |
+ lua_pushliteral(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} lua_error(lstate); |
} |
|
- LuaRef cb = nlua_ref(lstate, {5:1}); |
+ LuaRef cb = nlua_ref(lstate, {26:1}); |
|
multiqueue_put(main_loop.events, nlua_schedule_event, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
} |
{1:~ }|*2
|
]]
local hl_grid_ts_c = [[
- {2:^/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
+ {18:^/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
{ |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
} |
|
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
|
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
} |
{1:~ }|*2
|
@@ -145,10 +146,10 @@ local injection_grid_c = [[
]]
local injection_grid_expected_c = [[
- {3:int} x = {5:INT_MAX}; |
- #define {5:READ_STRING}(x, y) ({3:char} *)read_string((x), ({3:size_t})(y)) |
- #define foo {3:void} main() { \ |
- {4:return} {5:42}; \ |
+ {6:int} x = {26:INT_MAX}; |
+ #define {26:READ_STRING}(x, y) ({6:char} *)read_string((x), ({6:size_t})(y)) |
+ #define foo {6:void} main() { \ |
+ {15:return} {26:42}; \ |
} |
^ |
{1:~ }|*11
@@ -161,20 +162,6 @@ describe('treesitter highlighting (C)', function()
before_each(function()
clear()
screen = Screen.new(65, 18)
- screen:set_default_attr_ids {
- [1] = { bold = true, foreground = Screen.colors.Blue1 },
- [2] = { foreground = Screen.colors.Blue1 },
- [3] = { bold = true, foreground = Screen.colors.SeaGreen4 },
- [4] = { bold = true, foreground = Screen.colors.Brown },
- [5] = { foreground = Screen.colors.Magenta },
- [6] = { foreground = Screen.colors.Red },
- [7] = { bold = true, foreground = Screen.colors.SlateBlue },
- [8] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
- [9] = { foreground = Screen.colors.Magenta, background = Screen.colors.Red },
- [10] = { foreground = Screen.colors.Red, background = Screen.colors.Red },
- [11] = { foreground = Screen.colors.Cyan4 },
- }
-
command [[ hi link @error ErrorMsg ]]
command [[ hi link @warning WarningMsg ]]
end)
@@ -246,124 +233,124 @@ describe('treesitter highlighting (C)', function()
feed('5Goc<esc>dd')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:^lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- } |
- {1:~ }|*2
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:^lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|*2
+ |
+ ]],
+ })
feed('7Go*/<esc>')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:lua_pushliteral}(lstate, {5:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
- {8:*^/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- } |
- {1:~ }|
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ {9:*^/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|
+ |
+ ]],
+ })
feed('3Go/*<esc>')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {2:/^*} |
- {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
- {2: || lstate != lstate) {} |
- {2: lua_pushliteral(lstate, "vim.schedule: expected function");} |
- {2: return lua_error(lstate);} |
- {2:*/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- {8:}} |
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {18:/^*} |
+ {18: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
+ {18: || lstate != lstate) {} |
+ {18: lua_pushliteral(lstate, "vim.schedule: expected function");} |
+ {18: return lua_error(lstate);} |
+ {18:*/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ {9:}} |
+ |
+ ]],
+ })
feed('gg$')
feed('~')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queu^E} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {2:/*} |
- {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
- {2: || lstate != lstate) {} |
- {2: lua_pushliteral(lstate, "vim.schedule: expected function");} |
- {2: return lua_error(lstate);} |
- {2:*/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- {8:}} |
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queu^E} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {18:/*} |
+ {18: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
+ {18: || lstate != lstate) {} |
+ {18: lua_pushliteral(lstate, "vim.schedule: expected function");} |
+ {18: return lua_error(lstate);} |
+ {18:*/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ {9:}} |
+ |
+ ]],
+ })
feed('re')
- screen:expect {
+ screen:expect({
grid = [[
- {2:/// Schedule Lua callback on main loop's event queu^e} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {2:/*} |
- {2: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
- {2: || lstate != lstate) {} |
- {2: lua_pushliteral(lstate, "vim.schedule: expected function");} |
- {2: return lua_error(lstate);} |
- {2:*/} |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- {8:}} |
- |
- ]],
- }
+ {18:/// Schedule Lua callback on main loop's event queu^e} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {18:/*} |
+ {18: if (lua_type(lstate, 1) != LUA_TFUNCTION} |
+ {18: || lstate != lstate) {} |
+ {18: lua_pushliteral(lstate, "vim.schedule: expected function");} |
+ {18: return lua_error(lstate);} |
+ {18:*/} |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ {9:}} |
+ |
+ ]],
+ })
end)
it('is updated with :sort', function()
@@ -372,83 +359,79 @@ describe('treesitter highlighting (C)', function()
local parser = vim.treesitter.get_parser(0, 'c')
vim.treesitter.highlighter.new(parser, { queries = { c = hl_query_c } })
end)
- screen:expect {
+ screen:expect({
grid = [[
- {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; |
- {3:bool} ext_widgets[kUIExtCount]; |
- {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { |
- ext_widgets[i] = true; |
- } |
- |
- {3:bool} inclusive = ui_override(); |
- {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { |
- {3:UI} *ui = uis[i]; |
- width = {5:MIN}(ui->width, width); |
- height = {5:MIN}(ui->height, height); |
- foo = {5:BAR}(ui->bazaar, bazaar); |
- {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- } |
- } |
- ^} |
- |
- ]],
- }
+ {6:int} width = {26:INT_MAX}, height = {26:INT_MAX}; |
+ {6:bool} ext_widgets[kUIExtCount]; |
+ {15:for} ({6:UIExtension} i = {26:0}; ({6:int})i < kUIExtCount; i++) { |
+ ext_widgets[i] = true; |
+ } |
+ |
+ {6:bool} inclusive = ui_override(); |
+ {15:for} ({6:size_t} i = {26:0}; i < ui_count; i++) { |
+ {6:UI} *ui = uis[i]; |
+ width = {26:MIN}(ui->width, width); |
+ height = {26:MIN}(ui->height, height); |
+ foo = {26:BAR}(ui->bazaar, bazaar); |
+ {15:for} ({6:UIExtension} j = {26:0}; ({6:int})j < kUIExtCount; j++) { |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ } |
+ } |
+ ^} |
+ |
+ ]],
+ })
feed ':sort<cr>'
- screen:expect {
+ screen:expect({
grid = [[
- ^ |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- {3:UI} *ui = uis[i]; |
- ext_widgets[i] = true; |
- foo = {5:BAR}(ui->bazaar, bazaar); |
- {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { |
- height = {5:MIN}(ui->height, height); |
- width = {5:MIN}(ui->width, width); |
- } |
- {3:bool} ext_widgets[kUIExtCount]; |
- {3:bool} inclusive = ui_override(); |
- {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { |
- {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { |
- {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; |
- } |*2
- {3:void} ui_refresh({3:void}) |
- :sort |
- ]],
- }
+ ^ |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ {6:UI} *ui = uis[i]; |
+ ext_widgets[i] = true; |
+ foo = {26:BAR}(ui->bazaar, bazaar); |
+ {15:for} ({6:UIExtension} j = {26:0}; ({6:int})j < kUIExtCount; j++) { |
+ height = {26:MIN}(ui->height, height); |
+ width = {26:MIN}(ui->width, width); |
+ } |
+ {6:bool} ext_widgets[kUIExtCount]; |
+ {6:bool} inclusive = ui_override(); |
+ {15:for} ({6:UIExtension} i = {26:0}; ({6:int})i < kUIExtCount; i++) { |
+ {15:for} ({6:size_t} i = {26:0}; i < ui_count; i++) { |
+ {6:int} width = {26:INT_MAX}, height = {26:INT_MAX}; |
+ } |*2
+ {6:void} ui_refresh({6:void}) |
+ :sort |
+ ]],
+ })
- feed 'u'
+ feed 'u:<esc>'
- screen:expect {
+ screen:expect({
grid = [[
- {3:int} width = {5:INT_MAX}, height = {5:INT_MAX}; |
- {3:bool} ext_widgets[kUIExtCount]; |
- {4:for} ({3:UIExtension} i = {5:0}; ({3:int})i < kUIExtCount; i++) { |
- ext_widgets[i] = true; |
- } |
- |
- {3:bool} inclusive = ui_override(); |
- {4:for} ({3:size_t} i = {5:0}; i < ui_count; i++) { |
- {3:UI} *ui = uis[i]; |
- width = {5:MIN}(ui->width, width); |
- height = {5:MIN}(ui->height, height); |
- foo = {5:BAR}(ui->bazaar, bazaar); |
- {4:for} ({3:UIExtension} j = {5:0}; ({3:int})j < kUIExtCount; j++) { |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- } |
- } |
- ^} |
- 19 changes; before #2 {MATCH:.*}|
- ]],
- }
+ {6:int} width = {26:INT_MAX}, height = {26:INT_MAX}; |
+ {6:bool} ext_widgets[kUIExtCount]; |
+ {15:for} ({6:UIExtension} i = {26:0}; ({6:int})i < kUIExtCount; i++) { |
+ ext_widgets[i] = true; |
+ } |
+ |
+ {6:bool} inclusive = ui_override(); |
+ {15:for} ({6:size_t} i = {26:0}; i < ui_count; i++) { |
+ {6:UI} *ui = uis[i]; |
+ width = {26:MIN}(ui->width, width); |
+ height = {26:MIN}(ui->height, height); |
+ foo = {26:BAR}(ui->bazaar, bazaar); |
+ {15:for} ({6:UIExtension} j = {26:0}; ({6:int})j < kUIExtCount; j++) { |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ } |
+ } |
+ ^} |
+ |
+ ]],
+ })
end)
it('supports with custom parser', function()
- screen:set_default_attr_ids {
- [1] = { bold = true, foreground = Screen.colors.SeaGreen4 },
- }
-
insert(test_text_c)
screen:expect {
@@ -488,28 +471,28 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(parser, { queries = { c = '(identifier) @type' } })
end)
- screen:expect {
+ screen:expect({
grid = [[
- int {1:width} = {1:INT_MAX}, {1:height} = {1:INT_MAX}; |
- bool {1:ext_widgets}[{1:kUIExtCount}]; |
- for (UIExtension {1:i} = 0; (int)i < kUIExtCount; i++) { |
- ext_widgets[i] = true; |
- } |
- |
- bool {1:inclusive} = {1:ui_override}(); |
- for (size_t {1:i} = 0; i < ui_count; i++) { |
- UI *{1:ui} = {1:uis}[{1:i}]; |
- width = MIN(ui->width, width); |
- height = MIN(ui->height, height); |
- foo = BAR(ui->bazaar, bazaar); |
- for (UIExtension {1:j} = 0; (int)j < kUIExtCount; j++) { |
- ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
- } |
- } |
- ^} |
- |
- ]],
- }
+ int {6:width} = {6:INT_MAX}, {6:height} = {6:INT_MAX}; |
+ bool {6:ext_widgets}[{6:kUIExtCount}]; |
+ for (UIExtension {6:i} = 0; (int)i < kUIExtCount; i++) { |
+ ext_widgets[i] = true; |
+ } |
+ |
+ bool {6:inclusive} = {6:ui_override}(); |
+ for (size_t {6:i} = 0; i < ui_count; i++) { |
+ UI *{6:ui} = {6:uis}[{6:i}]; |
+ width = MIN(ui->width, width); |
+ height = MIN(ui->height, height); |
+ foo = BAR(ui->bazaar, bazaar); |
+ for (UIExtension {6:j} = 0; (int)j < kUIExtCount; j++) { |
+ ext_widgets[j] &= (ui->ui_ext[j] || inclusive); |
+ } |
+ } |
+ ^} |
+ |
+ ]],
+ })
end)
it('supports injected languages', function()
@@ -567,18 +550,18 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
end)
- screen:expect {
+ screen:expect({
grid = [[
- {3:int} x = {5:INT_MAX}; |
- #define {5:READ_STRING}(x, y) ({3:char} *)read_string((x), ({3:size_t})(y)) |
- #define foo {3:void} main() { \ |
- {4:return} {5:42}; \ |
- } |
- ^ |
- {1:~ }|*11
- |
- ]],
- }
+ {6:int} x = {26:INT_MAX}; |
+ #define {26:READ_STRING}(x, y) ({6:char} *)read_string((x), ({6:size_t})(y)) |
+ #define foo {6:void} main() { \ |
+ {15:return} {26:42}; \ |
+ } |
+ ^ |
+ {1:~ }|*11
+ |
+ ]],
+ })
end)
it('supports highlighting with custom highlight groups', function()
@@ -595,27 +578,27 @@ describe('treesitter highlighting (C)', function()
-- This will change ONLY the literal strings to look like comments
-- The only literal string is the "vim.schedule: expected function" in this test.
exec_lua [[vim.cmd("highlight link @string.nonexistent_specializer comment")]]
- screen:expect {
+ screen:expect({
grid = [[
- {2:^/// Schedule Lua callback on main loop's event queue} |
- {3:static} {3:int} {11:nlua_schedule}({3:lua_State} *{3:const} lstate) |
- { |
- {4:if} ({11:lua_type}(lstate, {5:1}) != {5:LUA_TFUNCTION} |
- || {6:lstate} != {6:lstate}) { |
- {11:lua_pushliteral}(lstate, {2:"vim.schedule: expected function"}); |
- {4:return} {11:lua_error}(lstate); |
- } |
- |
- {7:LuaRef} cb = {11:nlua_ref}(lstate, {5:1}); |
- |
- multiqueue_put(main_loop.events, {11:nlua_schedule_event}, |
- {5:1}, ({3:void} *)({3:ptrdiff_t})cb); |
- {4:return} {5:0}; |
- } |
- {1:~ }|*2
- |
- ]],
- }
+ {18:^/// Schedule Lua callback on main loop's event queue} |
+ {6:static} {6:int} {25:nlua_schedule}({6:lua_State} *{6:const} lstate) |
+ { |
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {18:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|*2
+ |
+ ]],
+ })
screen:expect { unchanged = true }
end)
@@ -657,8 +640,8 @@ describe('treesitter highlighting (C)', function()
}
eq({
- { capture = 'constant', metadata = { priority = '101' }, lang = 'c' },
- { capture = 'type', metadata = {}, lang = 'c' },
+ { capture = 'constant', metadata = { priority = '101' }, lang = 'c', id = 14 },
+ { capture = 'type', metadata = {}, lang = 'c', id = 3 },
}, exec_lua [[ return vim.treesitter.get_captures_at_pos(0, 0, 2) ]])
end)
@@ -691,25 +674,25 @@ describe('treesitter highlighting (C)', function()
)
end)
- screen:expect {
+ screen:expect({
grid = [[
- {3:char}* x = {5:"Will somebody ever read this?"}; |
- ^ |
- {1:~ }|*15
- |
- ]],
- }
+ {6:char}* x = {26:"Will somebody ever read this?"}; |
+ ^ |
+ {1:~ }|*15
+ |
+ ]],
+ })
-- clearing specialization reactivates fallback
command [[ hi clear @foo.bar ]]
- screen:expect {
+ screen:expect({
grid = [[
- {5:char}* x = {5:"Will somebody ever read this?"}; |
- ^ |
- {1:~ }|*15
- |
- ]],
- }
+ {26:char}* x = {26:"Will somebody ever read this?"}; |
+ ^ |
+ {1:~ }|*15
+ |
+ ]],
+ })
end
)
@@ -740,27 +723,27 @@ describe('treesitter highlighting (C)', function()
})
end)
- screen:expect {
+ screen:expect({
grid = [[
- /// Schedule Lua callback on main loop's event queue |
- {4:R} int nlua_schedule(lua_State *const ) |
- { |
- if (lua_type(, 1) != LUA_TFUNCTION |
- || != ) { |
- lua_pushliteral(, "vim.schedule: expected function"); |
- return lua_error(); |
- } |
- |
- LuaRef cb = nlua_ref(, 1); |
- |
- {11:V}(main_loop.events, nlua_schedule_event, |
- 1, (void *)(ptrdiff_t)cb); |
- return 0; |
- ^} |
- {1:~ }|*2
- |
- ]],
- }
+ /// Schedule Lua callback on main loop's event queue |
+ {15:R} int nlua_schedule(lua_State *const ) |
+ { |
+ if (lua_type(, 1) != LUA_TFUNCTION |
+ || != ) { |
+ lua_pushliteral(, "vim.schedule: expected function"); |
+ return lua_error(); |
+ } |
+ |
+ LuaRef cb = nlua_ref(, 1); |
+ |
+ {25:V}(main_loop.events, nlua_schedule_event, |
+ 1, (void *)(ptrdiff_t)cb); |
+ return 0; |
+ ^} |
+ {1:~ }|*2
+ |
+ ]],
+ })
end)
it('@foo.bar groups has the correct fallback behavior', function()
@@ -801,16 +784,16 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
end)
- screen:expect {
+ screen:expect({
grid = [[
- {5:int x = 4;} |
- {5:int y = 5;} |
- {5:int z = 6;} |
- ^ |
- {1:~ }|*13
- |
- ]],
- }
+ {26:int x = 4;} |
+ {26:int y = 5;} |
+ {26:int z = 6;} |
+ ^ |
+ {1:~ }|*13
+ |
+ ]],
+ })
end)
it('gives higher priority to more specific captures #27895', function()
@@ -830,14 +813,52 @@ describe('treesitter highlighting (C)', function()
vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
end)
- screen:expect {
+ screen:expect({
grid = [[
- void foo(int {4:*}{11:bar}); |
- ^ |
- {1:~ }|*15
- |
- ]],
- }
+ void foo(int {15:*}{25:bar}); |
+ ^ |
+ {1:~ }|*15
+ |
+ ]],
+ })
+ end)
+
+ it('highlights applied to first line of closed fold', function()
+ insert(hl_text_c)
+ exec_lua(function()
+ vim.treesitter.query.set('c', 'highlights', hl_query_c)
+ vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c'))
+ end)
+ feed('ggjzfj')
+ command('set foldtext=')
+ screen:add_extra_attr_ids({
+ [100] = {
+ bold = true,
+ background = Screen.colors.LightGray,
+ foreground = Screen.colors.SeaGreen4,
+ },
+ [101] = { background = Screen.colors.LightGray, foreground = Screen.colors.DarkCyan },
+ })
+ screen:expect({
+ grid = [[
+ {18:/// Schedule Lua callback on main loop's event queue} |
+ {100:^static}{13: }{100:int}{13: }{101:nlua_schedule}{13:(}{100:lua_State}{13: *}{100:const}{13: lstate)················}|
+ {15:if} ({25:lua_type}(lstate, {26:1}) != {26:LUA_TFUNCTION} |
+ || {19:lstate} != {19:lstate}) { |
+ {25:lua_pushliteral}(lstate, {26:"vim.schedule: expected function"}); |
+ {15:return} {25:lua_error}(lstate); |
+ } |
+ |
+ {29:LuaRef} cb = {25:nlua_ref}(lstate, {26:1}); |
+ |
+ multiqueue_put(main_loop.events, {25:nlua_schedule_event}, |
+ {26:1}, ({6:void} *)({6:ptrdiff_t})cb); |
+ {15:return} {26:0}; |
+ } |
+ {1:~ }|*3
+ |
+ ]],
+ })
end)
end)
@@ -847,13 +868,6 @@ describe('treesitter highlighting (lua)', function()
before_each(function()
clear()
screen = Screen.new(65, 18)
- screen:set_default_attr_ids {
- [1] = { bold = true, foreground = Screen.colors.Blue },
- [2] = { foreground = Screen.colors.DarkCyan },
- [3] = { foreground = Screen.colors.Magenta },
- [4] = { foreground = Screen.colors.SlateBlue },
- [5] = { bold = true, foreground = Screen.colors.Brown },
- }
end)
it('supports language injections', function()
@@ -867,15 +881,15 @@ describe('treesitter highlighting (lua)', function()
vim.treesitter.start()
end)
- screen:expect {
+ screen:expect({
grid = [[
- {5:local} {2:ffi} {5:=} {4:require(}{3:'ffi'}{4:)} |
- {2:ffi}{4:.}{2:cdef}{4:(}{3:"}{4:int}{3: }{4:(}{5:*}{3:fun}{4:)(int,}{3: }{4:char}{3: }{5:*}{4:);}{3:"}{4:)} |
- ^ |
- {1:~ }|*14
- |
- ]],
- }
+ {15:local} {25:ffi} {15:=} {16:require(}{26:'ffi'}{16:)} |
+ {25:ffi}{16:.}{25:cdef}{16:(}{26:"}{16:int}{26: }{16:(}{15:*}{26:fun}{16:)(int,}{26: }{16:char}{26: }{15:*}{16:);}{26:"}{16:)} |
+ ^ |
+ {1:~ }|*14
+ |
+ ]],
+ })
end)
end)
@@ -885,16 +899,6 @@ describe('treesitter highlighting (help)', function()
before_each(function()
clear()
screen = Screen.new(40, 6)
- screen:set_default_attr_ids {
- [1] = { foreground = Screen.colors.Blue1 },
- [2] = { bold = true, foreground = Screen.colors.Blue1 },
- [3] = { bold = true, foreground = Screen.colors.Brown },
- [4] = { foreground = Screen.colors.Cyan4 },
- [5] = { foreground = Screen.colors.Magenta1 },
- title = { bold = true, foreground = Screen.colors.Magenta1 },
- h1_delim = { nocombine = true, underdouble = true },
- h2_delim = { nocombine = true, underline = true },
- }
end)
it('defaults in vimdoc/highlights.scm', function()
@@ -918,13 +922,18 @@ describe('treesitter highlighting (help)', function()
vim.treesitter.start()
end)
+ screen:add_extra_attr_ids({
+ [100] = { nocombine = true, underdouble = true },
+ [101] = { foreground = Screen.colors.Fuchsia, bold = true },
+ [102] = { underline = true, nocombine = true },
+ })
screen:expect({
grid = [[
- {h1_delim:^========================================}|
- {title:NVIM DOCUMENTATION} |
+ {100:^========================================}|
+ {101:NVIM DOCUMENTATION} |
|
- {h2_delim:----------------------------------------}|
- {title:ABOUT NVIM} |
+ {102:----------------------------------------}|
+ {101:ABOUT NVIM} |
|
]],
})
@@ -943,42 +952,42 @@ describe('treesitter highlighting (help)', function()
vim.treesitter.start()
end)
- screen:expect {
+ screen:expect({
grid = [[
- {1:>}{3:ruby} |
- {1: -- comment} |
- {1: local this_is = 'actually_lua'} |
- {1:<} |
- ^ |
- |
- ]],
- }
+ {18:>}{15:ruby} |
+ {18: -- comment} |
+ {18: local this_is = 'actually_lua'} |
+ {18:<} |
+ ^ |
+ |
+ ]],
+ })
n.api.nvim_buf_set_text(0, 0, 1, 0, 5, { 'lua' })
- screen:expect {
+ screen:expect({
grid = [[
- {1:>}{3:lua} |
- {1: -- comment} |
- {1: }{3:local}{1: }{4:this_is}{1: }{3:=}{1: }{5:'actually_lua'} |
- {1:<} |
- ^ |
- |
- ]],
- }
+ {18:>}{15:lua} |
+ {18: -- comment} |
+ {18: }{15:local}{18: }{25:this_is}{18: }{15:=}{18: }{26:'actually_lua'} |
+ {18:<} |
+ ^ |
+ |
+ ]],
+ })
n.api.nvim_buf_set_text(0, 0, 1, 0, 4, { 'ruby' })
- screen:expect {
+ screen:expect({
grid = [[
- {1:>}{3:ruby} |
- {1: -- comment} |
- {1: local this_is = 'actually_lua'} |
- {1:<} |
- ^ |
- |
- ]],
- }
+ {18:>}{15:ruby} |
+ {18: -- comment} |
+ {18: local this_is = 'actually_lua'} |
+ {18:<} |
+ ^ |
+ |
+ ]],
+ })
end)
it('correctly redraws injections subpriorities', function()
@@ -1003,16 +1012,16 @@ describe('treesitter highlighting (help)', function()
vim.treesitter.highlighter.new(parser)
end)
- screen:expect {
+ screen:expect({
grid = [=[
- {3:local} {4:s} {3:=} {5:[[} |
- {5: }{3:local}{5: }{4:also}{5: }{3:=}{5: }{4:lua} |
- {5:]]} |
- ^ |
- {2:~ }|
- |
- ]=],
- }
+ {15:local} {25:s} {15:=} {26:[[} |
+ {26: }{15:local}{26: }{25:also}{26: }{15:=}{26: }{25:lua} |
+ {26:]]} |
+ ^ |
+ {1:~ }|
+ |
+ ]=],
+ })
end)
end)
@@ -1022,12 +1031,6 @@ describe('treesitter highlighting (nested injections)', function()
before_each(function()
clear()
screen = Screen.new(80, 7)
- screen:set_default_attr_ids {
- [1] = { foreground = Screen.colors.SlateBlue },
- [2] = { bold = true, foreground = Screen.colors.Brown },
- [3] = { foreground = Screen.colors.Cyan4 },
- [4] = { foreground = Screen.colors.Fuchsia },
- }
end)
it('correctly redraws nested injections (GitHub #25252)', function()
@@ -1054,32 +1057,32 @@ vim.cmd([[
-- invalidate the language tree
feed('ggi--[[<ESC>04x')
- screen:expect {
+ screen:expect({
grid = [[
- {2:^function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
- |
- {2:local} {3:lorem} {2:=} {1:{} |
- {3:ipsum} {2:=} {1:{},} |
- {3:bar} {2:=} {1:{},} |
- {1:}} |
- |
- ]],
- }
+ {15:^function} {25:foo}{16:()} {16:print(}{26:"Lua!"}{16:)} {15:end} |
+ |
+ {15:local} {25:lorem} {15:=} {16:{} |
+ {25:ipsum} {15:=} {16:{},} |
+ {25:bar} {15:=} {16:{},} |
+ {16:}} |
+ |
+ ]],
+ })
-- spam newline insert/delete to invalidate Lua > Vim > Lua region
feed('3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0')
- screen:expect {
+ screen:expect({
grid = [[
- {2:function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
- |
- {2:local} {3:lorem} {2:=} {1:{} |
- ^ {3:ipsum} {2:=} {1:{},} |
- {3:bar} {2:=} {1:{},} |
- {1:}} |
- |
- ]],
- }
+ {15:function} {25:foo}{16:()} {16:print(}{26:"Lua!"}{16:)} {15:end} |
+ |
+ {15:local} {25:lorem} {15:=} {16:{} |
+ ^ {25:ipsum} {15:=} {16:{},} |
+ {25:bar} {15:=} {16:{},} |
+ {16:}} |
+ |
+ ]],
+ })
end)
end)
@@ -1108,7 +1111,7 @@ describe('treesitter highlighting (markdown)', function()
})
screen:expect({
grid = [[
- {25:[}{100:This link text}{25:](}{101:https://example.com}{25:)} is|
+ {100:[This link text](}{101:https://example.com}{100:)} is|
a hyperlink^. |
{1:~ }|*3
|
@@ -1156,20 +1159,6 @@ it('starting and stopping treesitter highlight in init.lua works #29541', functi
eq('', api.nvim_get_vvar('errmsg'))
local screen = Screen.new(65, 18)
- screen:set_default_attr_ids {
- [1] = { bold = true, foreground = Screen.colors.Blue1 },
- [2] = { foreground = Screen.colors.Blue1 },
- [3] = { bold = true, foreground = Screen.colors.SeaGreen4 },
- [4] = { bold = true, foreground = Screen.colors.Brown },
- [5] = { foreground = Screen.colors.Magenta },
- [6] = { foreground = Screen.colors.Red },
- [7] = { bold = true, foreground = Screen.colors.SlateBlue },
- [8] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
- [9] = { foreground = Screen.colors.Magenta, background = Screen.colors.Red },
- [10] = { foreground = Screen.colors.Red, background = Screen.colors.Red },
- [11] = { foreground = Screen.colors.Cyan4 },
- }
-
fn.setreg('r', hl_text_c)
feed('i<C-R><C-O>r<Esc>gg')
-- legacy syntax highlighting is used
diff --git a/test/functional/treesitter/inspect_tree_spec.lua b/test/functional/treesitter/inspect_tree_spec.lua
index 1f7d15cc96..68622140e4 100644
--- a/test/functional/treesitter/inspect_tree_spec.lua
+++ b/test/functional/treesitter/inspect_tree_spec.lua
@@ -120,14 +120,17 @@ describe('vim.treesitter.inspect_tree', function()
end)
it('updates source and tree buffer windows and closes them correctly', function()
+ local name = t.tmpname()
+ n.command('edit ' .. name)
insert([[
print()
]])
+ n.command('set filetype=lua | write')
-- setup two windows for the source buffer
exec_lua(function()
_G.source_win = vim.api.nvim_get_current_win()
- vim.api.nvim_open_win(0, false, {
+ _G.source_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
@@ -135,40 +138,103 @@ describe('vim.treesitter.inspect_tree', function()
-- setup three windows for the tree buffer
exec_lua(function()
- vim.treesitter.start(0, 'lua')
vim.treesitter.inspect_tree()
_G.tree_win = vim.api.nvim_get_current_win()
- _G.tree_win_copy_1 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win2 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
- _G.tree_win_copy_2 = vim.api.nvim_open_win(0, false, {
+ _G.tree_win3 = vim.api.nvim_open_win(0, false, {
win = 0,
split = 'left',
})
end)
- -- close original source window
- exec_lua('vim.api.nvim_win_close(source_win, false)')
+ -- close original source window without closing tree views
+ exec_lua('vim.api.nvim_set_current_win(source_win)')
+ feed(':quit<CR>')
+ eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win2)'))
+ eq(true, exec_lua('return vim.api.nvim_win_is_valid(tree_win3)'))
-- navigates correctly to the remaining source buffer window
+ exec_lua('vim.api.nvim_set_current_win(tree_win)')
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close original tree window
exec_lua(function()
- vim.api.nvim_set_current_win(_G.tree_win_copy_1)
+ vim.api.nvim_set_current_win(_G.tree_win2)
vim.api.nvim_win_close(_G.tree_win, false)
end)
-- navigates correctly to the remaining source buffer window
feed('<CR>')
eq('', n.api.nvim_get_vvar('errmsg'))
+ eq(true, exec_lua('return vim.api.nvim_get_current_win() == source_win2'))
-- close source buffer window and all remaining tree windows
- t.pcall_err(exec_lua, 'vim.api.nvim_win_close(0, false)')
+ n.expect_exit(n.command, 'quit')
+ end)
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_1)'))
- eq(false, exec_lua('return vim.api.nvim_win_is_valid(tree_win_copy_2)'))
+ it('shows which nodes are missing', function()
+ insert([[
+ int main() {
+ if (a.) {
+ // ^ MISSING field_identifier here
+ if (1) d()
+ // ^ MISSING ";" here
+ }
+ }
+ ]])
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'c')
+ vim.treesitter.inspect_tree()
+ end)
+ feed('a')
+
+ expect_tree [[
+ (translation_unit ; [0, 0] - [8, 0]
+ (function_definition ; [0, 0] - [6, 1]
+ type: (primitive_type) ; [0, 0] - [0, 3]
+ declarator: (function_declarator ; [0, 4] - [0, 10]
+ declarator: (identifier) ; [0, 4] - [0, 8]
+ parameters: (parameter_list ; [0, 8] - [0, 10]
+ "(" ; [0, 8] - [0, 9]
+ ")")) ; [0, 9] - [0, 10]
+ body: (compound_statement ; [0, 11] - [6, 1]
+ "{" ; [0, 11] - [0, 12]
+ (if_statement ; [1, 4] - [5, 5]
+ "if" ; [1, 4] - [1, 6]
+ condition: (parenthesized_expression ; [1, 7] - [1, 11]
+ "(" ; [1, 7] - [1, 8]
+ (field_expression ; [1, 8] - [1, 10]
+ argument: (identifier) ; [1, 8] - [1, 9]
+ operator: "." ; [1, 9] - [1, 10]
+ field: (MISSING field_identifier)) ; [1, 10] - [1, 10]
+ ")") ; [1, 10] - [1, 11]
+ consequence: (compound_statement ; [1, 12] - [5, 5]
+ "{" ; [1, 12] - [1, 13]
+ (comment) ; [2, 4] - [2, 41]
+ (if_statement ; [3, 8] - [4, 36]
+ "if" ; [3, 8] - [3, 10]
+ condition: (parenthesized_expression ; [3, 11] - [3, 14]
+ "(" ; [3, 11] - [3, 12]
+ (number_literal) ; [3, 12] - [3, 13]
+ ")") ; [3, 13] - [3, 14]
+ consequence: (expression_statement ; [3, 15] - [4, 36]
+ (call_expression ; [3, 15] - [3, 18]
+ function: (identifier) ; [3, 15] - [3, 16]
+ arguments: (argument_list ; [3, 16] - [3, 18]
+ "(" ; [3, 16] - [3, 17]
+ ")")) ; [3, 17] - [3, 18]
+ (comment) ; [4, 8] - [4, 36]
+ (MISSING ";"))) ; [4, 36] - [4, 36]
+ "}")) ; [5, 4] - [5, 5]
+ "}"))) ; [6, 0] - [6, 1]
+ ]]
end)
end)
diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua
index 120a15d7f9..a93b1063a1 100644
--- a/test/functional/treesitter/language_spec.lua
+++ b/test/functional/treesitter/language_spec.lua
@@ -117,6 +117,7 @@ describe('treesitter language API', function()
'<node translation_unit>',
exec_lua(function()
local langtree = vim.treesitter.get_parser(0, 'c')
+ langtree:parse()
local tree = langtree:tree_for_range({ 1, 3, 1, 3 })
return tostring(tree:root())
end)
@@ -133,6 +134,7 @@ describe('treesitter language API', function()
'<node translation_unit>',
exec_lua(function()
local langtree = vim.treesitter.get_parser(0, 'c')
+ langtree:parse()
local tree = langtree:tree_for_range({ 10, 10, 10, 10 })
return tostring(tree:root())
end)
@@ -149,6 +151,7 @@ describe('treesitter language API', function()
'<node primitive_type>',
exec_lua(function()
local langtree = vim.treesitter.get_parser(0, 'c')
+ langtree:parse()
local node = langtree:named_node_for_range({ 1, 3, 1, 3 })
return tostring(node)
end)
@@ -160,6 +163,7 @@ describe('treesitter language API', function()
exec_lua(function()
_G.langtree = vim.treesitter.get_parser(0, 'lua')
+ _G.langtree:parse()
_G.node = _G.langtree:node_for_range({ 0, 3, 0, 3 })
end)
diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua
index c87a56b160..235bf7861c 100644
--- a/test/functional/treesitter/node_spec.lua
+++ b/test/functional/treesitter/node_spec.lua
@@ -20,6 +20,7 @@ describe('treesitter node API', function()
insert('F')
exec_lua(function()
vim.treesitter.start(0, 'lua')
+ vim.treesitter.get_parser(0):parse()
vim.treesitter.get_node():tree()
vim.treesitter.get_node():tree()
collectgarbage()
@@ -45,6 +46,7 @@ describe('treesitter node API', function()
-- this buffer doesn't have filetype set!
insert('local foo = function() end')
exec_lua(function()
+ vim.treesitter.get_parser(0, 'lua'):parse()
_G.node = vim.treesitter.get_node({
bufnr = 0,
pos = { 0, 6 }, -- on "foo"
@@ -161,32 +163,6 @@ describe('treesitter node API', function()
eq(3, lua_eval('child:byte_length()'))
end)
- it('child_containing_descendant() works', function()
- insert([[
- int main() {
- int x = 3;
- }]])
-
- exec_lua(function()
- local tree = vim.treesitter.get_parser(0, 'c'):parse()[1]
- _G.root = tree:root()
- _G.main = _G.root:child(0)
- _G.body = _G.main:child(2)
- _G.statement = _G.body:child(1)
- _G.declarator = _G.statement:child(1)
- _G.value = _G.declarator:child(1)
- end)
-
- eq(lua_eval('main:type()'), lua_eval('root:child_containing_descendant(value):type()'))
- eq(lua_eval('body:type()'), lua_eval('main:child_containing_descendant(value):type()'))
- eq(lua_eval('statement:type()'), lua_eval('body:child_containing_descendant(value):type()'))
- eq(
- lua_eval('declarator:type()'),
- lua_eval('statement:child_containing_descendant(value):type()')
- )
- eq(vim.NIL, lua_eval('declarator:child_containing_descendant(value)'))
- end)
-
it('child_with_descendant() works', function()
insert([[
int main() {
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 2f8d204d36..eb4651a81d 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -1,5 +1,6 @@
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
+local ts_t = require('test.functional.treesitter.testutil')
local clear = n.clear
local dedent = t.dedent
@@ -8,6 +9,8 @@ local insert = n.insert
local exec_lua = n.exec_lua
local pcall_err = t.pcall_err
local feed = n.feed
+local run_query = ts_t.run_query
+local assert_alive = n.assert_alive
describe('treesitter parser API', function()
before_each(function()
@@ -88,6 +91,197 @@ describe('treesitter parser API', function()
eq(true, exec_lua('return parser:parse()[1] == tree2'))
end)
+ it('parses buffer asynchronously', function()
+ insert([[
+ int main() {
+ int x = 3;
+ }]])
+
+ exec_lua(function()
+ _G.parser = vim.treesitter.get_parser(0, 'c')
+ _G.lang = vim.treesitter.language.inspect('c')
+ _G.parser:parse(nil, function(_, trees)
+ _G.tree = trees[1]
+ _G.root = _G.tree:root()
+ end)
+ vim.wait(100, function() end)
+ end)
+
+ eq('<tree>', exec_lua('return tostring(tree)'))
+ eq('<node translation_unit>', exec_lua('return tostring(root)'))
+ eq({ 0, 0, 3, 0 }, exec_lua('return {root:range()}'))
+
+ eq(1, exec_lua('return root:child_count()'))
+ exec_lua('child = root:child(0)')
+ eq('<node function_definition>', exec_lua('return tostring(child)'))
+ eq({ 0, 0, 2, 1 }, exec_lua('return {child:range()}'))
+
+ eq('function_definition', exec_lua('return child:type()'))
+ eq(true, exec_lua('return child:named()'))
+ eq('number', type(exec_lua('return child:symbol()')))
+ eq(true, exec_lua('return lang.symbols[child:type()]'))
+
+ exec_lua('anon = root:descendant_for_range(0,8,0,9)')
+ eq('(', exec_lua('return anon:type()'))
+ eq(false, exec_lua('return anon:named()'))
+ eq('number', type(exec_lua('return anon:symbol()')))
+ eq(false, exec_lua([=[return lang.symbols[string.format('"%s"', anon:type())]]=]))
+
+ exec_lua('descendant = root:descendant_for_range(1,2,1,12)')
+ eq('<node declaration>', exec_lua('return tostring(descendant)'))
+ eq({ 1, 2, 1, 12 }, exec_lua('return {descendant:range()}'))
+ eq(
+ '(declaration type: (primitive_type) declarator: (init_declarator declarator: (identifier) value: (number_literal)))',
+ exec_lua('return descendant:sexpr()')
+ )
+
+ feed('2G7|ay')
+ exec_lua(function()
+ _G.parser:parse(nil, function(_, trees)
+ _G.tree2 = trees[1]
+ _G.root2 = _G.tree2:root()
+ _G.descendant2 = _G.root2:descendant_for_range(1, 2, 1, 13)
+ end)
+ vim.wait(100, function() end)
+ end)
+ eq(false, exec_lua('return tree2 == tree1'))
+ eq(false, exec_lua('return root2 == root'))
+ eq('<node declaration>', exec_lua('return tostring(descendant2)'))
+ eq({ 1, 2, 1, 13 }, exec_lua('return {descendant2:range()}'))
+
+ eq(true, exec_lua('return child == child'))
+ -- separate lua object, but represents same node
+ eq(true, exec_lua('return child == root:child(0)'))
+ eq(false, exec_lua('return child == descendant2'))
+ eq(false, exec_lua('return child == nil'))
+ eq(false, exec_lua('return child == tree'))
+
+ eq('string', exec_lua('return type(child:id())'))
+ eq(true, exec_lua('return child:id() == child:id()'))
+ -- separate lua object, but represents same node
+ eq(true, exec_lua('return child:id() == root:child(0):id()'))
+ eq(false, exec_lua('return child:id() == descendant2:id()'))
+ eq(false, exec_lua('return child:id() == nil'))
+ eq(false, exec_lua('return child:id() == tree'))
+
+ -- unchanged buffer: return the same tree
+ eq(true, exec_lua('return parser:parse()[1] == tree2'))
+ end)
+
+ it('does not crash when editing large files', function()
+ insert([[printf("%s", "some text");]])
+ feed('yy49999p')
+
+ exec_lua(function()
+ _G.parser = vim.treesitter.get_parser(0, 'c')
+ _G.done = false
+ vim.treesitter.start(0, 'c')
+ _G.parser:parse(nil, function()
+ _G.done = true
+ end)
+ while not _G.done do
+ -- Busy wait until async parsing has completed
+ vim.wait(100, function() end)
+ end
+ end)
+
+ eq(true, exec_lua([[return done]]))
+ exec_lua(function()
+ vim.api.nvim_input('Lxj')
+ end)
+ exec_lua(function()
+ vim.api.nvim_input('xj')
+ end)
+ exec_lua(function()
+ vim.api.nvim_input('xj')
+ end)
+ assert_alive()
+ end)
+
+ it('resets parsing state on tree changes', function()
+ insert([[vim.api.nvim_set_hl(0, 'test2', { bg = 'green' })]])
+ feed('yy1000p')
+
+ exec_lua(function()
+ vim.cmd('set ft=lua')
+
+ vim.treesitter.start(0)
+ local parser = assert(vim.treesitter.get_parser(0))
+
+ parser:parse(true, function() end)
+ vim.api.nvim_buf_set_lines(0, 1, -1, false, {})
+ parser:parse(true)
+ end)
+ end)
+
+ it('resets when buffer was editing during an async parse', function()
+ insert([[printf("%s", "some text");]])
+ feed('yy49999p')
+ feed('gg4jO// Comment<Esc>')
+
+ exec_lua(function()
+ _G.parser = vim.treesitter.get_parser(0, 'c')
+ _G.done = false
+ vim.treesitter.start(0, 'c')
+ _G.parser:parse(nil, function()
+ _G.done = true
+ end)
+ end)
+
+ exec_lua(function()
+ vim.api.nvim_input('ggdj')
+ end)
+
+ eq(false, exec_lua([[return done]]))
+ exec_lua(function()
+ while not _G.done do
+ -- Busy wait until async parsing finishes
+ vim.wait(100, function() end)
+ end
+ end)
+ eq(true, exec_lua([[return done]]))
+ eq('comment', exec_lua([[return parser:parse()[1]:root():named_child(2):type()]]))
+ eq({ 2, 0, 2, 10 }, exec_lua([[return {parser:parse()[1]:root():named_child(2):range()}]]))
+ end)
+
+ it('handles multiple async parse calls', function()
+ insert([[printf("%s", "some text");]])
+ feed('yy49999p')
+
+ exec_lua(function()
+ -- Spy on vim.schedule
+ local schedule = vim.schedule
+ vim.schedule = function(fn)
+ _G.schedules = _G.schedules + 1
+ schedule(fn)
+ end
+ _G.schedules = 0
+ _G.parser = vim.treesitter.get_parser(0, 'c')
+ for i = 1, 5 do
+ _G['done' .. i] = false
+ _G.parser:parse(nil, function()
+ _G['done' .. i] = true
+ end)
+ end
+ schedule(function()
+ _G.schedules_snapshot = _G.schedules
+ end)
+ end)
+
+ eq(2, exec_lua([[return schedules_snapshot]]))
+ eq(
+ { false, false, false, false, false },
+ exec_lua([[return { done1, done2, done3, done4, done5 }]])
+ )
+ exec_lua(function()
+ while not _G.done1 do
+ -- Busy wait until async parsing finishes
+ vim.wait(100, function() end)
+ end
+ end)
+ eq({ true, true, true, true, true }, exec_lua([[return { done1, done2, done3, done4, done5 }]]))
+ end)
+
local test_text = [[
void ui_refresh(void)
{
@@ -310,6 +504,15 @@ end]]
eq({ 0, 0, 0, 13 }, ret)
end)
+ it('can run async parses with string parsers', function()
+ local ret = exec_lua(function()
+ local parser = vim.treesitter.get_string_parser('int foo = 42;', 'c')
+ return { parser:parse(nil, function() end)[1]:root():range() }
+ end)
+
+ eq({ 0, 0, 0, 13 }, ret)
+ end)
+
it('allows to run queries with string parsers', function()
local txt = [[
int foo = 42;
@@ -430,7 +633,7 @@ int x = INT_MAX;
}, get_ranges())
n.feed('7ggI//<esc>')
- exec_lua([[parser:parse({6, 7})]])
+ exec_lua([[parser:parse({5, 6})]])
eq('table', exec_lua('return type(parser:children().c)'))
eq(2, exec_lua('return #parser:children().c:trees()'))
eq({
@@ -644,6 +847,109 @@ print()
end)
end)
+ describe('trim! directive', function()
+ it('can trim all whitespace', function()
+ -- luacheck: push ignore 611 613
+ insert([=[
+ print([[
+
+ f
+ helllo
+ there
+ asdf
+ asdfassd
+
+
+
+ ]])
+ print([[
+
+
+
+ ]])
+
+ print([[]])
+
+ print([[
+ ]])
+
+ print([[ hello 😃 ]])
+ ]=])
+ -- luacheck: pop
+
+ local query_text = [[
+ ; query
+ ((string_content) @str
+ (#trim! @str 1 1 1 1))
+ ]]
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'lua')
+ end)
+
+ eq({
+ { 'str', { 2, 12, 6, 10 } },
+ { 'str', { 11, 10, 11, 10 } },
+ { 'str', { 17, 10, 17, 10 } },
+ { 'str', { 19, 10, 19, 10 } },
+ { 'str', { 22, 15, 22, 25 } },
+ }, run_query('lua', query_text))
+ end)
+
+ it('trims only empty lines by default (backwards compatible)', function()
+ insert(dedent [[
+ ## Heading
+
+ With some text
+
+ ## And another
+
+ With some more here]])
+
+ local query_text = [[
+ ; query
+ ((section) @fold
+ (#trim! @fold))
+ ]]
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'markdown')
+ end)
+
+ eq({
+ { 'fold', { 0, 0, 2, 14 } },
+ { 'fold', { 4, 0, 6, 19 } },
+ }, run_query('markdown', query_text))
+ end)
+
+ it('can trim lines', function()
+ insert(dedent [[
+ - Fold list
+ - Fold list
+ - Fold list
+ - Fold list
+ - Fold list
+ - Fold list
+ ]])
+
+ local query_text = [[
+ ; query
+ ((list_item
+ (list)) @fold
+ (#trim! @fold 1 1 1 1))
+ ]]
+
+ exec_lua(function()
+ vim.treesitter.start(0, 'markdown')
+ end)
+
+ eq({
+ { 'fold', { 0, 0, 4, 13 } },
+ { 'fold', { 1, 2, 3, 15 } },
+ }, run_query('markdown', query_text))
+ end)
+ end)
+
it('tracks the root range properly (#22911)', function()
insert([[
int main() {
@@ -659,32 +965,19 @@ print()
vim.treesitter.start(0, 'c')
end)
- local function run_query()
- return exec_lua(function()
- local query = vim.treesitter.query.parse('c', query0)
- local parser = vim.treesitter.get_parser()
- local tree = parser:parse()[1]
- local res = {}
- for id, node in query:iter_captures(tree:root()) do
- table.insert(res, { query.captures[id], node:range() })
- end
- return res
- end)
- end
-
eq({
- { 'function', 0, 0, 2, 1 },
- { 'declaration', 1, 2, 1, 12 },
- }, run_query())
+ { 'function', { 0, 0, 2, 1 } },
+ { 'declaration', { 1, 2, 1, 12 } },
+ }, run_query('c', query0))
n.command 'normal ggO'
insert('int a;')
eq({
- { 'declaration', 0, 0, 0, 6 },
- { 'function', 1, 0, 3, 1 },
- { 'declaration', 2, 2, 2, 12 },
- }, run_query())
+ { 'declaration', { 0, 0, 0, 6 } },
+ { 'function', { 1, 0, 3, 1 } },
+ { 'declaration', { 2, 2, 2, 12 } },
+ }, run_query('c', query0))
end)
it('handles ranges when source is a multiline string (#20419)', function()
@@ -858,11 +1151,13 @@ print()
feed(':set ft=help<cr>')
exec_lua(function()
- vim.treesitter.get_parser(0, 'vimdoc', {
- injections = {
- vimdoc = '((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))',
- },
- })
+ vim.treesitter
+ .get_parser(0, 'vimdoc', {
+ injections = {
+ vimdoc = '((codeblock (language) @injection.language (code) @injection.content) (#set! injection.include-children))',
+ },
+ })
+ :parse()
end)
end)
diff --git a/test/functional/treesitter/query_spec.lua b/test/functional/treesitter/query_spec.lua
index 634f8af83d..6db0ffe5a0 100644
--- a/test/functional/treesitter/query_spec.lua
+++ b/test/functional/treesitter/query_spec.lua
@@ -86,7 +86,7 @@ void ui_refresh(void)
local before = vim.api.nvim__stats().ts_query_parse_count
collectgarbage('stop')
for _ = 1, _n, 1 do
- vim.treesitter.query.parse('c', long_query, _n)
+ vim.treesitter.query.parse('c', long_query)
end
collectgarbage('restart')
collectgarbage('collect')
@@ -96,8 +96,39 @@ void ui_refresh(void)
end
eq(1, q(1))
- -- cache is cleared by garbage collection even if valid "cquery" reference is kept around
- eq(1, q(100))
+ -- cache is retained even after garbage collection
+ eq(0, q(100))
+ end)
+
+ it('cache is cleared upon runtimepath changes, or setting query manually', function()
+ ---@return number
+ exec_lua(function()
+ _G.query_parse_count = _G.query_parse_count or 0
+ local parse = vim.treesitter.query.parse
+ vim.treesitter.query.parse = function(...)
+ _G.query_parse_count = _G.query_parse_count + 1
+ return parse(...)
+ end
+ end)
+
+ local function q(_n)
+ return exec_lua(function()
+ for _ = 1, _n, 1 do
+ vim.treesitter.query.get('c', 'highlights')
+ end
+ return _G.query_parse_count
+ end)
+ end
+
+ eq(1, q(10))
+ exec_lua(function()
+ vim.opt.rtp:prepend('/another/dir')
+ end)
+ eq(2, q(100))
+ exec_lua(function()
+ vim.treesitter.query.set('c', 'highlights', [[; test]])
+ end)
+ eq(3, q(100))
end)
it('supports query and iter by capture (iter_captures)', function()
@@ -781,6 +812,34 @@ void ui_refresh(void)
)
end)
+ it('supports "; extends" modeline in custom queries', function()
+ insert('int zeero = 0;')
+ local result = exec_lua(function()
+ vim.treesitter.query.set(
+ 'c',
+ 'highlights',
+ [[; extends
+ (identifier) @spell]]
+ )
+ local query = vim.treesitter.query.get('c', 'highlights')
+ local parser = vim.treesitter.get_parser(0, 'c')
+ local root = parser:parse()[1]:root()
+ local res = {}
+ for id, node in query:iter_captures(root, 0) do
+ table.insert(res, { query.captures[id], vim.treesitter.get_node_text(node, 0) })
+ end
+ return res
+ end)
+ eq({
+ { 'type.builtin', 'int' },
+ { 'variable', 'zeero' },
+ { 'spell', 'zeero' },
+ { 'operator', '=' },
+ { 'number', '0' },
+ { 'punctuation.delimiter', ';' },
+ }, result)
+ end)
+
describe('Query:iter_captures', function()
it('includes metadata for all captured nodes #23664', function()
insert([[
@@ -835,9 +894,9 @@ void ui_refresh(void)
local result = exec_lua(function()
local query0 = vim.treesitter.query.parse('c', query)
- local match_preds = query0.match_preds
+ local match_preds = query0._match_predicates
local called = 0
- function query0:match_preds(...)
+ function query0:_match_predicates(...)
called = called + 1
return match_preds(self, ...)
end
diff --git a/test/functional/treesitter/testutil.lua b/test/functional/treesitter/testutil.lua
new file mode 100644
index 0000000000..f8934f06c3
--- /dev/null
+++ b/test/functional/treesitter/testutil.lua
@@ -0,0 +1,25 @@
+local n = require('test.functional.testnvim')()
+
+local exec_lua = n.exec_lua
+
+local M = {}
+
+---@param language string
+---@param query_string string
+function M.run_query(language, query_string)
+ return exec_lua(function(lang, query_str)
+ local query = vim.treesitter.query.parse(lang, query_str)
+ local parser = vim.treesitter.get_parser()
+ local tree = parser:parse()[1]
+ local res = {}
+ for id, node, metadata in query:iter_captures(tree:root(), 0) do
+ table.insert(
+ res,
+ { query.captures[id], metadata[id] and metadata[id].range or { node:range() } }
+ )
+ end
+ return res
+ end, language, query_string)
+end
+
+return M
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index 0221c1e0b0..ce7c9596bb 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -91,25 +91,27 @@ local function test_cmdline(linegrid)
{1:~ }|*3
|
]],
+ cmdline = { { abort = true } },
}
end)
it('works with input()', function()
feed(':call input("input", "default")<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- ^ |
- {1:~ }|*3
- |
- ]],
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
cmdline = {
{
- prompt = 'input',
content = { { 'default' } },
+ hl_id = 0,
pos = 7,
+ prompt = 'input',
},
},
- }
+ })
feed('<cr>')
screen:expect {
@@ -118,6 +120,7 @@ local function test_cmdline(linegrid)
{1:~ }|*3
|
]],
+ cmdline = { { abort = false } },
}
end)
@@ -210,6 +213,7 @@ local function test_cmdline(linegrid)
content = { { 'xx3' } },
pos = 3,
},
+ { abort = false },
},
}
@@ -220,6 +224,7 @@ local function test_cmdline(linegrid)
{1:~ }|*3
|
]],
+ cmdline = { { abort = true } },
}
end)
@@ -294,6 +299,7 @@ local function test_cmdline(linegrid)
{1:~ }|*3
|
]],
+ cmdline = { { abort = false } },
}
-- Try once more, to check buffer is reinitialized. #8007
@@ -324,6 +330,7 @@ local function test_cmdline(linegrid)
{1:~ }|*3
|
]],
+ cmdline = { { abort = false } },
}
end)
@@ -353,6 +360,7 @@ local function test_cmdline(linegrid)
{3:[Command Line] }|
|
]],
+ cmdline = { { abort = false } },
}
-- nested cmdline
@@ -404,6 +412,7 @@ local function test_cmdline(linegrid)
{3:[Command Line] }|
|
]],
+ cmdline = { [2] = { abort = true } },
}
feed('<c-c>')
@@ -452,6 +461,7 @@ local function test_cmdline(linegrid)
cmdline = {
{
prompt = 'secret:',
+ hl_id = 0,
content = { { '******' } },
pos = 6,
},
@@ -495,6 +505,7 @@ local function test_cmdline(linegrid)
cmdline = {
{
prompt = '>',
+ hl_id = 0,
content = {
{ '(', 30 },
{ 'a' },
@@ -797,11 +808,14 @@ local function test_cmdline(linegrid)
-- This used to send an invalid event where pos where larger than the total
-- length of content. Checked in _handle_cmdline_show.
feed('<esc>')
- screen:expect([[
- ^ |
- {1:~ }|*3
- |
- ]])
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
+ cmdline = { { abort = true } },
+ })
end)
it('does not move cursor to curwin #20309', function()
@@ -827,6 +841,30 @@ local function test_cmdline(linegrid)
} },
}
end)
+
+ it('show prompt hl_id', function()
+ screen:expect([[
+ ^ |
+ {1:~ }|*3
+ |
+ ]])
+ feed(':echohl Error | call input("Prompt:")<CR>')
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 242,
+ pos = 0,
+ prompt = 'Prompt:',
+ },
+ },
+ })
+ end)
end
-- the representation of cmdline and cmdline_block contents changed with ext_linegrid
@@ -1000,6 +1038,36 @@ describe('cmdline redraw', function()
]],
}
end)
+
+ it('silent prompt', function()
+ command([[nmap <silent> T :call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
+ feed('T')
+ screen:expect([[
+ |
+ {3: }|
+ |
+ {6:Save changes?} |
+ {6:[Y]es, (N)o, (C)ancel: }^ |
+ ]])
+ end)
+
+ it('substitute confirm prompt does not scroll', function()
+ screen:try_resize(75, screen._height)
+ command('call setline(1, "foo")')
+ command('set report=0')
+ feed(':%s/foo/bar/c<CR>')
+ screen:expect([[
+ {2:foo} |
+ {1:~ }|*3
+ {6:replace with bar? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)}^ |
+ ]])
+ feed('y')
+ screen:expect([[
+ ^bar |
+ {1:~ }|*3
+ 1 substitution on 1 line |
+ ]])
+ end)
end)
describe('statusline is redrawn on entering cmdline', function()
@@ -1447,31 +1515,29 @@ describe('cmdheight=0', function()
it('when substitute text', function()
command('set cmdheight=0 noruler laststatus=3')
feed('ifoo<ESC>')
- screen:expect {
- grid = [[
+ screen:try_resize(screen._width, 7)
+ screen:expect([[
fo^o |
- {1:~ }|*3
+ {1:~ }|*5
{3:[No Name] [+] }|
- ]],
- }
+ ]])
feed(':%s/foo/bar/gc<CR>')
- screen:expect {
- grid = [[
+ screen:expect([[
{2:foo} |
- {1:~ }|*3
- {6:replace wi...q/l/^E/^Y)?}^ |
- ]],
- }
+ {3: }|
+ |*2
+ {6:replace with bar? (y)es/(}|
+ {6:n)o/(a)ll/(q)uit/(l)ast/s}|
+ {6:croll up(^E)/down(^Y)}^ |
+ ]])
feed('y')
- screen:expect {
- grid = [[
+ screen:expect([[
^bar |
- {1:~ }|*3
+ {1:~ }|*5
{3:[No Name] [+] }|
- ]],
- }
+ ]])
assert_alive()
end)
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index d7c0657820..825a90fbc8 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -190,6 +190,19 @@ describe('ui/cursor', function()
attr_lm = {},
short_name = 'sm',
},
+ [18] = {
+ blinkoff = 500,
+ blinkon = 500,
+ blinkwait = 0,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ name = 'terminal',
+ hl_id = 3,
+ id_lm = 3,
+ attr = { reverse = true },
+ attr_lm = { reverse = true },
+ short_name = 't',
+ },
}
screen:expect(function()
@@ -245,17 +258,20 @@ describe('ui/cursor', function()
end
end
if m.hl_id then
- m.hl_id = 66
+ m.hl_id = 65
m.attr = { background = Screen.colors.DarkGray }
end
if m.id_lm then
- m.id_lm = 73
+ m.id_lm = 72
+ m.attr_lm = {}
end
end
-- Assert the new expectation.
screen:expect(function()
- eq(expected_mode_info, screen._mode_info)
+ for i, v in ipairs(expected_mode_info) do
+ eq(v, screen._mode_info[i])
+ end
eq(true, screen._cursor_style_enabled)
eq('normal', screen.mode)
end)
@@ -361,4 +377,38 @@ describe('ui/cursor', function()
end
end)
end)
+
+ it(':sleep does not hide cursor when sleeping', function()
+ n.feed(':sleep 100m | echo 42\n')
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ :sleep 100m | echo 42 |
+ ]],
+ timeout = 100,
+ })
+ screen:expect([[
+ ^ |
+ {1:~ }|*3
+ 42 |
+ ]])
+ end)
+
+ it(':sleep! hides cursor when sleeping', function()
+ n.feed(':sleep! 100m | echo 42\n')
+ screen:expect({
+ grid = [[
+ |
+ {1:~ }|*3
+ :sleep! 100m | echo 42 |
+ ]],
+ timeout = 100,
+ })
+ screen:expect([[
+ ^ |
+ {1:~ }|*3
+ 42 |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index fbf16f3afe..7969dd5d3b 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -509,6 +509,69 @@ describe('decorations providers', function()
]]}
end)
+ it('can have virtual text of the style: eol_right_align', function()
+ insert(mulholland)
+ setup_provider [[
+ local hl = api.nvim_get_hl_id_by_name "ErrorMsg"
+ local test_ns = api.nvim_create_namespace "mulholland"
+ function on_do(event, ...)
+ if event == "line" then
+ local win, buf, line = ...
+ api.nvim_buf_set_extmark(buf, test_ns, line, 0, {
+ virt_text = {{'+'}, {'1234567890', 'ErrorMsg'}};
+ virt_text_pos='eol_right_align';
+ ephemeral = true;
+ })
+ end
+ end
+ ]]
+
+ screen:expect{grid=[[
+ // just to see if there was an accident |
+ // on Mulholland Drive +{2:1234567890}|
+ try_start(); +{2:1234567890}|
+ bufref_T save_buf; +{2:1234567890}|
+ switch_buffer(&save_buf, buf); +{2:12345678}|
+ posp = getmark(mark, false); +{2:1234567890}|
+ restore_buffer(&save_buf);^ +{2:1234567890}|
+ |
+ ]]}
+ end)
+
+ it('multiple eol_right_align', function()
+ insert(mulholland)
+ setup_provider [[
+ local hl = api.nvim_get_hl_id_by_name "ErrorMsg"
+ local test_ns = api.nvim_create_namespace "mulholland"
+ function on_do(event, ...)
+ if event == "line" then
+ local win, buf, line = ...
+ api.nvim_buf_set_extmark(buf, test_ns, line, 0, {
+ virt_text = {{'11111'}};
+ virt_text_pos='eol_right_align';
+ ephemeral = true;
+ })
+ api.nvim_buf_set_extmark(0, test_ns, line, 0, {
+ virt_text = {{'22222'}};
+ virt_text_pos='eol_right_align';
+ ephemeral = true;
+ })
+ end
+ end
+ ]]
+
+ screen:expect{grid=[[
+ // just to see if there was an accident |
+ // on Mulholland Drive 11111 22222|
+ try_start(); 11111 22222|
+ bufref_T save_buf; 11111 22222|
+ switch_buffer(&save_buf, buf); 11111 222|
+ posp = getmark(mark, false); 11111 22222|
+ restore_buffer(&save_buf);^ 11111 22222|
+ |
+ ]]}
+ end)
+
it('virtual text works with wrapped lines', function()
insert(mulholland)
feed('ggJj3JjJ')
@@ -631,7 +694,7 @@ describe('decorations providers', function()
{14: }hello97 |
{14: }hello98 |
{14: }hello99 |
- X ^hello100 |
+ {14:X }^hello100 |
{14: }hello101 |
{14: }hello102 |
{14: }hello103 |
@@ -744,6 +807,30 @@ describe('decorations providers', function()
]])
eq(2, exec_lua([[return _G.cnt]]))
end)
+
+ it('can do large changes to the marktree', function()
+ insert("line1 with a lot of text\nline2 with a lot of text")
+ setup_provider([[
+ function on_do(event, _, _, row)
+ if event == 'win' or (event == 'line' and row == 1) then
+ vim.api.nvim_buf_clear_namespace(0, ns1, 0, -1)
+ for i = 0,1 do
+ for j = 0,23 do
+ vim.api.nvim_buf_set_extmark(0, ns1, i, j, {hl_group='ErrorMsg', end_col = j+1})
+ end
+ end
+ end
+ end
+ ]])
+
+ -- Doesn't crash when modifying the marktree between line1 and line2
+ screen:expect([[
+ {2:line1 with a lot of text} |
+ {2:line2 with a lot of tex^t} |
+ {1:~ }|*5
+ |
+ ]])
+ end)
end)
local example_text = [[
@@ -810,6 +897,9 @@ describe('extmark decorations', function()
[42] = {undercurl = true, special = Screen.colors.Red};
[43] = {background = Screen.colors.Yellow, undercurl = true, special = Screen.colors.Red};
[44] = {background = Screen.colors.LightMagenta};
+ [45] = { background = Screen.colors.Red, special = Screen.colors.Red, foreground = Screen.colors.Red };
+ [46] = { background = Screen.colors.Blue, foreground = Screen.colors.Blue, special = Screen.colors.Red };
+ [47] = { background = Screen.colors.Green, foreground = Screen.colors.Blue, special = Screen.colors.Red };
}
ns = api.nvim_create_namespace 'test'
@@ -1900,6 +1990,46 @@ describe('extmark decorations', function()
]]}
end)
+ it('highlight can combine multiple groups', function()
+ screen:try_resize(50, 3)
+ command('hi Group1 guibg=Red guifg=Red guisp=Red')
+ command('hi Group2 guibg=Blue guifg=Blue')
+ command('hi Group3 guibg=Green')
+ insert([[example text]])
+ api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row=1, hl_group = {} })
+ screen:expect([[
+ example tex^t |
+ {1:~ }|
+ |
+ ]])
+
+ api.nvim_buf_clear_namespace(0, ns, 0, -1)
+ api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row=1, hl_group = {'Group1'} })
+ screen:expect([[
+ {45:example tex^t} |
+ {1:~ }|
+ |
+ ]])
+ api.nvim_buf_clear_namespace(0, ns, 0, -1)
+ api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 1, hl_group = {'Group1', 'Group2'} })
+ screen:expect([[
+ {46:example tex^t} |
+ {1:~ }|
+ |
+ ]])
+ api.nvim_buf_clear_namespace(0, ns, 0, -1)
+ api.nvim_buf_set_extmark(0, ns, 0, 0, { end_row = 1, hl_group = {'Group1', 'Group2', 'Group3'}, hl_eol=true })
+ screen:expect([[
+ {47:example tex^t }|
+ {1:~ }|
+ |
+ ]])
+
+ eq('Invalid hl_group: hl_group item',
+ pcall_err(api.nvim_buf_set_extmark, 0, ns, 0, 0, { end_row = 1, hl_group = {'Group1', 'Group2', {'fail'}}, hl_eol=true }))
+ end)
+
+
it('highlight works after TAB with sidescroll #14201', function()
screen:try_resize(50, 3)
command('set nowrap')
@@ -2301,13 +2431,16 @@ describe('extmark decorations', function()
it('works with both hl_group and sign_hl_group', function()
screen:try_resize(50, 3)
+ screen:add_extra_attr_ids({
+ [100] = { background = Screen.colors.WebGray, foreground = Screen.colors.Blue, bold = true },
+ })
insert('abcdefghijklmn')
api.nvim_buf_set_extmark(0, ns, 0, 0, {sign_text='S', sign_hl_group='NonText', hl_group='Error', end_col=14})
- screen:expect{grid=[[
- {1:S }{4:abcdefghijklm^n} |
+ screen:expect([[
+ {100:S }{9:abcdefghijklm^n} |
{1:~ }|
|
- ]]}
+ ]])
end)
it('virt_text_repeat_linebreak repeats virtual text on wrapped lines', function()
@@ -5064,16 +5197,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S'})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S l2 |
+ {7:S }l2 |
{7: }l3 |
{7: }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add a single sign (with end row)', function()
@@ -5082,16 +5215,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S l2 |
+ {7:S }l2 |
{7: }l3 |
{7: }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add a single sign and text highlight', function()
@@ -5099,16 +5232,16 @@ l5
feed 'gg'
api.nvim_buf_set_extmark(0, ns, 1, 0, {sign_text='S', hl_group='Todo', end_col=1})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S {100:l}2 |
+ {7:S }{100:l}2 |
{7: }l3 |
{7: }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
api.nvim_buf_clear_namespace(0, ns, 0, -1)
end)
@@ -5119,16 +5252,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S l2 |
- S l3 |
+ {7:S }l2 |
+ {7:S }l3 |
{7: }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add multiple signs (multiple extmarks)', function()
@@ -5138,16 +5271,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
api.nvim_buf_set_extmark(0, ns, 3, -1, {sign_text='S2', end_row = 4})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S1l2 |
+ {7:S1}l2 |
{7: }l3 |
- S2l4 |
- S2l5 |
+ {7:S2}l4 |
+ {7:S2}l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add multiple signs (multiple extmarks) 2', function()
@@ -5156,16 +5289,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 3, -1, {sign_text='S1'})
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row = 3})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S2{7: }l2 |
- S2{7: }l3 |
- S2S1l4 |
+ {7:S2 }l2 |
+ {7:S2 }l3 |
+ {7:S2S1}l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add multiple signs (multiple extmarks) 3', function()
@@ -5176,16 +5309,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2})
api.nvim_buf_set_extmark(0, ns, 2, -1, {sign_text='S2', end_row=3})
- screen:expect{grid=[[
+ screen:expect([[
{7: }^l1 |
- S1{7: }l2 |
- S2S1l3 |
- S2{7: }l4 |
+ {7:S1 }l2 |
+ {7:S2S1}l3 |
+ {7:S2 }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add multiple signs (multiple extmarks) 4', function()
@@ -5195,16 +5328,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0})
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row=1})
- screen:expect{grid=[[
- S1^l1 |
- S2l2 |
+ screen:expect([[
+ {7:S1}^l1 |
+ {7:S2}l2 |
{7: }l3 |
{7: }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('works with old signs', function()
@@ -5219,16 +5352,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
api.nvim_buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
- screen:expect{grid=[[
- S4S1^l1 |
- S2x l2 |
- S5{7: }l3 |
+ screen:expect([[
+ {7:S4S1}^l1 |
+ {7:S2x }l2 |
+ {7:S5 }l3 |
{7: }l4 |
{7: }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('works with old signs (with range)', function()
@@ -5244,16 +5377,16 @@ l5
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
api.nvim_buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
- screen:expect{grid=[[
- S4S3S1^l1 |
- S3S2x l2 |
- S5S3{7: }l3 |
- S3{7: }l4 |
- S3{7: }l5 |
+ screen:expect([[
+ {7:S4S3S1}^l1 |
+ {7:S3S2x }l2 |
+ {7:S5S3 }l3 |
+ {7:S3 }l4 |
+ {7:S3 }l5 |
{7: } |
{1:~ }|*3
|
- ]]}
+ ]])
end)
it('can add a ranged sign (with start out of view)', function()
@@ -5264,14 +5397,14 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='X', end_row=3})
- screen:expect{grid=[[
- X {7: }^l3 |
- X {7: }l4 |
+ screen:expect([[
+ {7:X }^l3 |
+ {7:X }l4 |
{7: }l5 |
{7: } |
{1:~ }|*5
|
- ]]}
+ ]])
end)
it('can add lots of signs', function()
@@ -5293,11 +5426,11 @@ l5
api.nvim_buf_set_extmark(0, ns, i, -1, { sign_text='Z' })
end
- screen:expect{grid=[[
- Z Y X W {100:a} {100:b} {100:c} {100:d} {100:e} {100:f} {100:g} {100:h} |*8
- Z Y X W {100:a} {100:b} {100:c} {100:d} {100:e} {100:f} {100:g} {100:^h} |
+ screen:expect([[
+ {7:Z Y X W }{100:a} {100:b} {100:c} {100:d} {100:e} {100:f} {100:g} {100:h} |*8
+ {7:Z Y X W }{100:a} {100:b} {100:c} {100:d} {100:e} {100:f} {100:g} {100:^h} |
|
- ]]}
+ ]])
end)
it('works with priority #19716', function()
@@ -5313,20 +5446,20 @@ l5
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S5', priority=200})
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S1', priority=1})
- screen:expect{grid=[[
- S5S4O3S2S1^l1 |
+ screen:expect([[
+ {7:S5S4O3S2S1}^l1 |
{7: }l2 |
|
- ]]}
+ ]])
-- Check truncation works too
api.nvim_set_option_value('signcolumn', 'auto', {})
- screen:expect{grid=[[
- S5^l1 |
+ screen:expect([[
+ {7:S5}^l1 |
{7: }l2 |
|
- ]]}
+ ]])
end)
it('does not overflow with many old signs #23852', function()
@@ -5343,21 +5476,21 @@ l5
command([[exe 'sign place 07 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
command([[exe 'sign place 08 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
command([[exe 'sign place 09 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]])
- screen:expect{grid=[[
- O3O3O3O3O3O3O3O3O3^ |
+ screen:expect([[
+ {7:O3O3O3O3O3O3O3O3O3}^ |
{1:~ }|
|
- ]]}
+ ]])
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S1', priority=1})
screen:expect_unchanged()
api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S5', priority=200})
- screen:expect{grid=[[
- S5O3O3O3O3O3O3O3O3^ |
+ screen:expect([[
+ {7:S5O3O3O3O3O3O3O3O3}^ |
{1:~ }|
|
- ]]}
+ ]])
assert_alive()
end)
@@ -5383,12 +5516,12 @@ l5
api.nvim_buf_set_extmark(0, ns, 1, -1, {invalidate = true, sign_text='S3'})
feed('2Gdd')
- screen:expect{grid=[[
- S1l1 |
- S1^l3 |
- S1l4 |
+ screen:expect([[
+ {7:S1}l1 |
+ {7:S1}^l3 |
+ {7:S1}l4 |
|
- ]]}
+ ]])
end)
it('correct width with multiple overlapping signs', function()
@@ -5400,36 +5533,36 @@ l5
feed('gg')
local s1 = [[
- S2S1^l1 |
- S3S2l2 |
- S3S2l3 |
+ {7:S2S1}^l1 |
+ {7:S3S2}l2 |
+ {7:S3S2}l3 |
|
]]
- screen:expect{grid=s1}
+ screen:expect(s1)
-- Correct width when :move'ing a line with signs
command('move2')
- screen:expect{grid=[[
- S3{7: }l2 |
- S3S2S1^l1 |
+ screen:expect([[
+ {7:S3 }l2 |
+ {7:S3S2S1}^l1 |
{7: }l3 |
|
- ]]}
+ ]])
command('silent undo')
screen:expect{grid=s1}
command('d')
- screen:expect{grid=[[
- S3S2S1^l2 |
- S3S2{7: }l3 |
+ screen:expect([[
+ {7:S3S2S1}^l2 |
+ {7:S3S2 }l3 |
{7: }l4 |
|
- ]]}
+ ]])
command('d')
- screen:expect{grid=[[
- S3S2S1^l3 |
+ screen:expect([[
+ {7:S3S2S1}^l3 |
{7: }l4 |
{7: }l5 |
|
- ]]}
+ ]])
end)
it('correct width when adding and removing multiple signs', function()
@@ -5452,12 +5585,12 @@ l5
redraw!
call nvim_buf_del_extmark(0, ns, s1)
]])
- screen:expect{grid=[[
- S1^l1 |
- S1l2 |
- S1l3 |
+ screen:expect([[
+ {7:S1}^l1 |
+ {7:S1}l2 |
+ {7:S1}l3 |
|
- ]]}
+ ]])
end)
it('correct width when deleting lines', function()
@@ -5472,12 +5605,12 @@ l5
call nvim_buf_del_extmark(0, ns, s3)
norm 4Gdd
]])
- screen:expect{grid=[[
+ screen:expect([[
{7: }l3 |
- S2S1l5 |
+ {7:S2S1}l5 |
{7: }^ |
|
- ]]}
+ ]])
end)
it('correct width when splitting lines with signs on different columns', function()
@@ -5487,12 +5620,12 @@ l5
api.nvim_buf_set_extmark(0, ns, 0, 0, {sign_text='S1'})
api.nvim_buf_set_extmark(0, ns, 0, 1, {sign_text='S2'})
feed('a<cr><esc>')
- screen:expect{grid=[[
- S1l |
- S2^1 |
+ screen:expect([[
+ {7:S1}l |
+ {7:S2}^1 |
{7: }l2 |
|
- ]]}
+ ]])
end)
it('correct width after wiping a buffer', function()
@@ -5501,12 +5634,12 @@ l5
feed('gg')
local buf = api.nvim_get_current_buf()
api.nvim_buf_set_extmark(buf, ns, 0, 0, { sign_text = 'h' })
- screen:expect{grid=[[
- h ^l1 |
+ screen:expect([[
+ {7:h }^l1 |
{7: }l2 |
{7: }l3 |
|
- ]]}
+ ]])
api.nvim_win_set_buf(0, api.nvim_create_buf(false, true))
api.nvim_buf_delete(buf, {unload=true, force=true})
api.nvim_buf_set_lines(buf, 0, -1, false, {''})
@@ -5537,12 +5670,12 @@ l5
end)
]])
- screen:expect{grid=[[
- S1^l1 |
- S2l2 |
- S4l3 |
+ screen:expect([[
+ {7:S1}^l1 |
+ {7:S2}l2 |
+ {7:S4}l3 |
|
- ]]}
+ ]])
end)
it('no crash with sign after many marks #27137', function()
@@ -5553,11 +5686,11 @@ l5
end
api.nvim_buf_set_extmark(0, ns, 0, 0, {sign_text = 'S1'})
- screen:expect{grid=[[
- S1{9:^a} |
+ screen:expect([[
+ {7:S1}{9:^a} |
{1:~ }|*2
|
- ]]}
+ ]])
end)
it('correct sort order with multiple namespaces and same id', function()
@@ -5565,11 +5698,11 @@ l5
api.nvim_buf_set_extmark(0, ns, 0, 0, {sign_text = 'S1', id = 1})
api.nvim_buf_set_extmark(0, ns2, 0, 0, {sign_text = 'S2', id = 1})
- screen:expect{grid=[[
- S2S1^ |
+ screen:expect([[
+ {7:S2S1}^ |
{1:~ }|*8
|
- ]]}
+ ]])
end)
it('correct number of signs after deleting text (#27046)', function()
@@ -5586,12 +5719,12 @@ l5
api.nvim_buf_set_extmark(0, ns, 30, 0, {end_row = 30, end_col = 3, hl_group = 'Error'})
command('0d29')
- screen:expect{grid=[[
- S4S3S2S1{9:^foo} |
- S5{7: }{9:foo} |
+ screen:expect([[
+ {7:S4S3S2S1}{9:^foo} |
+ {7:S5 }{9:foo} |
{1:~ }|*7
29 fewer lines |
- ]]}
+ ]])
api.nvim_buf_clear_namespace(0, ns, 0, -1)
end)
@@ -5599,21 +5732,17 @@ l5
it([[correct numberwidth with 'signcolumn' set to "number" #28984]], function()
command('set number numberwidth=1 signcolumn=number')
api.nvim_buf_set_extmark(0, ns, 0, 0, { sign_text = 'S1' })
- screen:expect({
- grid = [[
- S1 ^ |
- {1:~ }|*8
- |
- ]]
- })
+ screen:expect([[
+ {7:S1 }^ |
+ {1:~ }|*8
+ |
+ ]])
api.nvim_buf_del_extmark(0, ns, 1)
- screen:expect({
- grid = [[
- {8:1 }^ |
- {1:~ }|*8
- |
- ]]
- })
+ screen:expect([[
+ {8:1 }^ |
+ {1:~ }|*8
+ |
+ ]])
end)
it('supports emoji as signs', function()
@@ -5626,10 +5755,10 @@ l5
api.nvim_buf_set_extmark(0, ns, 4, 0, {sign_text='❤x'})
screen:expect([[
{7: }^l1 |
- 🧑‍🌾l2 |
- ❤️l3 |
- ❤ l4 |
- ❤xl5 |
+ {7:🧑‍🌾}l2 |
+ {7:❤️}l3 |
+ {7:❤ }l4 |
+ {7:❤x}l5 |
{7: } |
{1:~ }|*3
|
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
index 95159011f1..dae373297a 100644
--- a/test/functional/ui/diff_spec.lua
+++ b/test/functional/ui/diff_spec.lua
@@ -2044,6 +2044,26 @@ it('diff mode overlapped diff blocks will be merged', function()
{2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
|
]])
+
+ WriteDiffFiles3('a\nb\nc', 'd\ne', 'b\nf')
+ screen:expect([[
+ {7: }{27:a}{4: }│{7: }{27:d}{4: }│{7: }{27:^b}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:e}{4: }│{7: }{27:f}{4: }|
+ {7: }{22:c }│{7: }{23:---------}│{7: }{23:---------}|
+ {1:~ }│{1:~ }│{1:~ }|*15
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
+
+ WriteDiffFiles3('a\nb\nc', 'd\ne', 'b')
+ screen:expect([[
+ {7: }{27:a}{4: }│{7: }{27:d}{4: }│{7: }{27:^b}{4: }|
+ {7: }{27:b}{4: }│{7: }{27:e}{4: }│{7: }{23:---------}|
+ {7: }{22:c }│{7: }{23:---------}│{7: }{23:---------}|
+ {1:~ }│{1:~ }│{1:~ }|*15
+ {2:Xdifile1 Xdifile2 }{3:Xdifile3 }|
+ |
+ ]])
end)
-- oldtest: Test_diff_topline_noscroll()
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index 57ef9bcff6..15231e0f8c 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -1012,6 +1012,97 @@ describe('float window', function()
end)
end)
+ it('placed relative to tabline and laststatus', function()
+ local screen = Screen.new(20, 10)
+ screen:add_extra_attr_ids({ [100] = { bold = true, foreground = Screen.colors.Magenta } })
+ command('set showtabline=1 laststatus=1')
+ api.nvim_open_win(0, false, {
+ relative = 'laststatus',
+ border = 'single',
+ anchor = 'SE',
+ width = 5,
+ height = 1,
+ row = 0,
+ col = 1000,
+ })
+ local tabwin = api.nvim_open_win(0, false, {
+ relative = 'tabline',
+ border = 'single',
+ width = 5,
+ height = 1,
+ row = 0,
+ col = 1000,
+ })
+ screen:expect([[
+ ^ {2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {1:~ }|*3
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ command('tabnew | tabnext')
+ screen:expect([[
+ {5: }{100:3}{5: Name] }{24: No Name]X}|
+ ^ {2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {1:~ }|*2
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ command('vsplit')
+ screen:expect([[
+ {5: }{100:4}{5: Name] }{24: No Name]X}|
+ ^ {2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {1:~ }{2:│}{1:~}|
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {3:[No Name] }{2:<}|
+ |
+ ]])
+ command('quit')
+ api.nvim_win_set_config(tabwin, {
+ relative = 'tabline',
+ border = 'single',
+ width = 5,
+ height = 1,
+ row = 1,
+ col = 0,
+ })
+ screen:expect([[
+ {5: }{100:3}{5: Name] }{24: No Name]X}|
+ ^ |
+ {2:┌─────┐}{1: }|
+ {2:│}{4: }{2:│}{1: }|
+ {2:└─────┘}{1: }|
+ {1:~ }|
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ command('tabonly')
+ screen:expect([[
+ ^ |
+ {2:┌─────┐}{1: }|
+ {2:│}{4: }{2:│}{1: }|
+ {2:└─────┘}{1: }|
+ {1:~ }|*2
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ end)
+
local function with_ext_multigrid(multigrid)
local screen, attrs
before_each(function()
@@ -1046,6 +1137,8 @@ describe('float window', function()
[26] = {blend = 80, background = Screen.colors.Gray0};
[27] = {foreground = Screen.colors.Black, background = Screen.colors.LightGrey};
[28] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey};
+ [29] = {background = Screen.colors.Yellow1, foreground = Screen.colors.Blue4};
+ [30] = {background = Screen.colors.Grey, foreground = Screen.colors.Blue4, bold = true};
}
screen:set_default_attr_ids(attrs)
end)
@@ -1451,14 +1544,14 @@ describe('float window', function()
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
- {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
+ {19: }{29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
{19: }{14: 2 }{22:y} |
{19: }{14: 3 }{22: } |
{0:~ }|*3
## grid 3
|
## grid 4
- {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }|
+ {29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }|
{19: }{15:y }|
{19: }{15: }|
{15: }|
@@ -1466,9 +1559,9 @@ describe('float window', function()
else
screen:expect([[
- {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
+ {19: }{29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
{19: }{14: 2 }{22:y} |
- {19: }{14: 3 }{22: } {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } |
+ {19: }{14: 3 }{22: } {29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } |
{0:~ }{19: }{15:y }{0: }|
{0:~ }{19: }{15: }{0: }|
{0:~ }{15: }{0: }|
@@ -1551,14 +1644,14 @@ describe('float window', function()
[2:----------------------------------------]|*6
[3:----------------------------------------]|
## grid 2
- {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
+ {19: }{29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
{19: }{14: 2 }{22:y} |
{19: }{14: 3 }{22: } |
{0:~ }|*3
## grid 3
|
## grid 4
- {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }|
+ {29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }|
{19: }{15:y }|
{19: }{15: }|
{15: }|
@@ -1566,9 +1659,9 @@ describe('float window', function()
else
screen:expect([[
- {19: }{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
+ {19: }{29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{22:^x}{21: }|
{19: }{14: 2 }{22:y} |
- {19: }{14: 3 }{22: } {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } |
+ {19: }{14: 3 }{22: } {29:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } |
{0:~ }{19: }{15:y }{0: }|
{0:~ }{19: }{15: }{0: }|
{0:~ }{15: }{0: }|
@@ -1616,31 +1709,34 @@ describe('float window', function()
feed('ix<cr>y<cr><esc>gg')
api.nvim_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:----------------------------------------]|*6
- [3:----------------------------------------]|
- ## grid 2
- {20: 1}{19: }{22:^x}{21: }|
- {14: 2}{19: }{22:y} |
- {14: 3}{19: }{22: } |
- {0:~ }|*3
- ## grid 3
- |
- ## grid 4
- {15:x }|
- {15:y }|
- {15: }|*2
- ]], float_pos={[4] = {1001, "NW", 1, 4, 10, true}}}
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*6
+ [3:----------------------------------------]|
+ ## grid 2
+ {20: 1}{19: }{22:^x}{21: }|
+ {14: 2}{19: }{22:y} |
+ {14: 3}{19: }{22: } |
+ {0:~ }|*3
+ ## grid 3
+ |
+ ## grid 4
+ {15:x }|
+ {15:y }|
+ {15: }|*2
+ ]],
+ float_pos = { [4] = { 1001, "NW", 1, 4, 10, true, 50 } },
+ })
else
- screen:expect{grid=[[
+ screen:expect([[
{20: 1}{19: }{22:^x}{21: }|
{14: 2}{19: }{22:y} |
{14: 3}{19: }{22: } {15:x } |
{0:~ }{15:y }{0: }|
{0:~ }{15: }{0: }|*2
|
- ]]}
+ ]])
end
end)
@@ -2237,6 +2333,61 @@ describe('float window', function()
|
]]}
end
+
+ -- reuse before title pos
+ api.nvim_win_set_config(win, {title= 'new'})
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*6
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|*5
+ ## grid 3
+ |
+ ## grid 4
+ {5:╔══════}{11:new}{5:╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚═════════╝}|
+ ]],
+ float_pos = {
+ [4] = {1001, "NW", 1, 2, 5, true, 50};
+ },
+ win_viewport = {
+ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0};
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000
+ },
+ [4] = {
+ bottom = 1,
+ left = 1,
+ right = 1,
+ top = 1,
+ win = 1001
+ }
+ },
+ })
+ else
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔══════}{11:new}{5:╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚═════════╝}{0: }|
+ |
+ ]])
+ end
end)
it('border with footer', function()
@@ -2382,6 +2533,61 @@ describe('float window', function()
|
]]}
end
+
+ -- reuse before footer pos
+ api.nvim_win_set_config(win, { footer = 'new' })
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*6
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|*5
+ ## grid 3
+ |
+ ## grid 4
+ {5:╔═════════╗}|
+ {5:║}{1: halloj! }{5:║}|
+ {5:║}{1: BORDAA }{5:║}|
+ {5:╚══════}{11:new}{5:╝}|
+ ]],
+ float_pos = {
+ [4] = {1001, "NW", 1, 2, 5, true, 50};
+ },
+ win_viewport = {
+ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0};
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000
+ },
+ [4] = {
+ bottom = 1,
+ left = 1,
+ right = 1,
+ top = 1,
+ win = 1001
+ }
+ },
+ })
+ else
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }{5:╔═════════╗}{0: }|
+ {0:~ }{5:║}{1: halloj! }{5:║}{0: }|
+ {0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
+ {0:~ }{5:╚══════}{11:new}{5:╝}{0: }|
+ |
+ ]])
+ end
end)
it('border with title and footer', function()
@@ -8542,6 +8748,131 @@ describe('float window', function()
|
]]}
end
+
+ --
+ -- Check that floats are positioned correctly after changing the zindexes.
+ --
+ command('fclose')
+ exec_lua([[
+ local win1, win3 = ...
+ vim.api.nvim_win_set_config(win1, { zindex = 400, title = 'win_400', title_pos = 'center', border = 'double' })
+ vim.api.nvim_win_set_config(win3, { zindex = 300, title = 'win_300', title_pos = 'center', border = 'single' })
+ ]], win1, win3)
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*6
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|*5
+ ## grid 3
+ |
+ ## grid 4
+ {5:╔══════}{11:win_400}{5:═══════╗}|
+ {5:║}{7: }{5:║}|
+ {5:║}{7:~ }{5:║}|*2
+ {5:╚════════════════════╝}|
+ ## grid 6
+ {5:┌──────}{11:win_300}{5:───────┐}|
+ {5:│}{8: }{5:│}|
+ {5:│}{8:~ }{5:│}|*2
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [4] = {1001, "NW", 1, 1, 5, true, 400};
+ [6] = {1003, "NW", 1, 3, 7, true, 300};
+ }, win_viewport={
+ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [6] = {win = 1003, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ }, win_viewport_margins={
+ [2] = { bottom = 0, left = 0, right = 0, top = 0, win = 1000 },
+ [4] = { bottom = 1, left = 1, right = 1, top = 1, win = 1001 },
+ [6] = { bottom = 1, left = 1, right = 1, top = 1, win = 1003 }
+ }})
+ else
+ screen:expect({
+ grid = [[
+ ^ |
+ {0:~ }{5:╔══════}{11:win_400}{5:═══════╗}{0: }|
+ {0:~ }{5:║}{7: }{5:║─┐}{0: }|
+ {0:~ }{5:║}{7:~ }{5:║}{8: }{5:│}{0: }|*2
+ {0:~ }{5:╚════════════════════╝}{8: }{5:│}{0: }|
+ {5:└────────────────────┘} |
+ ]]
+ })
+ end
+ exec_lua([[
+ local win1, win3 = ...
+ vim.api.nvim_win_set_config(win1, { zindex = 100, title='win_100' })
+ vim.api.nvim_win_set_config(win3, { zindex = 150, title='win_150' })
+ ]], win1, win3)
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*6
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|*5
+ ## grid 3
+ |
+ ## grid 4
+ {5:╔══════}{11:win_100}{5:═══════╗}|
+ {5:║}{7: }{5:║}|
+ {5:║}{7:~ }{5:║}|*2
+ {5:╚════════════════════╝}|
+ ## grid 6
+ {5:┌──────}{11:win_150}{5:───────┐}|
+ {5:│}{8: }{5:│}|
+ {5:│}{8:~ }{5:│}|*2
+ {5:└────────────────────┘}|
+ ]],
+ float_pos = {
+ [4] = {1001, "NW", 1, 1, 5, true, 100};
+ [6] = {1003, "NW", 1, 3, 7, true, 150};
+ },
+ win_viewport = {
+ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [6] = {win = 1003, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000
+ },
+ [4] = {
+ bottom = 1,
+ left = 1,
+ right = 1,
+ top = 1,
+ win = 1001
+ },
+ [6] = {
+ bottom = 1,
+ left = 1,
+ right = 1,
+ top = 1,
+ win = 1003
+ }
+ },
+ })
+ else
+ screen:expect([[
+ ^ |
+ {0:~ }{5:╔═┌──────}{11:win_150}{5:───────┐}{0: }|
+ {0:~ }{5:║}{7: }{5:│}{8: }{5:│}{0: }|
+ {0:~ }{5:║}{7:~}{5:│}{8:~ }{5:│}{0: }|*2
+ {0:~ }{5:╚═└────────────────────┘}{0: }|
+ |
+ ]])
+ end
end)
it('can use winbar', function()
@@ -9523,4 +9854,3 @@ describe('float window', function()
with_ext_multigrid(false)
end)
end)
-
diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua
index f8f5ee9488..e10c79fa48 100644
--- a/test/functional/ui/hlstate_spec.lua
+++ b/test/functional/ui/hlstate_spec.lua
@@ -224,10 +224,10 @@ describe('ext_hlstate detailed highlights', function()
[6] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { 5, 1 } },
[7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } },
})
- command(("enew | call termopen(['%s'])"):format(testprg('tty-test')))
+ command(("enew | call jobstart(['%s'],{'term':v:true})"):format(testprg('tty-test')))
screen:expect([[
^tty ready |
- {1: } |
+ |
|*5
{7: }|
]])
@@ -242,7 +242,7 @@ describe('ext_hlstate detailed highlights', function()
screen:expect([[
^tty ready |
x {5:y z} |
- {1: } |
+ |
|*4
{7: }|
]])
@@ -250,7 +250,7 @@ describe('ext_hlstate detailed highlights', function()
screen:expect([[
^tty ready |
x {2:y }{3:z} |
- {1: } |
+ |
|*4
{7: }|
]])
@@ -268,7 +268,7 @@ describe('ext_hlstate detailed highlights', function()
else
screen:expect([[
^tty ready |
- x {4:y}{2: }{3:z} |
+ x {2:y }{3:z} |
|*5
{7: }|
]])
diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua
index 2d26d2c5e0..3eee9a6e07 100644
--- a/test/functional/ui/inccommand_user_spec.lua
+++ b/test/functional/ui/inccommand_user_spec.lua
@@ -253,6 +253,50 @@ describe("'inccommand' for user commands", function()
]]
end)
+ it("can preview 'nomodifiable' buffer", function()
+ exec_lua([[
+ vim.api.nvim_create_user_command("PreviewTest", function() end, {
+ preview = function(ev)
+ vim.bo.modifiable = true
+ vim.api.nvim_buf_set_lines(0, 0, -1, false, {"cats"})
+ return 2
+ end,
+ })
+ ]])
+ command('set inccommand=split')
+
+ command('set nomodifiable')
+ eq(false, api.nvim_get_option_value('modifiable', { buf = 0 }))
+
+ feed(':PreviewTest')
+
+ screen:expect([[
+ cats |
+ {1:~ }|*8
+ {3:[No Name] [+] }|
+ |
+ {1:~ }|*4
+ {2:[Preview] }|
+ :PreviewTest^ |
+ ]])
+ feed('<Esc>')
+ screen:expect([[
+ text on line 1 |
+ more text on line 2 |
+ oh no, even more text |
+ will the text ever stop |
+ oh well |
+ did the text stop |
+ why won't it stop |
+ make the text stop |
+ ^ |
+ {1:~ }|*7
+ |
+ ]])
+
+ eq(false, api.nvim_get_option_value('modifiable', { buf = 0 }))
+ end)
+
it('works with inccommand=nosplit', function()
command('set inccommand=nosplit')
feed(':Replace text cats')
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 90e0b3e380..98312c42c9 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -368,7 +368,7 @@ describe('input non-printable chars', function()
"Xtest-overwrite" |
{9:WARNING: The file has been changed since reading it!!!} |
{6:Do you really want to write to it (y/n)?}u |
- {6:Do you really want to write to it (y/n)?} |
+ {6:Do you really want to write to it (y/n)?}{18:^E} |
{6:Do you really want to write to it (y/n)?}^ |
]])
@@ -379,7 +379,7 @@ describe('input non-printable chars', function()
"Xtest-overwrite" |
{9:WARNING: The file has been changed since reading it!!!} |
{6:Do you really want to write to it (y/n)?}u |
- {6:Do you really want to write to it (y/n)?} |
+ {6:Do you really want to write to it (y/n)?}{18:^E} |
{6:Do you really want to write to it (y/n)?}n |
{6:Press ENTER or type command to continue}^ |
]])
diff --git a/test/functional/ui/linematch_spec.lua b/test/functional/ui/linematch_spec.lua
index b564c01eaa..3593604c49 100644
--- a/test/functional/ui/linematch_spec.lua
+++ b/test/functional/ui/linematch_spec.lua
@@ -1147,4 +1147,32 @@ describe('regressions', function()
},
}
end)
+
+ -- oldtest: Test_linematch_3diffs_sanity_check()
+ it('sanity check with 3 diff buffers', function()
+ clear()
+ screen = Screen.new(75, 20)
+ n.api.nvim_buf_set_lines(0, 0, -1, false, { 'abcd', 'def', 'hij' })
+ n.exec('rightbelow vnew')
+ n.api.nvim_buf_set_lines(0, 0, -1, false, { 'defq', 'hijk', 'nopq' })
+ n.exec('rightbelow vnew')
+ n.api.nvim_buf_set_lines(0, 0, -1, false, { 'hijklm', 'nopqr', 'stuv' })
+ n.exec([[
+ set diffopt+=linematch:60
+ windo diffthis | wincmd t
+ call feedkeys("Aq\<esc>")
+ call feedkeys("GAklm\<esc>")
+ call feedkeys("o")
+ ]])
+ screen:expect([[
+ {7: }{22:abcdq }│{7: }{23:----------------------}│{7: }{23:-----------------------}|
+ {7: }{4:def }│{7: }{4:def}{27:q}{4: }│{7: }{23:-----------------------}|
+ {7: }{4:hijk}{27:lm}{4: }│{7: }{4:hijk }│{7: }{4:hijk}{27:lm}{4: }|
+ {7: }{23:----------------------}│{7: }{4:nopq }│{7: }{4:nopq}{27:r}{4: }|
+ {7: }{4:^ }│{7: }{23:----------------------}│{7: }{27:stuv}{4: }|
+ {1:~ }│{1:~ }│{1:~ }|*13
+ {3:[No Name] [+] }{2:[No Name] [+] [No Name] [+] }|
+ {5:-- INSERT --} |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
index 734877d262..5c55dfe910 100644
--- a/test/functional/ui/messages_spec.lua
+++ b/test/functional/ui/messages_spec.lua
@@ -42,104 +42,130 @@ describe('ui/ext_messages', function()
it('msg_clear follows msg_show kind of confirm', function()
feed('iline 1<esc>')
feed(':call confirm("test")<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- line ^1 |
- {1:~ }|*4
- ]],
+ line ^1 |
+ {1:~ }|*4
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 10,
+ pos = 0,
+ prompt = '[O]k: ',
+ },
+ },
messages = {
{
- content = { { '\ntest\n[O]k: ', 6, 11 } },
+ content = { { '\ntest\n', 6, 10 } },
+ history = false,
kind = 'confirm',
},
},
- }
-
+ })
feed('<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- line ^1 |
- {1:~ }|*4
- ]],
- }
+ line ^1 |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
end)
- it('msg_show kind=confirm,confirm_sub,emsg,wmsg,quickfix', function()
+ it('msg_show kinds', function()
feed('iline 1\nline 2<esc>')
- -- kind=confirm
+ -- confirm is now cmdline prompt
feed(':echo confirm("test")<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- line 1 |
- line ^2 |
- {1:~ }|*3
- ]],
+ line 1 |
+ line ^2 |
+ {1:~ }|*3
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 10,
+ pos = 0,
+ prompt = '[O]k: ',
+ },
+ },
messages = {
{
- content = { { '\ntest\n[O]k: ', 6, 11 } },
+ content = { { '\ntest\n', 6, 10 } },
+ history = false,
kind = 'confirm',
},
},
- }
- feed('<cr><cr>')
- screen:expect {
+ })
+ feed('<cr>')
+ screen:expect({
grid = [[
- line 1 |
- line ^2 |
- {1:~ }|*3
- ]],
+ line 1 |
+ line ^2 |
+ {1:~ }|*3
+ ]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { '\ntest\n[O]k: ', 6, 11 } },
+ content = { { '\ntest\n', 6, 10 } },
+ history = false,
kind = 'confirm',
},
{
content = { { '1' } },
+ history = false,
kind = 'echo',
},
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
- }
- feed('<cr><cr>')
+ })
+ feed('<cr>')
- -- kind=confirm_sub
+ -- :substitute confirm is now cmdline prompt
feed(':%s/i/X/gc<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- l{2:i}ne 1 |
- l{10:i}ne ^2 |
- {1:~ }|*3
- ]],
- messages = {
+ l{2:^i}ne 1 |
+ l{10:i}ne 2 |
+ {1:~ }|*3
+ ]],
+ cmdline = {
{
- content = { { 'replace with X (y/n/a/q/l/^E/^Y)?', 6, 19 } },
- kind = 'confirm_sub',
+ content = { { '' } },
+ hl_id = 18,
+ pos = 0,
+ prompt = 'replace with X? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)',
},
},
- }
+ })
feed('nq')
-- kind=wmsg (editing readonly file)
command('write ' .. fname)
command('set readonly nohls')
feed('G$x')
- screen:expect {
+ screen:expect({
grid = [[
line 1 |
- line ^2 |
+ line^ |
{1:~ }|*3
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'W10: Warning: Changing a readonly file', 19, 27 } },
+ content = { { 'W10: Warning: Changing a readonly file', 19, 26 } },
+ history = true,
kind = 'wmsg',
},
},
- }
+ })
-- kind=wmsg ('wrapscan' after search reaches EOF)
feed('uG$/i<cr>')
@@ -149,9 +175,11 @@ describe('ui/ext_messages', function()
line 2 |
{1:~ }|*3
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'search hit BOTTOM, continuing at TOP', 19, 27 } },
+ content = { { 'search hit BOTTOM, continuing at TOP', 19, 26 } },
+ history = true,
kind = 'wmsg',
},
},
@@ -160,22 +188,21 @@ describe('ui/ext_messages', function()
-- kind=emsg after :throw
feed(':throw "foo"<cr>')
screen:expect {
- grid = [[
- l^ine 1 |
- line 2 |
- {1:~ }|*3
- ]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'Error detected while processing :', 9, 7 } },
+ content = { { 'Error detected while processing :', 9, 6 } },
+ history = true,
kind = 'emsg',
},
{
- content = { { 'E605: Exception not caught: foo', 9, 7 } },
- kind = '',
+ content = { { 'E605: Exception not caught: foo', 9, 6 } },
+ history = true,
+ kind = 'emsg',
},
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -191,13 +218,213 @@ describe('ui/ext_messages', function()
^line 2 |
{1:~ }|*3
]],
+ cmdline = { { abort = false } },
messages = {
{
content = { { '(2 of 2): line2' } },
+ history = true,
kind = 'quickfix',
},
},
}
+
+ -- search_cmd
+ feed('?line<cr>')
+ screen:expect({
+ grid = [[
+ ^line 1 |
+ line 2 |
+ {1:~ }|*3
+ ]],
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { '?line ' } },
+ history = false,
+ kind = 'search_cmd',
+ },
+ },
+ })
+
+ -- highlight
+ feed(':filter character highlight<CR>')
+ screen:expect({
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = {
+ { '\n@character ' },
+ { 'xxx', 26, 155 },
+ { ' ' },
+ { 'links to', 18, 5 },
+ { ' Character\n@character.special ' },
+ { 'xxx', 16, 156 },
+ { ' ' },
+ { 'links to', 18, 5 },
+ { ' SpecialChar' },
+ },
+ history = false,
+ kind = 'list_cmd',
+ },
+ },
+ })
+
+ -- undo
+ feed('uu')
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*4
+ ]],
+ messages = {
+ {
+ content = { { 'Already at oldest change' } },
+ history = true,
+ kind = 'undo',
+ },
+ },
+ })
+
+ feed('<C-r><C-r><C-r>')
+ screen:expect({
+ grid = [[
+ line 1 |
+ line^ |
+ {1:~ }|*3
+ ]],
+ messages = {
+ {
+ content = { { 'Already at newest change' } },
+ history = true,
+ kind = 'undo',
+ },
+ },
+ })
+
+ -- kind=completion
+ command('set noshowmode')
+ feed('i<C-n>')
+ screen:expect({
+ messages = {
+ {
+ content = { { 'The only match' } },
+ history = false,
+ kind = 'completion',
+ },
+ },
+ })
+ feed('<Esc>')
+ command('set showmode')
+
+ -- kind=echoerr for nvim_echo() err
+ feed(':call nvim_echo([["Error"], ["Message", "Special"]], 1, #{ err:1 })<CR>')
+ screen:expect({
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { 'Error', 9, 6 }, { 'Message', 16, 99 } },
+ history = true,
+ kind = 'echoerr',
+ },
+ },
+ })
+
+ -- kind=verbose for nvim_echo() verbose
+ feed(':call nvim_echo([["Verbose Message"]], 1, #{ verbose:1 })<CR>')
+ screen:expect({
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { 'Verbose Message' } },
+ history = true,
+ kind = 'verbose',
+ },
+ },
+ })
+
+ -- kind=verbose for :verbose messages
+ feed(':1verbose filter Diff[AC] hi<CR>')
+ screen:expect({
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = {
+ { '\nDiffAdd ' },
+ { 'xxx', 22, 30 },
+ { ' ' },
+ { 'ctermbg=', 18, 5 },
+ { '81 ' },
+ { 'guibg=', 18, 5 },
+ { 'LightBlue' },
+ },
+ history = false,
+ kind = 'list_cmd',
+ },
+ {
+ content = { { '\n\tLast set from Lua (run Nvim with -V1 for more details)' } },
+ history = false,
+ kind = 'verbose',
+ },
+ {
+ content = {
+ { '\nDiffChange ' },
+ { 'xxx', 4, 31 },
+ { ' ' },
+ { 'ctermbg=', 18, 5 },
+ { '225 ' },
+ { 'guibg=', 18, 5 },
+ { 'LightMagenta' },
+ },
+ history = false,
+ kind = 'list_cmd',
+ },
+ {
+ content = { { '\n\tLast set from Lua (run Nvim with -V1 for more details)' } },
+ history = false,
+ kind = 'verbose',
+ },
+ {
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
+ kind = 'return_prompt',
+ },
+ },
+ })
+
+ -- kind=shell for :!cmd messages
+ local cmd = t.is_os('win') and 'echo stdout& echo stderr>&2& exit 3'
+ or '{ echo stdout; echo stderr >&2; exit 3; }'
+ feed(('<CR>:!%s<CR>'):format(cmd))
+ screen:expect({
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { (':!%s\r\n[No write since last change]\n'):format(cmd) } },
+ history = false,
+ kind = '',
+ },
+ {
+ content = { { ('stdout%s\n'):format(t.is_os('win') and '\r' or '') } },
+ history = false,
+ kind = 'shell_out',
+ },
+ {
+ content = { { ('stderr%s\n'):format(t.is_os('win') and '\r' or ''), 9, 6 } },
+ history = false,
+ kind = 'shell_err',
+ },
+ {
+ content = { { '\nshell returned 3\n\n' } },
+ history = false,
+ kind = 'shell_ret',
+ },
+ {
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
+ kind = 'return_prompt',
+ },
+ },
+ })
end)
it(':echoerr', function()
@@ -207,10 +434,14 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- messages = { {
- content = { { 'raa', 9, 7 } },
- kind = 'echoerr',
- } },
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { 'raa', 9, 6 } },
+ history = true,
+ kind = 'echoerr',
+ },
+ },
}
-- cmdline in a later input cycle clears error message
@@ -233,17 +464,21 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'bork', 9, 7 } },
+ content = { { 'bork', 9, 6 } },
+ history = true,
kind = 'echoerr',
},
{
- content = { { 'fail', 9, 7 } },
+ content = { { 'fail', 9, 6 } },
+ history = true,
kind = 'echoerr',
},
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -255,21 +490,26 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'bork', 9, 7 } },
+ content = { { 'bork', 9, 6 } },
+ history = true,
kind = 'echoerr',
},
{
- content = { { 'fail', 9, 7 } },
+ content = { { 'fail', 9, 6 } },
+ history = true,
kind = 'echoerr',
},
{
- content = { { 'extrafail', 9, 7 } },
+ content = { { 'extrafail', 9, 6 } },
+ history = true,
kind = 'echoerr',
},
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -290,13 +530,17 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- messages = { {
- content = { { 'problem', 9, 7 } },
- kind = 'echoerr',
- } },
+ messages = {
+ {
+ content = { { 'problem', 9, 6 } },
+ history = true,
+ kind = 'echoerr',
+ },
+ },
cmdline = {
{
prompt = 'foo> ',
+ hl_id = 0,
content = { { '' } },
pos = 0,
},
@@ -309,6 +553,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
}
eq('solution', eval('x'))
@@ -318,16 +563,18 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
msg_history = {
- { kind = 'echoerr', content = { { 'raa', 9, 7 } } },
- { kind = 'echoerr', content = { { 'bork', 9, 7 } } },
- { kind = 'echoerr', content = { { 'fail', 9, 7 } } },
- { kind = 'echoerr', content = { { 'extrafail', 9, 7 } } },
- { kind = 'echoerr', content = { { 'problem', 9, 7 } } },
+ { kind = 'echoerr', content = { { 'raa', 9, 6 } } },
+ { kind = 'echoerr', content = { { 'bork', 9, 6 } } },
+ { kind = 'echoerr', content = { { 'fail', 9, 6 } } },
+ { kind = 'echoerr', content = { { 'extrafail', 9, 6 } } },
+ { kind = 'echoerr', content = { { 'problem', 9, 6 } } },
},
messages = {
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -350,9 +597,11 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'bork\nfail', 9, 7 } },
+ content = { { 'bork\nfail', 9, 6 } },
+ history = true,
kind = 'echoerr',
},
},
@@ -364,15 +613,17 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
msg_history = {
{
- content = { { 'bork\nfail', 9, 7 } },
+ content = { { 'bork\nfail', 9, 6 } },
kind = 'echoerr',
},
},
@@ -390,8 +641,9 @@ describe('ui/ext_messages', function()
{10:line} 2 |
{1:~ }|*3
]],
+ cmdline = { { abort = false } },
messages = {
- { content = { { '/line W [1/2]' } }, kind = 'search_count' },
+ { content = { { '/line W [1/2]' } }, kind = 'search_count', history = false },
},
}
@@ -403,35 +655,7 @@ describe('ui/ext_messages', function()
{1:~ }|*3
]],
messages = {
- { content = { { '/line [2/2]' } }, kind = 'search_count' },
- },
- }
- end)
-
- it(':hi Group output', function()
- feed(':hi ErrorMsg<cr>')
- screen:expect {
- grid = [[
- ^ |
- {1:~ }|*4
- ]],
- messages = {
- {
- content = {
- { '\nErrorMsg ' },
- { 'xxx', 9, 7 },
- { ' ' },
- { 'ctermfg=', 18, 6 },
- { '15 ' },
- { 'ctermbg=', 18, 6 },
- { '1 ' },
- { 'guifg=', 18, 6 },
- { 'White ' },
- { 'guibg=', 18, 6 },
- { 'Red' },
- },
- kind = '',
- },
+ { content = { { '/line [2/2]' } }, kind = 'search_count', history = false },
},
}
end)
@@ -444,11 +668,13 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
- { content = { { 'x #1' } }, kind = '' },
- { content = { { 'y #2' } }, kind = '' },
+ { content = { { 'x #1' } }, kind = 'list_cmd', history = false },
+ { content = { { 'y #2' } }, kind = 'list_cmd', history = false },
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -463,7 +689,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { '-- INSERT --', 5, 12 } },
+ showmode = { { '-- INSERT --', 5, 11 } },
}
feed('alphpabet<cr>alphanum<cr>')
@@ -474,7 +700,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*2
]],
- showmode = { { '-- INSERT --', 5, 12 } },
+ showmode = { { '-- INSERT --', 5, 11 } },
}
feed('<c-x>')
@@ -485,7 +711,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*2
]],
- showmode = { { '-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)', 5, 12 } },
+ showmode = { { '-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)', 5, 11 } },
}
feed('<c-p>')
@@ -501,7 +727,7 @@ describe('ui/ext_messages', function()
items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } },
pos = 1,
},
- showmode = { { '-- Keyword Local completion (^N^P) ', 5, 12 }, { 'match 1 of 2', 6, 19 } },
+ showmode = { { '-- Keyword Local completion (^N^P) ', 5, 11 }, { 'match 1 of 2', 6, 18 } },
}
-- echomsg and showmode don't overwrite each other, this is the same
@@ -519,11 +745,14 @@ describe('ui/ext_messages', function()
items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } },
pos = 1,
},
- messages = { {
- content = { { 'stuff' } },
- kind = 'echomsg',
- } },
- showmode = { { '-- Keyword Local completion (^N^P) ', 5, 12 }, { 'match 1 of 2', 6, 19 } },
+ messages = {
+ {
+ content = { { 'stuff' } },
+ history = true,
+ kind = 'echomsg',
+ },
+ },
+ showmode = { { '-- Keyword Local completion (^N^P) ', 5, 11 }, { 'match 1 of 2', 6, 18 } },
}
feed('<c-p>')
@@ -539,11 +768,14 @@ describe('ui/ext_messages', function()
items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } },
pos = 0,
},
- messages = { {
- content = { { 'stuff' } },
- kind = 'echomsg',
- } },
- showmode = { { '-- Keyword Local completion (^N^P) ', 5, 12 }, { 'match 2 of 2', 6, 19 } },
+ messages = {
+ {
+ content = { { 'stuff' } },
+ history = true,
+ kind = 'echomsg',
+ },
+ },
+ showmode = { { '-- Keyword Local completion (^N^P) ', 5, 11 }, { 'match 2 of 2', 6, 18 } },
}
feed('<esc>:messages<cr>')
@@ -554,13 +786,15 @@ describe('ui/ext_messages', function()
alphpabe^t |
{1:~ }|*2
]],
+ cmdline = { { abort = false } },
msg_history = { {
content = { { 'stuff' } },
kind = 'echomsg',
} },
messages = {
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -574,7 +808,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { 'recording @q', 5, 12 } },
+ showmode = { { 'recording @q', 5, 11 } },
}
feed('i')
@@ -583,7 +817,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { '-- INSERT --recording @q', 5, 12 } },
+ showmode = { { '-- INSERT --recording @q', 5, 11 } },
}
feed('<esc>')
@@ -592,7 +826,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { 'recording @q', 5, 12 } },
+ showmode = { { 'recording @q', 5, 11 } },
}
feed('q')
@@ -611,7 +845,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { 'recording @q', 5, 12 } },
+ showmode = { { 'recording @q', 5, 11 } },
mode = 'normal',
}
@@ -621,7 +855,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { 'recording @q', 5, 12 } },
+ showmode = { { 'recording @q', 5, 11 } },
mode = 'insert',
}
@@ -631,7 +865,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { 'recording @q', 5, 12 } },
+ showmode = { { 'recording @q', 5, 11 } },
mode = 'normal',
}
@@ -653,7 +887,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- ruler = { { '0,0-1 All', 9, 62 } },
+ ruler = { { '0,0-1 All', 9, 61 } },
})
command('hi clear MsgArea')
feed('i')
@@ -662,7 +896,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- showmode = { { '-- INSERT --', 5, 12 } },
+ showmode = { { '-- INSERT --', 5, 11 } },
ruler = { { '0,1 All' } },
}
feed('abcde<cr>12345<esc>')
@@ -700,7 +934,7 @@ describe('ui/ext_messages', function()
{17:123}45 |
{1:~ }|*3
]],
- showmode = { { '-- VISUAL BLOCK --', 5, 12 } },
+ showmode = { { '-- VISUAL BLOCK --', 5, 11 } },
showcmd = { { '2x3' } },
ruler = { { '1,3 All' } },
})
@@ -752,10 +986,14 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- messages = { {
- content = { { 'howdy' } },
- kind = 'echomsg',
- } },
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { 'howdy' } },
+ history = true,
+ kind = 'echomsg',
+ },
+ },
}
-- always test a message without kind. If this one gets promoted to a
@@ -769,6 +1007,7 @@ describe('ui/ext_messages', function()
messages = {
{
content = { { 'Type :qa and press <Enter> to exit Nvim' } },
+ history = true,
kind = '',
},
},
@@ -780,10 +1019,14 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- messages = { {
- content = { { 'bork', 9, 7 } },
- kind = 'echoerr',
- } },
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { 'bork', 9, 6 } },
+ history = true,
+ kind = 'echoerr',
+ },
+ },
}
feed(':echo "xyz"<cr>')
@@ -792,10 +1035,14 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
- messages = { {
- content = { { 'xyz' } },
- kind = 'echo',
- } },
+ cmdline = { { abort = false } },
+ messages = {
+ {
+ content = { { 'xyz' } },
+ history = false,
+ kind = 'echo',
+ },
+ },
}
feed(':call nosuchfunction()<cr>')
@@ -804,9 +1051,11 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'E117: Unknown function: nosuchfunction', 9, 7 } },
+ content = { { 'E117: Unknown function: nosuchfunction', 9, 6 } },
+ history = true,
kind = 'emsg',
},
},
@@ -818,15 +1067,17 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
msg_history = {
{ kind = 'echomsg', content = { { 'howdy' } } },
{ kind = '', content = { { 'Type :qa and press <Enter> to exit Nvim' } } },
- { kind = 'echoerr', content = { { 'bork', 9, 7 } } },
- { kind = 'emsg', content = { { 'E117: Unknown function: nosuchfunction', 9, 7 } } },
+ { kind = 'echoerr', content = { { 'bork', 9, 6 } } },
+ { kind = 'emsg', content = { { 'E117: Unknown function: nosuchfunction', 9, 6 } } },
},
messages = {
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -851,11 +1102,14 @@ describe('ui/ext_messages', function()
}
feed('<cr>')
- screen:expect([[
- ^ |
- {1:~ }|*3
- |
- ]])
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*3
+ |
+ ]],
+ cmdline = { { abort = false } },
+ })
eq(1, eval('&cmdheight'))
feed(':set cmdheight=0')
@@ -874,10 +1128,13 @@ describe('ui/ext_messages', function()
},
}
feed('<cr>')
- screen:expect([[
- ^ |
- {1:~ }|*4
- ]])
+ screen:expect({
+ grid = [[
+ ^ |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
eq(0, eval('&cmdheight'))
end)
@@ -888,6 +1145,7 @@ describe('ui/ext_messages', function()
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
content = {
@@ -899,9 +1157,10 @@ stack traceback:
[C]: in function 'error'
[string ":lua"]:1: in main chunk]],
9,
- 7,
+ 6,
},
},
+ history = true,
kind = 'lua_error',
},
},
@@ -916,11 +1175,13 @@ stack traceback:
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
content = {
- { "Error invoking 'test_method' on channel 1:\ncomplete\nerror\n\nmessage", 9, 7 },
+ { "Error invoking 'test_method' on channel 1:\ncomplete\nerror\n\nmessage", 9, 6 },
},
+ history = true,
kind = 'rpc_error',
},
},
@@ -940,6 +1201,7 @@ stack traceback:
feed(':map<cr>')
screen:expect {
+ cmdline = { { abort = false } },
messages = {
{
content = {
@@ -947,7 +1209,8 @@ stack traceback:
{ '*', 18, 1 },
{ ' k' },
},
- kind = '',
+ history = false,
+ kind = 'list_cmd',
},
},
}
@@ -964,10 +1227,13 @@ stack traceback:
^ |
{1:~ }|*6
]],
- messages = { {
- content = { { 'wildmenu wildmode' } },
- kind = '',
- } },
+ messages = {
+ {
+ content = { { 'wildmenu wildmode\n' } },
+ history = false,
+ kind = 'wildlist',
+ },
+ },
cmdline = {
{
firstc = ':',
@@ -983,51 +1249,94 @@ stack traceback:
feed('ihelllo<esc>')
feed('z=')
- screen:expect {
+ screen:expect({
grid = [[
- {100:helllo} |
- {1:~ }|*3
- {1:^~ }|
- ]],
+ {100:^helllo} |
+ {1:~ }|*4
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 0,
+ pos = 0,
+ prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ',
+ },
+ },
messages = {
{
- content = {
- {
- 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ',
- },
- },
- kind = '',
+ content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\n' } },
+ history = false,
+ kind = 'list_cmd',
},
},
- }
+ })
feed('1')
- screen:expect {
+ screen:expect({
grid = [[
- {100:helllo} |
- {1:~ }|*3
- {1:^~ }|
- ]],
+ {100:^helllo} |
+ {1:~ }|*4
+ ]],
+ cmdline = {
+ {
+ content = { { '1' } },
+ hl_id = 0,
+ pos = 1,
+ prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ',
+ },
+ },
messages = {
{
- content = {
- {
- 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ',
- },
- },
- kind = '',
+ content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\n' } },
+ history = false,
+ kind = 'list_cmd',
},
- { content = { { '1' } }, kind = '' },
},
- }
+ })
feed('<cr>')
- screen:expect {
+ screen:expect({
grid = [[
- ^Hello |
- {1:~ }|*4
- ]],
- }
+ ^Hello |
+ {1:~ }|*4
+ ]],
+ cmdline = { { abort = false } },
+ })
+
+ async_meths.nvim_command("let g:n = inputlist(['input0', 'input1'])")
+ screen:expect({
+ grid = [[
+ ^Hello |
+ {1:~ }|*4
+ ]],
+ cmdline = {
+ {
+ content = { { '' } },
+ hl_id = 0,
+ pos = 0,
+ prompt = 'Type number and <Enter> or click with the mouse (q or empty cancels): ',
+ },
+ },
+ messages = {
+ {
+ content = { { 'input0\ninput1\n' } },
+ history = false,
+ kind = 'list_cmd',
+ },
+ },
+ })
+
+ feed('42<CR>')
+ screen:expect({
+ grid = [[
+ ^Hello |
+ {1:~ }|*4
+ ]],
+ cmdline = { {
+ abort = false,
+ } },
+ })
+ eq(42, eval('g:n'))
end)
it('supports nvim_echo messages with multiple attrs', function()
@@ -1043,7 +1352,8 @@ stack traceback:
]],
messages = {
{
- content = { { 'wow, ', 10, 9 }, { 'such\n\nvery ', 9, 7 }, { 'color', 8, 13 } },
+ content = { { 'wow, ', 10, 8 }, { 'such\n\nvery ', 9, 6 }, { 'color', 8, 12 } },
+ history = true,
kind = 'echomsg',
},
},
@@ -1055,8 +1365,13 @@ stack traceback:
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
- { content = { { '\n 1 %a "[No Name]" line 1' } }, kind = '' },
+ {
+ content = { { '\n 1 %a "[No Name]" line 1' } },
+ kind = 'list_cmd',
+ history = false,
+ },
},
}
@@ -1066,15 +1381,17 @@ stack traceback:
^ |
{1:~ }|*4
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
msg_history = {
{
- content = { { 'wow, ', 10, 9 }, { 'such\n\nvery ', 9, 7 }, { 'color', 8, 13 } },
+ content = { { 'wow, ', 10, 8 }, { 'such\n\nvery ', 9, 6 }, { 'color', 8, 12 } },
kind = 'echomsg',
},
},
@@ -1093,7 +1410,11 @@ stack traceback:
command('write ' .. fname)
screen:expect({
messages = {
- { content = { { string.format('"%s" [New] 0L, 0B written', fname) } }, kind = '' },
+ {
+ content = { { string.format('"%s" [New] 0L, 0B written', fname) } },
+ kind = 'bufwrite',
+ history = true,
+ },
},
})
end)
@@ -1105,13 +1426,25 @@ stack traceback:
screen_showmode(...)
showmode = showmode + 1
end
+ local s1 = [[
+ ^ |
+ {1:~ }|*4
+ ]]
+ screen:expect(s1)
+ eq(showmode, 0)
+ feed('i')
screen:expect({
- grid = [[
- ^ |
- {1:~ }|*4
- ]],
+ grid = s1,
+ showmode = { { '-- INSERT --', 5, 11 } },
})
- eq(showmode, 1)
+ eq(showmode, 2)
+ command('set noshowmode')
+ feed('<Esc>')
+ screen:expect(s1)
+ eq(showmode, 3)
+ feed('i')
+ screen:expect_unchanged()
+ eq(showmode, 3)
end)
it('emits single message for multiline print())', function()
@@ -1120,6 +1453,7 @@ stack traceback:
messages = {
{
content = { { 'foo\nbar\nbaz' } },
+ history = true,
kind = 'lua_print',
},
},
@@ -1133,6 +1467,7 @@ stack traceback:
messages = {
{
content = { { '{\n foo = "bar"\n}' } },
+ history = true,
kind = 'lua_print',
},
},
@@ -1140,6 +1475,36 @@ stack traceback:
exec_lua([[vim.print({ foo = "bar" })]])
screen:expect_unchanged()
end)
+
+ it('ruler redraw does not crash due to double grid_line_start()', function()
+ exec_lua([[
+ local ns = vim.api.nvim_create_namespace('')
+ vim.ui_attach(ns, { ext_messages = true }, function(event, ...)
+ if event == 'msg_ruler' then
+ vim.api.nvim__redraw({ flush = true })
+ end
+ end)
+ vim.o.ruler = true
+ vim.o.laststatus = 0
+ ]])
+ feed('i')
+ n.assert_alive()
+ end)
+
+ it(':digraph contains newlines', function()
+ command('digraph')
+ screen:expect({
+ condition = function()
+ local nl = 0
+ eq('list_cmd', screen.messages[1].kind)
+ for _, chunk in ipairs(screen.messages[1].content) do
+ nl = nl + (chunk[2]:find('\n') and 1 or 0)
+ end
+ eq(682, nl)
+ screen.messages = {}
+ end,
+ })
+ end)
end)
describe('ui/builtin messages', function()
@@ -1719,7 +2084,7 @@ describe('ui/ext_messages', function()
{1:~ }type :help iccf{18:<Enter>} for information {1: }|
{1:~ }|*5
]]
- local showmode = { { '-- INSERT --', 5, 12 } }
+ local showmode = { { '-- INSERT --', 5, 11 } }
screen:expect(introscreen)
-- <c-l> (same as :mode) does _not_ clear intro message
@@ -1792,9 +2157,11 @@ describe('ui/ext_messages', function()
type :help iccf{18:<Enter>} for information |
|*5
]],
+ cmdline = { { abort = false } },
messages = {
{
- content = { { 'Press ENTER or type command to continue', 6, 19 } },
+ content = { { 'Press ENTER or type command to continue', 6, 18 } },
+ history = false,
kind = 'return_prompt',
},
},
@@ -1874,8 +2241,9 @@ describe('ui/ext_messages', function()
{1:~ }|*10
{3:[No Name] }|
]],
+ cmdline = { { abort = false } },
messages = {
- { content = { { ' cmdheight=0' } }, kind = '' },
+ { content = { { ' cmdheight=0' } }, kind = 'list_cmd', history = false },
},
})
@@ -1890,8 +2258,9 @@ describe('ui/ext_messages', function()
{1:~ }|*9
{3:[No Name] }|
]],
+ cmdline = { { abort = false } },
messages = {
- { content = { { ' laststatus=3' } }, kind = '' },
+ { content = { { ' laststatus=3' } }, kind = 'list_cmd', history = false },
},
})
@@ -1910,8 +2279,9 @@ describe('ui/ext_messages', function()
{1:~ }|*10
{3:[No Name] }|
]],
+ cmdline = { { abort = false } },
messages = {
- { content = { { ' cmdheight=0' } }, kind = '' },
+ { content = { { ' cmdheight=0' } }, kind = 'list_cmd', history = false },
},
})
end)
@@ -2015,7 +2385,7 @@ describe('ui/msg_puts_printf', function()
)
cmd = cmd .. '"' .. nvim_prog .. '" -u NONE -i NONE -Es -V1'
- command([[call termopen(']] .. cmd .. [[')]])
+ command([[call jobstart(']] .. cmd .. [[',{'term':v:true})]])
screen:expect([[
^Exモードに入ります。ノー |
マルモードに戻るには "vis|
diff --git a/test/functional/ui/mode_spec.lua b/test/functional/ui/mode_spec.lua
index 8c6a284cd6..01f4dda227 100644
--- a/test/functional/ui/mode_spec.lua
+++ b/test/functional/ui/mode_spec.lua
@@ -94,6 +94,46 @@ describe('ui mode_change event', function()
}
end)
+ -- oldtest: Test_mouse_shape_indent_norm_with_gq()
+ it('is restored to Normal mode after "gq" indents using :normal #12309', function()
+ screen:try_resize(60, 6)
+ n.exec([[
+ func Indent()
+ exe "normal! \<Ignore>"
+ return 0
+ endfunc
+
+ setlocal indentexpr=Indent()
+ call setline(1, [repeat('a', 80), repeat('b', 80)])
+ ]])
+
+ feed('ggVG')
+ screen:expect {
+ grid = [[
+ {17:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {17:aaaaaaaaaaaaaaaaaaaa} |
+ ^b{17:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}|
+ {17:bbbbbbbbbbbbbbbbbbbb} |
+ {1:~ }|
+ {5:-- VISUAL LINE --} |
+ ]],
+ mode = 'visual',
+ }
+
+ feed('gq')
+ screen:expect {
+ grid = [[
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ aaaaaaaaaaaaaaaaaaaa |
+ ^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbb |
+ {1:~ }|
+ |
+ ]],
+ mode = 'normal',
+ }
+ end)
+
it('works in insert mode', function()
feed('i')
screen:expect {
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index 3afda0c4af..cac7174cb6 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -1094,7 +1094,7 @@ describe('ext_multigrid', function()
end)
it('supports mouse', function()
- command('autocmd! nvim_popupmenu') -- Delete the default MenuPopup event handler.
+ command('autocmd! nvim.popupmenu') -- Delete the default MenuPopup event handler.
insert('some text\nto be clicked')
screen:expect{grid=[[
## grid 1
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
index b5a09d814c..37e0e1344b 100644
--- a/test/functional/ui/output_spec.lua
+++ b/test/functional/ui/output_spec.lua
@@ -34,7 +34,7 @@ describe('shell command :!', function()
n.nvim_set .. ' notermguicolors',
})
screen:expect([[
- {1: } |
+ ^ |
{4:~ }|*4
|
{3:-- TERMINAL --} |
@@ -78,7 +78,7 @@ describe('shell command :!', function()
29999: foo |
30000: foo |
|
- {10:Press ENTER or type command to continue}{1: } |
+ {10:Press ENTER or type command to continue}^ |
{3:-- TERMINAL --} |
]],
{
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 8fe8975b4a..4c5b1d2bd2 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -841,7 +841,7 @@ describe('ui/ext_popupmenu', function()
aunmenu PopUp
" Delete the default MenuPopup event handler.
- autocmd! nvim_popupmenu
+ autocmd! nvim.popupmenu
menu PopUp.foo :let g:menustr = 'foo'<CR>
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
@@ -1162,6 +1162,8 @@ describe('builtin popupmenu', function()
[6] = { foreground = Screen.colors.White, background = Screen.colors.Red },
[7] = { background = Screen.colors.Yellow }, -- Search
[8] = { foreground = Screen.colors.Red },
+ [9] = { foreground = Screen.colors.Yellow, background = Screen.colors.Green },
+ [10] = { foreground = Screen.colors.White, background = Screen.colors.Green },
ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey },
kn = { foreground = Screen.colors.Red, background = Screen.colors.Plum1 },
xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey },
@@ -1542,6 +1544,81 @@ describe('builtin popupmenu', function()
end)
if not multigrid then
+ describe('popup and preview window do not overlap', function()
+ before_each(function()
+ screen:try_resize(53, 20)
+ end)
+
+ -- oldtest: Test_popup_and_previewwindow_dump_pedit()
+ it('with :pedit', function()
+ exec([[
+ set previewheight=9
+ silent! pedit
+ call setline(1, map(repeat(["ab"], 10), "v:val .. v:key"))
+ exec "norm! G\<C-E>\<C-E>"
+ ]])
+ feed('o')
+ n.poke_eventloop()
+ feed('<C-X><C-N>')
+ screen:expect([[
+ ab0 |
+ ab1 |
+ ab2 |
+ ab3 |
+ ab4 |
+ ab5 |
+ ab6 |
+ ab7 |
+ ab8 |
+ {s:ab0 }{c: }{3:ew][+] }|
+ {n:ab1 }{c: } |
+ {n:ab2 }{c: } |
+ {n:ab3 }{c: } |
+ {n:ab4 }{s: } |
+ {n:ab5 }{s: } |
+ {n:ab6 }{s: } |
+ ab0^ |
+ {1:~ }|
+ {4:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 10} |
+ ]])
+ end)
+
+ -- oldtest: Test_popup_and_previewwindow_dump_pbuffer()
+ it('with :pbuffer', function()
+ exec([[
+ set previewheight=9
+ silent! pbuffer
+ call setline(1, map(repeat(["ab"], 10), "v:val .. v:key"))
+ exec "norm! G\<C-E>\<C-E>\<C-E>"
+ ]])
+ feed('o')
+ n.poke_eventloop()
+ feed('<C-X><C-N>')
+ screen:expect([[
+ ab0 |
+ ab1 |
+ ab2 |
+ ab3 |
+ ab4 |
+ ab5 |
+ ab6 |
+ ab7 |
+ ab8 |
+ {s:ab0 }{c: }{3:ew][+] }|
+ {n:ab1 }{c: } |
+ {n:ab2 }{c: } |
+ {n:ab3 }{s: } |
+ {n:ab4 }{s: } |
+ {n:ab5 }{s: } |
+ ab0^ |
+ {1:~ }|*2
+ {4:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 10} |
+ ]])
+ end)
+ end)
+
-- oldtest: Test_pum_with_preview_win()
it('preview window opened during completion', function()
exec([[
@@ -1603,7 +1680,7 @@ describe('builtin popupmenu', function()
end)
end
- describe('floating window preview #popup', function()
+ describe('floating window preview popup', function()
it('pum popup preview', function()
--row must > 10
screen:try_resize(40, 11)
@@ -1616,14 +1693,29 @@ describe('builtin popupmenu', function()
endfunc
set omnifunc=Omni_test
set completeopt=menu,popup
-
funct Set_info()
let comp_info = complete_info()
if comp_info['selected'] == 2
call nvim__complete_set(comp_info['selected'], {"info": "3info"})
endif
endfunc
- autocmd CompleteChanged * call Set_info()
+ funct TsHl()
+ let comp_info = complete_info()
+ if get(comp_info, 'previewbufnr', 0) > 0
+ call v:lua.vim.treesitter.start(comp_info['preview_bufnr'], 'markdown')
+ endif
+ if comp_info['selected'] == 0
+ call nvim__complete_set(comp_info['selected'], {"info": "```lua\nfunction test()\n print('foo')\nend\n```"})
+ endif
+ endfunc
+ augroup Group
+ au!
+ autocmd CompleteChanged * :call Set_info()
+ augroup END
+ funct TestTs()
+ autocmd! Group
+ autocmd CompleteChanged * call TsHl()
+ endfunc
]])
feed('Gi<C-x><C-o>')
--floating preview in right
@@ -1684,25 +1776,26 @@ describe('builtin popupmenu', function()
}
end
- -- info window position should be adjusted when new leader add
- feed('<C-P>o')
+ -- delete one character make the pum width smaller than before
+ -- info window position should be adjusted when popupmenu width changed
+ feed('<BS>')
if multigrid then
- screen:expect {
+ screen:expect({
grid = [[
- ## grid 1
- [2:----------------------------------------]|*10
- [3:----------------------------------------]|
- ## grid 2
- o^ |
- {1:~ }|*9
- ## grid 3
- {2:-- }{8:Back at original} |
- ## grid 4
- {n:1info}|
- {n: }|
- ## grid 5
- {n:one }|
- ]],
+ ## grid 1
+ [2:----------------------------------------]|*10
+ [3:----------------------------------------]|
+ ## grid 2
+ on^ |
+ {1:~ }|*9
+ ## grid 3
+ {2:-- }{5:match 1 of 3} |
+ ## grid 4
+ {n:1info}|
+ {n: }|
+ ## grid 5
+ {s:one }|
+ ]],
float_pos = {
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
[4] = { 1001, 'NW', 1, 1, 15, false, 50 },
@@ -1713,7 +1806,7 @@ describe('builtin popupmenu', function()
topline = 0,
botline = 2,
curline = 0,
- curcol = 1,
+ curcol = 2,
linecount = 1,
sum_scroll_delta = 0,
},
@@ -1727,22 +1820,88 @@ describe('builtin popupmenu', function()
sum_scroll_delta = 0,
},
},
- }
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000,
+ },
+ [4] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1001,
+ },
+ },
+ })
else
- screen:expect {
+ screen:expect({
grid = [[
- o^ |
- {n:one 1info}{1: }|
- {1:~ }{n: }{1: }|
- {1:~ }|*7
- {2:-- }{8:Back at original} |
- ]],
- }
+ on^ |
+ {s:one }{n:1info}{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }|*7
+ {2:-- }{5:match 1 of 3} |
+ ]],
+ })
+ end
+
+ -- when back to original the preview float should be closed.
+ feed('<C-P>')
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*10
+ [3:----------------------------------------]|
+ ## grid 2
+ on^ |
+ {1:~ }|*9
+ ## grid 3
+ {2:-- }{8:Back at original} |
+ ## grid 5
+ {n:one }|
+ ]],
+ float_pos = {
+ [5] = { -1, 'NW', 2, 1, 0, false, 100 },
+ },
+ win_viewport = {
+ [2] = {
+ win = 1000,
+ topline = 0,
+ botline = 2,
+ curline = 0,
+ curcol = 2,
+ linecount = 1,
+ sum_scroll_delta = 0,
+ },
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000,
+ },
+ },
+ })
+ else
+ screen:expect({
+ grid = [[
+ on^ |
+ {n:one }{1: }|
+ {1:~ }|*8
+ {2:-- }{8:Back at original} |
+ ]],
+ })
end
-- test nvim__complete_set_info
- feed('<ESC>cc<C-X><C-O><C-N><C-N>')
- vim.uv.sleep(10)
+ feed('<ESC>S<C-X><C-O><C-N><C-N>')
if multigrid then
screen:expect {
grid = [[
@@ -1758,13 +1917,13 @@ describe('builtin popupmenu', function()
{n:one }|
{n:two }|
{s:looooooooooooooong }|
- ## grid 6
+ ## grid 7
{n:3info}|
{n: }|
]],
float_pos = {
[5] = { -1, 'NW', 2, 1, 0, false, 100 },
- [6] = { 1002, 'NW', 1, 1, 19, false, 50 },
+ [7] = { 1003, 'NW', 1, 1, 19, false, 50 },
},
win_viewport = {
[2] = {
@@ -1776,8 +1935,8 @@ describe('builtin popupmenu', function()
linecount = 1,
sum_scroll_delta = 0,
},
- [6] = {
- win = 1002,
+ [7] = {
+ win = 1003,
topline = 0,
botline = 2,
curline = 0,
@@ -1819,12 +1978,12 @@ describe('builtin popupmenu', function()
{s: one }|
{n: two }|
{n: looooooooooooooong }|
- ## grid 7
+ ## grid 8
{n:1info}|
{n: }|
]],
float_pos = {
- [7] = { 1003, 'NW', 1, 1, 14, false, 50 },
+ [8] = { 1004, 'NW', 1, 1, 14, false, 50 },
[5] = { -1, 'NW', 2, 1, 19, false, 100 },
},
win_viewport = {
@@ -1837,8 +1996,8 @@ describe('builtin popupmenu', function()
linecount = 1,
sum_scroll_delta = 0,
},
- [7] = {
- win = 1003,
+ [8] = {
+ win = 1004,
topline = 0,
botline = 2,
curline = 0,
@@ -1860,6 +2019,90 @@ describe('builtin popupmenu', function()
]],
}
end
+ feed('<C-E><Esc>')
+
+ -- works when scroll with treesitter highlight
+ command('call TestTs()')
+ feed('S<C-x><C-o>')
+ if multigrid then
+ screen:expect({
+ grid = [[
+ ## grid 1
+ [2:----------------------------------------]|*10
+ [3:----------------------------------------]|
+ ## grid 2
+ one^ |
+ {1:~ }|*9
+ ## grid 3
+ {2:-- }{5:match 1 of 3} |
+ ## grid 5
+ {s:one }|
+ {n:two }|
+ {n:looooooooooooooong }|
+ ## grid 9
+ {n:```lua }|
+ {n:function test()}|
+ {n: print('foo') }|
+ {n:end }|
+ {n:``` }|
+ {n: }|
+ ]],
+ float_pos = {
+ [5] = { -1, 'NW', 2, 1, 0, false, 100 },
+ [9] = { 1005, 'NW', 1, 1, 19, false, 50 },
+ },
+ win_viewport = {
+ [2] = {
+ win = 1000,
+ topline = 0,
+ botline = 2,
+ curline = 0,
+ curcol = 3,
+ linecount = 1,
+ sum_scroll_delta = 0,
+ },
+ [9] = {
+ win = 1005,
+ topline = 0,
+ botline = 6,
+ curline = 0,
+ curcol = 0,
+ linecount = 5,
+ sum_scroll_delta = 0,
+ },
+ },
+ win_viewport_margins = {
+ [2] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1000,
+ },
+ [9] = {
+ bottom = 0,
+ left = 0,
+ right = 0,
+ top = 0,
+ win = 1005,
+ },
+ },
+ })
+ else
+ screen:expect({
+ grid = [[
+ one^ |
+ {s:one }{n:```lua }{1: }|
+ {n:two function test()}{1: }|
+ {n:looooooooooooooong print('foo') }{1: }|
+ {1:~ }{n:end }{1: }|
+ {1:~ }{n:``` }{1: }|
+ {1:~ }{n: }{1: }|
+ {1:~ }|*3
+ {2:-- }{5:match 1 of 3} |
+ ]],
+ })
+ end
end)
end)
@@ -3846,7 +4089,7 @@ describe('builtin popupmenu', function()
set mouse=a mousemodel=popup
" Delete the default MenuPopup event handler.
- autocmd! nvim_popupmenu
+ autocmd! nvim.popupmenu
aunmenu PopUp
menu PopUp.foo :let g:menustr = 'foo'<CR>
menu PopUp.bar :let g:menustr = 'bar'<CR>
@@ -4703,7 +4946,7 @@ describe('builtin popupmenu', function()
it(':popup command', function()
exec([[
" Delete the default MenuPopup event handler.
- autocmd! nvim_popupmenu
+ autocmd! nvim.popupmenu
func ChangeMenu()
aunmenu PopUp.&Paste
@@ -4863,7 +5106,7 @@ describe('builtin popupmenu', function()
exec([[
set mousemodel=popup_setpos
" Delete the default MenuPopup event handler.
- autocmd! nvim_popupmenu
+ autocmd! nvim.popupmenu
aunmenu *
source $VIMRUNTIME/menu.vim
call setline(1, join(range(20)))
@@ -5172,6 +5415,45 @@ describe('builtin popupmenu', function()
feed('<C-E><Esc>')
end)
+ -- oldtest: Test_pum_highlights_match_with_abbr()
+ it('can highlight matched text with abbr', function()
+ exec([[
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return {
+ \ 'words': [
+ \ { 'word': 'foobar', 'abbr': "foobar\t\t!" },
+ \ { 'word': 'foobaz', 'abbr': "foobaz\t\t!" },
+ \]}
+ endfunc
+
+ set omnifunc=Omni_test
+ set completeopt=menuone,noinsert
+ hi PmenuMatchSel guifg=Blue guibg=Grey
+ hi PmenuMatch guifg=Blue guibg=Plum1
+ ]])
+ feed('i<C-X><C-O>')
+ screen:expect([[
+ ^ |
+ {s:foobar ! }{1: }|
+ {n:foobaz ! }{1: }|
+ {1:~ }|*16
+ {2:-- }{5:match 1 of 2} |
+ ]])
+ feed('foo')
+ screen:expect([[
+ foo^ |
+ {ms:foo}{s:bar ! }{1: }|
+ {mn:foo}{n:baz ! }{1: }|
+ {1:~ }|*16
+ {2:-- }{5:match 1 of 2} |
+ ]])
+
+ feed('<C-E><Esc>')
+ end)
+
-- oldtest: Test_pum_user_abbr_hlgroup()
it('custom abbr_hlgroup override', function()
exec([[
@@ -5419,6 +5701,240 @@ describe('builtin popupmenu', function()
]])
feed('<C-E><ESC>')
end)
+
+ -- oldtest: Test_pum_matchins_highlight()
+ it('with ComplMatchIns highlight', function()
+ exec([[
+ let g:change = 0
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ if g:change == 0
+ return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}]
+ endif
+ return [#{word: "foo", info: "info"}, #{word: "bar"}, #{word: "你好"}]
+ endfunc
+ set omnifunc=Omni_test
+ hi ComplMatchIns guifg=red
+ ]])
+
+ feed('Sαβγ <C-X><C-O>')
+ screen:expect([[
+ αβγ {8:foo}^ |
+ {1:~ }{s: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<C-E><Esc>')
+
+ feed('Sαβγ <C-X><C-O><C-N>')
+ screen:expect([[
+ αβγ {8:bar}^ |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{s: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 2 of 3} |
+ ]])
+ feed('<C-E><Esc>')
+
+ feed('Sαβγ <C-X><C-O><C-N><C-N>')
+ screen:expect([[
+ αβγ {8:你好}^ |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{s: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 3 of 3} |
+ ]])
+ feed('<C-E><Esc>')
+
+ -- restore after accept
+ feed('Sαβγ <C-X><C-O><C-Y>')
+ screen:expect([[
+ αβγ foo^ |
+ {1:~ }|*18
+ {2:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+
+ -- restore after cancel completion
+ feed('Sαβγ <C-X><C-O><Space>')
+ screen:expect([[
+ αβγ foo ^ |
+ {1:~ }|*18
+ {2:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+
+ -- text after the inserted text shouldn't be highlighted
+ feed('0ea <C-X><C-O>')
+ screen:expect([[
+ αβγ {8:foo}^ foo |
+ {1:~ }{s: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<C-P>')
+ screen:expect([[
+ αβγ ^ foo |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{8:Back at original} |
+ ]])
+ feed('<C-P>')
+ screen:expect([[
+ αβγ {8:你好}^ foo |
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{s: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 3 of 3} |
+ ]])
+ feed('<C-Y>')
+ screen:expect([[
+ αβγ 你好^ foo |
+ {1:~ }|*18
+ {2:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+
+ feed(':let g:change=1<CR>S<C-X><C-O>')
+ screen:expect([[
+ info |
+ {1:~ }|*2
+ {3:[Scratch] [Preview] }|
+ {8:foo}^ |
+ {s:foo }{1: }|
+ {n:bar }{1: }|
+ {n:你好 }{1: }|
+ {1:~ }|*10
+ {4:[No Name] [+] }|
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<Esc>')
+ end)
+
+ -- oldtest: Test_pum_matchins_highlight_combine()
+ it('with ComplMatchIns, Normal and CursorLine highlights', function()
+ exec([[
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}]
+ endfunc
+ set omnifunc=Omni_test
+ hi Normal guibg=blue
+ hi CursorLine guibg=green guifg=white
+ set cursorline
+ call setline(1, 'aaa bbb')
+ ]])
+
+ -- when ComplMatchIns is not set, CursorLine applies normally
+ feed('0ea <C-X><C-O>')
+ screen:expect([[
+ {10:aaa foo^ bbb }|
+ {1:~ }{s: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<C-E>')
+ screen:expect([[
+ {10:aaa ^ bbb }|
+ {1:~ }|*18
+ {2:-- INSERT --} |
+ ]])
+ feed('<BS><Esc>')
+
+ -- when ComplMatchIns is set, it is applied over CursorLine
+ command('hi ComplMatchIns guifg=Yellow')
+ feed('0ea <C-X><C-O>')
+ screen:expect([[
+ {10:aaa }{9:foo}{10:^ bbb }|
+ {1:~ }{s: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<C-P>')
+ screen:expect([[
+ {10:aaa ^ bbb }|
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{n: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{8:Back at original} |
+ ]])
+ feed('<C-P>')
+ screen:expect([[
+ {10:aaa }{9:你好}{10:^ bbb }|
+ {1:~ }{n: foo }{1: }|
+ {1:~ }{n: bar }{1: }|
+ {1:~ }{s: 你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{5:match 3 of 3} |
+ ]])
+ feed('<C-E>')
+ screen:expect([[
+ {10:aaa ^ bbb }|
+ {1:~ }|*18
+ {2:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+
+ -- Does not highlight the compl leader
+ command('set cot+=menuone,noselect')
+ feed('S<C-X><C-O>')
+ local pum_start = [[
+ {10:^ }|
+ {n:foo }{1: }|
+ {n:bar }{1: }|
+ {n:你好 }{1: }|
+ {1:~ }|*15
+ {2:-- }{8:Back at original} |
+ ]]
+ screen:expect(pum_start)
+ feed('f<C-N>')
+ screen:expect([[
+ {10:f}{9:oo}{10:^ }|
+ {s:foo }{1: }|
+ {1:~ }|*17
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<C-E><ESC>')
+
+ command('set cot+=fuzzy')
+ feed('S<C-X><C-O>')
+ screen:expect(pum_start)
+ feed('f<C-N>')
+ screen:expect([[
+ {10:foo^ }|
+ {s:foo }{1: }|
+ {1:~ }|*17
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ feed('<C-E><Esc>')
+
+ command('set cot-=fuzzy')
+ feed('Sf<C-N>')
+ screen:expect([[
+ {10:f^ }|
+ {1:~ }|*18
+ {2:-- }{6:Pattern not found} |
+ ]])
+ feed('<C-E><Esc>')
+ end)
end
end
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 8e15e6c35f..6a8e7df6a0 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -37,10 +37,10 @@
-- Tests will often share a group of extra attribute sets to expect(). Those can be
-- defined at the beginning of a test:
--
--- screen:add_extra_attr_ids {
+-- screen:add_extra_attr_ids({
-- [100] = { background = Screen.colors.Plum1, underline = true },
-- [101] = { background = Screen.colors.Red1, bold = true, underline = true },
--- }
+-- })
--
-- To help write screen tests, see Screen:snapshot_util().
-- To debug screen tests, see Screen:redraw_debug().
@@ -79,6 +79,7 @@ end
--- @field win_position table<integer,table<string,integer>>
--- @field float_pos table<integer,table>
--- @field cmdline table<integer,table>
+--- @field cmdline_hide_level integer?
--- @field cmdline_block table[]
--- @field hl_groups table<string,integer>
--- @field messages table<integer,table>
@@ -454,7 +455,7 @@ end
--- screen:expect(grid, [attr_ids])
--- screen:expect(condition)
--- or keyword args (supports more options):
---- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end}
+--- screen:expect({ grid=[[...]], cmdline={...}, condition=function() ... end })
---
--- @param expected string|function|test.function.ui.screen.Expect
--- @param attr_ids? table<integer,table<string,any>>
@@ -654,6 +655,12 @@ screen:redraw_debug() to show all intermediate screen states.]]
end
end
+ -- Only test the abort state of a cmdline level once.
+ if self.cmdline_hide_level ~= nil then
+ self.cmdline[self.cmdline_hide_level] = nil
+ self.cmdline_hide_level = nil
+ end
+
if expected.hl_groups ~= nil then
for name, id in pairs(expected.hl_groups) do
local expected_hl = attr_state.ids[id]
@@ -967,11 +974,11 @@ function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info)
self._cursor_style_enabled = cursor_style_enabled
for _, item in pairs(mode_info) do
-- attr IDs are not stable, but their value should be
- if item.attr_id ~= nil then
+ if item.attr_id ~= nil and self._attr_table[item.attr_id] ~= nil then
item.attr = self._attr_table[item.attr_id][1]
item.attr_id = nil
end
- if item.attr_id_lm ~= nil then
+ if item.attr_id_lm ~= nil and self._attr_table[item.attr_id_lm] ~= nil then
item.attr_lm = self._attr_table[item.attr_id_lm][1]
item.attr_id_lm = nil
end
@@ -1296,7 +1303,7 @@ function Screen:_handle_popupmenu_hide()
self.popupmenu = nil
end
-function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level)
+function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level, hl_id)
if firstc == '' then
firstc = nil
end
@@ -1320,11 +1327,13 @@ function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level
firstc = firstc,
prompt = prompt,
indent = indent,
+ hl_id = prompt and hl_id,
}
end
-function Screen:_handle_cmdline_hide(level)
- self.cmdline[level] = nil
+function Screen:_handle_cmdline_hide(level, abort)
+ self.cmdline[level] = { abort = abort }
+ self.cmdline_hide_level = level
end
function Screen:_handle_cmdline_special_char(char, shift, level)
@@ -1360,12 +1369,12 @@ function Screen:_handle_wildmenu_hide()
self.wildmenu_items, self.wildmenu_pos = nil, nil
end
-function Screen:_handle_msg_show(kind, chunks, replace_last)
+function Screen:_handle_msg_show(kind, chunks, replace_last, history)
local pos = #self.messages
if not replace_last or pos == 0 then
pos = pos + 1
end
- self.messages[pos] = { kind = kind, content = chunks }
+ self.messages[pos] = { kind = kind, content = chunks, history = history }
end
function Screen:_handle_msg_clear()
@@ -1468,7 +1477,9 @@ function Screen:_extstate_repr(attr_state)
local cmdline = {}
for i, entry in pairs(self.cmdline) do
entry = shallowcopy(entry)
- entry.content = self:_chunks_repr(entry.content, attr_state)
+ if entry.content ~= nil then
+ entry.content = self:_chunks_repr(entry.content, attr_state)
+ end
cmdline[i] = entry
end
@@ -1479,7 +1490,11 @@ function Screen:_extstate_repr(attr_state)
local messages = {}
for i, entry in ipairs(self.messages) do
- messages[i] = { kind = entry.kind, content = self:_chunks_repr(entry.content, attr_state) }
+ messages[i] = {
+ kind = entry.kind,
+ content = self:_chunks_repr(entry.content, attr_state),
+ history = entry.history,
+ }
end
local msg_history = {}
@@ -1713,21 +1728,24 @@ function Screen:_print_snapshot()
end
end
local fn_name = modify_attrs and 'add_extra_attr_ids' or 'set_default_attr_ids'
- attrstr = ('screen:' .. fn_name .. ' {\n' .. table.concat(attrstrs, '\n') .. '\n}\n\n')
+ attrstr = ('screen:' .. fn_name .. '({\n' .. table.concat(attrstrs, '\n') .. '\n})\n\n')
end
- local result = ('%sscreen:expect({\n grid = [[\n %s\n ]]'):format(
- attrstr,
- kwargs.grid:gsub('\n', '\n ')
- )
+ local extstr = ''
for _, k in ipairs(ext_keys) do
if ext_state[k] ~= nil and not (k == 'win_viewport' and not self.options.ext_multigrid) then
- result = result .. ', ' .. k .. '=' .. fmt_ext_state(k, ext_state[k])
+ extstr = extstr .. '\n ' .. k .. ' = ' .. fmt_ext_state(k, ext_state[k]) .. ','
end
end
- result = result .. '\n})'
- return result
+ return ('%sscreen:expect(%s%s%s%s%s'):format(
+ attrstr,
+ #extstr > 0 and '{\n grid = [[\n ' or '[[\n',
+ #extstr > 0 and kwargs.grid:gsub('\n', '\n ') or kwargs.grid,
+ #extstr > 0 and '\n ]],' or '\n]]',
+ extstr,
+ #extstr > 0 and '\n})' or ')'
+ )
end
function Screen:print_snapshot()
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index f39e9ecc33..295c40b9b6 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -2,7 +2,7 @@ local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
-local spawn, set_session, clear = n.spawn, n.set_session, n.clear
+local set_session, clear = n.set_session, n.clear
local feed, command = n.feed, n.command
local exec = n.exec
local insert = n.insert
@@ -26,7 +26,7 @@ describe('screen', function()
}
before_each(function()
- local screen_nvim = spawn(nvim_argv)
+ local screen_nvim = n.new_session(false, { args = nvim_argv, merge = false })
set_session(screen_nvim)
screen = Screen.new()
end)
@@ -645,6 +645,59 @@ local function screen_tests(linegrid)
|
]])
end)
+
+ it('clamps &cmdheight for current tabpage', function()
+ command('set cmdheight=10 laststatus=2')
+ screen:expect([[
+ ^ |
+ {0:~ }|*2
+ {1:[No Name] }|
+ |*10
+ ]])
+ screen:try_resize(53, 8)
+ screen:expect([[
+ ^ |
+ {1:[No Name] }|
+ |*6
+ ]])
+ eq(6, api.nvim_get_option_value('cmdheight', {}))
+ end)
+
+ it('clamps &cmdheight for another tabpage #31380', function()
+ command('tabnew')
+ command('set cmdheight=9 laststatus=2')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|*2
+ {1:[No Name] }|
+ |*9
+ ]])
+ command('tabprev')
+ screen:expect([[
+ {2: [No Name] }{4: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|*10
+ {1:[No Name] }|
+ |
+ ]])
+ screen:try_resize(53, 8)
+ screen:expect([[
+ {2: [No Name] }{4: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|*4
+ {1:[No Name] }|
+ |
+ ]])
+ command('tabnext')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {1:[No Name] }|
+ |*5
+ ]])
+ eq(5, api.nvim_get_option_value('cmdheight', {}))
+ end)
end)
describe('press enter', function()
@@ -713,7 +766,7 @@ describe('Screen default colors', function()
'colorscheme vim',
'--embed',
}
- local screen_nvim = spawn(nvim_argv)
+ local screen_nvim = n.new_session(false, { args = nvim_argv, merge = false })
set_session(screen_nvim)
screen = Screen.new(53, 14, { rgb = true, ext_termcolors = termcolors or nil })
end
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index 7874c04c39..ff03d86979 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -14,6 +14,10 @@ describe('Signs', function()
screen = Screen.new()
screen:add_extra_attr_ids {
[100] = { bold = true, foreground = Screen.colors.Magenta1 },
+ [101] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Yellow1 },
+ [102] = { foreground = Screen.colors.Brown, background = Screen.colors.Yellow },
+ [103] = { background = Screen.colors.Yellow, reverse = true },
+ [104] = { reverse = true, foreground = Screen.colors.Grey100, background = Screen.colors.Red },
}
end)
@@ -27,8 +31,8 @@ describe('Signs', function()
sign place 2 line=2 name=piet2 buffer=1
]])
screen:expect([[
- {10:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}a |
- {10:𠜎̀́̂̃̄̅}b |
+ {101:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}a |
+ {101:𠜎̀́̂̃̄̅}b |
{7: }^ |
{1:~ }|*10
|
@@ -45,9 +49,9 @@ describe('Signs', function()
sign place 3 line=1 name=pietx buffer=1
]])
screen:expect([[
- {10:>!}a |
+ {101:>!}a |
{7: }b |
- {10:>>}c |
+ {101:>>}c |
{7: }^ |
{1:~ }|*9
|
@@ -80,13 +84,13 @@ describe('Signs', function()
]])
screen:expect([[
{7: }{21:^a }|
- {10:>>}b |
+ {101:>>}b |
{7: }c |
{7: } |
{1:~ }|*2
{3:[No Name] [+] }|
{7: }{21:a }|
- {10:>>}b |
+ {101:>>}b |
{7: }c |
{7: } |
{1:~ }|
@@ -110,10 +114,10 @@ describe('Signs', function()
sign place 6 line=4 name=pietxx buffer=1
]])
screen:expect([[
- {10:>>}{8: 1 }a |
+ {101:>>}{8: 1 }a |
{7: }{8: 2 }{9:b }|
{7: }{13: 3 }c |
- {10:>>}{13: 4 }{9:^ }|
+ {101:>>}{13: 4 }{9:^ }|
{1:~ }|*9
|
]])
@@ -132,33 +136,33 @@ describe('Signs', function()
set cursorline
]])
screen:expect([[
- {10:>>}a |
- {10:>>}b |
+ {101:>>}a |
+ {101:>>}b |
{9:>>}{21:^c }|
{1:~ }|*10
|
]])
feed('k')
screen:expect([[
- {10:>>}a |
+ {101:>>}a |
{9:>>}{21:^b }|
- {10:>>}c |
+ {101:>>}c |
{1:~ }|*10
|
]])
exec('set nocursorline')
screen:expect([[
- {10:>>}a |
- {10:>>}^b |
- {10:>>}c |
+ {101:>>}a |
+ {101:>>}^b |
+ {101:>>}c |
{1:~ }|*10
|
]])
exec('set cursorline cursorlineopt=line')
screen:expect([[
- {10:>>}a |
- {10:>>}{21:^b }|
- {10:>>}c |
+ {101:>>}a |
+ {101:>>}{21:^b }|
+ {101:>>}c |
{1:~ }|*10
|
]])
@@ -166,13 +170,14 @@ describe('Signs', function()
exec('hi! link SignColumn IncSearch')
feed('Go<esc>2G')
screen:expect([[
- {10:>>}a |
- {9:>>}^b |
- {10:>>}c |
+ {103:>>}a |
+ {104:>>}^b |
+ {103:>>}c |
{2: } |
{1:~ }|*9
|
]])
+
-- Check that 'statuscolumn' cursorline/signcolumn highlights are the same (#21726)
exec('set statuscolumn=%s')
screen:expect_unchanged()
@@ -196,7 +201,7 @@ describe('Signs', function()
screen:expect([[
{7: }{8: 1 }a |
{7: }{8: 2 }b |
- WW{10:>>}{8: 3 }c |
+ {7:WW}{101:>>}{8: 3 }c |
{7: }{8: 4 }^ |
{1:~ }|*9
|
@@ -209,9 +214,9 @@ describe('Signs', function()
sign place 3 line=2 name=pietError buffer=1
]])
screen:expect([[
- {9:XX}{10:>>}{8: 1 }a |
- {10:>>}{9:XX}{8: 2 }b |
- WW{10:>>}{8: 3 }c |
+ {9:XX}{101:>>}{8: 1 }a |
+ {101:>>}{9:XX}{8: 2 }b |
+ {7:WW}{101:>>}{8: 3 }c |
{7: }{8: 4 }^ |
{1:~ }|*9
|
@@ -220,8 +225,8 @@ describe('Signs', function()
exec('set signcolumn=yes:1')
screen:expect([[
{9:XX}{8: 1 }a |
- {10:>>}{8: 2 }b |
- WW{8: 3 }c |
+ {101:>>}{8: 2 }b |
+ {7:WW}{8: 3 }c |
{7: }{8: 4 }^ |
{1:~ }|*9
|
@@ -229,9 +234,9 @@ describe('Signs', function()
-- "auto:3" accommodates all the signs we defined so far.
exec('set signcolumn=auto:3')
local s3 = [[
- {9:XX}{10:>>}{7: }{8: 1 }a |
- {10:>>}{9:XX}{7: }{8: 2 }b |
- WW{10:>>}{9:XX}{8: 3 }c |
+ {9:XX}{101:>>}{7: }{8: 1 }a |
+ {101:>>}{9:XX}{7: }{8: 2 }b |
+ {7:WW}{101:>>}{9:XX}{8: 3 }c |
{7: }{8: 4 }^ |
{1:~ }|*9
|
@@ -240,9 +245,9 @@ describe('Signs', function()
-- Check "yes:9".
exec('set signcolumn=yes:9')
screen:expect([[
- {9:XX}{10:>>}{7: }{8: 1 }a |
- {10:>>}{9:XX}{7: }{8: 2 }b |
- WW{10:>>}{9:XX}{7: }{8: 3 }c |
+ {9:XX}{101:>>}{7: }{8: 1 }a |
+ {101:>>}{9:XX}{7: }{8: 2 }b |
+ {7:WW}{101:>>}{9:XX}{7: }{8: 3 }c |
{7: }{8: 4 }^ |
{1:~ }|*9
|
@@ -255,8 +260,8 @@ describe('Signs', function()
exec('3move1')
exec('2d')
screen:expect([[
- {9:XX}{10:>>}{8: 1 }a |
- {10:>>}{9:XX}{8: 2 }^b |
+ {9:XX}{101:>>}{8: 1 }a |
+ {101:>>}{9:XX}{8: 2 }^b |
{7: }{8: 3 } |
{1:~ }|*10
|
@@ -264,8 +269,8 @@ describe('Signs', function()
-- character deletion does not delete signs.
feed('x')
screen:expect([[
- {9:XX}{10:>>}{8: 1 }a |
- {10:>>}{9:XX}{8: 2 }^ |
+ {9:XX}{101:>>}{8: 1 }a |
+ {101:>>}{9:XX}{8: 2 }^ |
{7: }{8: 3 } |
{1:~ }|*10
|
@@ -301,7 +306,7 @@ describe('Signs', function()
exec('sign define pietSearch text=>> texthl=Search')
exec('sign place 1 line=1 name=pietSearch buffer=1')
screen:expect([[
- {10:>>}{7: }{8: 1 }a |
+ {101:>>}{7: }{8: 1 }a |
{7: }{8: 2 }b |
{7: }{8: 3 }c |
{7: }{8: 4 }^ |
@@ -316,7 +321,7 @@ describe('Signs', function()
sign place 4 line=1 name=pietSearch buffer=1
]])
screen:expect([[
- {10:>>>>>>>>}{8: 1 }a |
+ {101:>>>>>>>>}{8: 1 }a |
{7: }{8: 2 }b |
{7: }{8: 3 }c |
{7: }{8: 4 }^ |
@@ -328,7 +333,7 @@ describe('Signs', function()
screen:expect_unchanged()
exec('sign unplace 4')
screen:expect([[
- {10:>>>>>>}{8: 1 }a |
+ {101:>>>>>>}{8: 1 }a |
{7: }{8: 2 }b |
{7: }{8: 3 }c |
{7: }{8: 4 }^ |
@@ -345,7 +350,7 @@ describe('Signs', function()
sign place 8 line=1 name=pietSearch buffer=1
]])
screen:expect([[
- {10:>>>>>>>>>>}{8: 1 }a |
+ {101:>>>>>>>>>>}{8: 1 }a |
{7: }{8: 2 }b |
{7: }{8: 3 }c |
{7: }{8: 4 }^ |
@@ -375,7 +380,7 @@ describe('Signs', function()
-- single column with 1 sign with text and one sign without
exec('sign place 1 line=1 name=pietSearch buffer=1')
screen:expect([[
- {10:>>}{8: 1 }a |
+ {101:>>}{8: 1 }a |
{7: }{8: 2 }b |
{7: }{8: 3 }c |
{7: }{8: 4 }^ |
@@ -396,7 +401,7 @@ describe('Signs', function()
-- line number should be drawn if sign has no text
-- no signcolumn, line number for "a" is Search, for "b" is Error, for "c" is LineNr
screen:expect([[
- {10: >> }a |
+ {101: >> }a |
{9: 2 }b |
{8: 3 }c |
{8: 4 }^ |
@@ -406,7 +411,7 @@ describe('Signs', function()
-- number column on wrapped part of a line should be empty
feed('gg100aa<Esc>')
screen:expect([[
- {10: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {101: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{9: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{9: }aa^a |
{9: 2 }b |
@@ -423,7 +428,7 @@ describe('Signs', function()
-- number column on virtual lines should be empty
screen:expect([[
{8: }VIRT LINES |
- {10: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {101: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{9: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{9: }aa^a |
{9: 2 }b |
@@ -439,7 +444,7 @@ describe('Signs', function()
exec('sign place 100000 line=1 name=piet buffer=1')
feed(':sign place<cr>')
screen:expect([[
- {10:>>} |
+ {101:>>} |
{1:~ }|*6
{3: }|
:sign place |
@@ -452,7 +457,7 @@ describe('Signs', function()
feed('<cr>')
screen:expect([[
- {10:>>}^ |
+ {101:>>}^ |
{1:~ }|*12
|
]])
@@ -470,7 +475,7 @@ describe('Signs', function()
{7: }a |
{7: }^c |
{7: }d |
- >>e |
+ {7:>>}e |
{1:~ }|*9
|
]])
@@ -498,7 +503,7 @@ describe('Signs', function()
{7: }b |
{7: }c |
{7: }d |
- >>e |
+ {7:>>}e |
{1:~ }|*7
|
]])
@@ -550,7 +555,7 @@ describe('Signs', function()
exec('silent undo')
screen:expect([[
{7: }1 |
- S1^2 |
+ {7:S1}^2 |
{7: }3 |
{7: }4 |
{1:~ }|*9
@@ -575,23 +580,19 @@ describe('Signs', function()
sign place 2 line=9 name=S2
]])
-- Now placed at end of buffer
- local s1 = {
- grid = [[
- S2^ |
- {1:~ }|*12
- |
- ]],
- }
+ local s1 = [[
+ {7:S2}^ |
+ {1:~ }|*12
+ |
+ ]]
screen:expect(s1)
-- Signcolumn tracking used to not count signs placed beyond end of buffer here
exec('set signcolumn=auto:9')
- screen:expect({
- grid = [[
- S2S1^ |
- {1:~ }|*12
- |
- ]],
- })
+ screen:expect([[
+ {7:S2S1}^ |
+ {1:~ }|*12
+ |
+ ]])
-- Unplacing the sign does not crash by decrementing tracked signs below zero
exec('sign unplace 1')
screen:expect(s1)
@@ -607,4 +608,77 @@ describe('Signs', function()
eq(6, infos[1].textoff)
eq(6, infos[2].textoff)
end)
+
+ it('auto width updated in all windows after sign placed in on_win #31438', function()
+ exec_lua([[
+ vim.cmd.call('setline(1, range(1, 500))')
+ vim.cmd('wincmd s | wincmd v | wincmd j | wincmd v')
+
+ _G.log, _G.needs_clear = {}, false
+ local ns_id, mark_id = vim.api.nvim_create_namespace('test'), nil
+
+ -- Add decoration which possibly clears all extmarks and adds one on line 499
+ local on_win = function(_, winid, bufnr, toprow, botrow)
+ if _G.needs_clear then
+ vim.api.nvim_buf_clear_namespace(bufnr, ns_id, 0, -1)
+ _G.needs_clear = false
+ end
+
+ if toprow < 499 and 499 <= botrow then
+ mark_id = vim.api.nvim_buf_set_extmark(bufnr, ns_id, 499, 0, { id = mark_id, sign_text = '!', invalidate = true })
+ end
+ end
+ vim.api.nvim_set_decoration_provider(ns_id, { on_win = on_win })
+ ]])
+ screen:expect([[
+ 1 │1 |
+ 2 │2 |
+ 3 │3 |
+ 4 │4 |
+ 5 │5 |
+ 6 │6 |
+ {2:[No Name] [+] [No Name] [+] }|
+ ^1 │1 |
+ 2 │2 |
+ 3 │3 |
+ 4 │4 |
+ 5 │5 |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed('G')
+ screen:expect([[
+ {7: }1 │{7: }1 |
+ {7: }2 │{7: }2 |
+ {7: }3 │{7: }3 |
+ {7: }4 │{7: }4 |
+ {7: }5 │{7: }5 |
+ {7: }6 │{7: }6 |
+ {2:[No Name] [+] [No Name] [+] }|
+ {7: }496 │{7: }1 |
+ {7: }497 │{7: }2 |
+ {7: }498 │{7: }3 |
+ {7: }499 │{7: }4 |
+ {7:! }^500 │{7: }5 |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]])
+ feed(':lua log, needs_clear = {}, true<CR>')
+ screen:expect([[
+ {7: }1 │{7: }1 |
+ {7: }2 │{7: }2 |
+ {7: }3 │{7: }3 |
+ {7: }4 │{7: }4 |
+ {7: }5 │{7: }5 |
+ {7: }6 │{7: }6 |
+ {2:[No Name] [+] [No Name] [+] }|
+ {7: }496 │{7: }1 |
+ {7: }497 │{7: }2 |
+ {7: }498 │{7: }3 |
+ {7: }499 │{7: }4 |
+ {7:! }^500 │{7: }5 |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :lua log, needs_clear = {}, true |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua
index 268e7173e6..328e212a22 100644
--- a/test/functional/ui/statuscolumn_spec.lua
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -13,8 +13,6 @@ local api = n.api
local pcall_err = t.pcall_err
local assert_alive = n.assert_alive
-local mousemodels = { 'extend', 'popup', 'popup_setpos' }
-
describe('statuscolumn', function()
local screen
before_each(function()
@@ -229,15 +227,28 @@ describe('statuscolumn', function()
{1: }{8:8│}aaaaa |
|
]])
+ -- Last segment and fillchar are highlighted properly
+ command("set stc=%#Error#%{v:relnum?'Foo':'FooBar'}")
+ screen:expect([[
+ {9:Foo }aaaaa |*4
+ {9:FooBar}^aaaaa |
+ {9:Foo }aaaaa |*8
+ |
+ ]])
end)
it('works with wrapped lines, signs and folds', function()
- command([[set stc=%C%s%=%{v:virtnum?'':v:lnum}│\ ]])
- command("call setline(1,repeat([repeat('aaaaa',10)],16))")
screen:add_extra_attr_ids {
[100] = { foreground = Screen.colors.Red, background = Screen.colors.LightGray },
+ [101] = { background = Screen.colors.Gray90, bold = true },
+ [102] = { foreground = Screen.colors.Brown, background = Screen.colors.Grey },
+ [103] = { bold = true, background = Screen.colors.Grey, foreground = Screen.colors.Blue1 },
}
- command('hi! CursorLine guifg=Red guibg=NONE')
+ command([[set cursorline stc=%C%s%=%{v:virtnum?'':v:lnum}│\ ]])
+ command("call setline(1,repeat([repeat('aaaaa',10)],16))")
+ command('hi! CursorLine gui=bold')
+ command('sign define num1 numhl=Special')
+ command('sign place 1 line=8 name=num1 buffer=1')
screen:expect([[
{8: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{8: │ }a |
@@ -247,8 +258,8 @@ describe('statuscolumn', function()
{8: │ }a |
{8: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{8: │ }a |
- {8: 8│ }^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
- {8: │ }a |
+ {29: 8│ }{101:^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {29: │ }{101:a }|
{8: 9│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{8: │ }a |
{8:10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{1:@@@}|
@@ -256,7 +267,8 @@ describe('statuscolumn', function()
]])
command([[set stc=%C%s%=%l│\ ]])
screen:expect_unchanged()
- command('set signcolumn=auto:2 foldcolumn=auto')
+ command('hi! CursorLine guifg=Red guibg=NONE gui=NONE')
+ command('set nocursorline 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')
@@ -264,11 +276,11 @@ describe('statuscolumn', function()
command('sign place 3 line=6 name=piet1 buffer=1')
command('sign place 4 line=6 name=piet2 buffer=1')
screen:expect([[
- {8:>>}{7: }{8: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {102:>>}{7: }{8: 4│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │ }aaaaa |
- {1:>!}{7: }{8: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {103:>!}{7: }{8: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │ }aaaaa |
- {1:>!}{8:>> 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {103:>!}{102:>>}{8: 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │ }aaaaa |
{7: }{8: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │ }aaaaa |
@@ -283,11 +295,11 @@ describe('statuscolumn', function()
-- Check that alignment works properly with signs after %=
command([[set stc=%C%=%{v:virtnum?'':v:lnum}│%s\ ]])
screen:expect([[
- {7: }{8: 4│>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 4│}{102:>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
- {7: }{8: 5│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 5│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
- {7: }{8: 6│}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 6│}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
{7: }{8: 7│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
@@ -300,11 +312,11 @@ describe('statuscolumn', function()
]])
command('set cursorline')
screen:expect([[
- {7: }{8: 4│>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 4│}{102:>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
- {7: }{8: 5│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 5│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
- {7: }{8: 6│}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 6│}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
{7: }{8: 7│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaa |
@@ -318,11 +330,11 @@ describe('statuscolumn', function()
-- v:lnum is the same value on wrapped lines
command([[set stc=%C%=%{v:lnum}│%s\ ]])
screen:expect([[
- {7: }{8: 4│>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 4│}{102:>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 4│}{7: }{8: }aaaaaa |
- {7: }{8: 5│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 5│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 5│}{7: }{8: }aaaaaa |
- {7: }{8: 6│}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 6│}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 6│}{7: }{8: }aaaaaa |
{7: }{8: 7│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 7│}{7: }{8: }aaaaaa |
@@ -336,11 +348,11 @@ describe('statuscolumn', function()
-- v:relnum is the same value on wrapped lines
command([[set stc=%C%=\ %{v:relnum}│%s\ ]])
screen:expect([[
- {7: }{8: 4│>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 4│}{102:>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 4│}{7: }{8: }aaaaaaa |
- {7: }{8: 3│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 3│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 3│}{7: }{8: }aaaaaaa |
- {7: }{8: 2│}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 2│}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 2│}{7: }{8: }aaaaaaa |
{7: }{8: 1│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: 1│}{7: }{8: }aaaaaaa |
@@ -353,11 +365,11 @@ describe('statuscolumn', function()
]])
command([[set stc=%C%=\ %{v:virtnum?'':v:relnum}│%s\ ]])
screen:expect([[
- {7: }{8: 4│>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 4│}{102:>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaa |
- {7: }{8: 3│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 3│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaa |
- {7: }{8: 2│}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 2│}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaa |
{7: }{8: 1│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaa |
@@ -378,11 +390,11 @@ describe('statuscolumn', function()
command('sign place 10 line=6 name=piet2 buffer=1')
command('sign place 11 line=6 name=piet1 buffer=1')
screen:expect([[
- {7: }{8: 4│>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 4│}{102:>>}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaaaaaaaaaaaaaaaa |
- {7: }{8: 3│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 3│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaaaaaaaaaaaaaaaa |
- {7: }{8: 2│>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 2│}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaaaaaaaaaaaaaaaa |
{7: }{8: 1│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }{8: │}{7: }{8: }aaaaaaaaaaaaaaaaaaaaa |
@@ -397,11 +409,11 @@ describe('statuscolumn', function()
command('set cpoptions+=n')
feed('Hgjg0')
screen:expect([[
- {7: }{15: 0│}{8:>>}{7: }{15: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {7: }{15: 0│}{102:>>}{7: }{15: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{7: }{19:^aaaaaaaaaaaaaaaaaaaaa }|
- {7: }{8: 3│}{1:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 3│}{103:>!}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }aaaaaaaaaaaaaaaaaaaaa |
- {7: }{8: 2│>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 2│}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }aaaaaaaaaaaaaaaaaaaaa |
{7: }{8: 1│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }aaaaaaaaaaaaaaaaaaaaa |
@@ -416,11 +428,11 @@ describe('statuscolumn', function()
command('sign unplace 2')
feed('J2gjg0')
screen:expect([[
- {7: }{15: 0│}{8:>>}{7: }{15: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {7: }{15: 0│}{102:>>}{7: }{15: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{7: } {19:aaaaaaaaaaaaaaaaaaaaa aaaaaaa}|
{7: } {19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{7: } {19:^aaaaaaaaaaaaaa }|
- {7: }{8: 1│>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 1│}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: } aaaaaaaaaaaaaaaaaaaaa |
{7: }{8: 2│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: } aaaaaaaaaaaaaaaaaaaaa |
@@ -434,11 +446,11 @@ describe('statuscolumn', function()
command('set nobreakindent')
feed('$g0')
screen:expect([[
- {7: }{15: 0│}{8:>>}{7: }{15: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
+ {7: }{15: 0│}{102:>>}{7: }{15: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{7: }{19:aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa}|
{7: }{19:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{7: }{19:^aaaa }|
- {7: }{8: 1│>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>>}{1:>!}{8:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {7: }{8: 1│}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{103:>!}{102:>>}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }aaaaaaaaaaaaaaaaaaaaa |
{7: }{8: 2│}{7: }{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
{7: }aaaaaaaaaaaaaaaaaaaaa |
@@ -460,11 +472,11 @@ describe('statuscolumn', function()
]])
command('set foldcolumn=0 signcolumn=number stc=%l')
screen:expect([[
- {8:>>}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ {102:>>}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
{8: 5}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
{8: }virt_line |
{8: }virt_line above |
- {8:>>}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+ {102:>>}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
{8: 7}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
{15: 8}{100:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|
{8: 9}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
@@ -580,13 +592,13 @@ describe('statuscolumn', function()
command([[set stc=%6s\ %l]])
exec_lua('vim.api.nvim_buf_set_extmark(0, ns, 7, 0, {sign_text = "𒀀"})')
screen:expect([[
- {8: 𒀀 8}^aaaaa |
+ {8: }{7:𒀀 }{8: 8}^aaaaa |
{8: }{7: }{8: 9}aaaaa |
|
]])
end)
- for _, model in ipairs(mousemodels) do
+ for _, model in ipairs({ 'extend', 'popup', 'popup_setpos' }) do
describe('with mousemodel=' .. model, function()
before_each(function()
command('set mousemodel=' .. model)
@@ -645,23 +657,56 @@ describe('statuscolumn', function()
-- Check that statusline click doesn't register as statuscolumn click
api.nvim_input_mouse('right', 'press', '', 0, 12, 0)
eq('', eval('g:testvar'))
+ -- Check that rightclick still opens popupmenu if there is no clickdef
+ if model == 'popup' then
+ api.nvim_set_option_value('statuscolumn', '%0@MyClickFunc@%=%l%TNoClick', {})
+ api.nvim_input_mouse('right', 'press', '', 0, 1, 0)
+ screen:expect([[
+ {5:[No Name] }|
+ {8: 4NoClick}^aaaaa |
+ {8: 5NoClick}aaaaa |
+ {8: 6NoClick}aaaaa |
+ {8: 7NoClick}aaaaa |
+ {8: 8NoClick}aaaaa |
+ {8: 9NoClick}aaaaa |
+ {8:10NoClick}aaaaa |
+ {8:11NoClick}aaaaa |
+ {8:12NoClick}aaaaa |
+ {8:13NoClick}aaaaa |
+ {8:14NoClick}aaaaa |
+ {3:[No Name] [+] }|
+ |
+ ]])
+ api.nvim_input_mouse('right', 'press', '', 0, 1, 3)
+ screen:expect([[
+ {5:[No Name] }|
+ {8: 4NoClick}^aaaaa |
+ {8: 5}{4: Inspect } |
+ {8: 6}{4: } |
+ {8: 7}{4: Paste } |
+ {8: 8}{4: Select All } |
+ {8: 9}{4: } |
+ {8:10}{4: How-to disable mouse } |
+ {8:11NoClick}aaaaa |
+ {8:12NoClick}aaaaa |
+ {8:13NoClick}aaaaa |
+ {8:14NoClick}aaaaa |
+ {3:[No Name] [+] }|
+ |
+ ]])
+ end
end)
it('clicks and highlights work with control characters', function()
api.nvim_set_option_value('statuscolumn', '\t%#NonText#\1%0@MyClickFunc@\t\1%T\t%##\1', {})
- screen:expect {
- grid = [[
- {1:^I}{0:^A^I^A^I}{1:^A}aaaaa |*4
- {1:^I}{0:^A^I^A^I}{1:^A}^aaaaa |
- {1:^I}{0:^A^I^A^I}{1:^A}aaaaa |*8
+ screen:expect([[
+ {8:^I}{1:^A^I^A^I}{8:^A}aaaaa |*4
+ {8:^I}{1:^A^I^A^I}{8:^A}^aaaaa |
+ {8:^I}{1:^A^I^A^I}{8:^A}aaaaa |*8
|
- ]],
- attr_ids = {
- [0] = { foreground = Screen.colors.Blue, bold = true }, -- NonText
- [1] = { foreground = Screen.colors.Brown }, -- LineNr
- },
- }
+ ]])
api.nvim_input_mouse('right', 'press', '', 0, 4, 3)
+ feed('<Esc>') -- Close popupmenu
eq('', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 5, 8)
eq('', eval('g:testvar'))
@@ -707,6 +752,36 @@ describe('statuscolumn', function()
|
]])
end)
+
+ it('foldcolumn item can be clicked', function()
+ api.nvim_set_option_value('statuscolumn', '|%C|', {})
+ api.nvim_set_option_value('foldcolumn', '2', {})
+ api.nvim_set_option_value('mousetime', 0, {})
+ feed('ggzfjzfjzo')
+ local s1 = [[
+ {8:|}{7:-+}{8:|}{13:^+--- 2 lines: aaaaa·····························}|
+ {8:|}{7:│ }{8:|}aaaaa |
+ {8:|}{7: }{8:|}aaaaa |*11
+ |
+ ]]
+ screen:expect(s1)
+ api.nvim_input_mouse('left', 'press', '', 0, 0, 2)
+ screen:expect([[
+ {8:|}{7:--}{8:|}^aaaaa |
+ {8:|}{7:││}{8:|}aaaaa |
+ {8:|}{7:│ }{8:|}aaaaa |
+ {8:|}{7: }{8:|}aaaaa |*10
+ |
+ ]])
+ api.nvim_input_mouse('left', 'press', '', 0, 0, 1)
+ screen:expect(s1)
+ api.nvim_input_mouse('left', 'press', '', 0, 0, 1)
+ screen:expect([[
+ {8:|}{7:+ }{8:|}{13:^+-- 3 lines: aaaaa······························}|
+ {8:|}{7: }{8:|}aaaaa |*12
+ |
+ ]])
+ end)
end)
end
diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua
index 1d0f181244..50e31ac6a9 100644
--- a/test/functional/ui/statusline_spec.lua
+++ b/test/functional/ui/statusline_spec.lua
@@ -507,268 +507,263 @@ describe('global statusline', function()
end)
end)
-it('statusline does not crash if it has Arabic characters #19447', function()
- clear()
- api.nvim_set_option_value('statusline', 'غً', {})
- api.nvim_set_option_value('laststatus', 2, {})
- command('redraw!')
- assert_alive()
-end)
+describe('statusline', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(40, 8)
+ screen:add_extra_attr_ids {
+ [100] = { bold = true, reverse = true, foreground = Screen.colors.Blue },
+ [101] = { reverse = true, bold = true, foreground = Screen.colors.SlateBlue },
+ }
+ end)
-it('statusline is redrawn with :resize from <Cmd> mapping #19629', function()
- clear()
- local screen = Screen.new(40, 8)
- exec([[
- set laststatus=2
- nnoremap <Up> <cmd>resize -1<CR>
- nnoremap <Down> <cmd>resize +1<CR>
- ]])
- feed('<Up>')
- screen:expect([[
- ^ |
- {1:~ }|*4
- {3:[No Name] }|
- |*2
- ]])
- feed('<Down>')
- screen:expect([[
- ^ |
- {1:~ }|*5
- {3:[No Name] }|
- |
- ]])
-end)
+ it('does not crash if it has Arabic characters #19447', function()
+ api.nvim_set_option_value('statusline', 'غً', {})
+ api.nvim_set_option_value('laststatus', 2, {})
+ command('redraw!')
+ assert_alive()
+ end)
-it('showcmdloc=statusline does not show if statusline is too narrow', function()
- clear()
- local screen = Screen.new(40, 8)
- command('set showcmd')
- command('set showcmdloc=statusline')
- command('1vsplit')
- screen:expect([[
- ^ │ |
- {1:~}│{1:~ }|*5
- {3:< }{2:[No Name] }|
- |
- ]])
- feed('1234')
- screen:expect_unchanged()
-end)
+ it('is redrawn with :resize from <Cmd> mapping #19629', function()
+ exec([[
+ set laststatus=2
+ nnoremap <Up> <cmd>resize -1<CR>
+ nnoremap <Down> <cmd>resize +1<CR>
+ ]])
+ feed('<Up>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*4
+ {3:[No Name] }|
+ |*2
+ ]])
+ feed('<Down>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:[No Name] }|
+ |
+ ]])
+ end)
-it('K_EVENT does not trigger a statusline redraw unnecessarily', function()
- clear()
- local _ = Screen.new(40, 8)
- -- does not redraw on vim.schedule (#17937)
- command([[
- set laststatus=2
- let g:counter = 0
- func Status()
- let g:counter += 1
- lua vim.schedule(function() end)
- return g:counter
- endfunc
- set statusline=%!Status()
- ]])
- sleep(50)
- eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
- -- also in insert mode
- feed('i')
- sleep(50)
- eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
- -- does not redraw on timer call (#14303)
- command([[
- let g:counter = 0
- func Timer(timer)
- endfunc
- call timer_start(1, 'Timer', {'repeat': 100})
- ]])
- sleep(50)
- eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
-end)
+ it('does not contain showmcd with showcmdloc=statusline when too narrow', function()
+ command('set showcmd')
+ command('set showcmdloc=statusline')
+ command('1vsplit')
+ screen:expect([[
+ ^ │ |
+ {1:~}│{1:~ }|*5
+ {3:< }{2:[No Name] }|
+ |
+ ]])
+ feed('1234')
+ screen:expect_unchanged()
+ end)
-it('statusline is redrawn on various state changes', function()
- clear()
- local screen = Screen.new(40, 4)
-
- -- recording state change #22683
- command('set ls=2 stl=%{repeat(reg_recording(),5)}')
- screen:expect([[
- ^ |
- {1:~ }|
- {3: }|
- |
- ]])
- feed('qQ')
- screen:expect([[
- ^ |
- {1:~ }|
- {3:QQQQQ }|
- {5:recording @Q} |
- ]])
- feed('q')
- screen:expect([[
- ^ |
- {1:~ }|
- {3: }|
- |
- ]])
-
- -- Visual mode change #23932
- command('set ls=2 stl=%{mode(1)}')
- screen:expect([[
- ^ |
- {1:~ }|
- {3:n }|
- |
- ]])
- feed('v')
- screen:expect([[
- ^ |
- {1:~ }|
- {3:v }|
- {5:-- VISUAL --} |
- ]])
- feed('V')
- screen:expect([[
- ^ |
- {1:~ }|
- {3:V }|
- {5:-- VISUAL LINE --} |
- ]])
- feed('<C-V>')
- screen:expect([[
- ^ |
- {1:~ }|
- {3:^V }|
- {5:-- VISUAL BLOCK --} |
- ]])
- feed('<Esc>')
- screen:expect([[
- ^ |
- {1:~ }|
- {3:n }|
- |
- ]])
-end)
+ it('does not redraw unnecessarily after K_EVENT', function()
+ -- does not redraw on vim.schedule (#17937)
+ command([[
+ set laststatus=2
+ let g:counter = 0
+ func Status()
+ let g:counter += 1
+ lua vim.schedule(function() end)
+ return g:counter
+ endfunc
+ set statusline=%!Status()
+ ]])
+ sleep(50)
+ eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
+ -- also in insert mode
+ feed('i')
+ sleep(50)
+ eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
+ -- does not redraw on timer call (#14303)
+ command([[
+ let g:counter = 0
+ func Timer(timer)
+ endfunc
+ call timer_start(1, 'Timer', {'repeat': 100})
+ ]])
+ sleep(50)
+ eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
+ end)
-it('ruler is redrawn in cmdline with redrawstatus #22804', function()
- clear()
- local screen = Screen.new(40, 2)
- command([[
- let g:n = 'initial value'
- set ls=1 ru ruf=%{g:n}
- redraw
- let g:n = 'other value'
- redrawstatus
- ]])
- screen:expect([[
- ^ |
- other value |
- ]])
-end)
+ it('is redrawn on various state changes', function()
+ -- recording state change #22683
+ command('set ls=2 stl=%{repeat(reg_recording(),5)}')
+ local s1 = [[
+ ^ |
+ {1:~ }|*5
+ {3: }|
+ |
+ ]]
+ screen:expect(s1)
+ feed('qQ')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:QQQQQ }|
+ {5:recording @Q} |
+ ]])
+ feed('q')
+ screen:expect(s1)
+
+ -- Visual mode change #23932
+ command('set ls=2 stl=%{mode(1)}')
+ local s2 = [[
+ ^ |
+ {1:~ }|*5
+ {3:n }|
+ |
+ ]]
+ screen:expect(s2)
+ feed('v')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:v }|
+ {5:-- VISUAL --} |
+ ]])
+ feed('V')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:V }|
+ {5:-- VISUAL LINE --} |
+ ]])
+ feed('<C-V>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {3:^V }|
+ {5:-- VISUAL BLOCK --} |
+ ]])
+ feed('<Esc>')
+ screen:expect(s2)
+ end)
-it('shows correct ruler in cmdline with no statusline', function()
- clear()
- local screen = Screen.new(30, 8)
- -- Use long ruler to check 'ruler' with 'rulerformat' set has correct width.
- command [[
- set ruler rulerformat=%{winnr()}longlonglong ls=0 winwidth=10
- split
- wincmd b
- vsplit
- wincmd t
- wincmd |
- mode
- ]]
- -- Window 1 is current. It has a statusline, so cmdline should show the
- -- last window's ruler, which has no statusline.
- command '1wincmd w'
- screen:expect [[
- ^ |
- {1:~ }|*2
- {3:[No Name] 1longlonglong }|
- │ |
- {1:~ }│{1:~ }|*2
- 3longlonglong |
- ]]
- -- Window 2 is current. It has no statusline, so cmdline should show its
- -- ruler instead.
- command '2wincmd w'
- screen:expect [[
- |
- {1:~ }|*2
- {2:[No Name] 1longlonglong }|
- ^ │ |
- {1:~ }│{1:~ }|*2
- 2longlonglong |
- ]]
- -- Window 3 is current. Cmdline should again show its ruler.
- command '3wincmd w'
- screen:expect [[
- |
- {1:~ }|*2
- {2:[No Name] 1longlonglong }|
- │^ |
- {1:~ }│{1:~ }|*2
- 3longlonglong |
- ]]
-end)
+ it('ruler is redrawn in cmdline with redrawstatus #22804', function()
+ command([[
+ let g:n = 'initial value'
+ set ls=1 ru ruf=%{g:n}
+ redraw
+ let g:n = 'other value'
+ redrawstatus
+ ]])
+ screen:expect([[
+ ^ |
+ {1:~ }|*6
+ other value |
+ ]])
+ end)
-it('uses "stl" and "stlnc" fillchars even if they are the same #19803', function()
- clear()
- local screen = Screen.new(53, 4)
- command('hi clear StatusLine')
- command('hi clear StatusLineNC')
- command('vsplit')
- screen:expect {
- grid = [[
- ^ │ |
- {1:~ }│{1:~ }|
- [No Name] [No Name] |
- |
- ]],
- }
-end)
+ it('hidden moves ruler to cmdline', function()
+ -- Use long ruler to check 'ruler' with 'rulerformat' set has correct width.
+ command [[
+ set ruler rulerformat=%{winnr()}longlonglong ls=0 winwidth=10
+ split
+ wincmd b
+ vsplit
+ wincmd t
+ wincmd |
+ mode
+ ]]
+ -- Window 1 is current. It has a statusline, so cmdline should show the
+ -- last window's ruler, which has no statusline.
+ command '1wincmd w'
+ screen:expect([[
+ ^ |
+ {1:~ }|*2
+ {3:[No Name] 1longlonglong }|
+ │ |
+ {1:~ }│{1:~ }|*2
+ 3longlonglong |
+ ]])
+ -- Window 2 is current. It has no statusline, so cmdline should show its
+ -- ruler instead.
+ command '2wincmd w'
+ screen:expect([[
+ |
+ {1:~ }|*2
+ {2:[No Name] 1longlonglong }|
+ ^ │ |
+ {1:~ }│{1:~ }|*2
+ 2longlonglong |
+ ]])
+ -- Window 3 is current. Cmdline should again show its ruler.
+ command '3wincmd w'
+ screen:expect([[
+ |
+ {1:~ }|*2
+ {2:[No Name] 1longlonglong }|
+ │^ |
+ {1:~ }│{1:~ }|*2
+ 3longlonglong |
+ ]])
+ end)
-it('showcmdloc=statusline works with vertical splits', function()
- clear()
- local screen = Screen.new(53, 4)
- command('rightbelow vsplit')
- command('set showcmd showcmdloc=statusline')
- feed('1234')
- screen:expect([[
- │^ |
- {1:~ }│{1:~ }|
- {2:[No Name] }{3:[No Name] 1234 }|
- |
- ]])
- feed('<Esc>')
- command('set laststatus=3')
- feed('1234')
- screen:expect([[
- │^ |
- {1:~ }│{1:~ }|
- {3:[No Name] 1234 }|
- |
- ]])
-end)
+ it('uses "stl" and "stlnc" fillchars even if they are the same #19803', function()
+ command('hi clear StatusLine')
+ command('hi clear StatusLineNC')
+ command('vsplit')
+ screen:expect([[
+ ^ │ |
+ {1:~ }│{1:~ }|*5
+ [No Name] [No Name] |
+ |
+ ]])
+ end)
-it('keymap is shown with vertical splits #27269', function()
- clear()
- local screen = Screen.new(53, 4)
- command('setlocal keymap=dvorak')
- command('rightbelow vsplit')
- screen:expect([[
- │^ |
- {1:~ }│{1:~ }|
- {2:[No Name] <en-dv> }{3:[No Name] <en-dv> }|
- |
- ]])
- command('set laststatus=3')
- screen:expect([[
- │^ |
- {1:~ }│{1:~ }|
- {3:[No Name] <en-dv> }|
- |
- ]])
+ it('showcmdloc=statusline works with vertical splits', function()
+ command('rightbelow vsplit')
+ command('set showcmd showcmdloc=statusline')
+ feed('1234')
+ screen:expect([[
+ │^ |
+ {1:~ }│{1:~ }|*5
+ {2:[No Name] }{3:[No Name] 1234 }|
+ |
+ ]])
+ feed('<Esc>')
+ command('set laststatus=3')
+ feed('1234')
+ screen:expect([[
+ │^ |
+ {1:~ }│{1:~ }|*5
+ {3:[No Name] 1234 }|
+ |
+ ]])
+ end)
+
+ it('keymap is shown with vertical splits #27269', function()
+ command('setlocal keymap=dvorak')
+ command('rightbelow vsplit')
+ screen:expect([[
+ │^ |
+ {1:~ }│{1:~ }|*5
+ {2:[No Name] <en-dv> }{3:[No Name] <en-dv> }|
+ |
+ ]])
+
+ command('set laststatus=3')
+ screen:expect([[
+ │^ |
+ {1:~ }│{1:~ }|*5
+ {3:[No Name] <en-dv> }|
+ |
+ ]])
+ end)
+
+ it("nested call from nvim_eval_statusline() doesn't overwrite items #32259", function()
+ exec_lua('vim.o.laststatus = 2')
+ exec_lua([[vim.o.statusline = '%#Special#B:%{nvim_eval_statusline("%f", []).str}']])
+ screen:expect([[
+ ^ |
+ {1:~ }|*5
+ {101:B:[No Name] }|
+ |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua
index 57d76e54df..80e38d974a 100644
--- a/test/functional/ui/syntax_conceal_spec.lua
+++ b/test/functional/ui/syntax_conceal_spec.lua
@@ -198,7 +198,7 @@ describe('Screen', function()
end)
end) -- a region of text (implicit concealing)
- it('cursor position is correct when entering Insert mode with cocu=ni #13916', function()
+ it('cursor position when entering Insert mode with cocu=ni #13916', function()
insert([[foobarfoobarfoobar]])
-- move to end of line
feed('$')
@@ -217,6 +217,37 @@ describe('Screen', function()
{4:-- INSERT --} |
]])
end)
+
+ it('cursor position when scrolling in Normal mode with cocu=n #31271', function()
+ insert(('foo\n'):rep(9) .. 'foofoobarfoofoo' .. ('\nfoo'):rep(9))
+ command('set concealcursor=n')
+ command('syn match Foo /bar/ conceal cchar=&')
+ feed('gg5<C-E>10gg$')
+ screen:expect([[
+ foo |*4
+ foofoo{1:&}foofo^o |
+ foo |*4
+ |
+ ]])
+ feed('zz')
+ screen:expect_unchanged()
+ feed('zt')
+ screen:expect([[
+ foofoo{1:&}foofo^o |
+ foo |*8
+ |
+ ]])
+ feed('zt')
+ screen:expect_unchanged()
+ feed('zb')
+ screen:expect([[
+ foo |*8
+ foofoo{1:&}foofo^o |
+ |
+ ]])
+ feed('zb')
+ screen:expect_unchanged()
+ end)
end) -- match and conceal
describe('let the conceal level be', function()
diff --git a/test/functional/ui/title_spec.lua b/test/functional/ui/title_spec.lua
index 66eb15478b..2de1e71457 100644
--- a/test/functional/ui/title_spec.lua
+++ b/test/functional/ui/title_spec.lua
@@ -37,6 +37,63 @@ describe('title', function()
end)
end)
+ it('is updated in Insert mode', function()
+ api.nvim_set_option_value('title', true, {})
+ screen:expect(function()
+ eq('[No Name] - Nvim', screen.title)
+ end)
+ feed('ifoo')
+ screen:expect(function()
+ eq('[No Name] + - Nvim', screen.title)
+ end)
+ feed('<Esc>')
+ api.nvim_set_option_value('titlestring', '%m %f (%{mode(1)}) | nvim', {})
+ screen:expect(function()
+ eq('[+] [No Name] (n) | nvim', screen.title)
+ end)
+ feed('i')
+ screen:expect(function()
+ eq('[+] [No Name] (i) | nvim', screen.title)
+ end)
+ feed('<Esc>')
+ screen:expect(function()
+ eq('[+] [No Name] (n) | nvim', screen.title)
+ end)
+ end)
+
+ it('is updated in Cmdline mode', function()
+ api.nvim_set_option_value('title', true, {})
+ api.nvim_set_option_value('titlestring', '%f (%{mode(1)}) | nvim', {})
+ screen:expect(function()
+ eq('[No Name] (n) | nvim', screen.title)
+ end)
+ feed(':')
+ screen:expect(function()
+ eq('[No Name] (c) | nvim', screen.title)
+ end)
+ feed('<Esc>')
+ screen:expect(function()
+ eq('[No Name] (n) | nvim', screen.title)
+ end)
+ end)
+
+ it('is updated in Terminal mode', function()
+ api.nvim_set_option_value('title', true, {})
+ api.nvim_set_option_value('titlestring', '(%{mode(1)}) | nvim', {})
+ fn.jobstart({ n.testprg('shell-test'), 'INTERACT' }, { term = true })
+ screen:expect(function()
+ eq('(nt) | nvim', screen.title)
+ end)
+ feed('i')
+ screen:expect(function()
+ eq('(t) | nvim', screen.title)
+ end)
+ feed([[<C-\><C-N>]])
+ screen:expect(function()
+ eq('(nt) | nvim', screen.title)
+ end)
+ end)
+
describe('is not changed by', function()
local file1 = is_os('win') and 'C:\\mydir\\myfile1' or '/mydir/myfile1'
local file2 = is_os('win') and 'C:\\mydir\\myfile2' or '/mydir/myfile2'
diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua
index 873e4f820d..85a74c0ab6 100644
--- a/test/functional/vimscript/ctx_functions_spec.lua
+++ b/test/functional/vimscript/ctx_functions_spec.lua
@@ -188,7 +188,10 @@ describe('context functions', function()
function RestoreFuncs()
call ctxpop()
endfunction
+
+ let g:sid = expand('<SID>')
]])
+ local sid = api.nvim_get_var('sid')
eq('Hello, World!', exec_capture([[call Greet('World')]]))
eq(
@@ -200,11 +203,11 @@ describe('context functions', function()
call('DeleteSFuncs')
eq(
- 'function Greet, line 1: Vim(call):E117: Unknown function: s:greet',
+ ('function Greet, line 1: Vim(call):E117: Unknown function: %sgreet'):format(sid),
pcall_err(command, [[call Greet('World')]])
)
eq(
- 'function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all',
+ ('function GreetAll, line 1: Vim(call):E117: Unknown function: %sgreet_all'):format(sid),
pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])
)
diff --git a/test/functional/vimscript/getchar_spec.lua b/test/functional/vimscript/getchar_spec.lua
new file mode 100644
index 0000000000..4ecf082f97
--- /dev/null
+++ b/test/functional/vimscript/getchar_spec.lua
@@ -0,0 +1,95 @@
+local n = require('test.functional.testnvim')()
+local Screen = require('test.functional.ui.screen')
+
+local clear = n.clear
+local exec = n.exec
+local feed = n.feed
+local async_command = n.async_meths.nvim_command
+local poke_eventloop = n.poke_eventloop
+
+describe('getchar()', function()
+ before_each(clear)
+
+ -- oldtest: Test_getchar_cursor_position()
+ it('cursor positioning', function()
+ local screen = Screen.new(40, 6)
+ exec([[
+ call setline(1, ['foobar', 'foobar', 'foobar'])
+ call cursor(3, 6)
+ ]])
+ screen:expect([[
+ foobar |*2
+ fooba^r |
+ {1:~ }|*2
+ |
+ ]])
+
+ -- Default: behaves like "msg" when immediately after printing a message,
+ -- even if :sleep moved cursor elsewhere.
+ for _, cmd in ipairs({
+ 'echo 1234 | call getchar()',
+ 'echo 1234 | call getchar(-1, {})',
+ "echo 1234 | call getchar(-1, #{cursor: 'msg'})",
+ 'echo 1234 | sleep 1m | call getchar()',
+ 'echo 1234 | sleep 1m | call getchar(-1, {})',
+ "echo 1234 | sleep 1m | call getchar(-1, #{cursor: 'msg'})",
+ }) do
+ async_command(cmd)
+ screen:expect([[
+ foobar |*3
+ {1:~ }|*2
+ 1234^ |
+ ]])
+ feed('a')
+ screen:expect([[
+ foobar |*2
+ fooba^r |
+ {1:~ }|*2
+ 1234 |
+ ]])
+ end
+
+ -- Default: behaves like "keep" when not immediately after printing a message.
+ for _, cmd in ipairs({
+ 'call getchar()',
+ 'call getchar(-1, {})',
+ "call getchar(-1, #{cursor: 'keep'})",
+ "echo 1234 | sleep 1m | call getchar(-1, #{cursor: 'keep'})",
+ }) do
+ async_command(cmd)
+ poke_eventloop()
+ screen:expect_unchanged()
+ feed('a')
+ poke_eventloop()
+ screen:expect_unchanged()
+ end
+
+ async_command("call getchar(-1, #{cursor: 'msg'})")
+ screen:expect([[
+ foobar |*3
+ {1:~ }|*2
+ ^1234 |
+ ]])
+ feed('a')
+ screen:expect([[
+ foobar |*2
+ fooba^r |
+ {1:~ }|*2
+ 1234 |
+ ]])
+
+ async_command("call getchar(-1, #{cursor: 'hide'})")
+ screen:expect([[
+ foobar |*3
+ {1:~ }|*2
+ 1234 |
+ ]])
+ feed('a')
+ screen:expect([[
+ foobar |*2
+ fooba^r |
+ {1:~ }|*2
+ 1234 |
+ ]])
+ end)
+end)
diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua
index 9a27239a6d..afd50f7cf9 100644
--- a/test/functional/vimscript/null_spec.lua
+++ b/test/functional/vimscript/null_spec.lua
@@ -116,7 +116,7 @@ describe('NULL', function()
null_expr_test(
'is accepted as an empty list by inputlist()',
'[feedkeys("\\n"), inputlist(L)]',
- 'Type number and <Enter> or click with the mouse (q or empty cancels): ',
+ '',
{ 0, 0 }
)
null_expr_test(
diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua
index d1b8bfe5d9..3e4e6de35c 100644
--- a/test/functional/vimscript/timer_spec.lua
+++ b/test/functional/vimscript/timer_spec.lua
@@ -94,12 +94,56 @@ describe('timers', function()
assert(0 <= diff and diff <= 4, 'expected (0 <= diff <= 4), got: ' .. tostring(diff))
end)
+ it('are triggered in inputlist() call #7857', function()
+ async_meths.nvim_exec2(
+ [[
+ call timer_start(5, 'MyHandler', {'repeat': -1})
+ let g:val = 0
+ let g:n = inputlist(['input0', 'input1'])
+ ]],
+ {}
+ )
+ retry(nil, nil, function()
+ local val = eval('g:val')
+ ok(val >= 2, '>= 2', tostring(val))
+ eq(0, eval("exists('g:n')"))
+ end)
+ feed('42<CR>')
+ eq(42, eval('g:n'))
+ end)
+
+ it('are triggered in confirm() call', function()
+ api.nvim_ui_attach(80, 24, {}) -- needed for confirm() to work
+ async_meths.nvim_exec2(
+ [[
+ call timer_start(5, 'MyHandler', {'repeat': -1})
+ let g:val = 0
+ let g:n = confirm('Are you sure?', "&Yes\n&No\n&Cancel")
+ ]],
+ {}
+ )
+ retry(nil, nil, function()
+ local val = eval('g:val')
+ ok(val >= 2, '>= 2', tostring(val))
+ eq(0, eval("exists('g:n')"))
+ end)
+ feed('c')
+ eq(3, eval('g:n'))
+ end)
+
it('are triggered in blocking getchar() call', function()
- command("call timer_start(5, 'MyHandler', {'repeat': -1})")
- async_meths.nvim_command('let g:val = 0 | let g:c = getchar()')
+ async_meths.nvim_exec2(
+ [[
+ call timer_start(5, 'MyHandler', {'repeat': -1})
+ let g:val = 0
+ let g:c = getchar()
+ ]],
+ {}
+ )
retry(nil, nil, function()
local val = eval('g:val')
ok(val >= 2, '>= 2', tostring(val))
+ eq(0, eval("exists('g:c')"))
eq(0, eval('getchar(1)'))
end)
feed('c')
@@ -126,39 +170,36 @@ describe('timers', function()
redraw
endfunc
]])
- async_meths.nvim_command('let g:c2 = getchar()')
+ async_meths.nvim_command("let g:c2 = getchar(-1, {'cursor': 'msg'})")
async_meths.nvim_command(
'call timer_start(' .. load_adjust(100) .. ", 'AddItem', {'repeat': -1})"
)
screen:expect([[
- ^ITEM 1 |
+ ITEM 1 |
ITEM 2 |
{1:~ }|*3
- |
+ ^ |
]])
async_meths.nvim_command('let g:cont = 1')
screen:expect([[
- ^ITEM 1 |
+ ITEM 1 |
ITEM 2 |
ITEM 3 |
{1:~ }|*2
- |
+ ^ |
]])
feed('3')
eq(51, eval('g:c2'))
- screen:expect {
- grid = [[
+ screen:expect([[
^ITEM 1 |
ITEM 2 |
ITEM 3 |
{1:~ }|*2
|
- ]],
- unchanged = true,
- }
+ ]])
end)
it('can be stopped', function()
diff --git a/test/old/testdir/gen_opt_test.vim b/test/old/testdir/gen_opt_test.vim
index be5a7e6ee4..f12ce8c7c4 100644
--- a/test/old/testdir/gen_opt_test.vim
+++ b/test/old/testdir/gen_opt_test.vim
@@ -87,7 +87,7 @@ let test_values = {
\ ['xxx', 'help,nofile']],
\ 'clipboard': [['', 'unnamed'], ['xxx', '\ze*', 'exclude:\\%(']],
\ 'completeopt': [['', 'menu', 'menuone', 'longest', 'preview', 'popup',
- \ 'noinsert', 'noselect', 'fuzzy', 'menu,longest'],
+ \ 'noinsert', 'noselect', 'fuzzy', 'preinsert', 'menu,longest'],
\ ['xxx', 'menu,,,longest,']],
\ 'encoding': [['utf8'], []],
\ 'foldcolumn': [[0, 1, 4, 'auto', 'auto:1', 'auto:9'], [-1, 13, 999]],
@@ -117,12 +117,11 @@ let test_values = {
"\ 'imstyle': [[0, 1], [-1, 2, 999]],
\ 'lines': [[2, 24, 1000], [-1, 0, 1]],
\ 'linespace': [[-1, 0, 2, 4, 999], ['']],
- \ 'msghistory': [[0, 1, 100, 10000], [-1, 10001]],
\ 'numberwidth': [[1, 4, 8, 10, 11, 20], [-1, 0, 21]],
\ 'regexpengine': [[0, 1, 2], [-1, 3, 999]],
\ 'report': [[0, 1, 2, 9999], [-1]],
- \ 'scroll': [[0, 1, 2, 20], [-1, 999]],
- \ 'scrolljump': [[-100, -1, 0, 1, 2, 20], [-101, 999]],
+ \ 'scroll': [[0, 1, 2, 15], [-1, 999]],
+ \ 'scrolljump': [[-100, -1, 0, 1, 2, 15], [-101, 999]],
\ 'scrolloff': [[0, 1, 8, 999], [-1]],
\ 'shiftwidth': [[0, 1, 8, 999], [-1]],
\ 'sidescroll': [[0, 1, 8, 999], [-1]],
@@ -146,8 +145,8 @@ let test_values = {
\ 'winwidth': [[1, 10, 999], [-1, 0]],
\
"\ string options
- \ 'ambiwidth': [['', 'single', 'double'], ['xxx']],
- \ 'background': [['', 'light', 'dark'], ['xxx']],
+ \ 'ambiwidth': [['single', 'double'], ['xxx']],
+ \ 'background': [['light', 'dark'], ['xxx']],
"\ 'backspace': [[0, 1, 2, 3, '', 'indent', 'eol', 'start', 'nostop',
"\ " 'eol,start', 'indent,eol,nostop'],
"\ " [-1, 4, 'xxx']],
@@ -187,7 +186,7 @@ let test_values = {
\ ['xxx']],
\ 'concealcursor': [['', 'n', 'v', 'i', 'c', 'nvic'], ['xxx']],
"\ 'completeopt': [['', 'menu', 'menuone', 'longest', 'preview', 'popup',
- "\ " 'popuphidden', 'noinsert', 'noselect', 'fuzzy', 'menu,longest'],
+ "\ " 'popuphidden', 'noinsert', 'noselect', 'fuzzy', 'preinsert', 'menu,longest'],
"\ " ['xxx', 'menu,,,longest,']],
\ 'completeitemalign': [['abbr,kind,menu', 'menu,abbr,kind'],
\ ['', 'xxx', 'abbr', 'abbr,menu', 'abbr,menu,kind,abbr',
@@ -210,17 +209,19 @@ let test_values = {
\ 'icase', 'iwhite', 'iwhiteall', 'horizontal', 'vertical',
\ 'closeoff', 'hiddenoff', 'foldcolumn:0', 'foldcolumn:12',
\ 'followwrap', 'internal', 'indent-heuristic', 'algorithm:myers',
- \ 'algorithm:minimal', 'algorithm:patience',
- \ 'algorithm:histogram', 'icase,iwhite'],
- \ ['xxx', 'foldcolumn:xxx', 'algorithm:xxx', 'algorithm:']],
+ \ 'icase,iwhite', 'algorithm:minimal', 'algorithm:patience',
+ \ 'algorithm:histogram', 'linematch:5'],
+ \ ['xxx', 'foldcolumn:', 'foldcolumn:x', 'foldcolumn:xxx',
+ \ 'linematch:', 'linematch:x', 'linematch:xxx', 'algorithm:',
+ \ 'algorithm:xxx', 'context:', 'context:x', 'context:xxx']],
\ 'display': [['', 'lastline', 'truncate', 'uhex', 'lastline,uhex'],
\ ['xxx']],
- \ 'eadirection': [['', 'both', 'ver', 'hor'], ['xxx', 'ver,hor']],
+ \ 'eadirection': [['both', 'ver', 'hor'], ['xxx', 'ver,hor']],
"\ 'encoding': [['latin1'], ['xxx', '']],
\ 'eventignore': [['', 'WinEnter', 'WinLeave,winenter', 'all,WinEnter'],
\ ['xxx']],
\ 'fileencoding': [['', 'latin1', 'xxx'], []],
- \ 'fileformat': [['', 'dos', 'unix', 'mac'], ['xxx']],
+ \ 'fileformat': [['dos', 'unix', 'mac'], ['xxx']],
\ 'fileformats': [['', 'dos', 'dos,unix'], ['xxx']],
\ 'fillchars': [['', 'stl:x', 'stlnc:x', 'vert:x', 'fold:x', 'foldopen:x',
\ 'foldclose:x', 'foldsep:x', 'diff:x', 'eob:x', 'lastline:x',
@@ -264,10 +265,18 @@ let test_values = {
\ 'eol:\\u21b5', 'eol:\\U000021b5', 'eol:x,space:y'],
\ ['xxx', 'eol:']],
\ 'matchpairs': [['', '(:)', '(:),<:>'], ['xxx']],
+ \ 'messagesopt': [['hit-enter,history:1', 'hit-enter,history:10000',
+ \ 'history:100,wait:100', 'history:0,wait:0',
+ \ 'hit-enter,history:1,wait:1'],
+ \ ['xxx', 'history:500', 'hit-enter,history:-1',
+ \ 'hit-enter,history:10001', 'history:0,wait:10001',
+ \ 'hit-enter', 'history:10,wait:99999999999999999999',
+ \ 'history:99999999999999999999,wait:10', 'wait:10',
+ \ 'history:-10', 'history:10,wait:-10']],
\ 'mkspellmem': [['10000,100,12'], ['', 'xxx', '10000,100']],
\ 'mouse': [['', 'n', 'v', 'i', 'c', 'h', 'a', 'r', 'nvi'],
\ ['xxx', 'n,v,i']],
- \ 'mousemodel': [['', 'extend', 'popup', 'popup_setpos'], ['xxx']],
+ \ 'mousemodel': [['extend', 'popup', 'popup_setpos'], ['xxx']],
\ 'mouseshape': [['', 'n:arrow'], ['xxx']],
\ 'nrformats': [['', 'alpha', 'octal', 'hex', 'bin', 'unsigned', 'blank',
\ 'alpha,hex,bin'],
@@ -292,7 +301,7 @@ let test_values = {
\ 'sessionoptions': [['', 'blank', 'curdir', 'sesdir',
\ 'help,options,slash'],
\ ['xxx', 'curdir,sesdir']],
- \ 'showcmdloc': [['', 'last', 'statusline', 'tabline'], ['xxx']],
+ \ 'showcmdloc': [['last', 'statusline', 'tabline'], ['xxx']],
"\ 'signcolumn': [['', 'auto', 'no', 'yes', 'number'], ['xxx', 'no,yes']],
\ 'spellfile': [['', 'file.en.add', 'xxx.en.add,yyy.gb.add,zzz.ja.add',
\ '/tmp/dir\ with\ space/en.utf-8.add',
@@ -304,7 +313,7 @@ let test_values = {
\ 'spellsuggest': [['', 'best', 'double', 'fast', '100', 'timeout:100',
\ 'timeout:-1', 'file:/tmp/file', 'expr:Func()', 'double,33'],
\ ['xxx', '-1', 'timeout:', 'best,double', 'double,fast']],
- \ 'splitkeep': [['', 'cursor', 'screen', 'topline'], ['xxx']],
+ \ 'splitkeep': [['cursor', 'screen', 'topline'], ['xxx']],
\ 'statusline': [['', 'xxx'], ['%$', '%{', '%{%', '%{%}', '%(', '%)']],
"\ 'swapsync': [['', 'sync', 'fsync'], ['xxx']],
\ 'switchbuf': [['', 'useopen', 'usetab', 'split', 'vsplit', 'newtab',
diff --git a/test/old/testdir/runnvim.vim b/test/old/testdir/runnvim.vim
index 578614c8a1..f5945aeaee 100644
--- a/test/old/testdir/runnvim.vim
+++ b/test/old/testdir/runnvim.vim
@@ -8,6 +8,7 @@ function s:logger.on_exit(id, data, event)
endfunction
let s:logger.env = #{VIMRUNTIME: $VIMRUNTIME}
+let s:logger.term = v:true
" Replace non-printable chars by special sequence, or "<%x>".
let s:escaped_char = {"\n": '\n', "\r": '\r', "\t": '\t'}
@@ -25,7 +26,7 @@ function Main()
set lines=25
set columns=80
enew
- let job = termopen(args, s:logger)
+ let job = jobstart(args, s:logger)
let results = jobwait([job], 5 * 60 * 1000)
" TODO(ZyX-I): Get colors
let screen = getline(1, '$')
diff --git a/test/old/testdir/runtest.vim b/test/old/testdir/runtest.vim
index 058635c332..3d210695c4 100644
--- a/test/old/testdir/runtest.vim
+++ b/test/old/testdir/runtest.vim
@@ -55,6 +55,10 @@ silent! endwhile
" In the GUI we can always change the screen size.
if has('gui_running')
+ if has('gui_gtk')
+ " to keep screendump size unchanged
+ set guifont=Monospace\ 10
+ endif
set columns=80 lines=25
endif
diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim
index e7b4bb1a88..61596fc83a 100644
--- a/test/old/testdir/setup.vim
+++ b/test/old/testdir/setup.vim
@@ -1,6 +1,5 @@
if exists('s:did_load')
" Align Nvim defaults to Vim.
- set backspace=
set commentstring=/*\ %s\ */
set complete=.,w,b,u,t,i
set define=^\\s*#\\s*define
@@ -66,7 +65,7 @@ mapclear
mapclear!
aunmenu *
tlunmenu *
-autocmd! nvim_popupmenu
+autocmd! nvim.popupmenu
" Undo the 'grepprg' and 'grepformat' setting in _defaults.lua.
set grepprg& grepformat&
diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim
index 64599c869a..d5f7c928de 100644
--- a/test/old/testdir/test_autocmd.vim
+++ b/test/old/testdir/test_autocmd.vim
@@ -1198,8 +1198,8 @@ func Test_OptionSet()
call assert_equal(g:opt[0], g:opt[1])
" 14: Setting option backspace through :let"
- let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']]
- let &bs = "eol,indent,start"
+ let g:options = [['backspace', 'indent,eol,start', 'indent,eol,start', 'indent,eol,start', '', 'global', 'set']]
+ let &bs = ''
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -4181,4 +4181,28 @@ func Test_autocmd_BufWinLeave_with_vsp()
exe "bw! " .. dummy
endfunc
+func Test_OptionSet_cmdheight()
+ set mouse=a laststatus=2
+ au OptionSet cmdheight :let &l:ch = v:option_new
+
+ resize -1
+ call assert_equal(2, &l:ch)
+ resize +1
+ call assert_equal(1, &l:ch)
+
+ call Ntest_setmouse(&lines - 1, 1)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call Ntest_setmouse(&lines - 2, 1)
+ call feedkeys("\<LeftDrag>", 'xt')
+ call assert_equal(2, &l:ch)
+
+ tabnew | resize +1
+ call assert_equal(1, &l:ch)
+ tabfirst
+ call assert_equal(2, &l:ch)
+
+ tabonly
+ set cmdheight& mouse& laststatus&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_bufwintabinfo.vim b/test/old/testdir/test_bufwintabinfo.vim
index 57492e07c9..0a4bd0b674 100644
--- a/test/old/testdir/test_bufwintabinfo.vim
+++ b/test/old/testdir/test_bufwintabinfo.vim
@@ -114,6 +114,18 @@ func Test_getbufwintabinfo()
wincmd t | only
endfunc
+function Test_get_wininfo_leftcol()
+ set nowrap
+ set winwidth=10
+ vsp
+ call setline(1, ['abcdefghijklmnopqrstuvwxyz'])
+ norm! 5zl
+ call assert_equal(5, getwininfo()[0].leftcol)
+ bwipe!
+ set wrap&
+ set winwidth&
+endfunc
+
function Test_get_buf_options()
let opts = bufnr()->getbufvar('&')
call assert_equal(v:t_dict, type(opts))
diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim
index 290af4a4ca..d4ad63d43e 100644
--- a/test/old/testdir/test_cmdline.vim
+++ b/test/old/testdir/test_cmdline.vim
@@ -291,8 +291,8 @@ func Test_changing_cmdheight()
call term_sendkeys(buf, ":resize -3\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_1', {})
- " using the space available doesn't change the status line
- call term_sendkeys(buf, ":set cmdheight+=3\<CR>")
+ " :resize now also changes 'cmdheight' accordingly
+ call term_sendkeys(buf, ":set cmdheight+=1\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_2', {})
" using more space moves the status line up
@@ -300,10 +300,10 @@ func Test_changing_cmdheight()
call VerifyScreenDump(buf, 'Test_changing_cmdheight_3', {})
" reducing cmdheight moves status line down
- call term_sendkeys(buf, ":set cmdheight-=2\<CR>")
+ call term_sendkeys(buf, ":set cmdheight-=3\<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', {})
@@ -316,6 +316,10 @@ func Test_changing_cmdheight()
call term_sendkeys(buf, ":call EchoOne()\<CR>")
call VerifyScreenDump(buf, 'Test_changing_cmdheight_7', {})
+ " window commands do not reduce 'cmdheight' to value lower than :set by user
+ call term_sendkeys(buf, "\<CR>:wincmd _\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_8', {})
+
" clean up
call StopVimInTerminal(buf)
endfunc
@@ -4036,6 +4040,27 @@ func Test_rulerformat_position()
call StopVimInTerminal(buf)
endfunc
+" Test for using "%!" in 'rulerformat' to use a function
+func Test_rulerformat_function()
+ CheckScreendump
+
+ let lines =<< trim END
+ func TestRulerFn()
+ return '10,20%=30%%'
+ endfunc
+ END
+ call writefile(lines, 'Xrulerformat_function', 'D')
+
+ let buf = RunVimInTerminal('-S Xrulerformat_function', #{rows: 2, cols: 40})
+ call term_sendkeys(buf, ":set ruler rulerformat=%!TestRulerFn()\<CR>")
+ call term_sendkeys(buf, ":redraw!\<CR>")
+ call term_wait(buf)
+ call VerifyScreenDump(buf, 'Test_rulerformat_function', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_getcompletion_usercmd()
command! -nargs=* -complete=command TestCompletion echo <q-args>
diff --git a/test/old/testdir/test_compiler.vim b/test/old/testdir/test_compiler.vim
index 07b57b76d9..1310cbadfc 100644
--- a/test/old/testdir/test_compiler.vim
+++ b/test/old/testdir/test_compiler.vim
@@ -18,6 +18,8 @@ func Test_compiler()
endif
e Xfoo.pl
+ " Play nice with other tests.
+ defer setqflist([])
compiler perl
call assert_equal('perl', b:current_compiler)
call assert_fails('let g:current_compiler', 'E121:')
@@ -65,10 +67,10 @@ func Test_compiler_completion()
call assert_match('^"compiler ' .. clist .. '$', @:)
call feedkeys(":compiler p\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('"compiler pandoc pbx perl\( p[a-z_]\+\)\+ pylint pyunit', @:)
+ call assert_match('"compiler pandoc pbx perl\( p[a-z_]\+\)\+ pyunit', @:)
call feedkeys(":compiler! p\<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('"compiler! pandoc pbx perl\( p[a-z_]\+\)\+ pylint pyunit', @:)
+ call assert_match('"compiler! pandoc pbx perl\( p[a-z_]\+\)\+ pyunit', @:)
endfunc
func Test_compiler_error()
@@ -78,3 +80,634 @@ func Test_compiler_error()
call assert_fails('compiler! doesnotexist', 'E666:')
unlet! g:current_compiler
endfunc
+
+func s:SpotBugsParseFilterMakePrg(dirname, makeprg)
+ let result = {}
+ let result.sourcepath = ''
+ let result.classfiles = []
+
+ " Get the argument after the rightmost occurrence of "-sourcepath".
+ let offset = strridx(a:makeprg, '-sourcepath')
+ if offset < 0
+ return result
+ endif
+ let offset += 1 + strlen('-sourcepath')
+ let result.sourcepath = matchstr(strpart(a:makeprg, offset), '.\{-}\ze[ \t]')
+ let offset += 1 + strlen(result.sourcepath)
+
+ " Get the class file arguments, dropping the pathname prefix.
+ let offset = stridx(a:makeprg, a:dirname, offset)
+ if offset < 0
+ return result
+ endif
+
+ while offset > -1
+ let candidate = matchstr(a:makeprg, '[^ \t]\{-}\.class\>', offset)
+ if empty(candidate)
+ break
+ endif
+ call add(result.classfiles, candidate)
+ let offset = stridx(a:makeprg, a:dirname, (1 + strlen(candidate) + offset))
+ endwhile
+
+ call sort(result.classfiles)
+ return result
+endfunc
+
+func Test_compiler_spotbugs_makeprg()
+ let save_shellslash = &shellslash
+ set shellslash
+
+ call assert_true(mkdir('Xspotbugs/src/tests/α/β/γ/δ', 'pR'))
+ call assert_true(mkdir('Xspotbugs/tests/α/β/γ/δ', 'pR'))
+
+ let lines =<< trim END
+ // EOL comment. /*
+ abstract class
+ 𐌂1 /* Multiline comment. */ {
+ /* Multiline comment. */ // EOL comment. /*
+ static final String COMMENT_A_LIKE = "/*";
+ { new Object() {/* Try globbing. */}; }
+ static { interface 𐌉𐌉1 {} }
+ static class 𐌂11 { interface 𐌉𐌉2 {} }
+ }
+ /* Multiline comment. */ // EOL comment. /*
+ final class 𐌂2 {
+ public static void main(String... aa) {
+ record 𐌓() {}
+ enum 𐌄 {}
+ }
+ } // class
+ END
+
+ " THE EXPECTED RESULTS.
+ let results = {}
+ let results['Xspotbugs/src/tests/𐌂1.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/𐌂1.java',
+ \ ':p:h:S')},
+ \ 'classfiles': sort([
+ \ 'Xspotbugs/tests/𐌂1$1.class',
+ \ 'Xspotbugs/tests/𐌂1$1𐌉𐌉1.class',
+ \ 'Xspotbugs/tests/𐌂1$𐌂11$𐌉𐌉2.class',
+ \ 'Xspotbugs/tests/𐌂1$𐌂11.class',
+ \ 'Xspotbugs/tests/𐌂1.class',
+ \ 'Xspotbugs/tests/𐌂2$1𐌄.class',
+ \ 'Xspotbugs/tests/𐌂2$1𐌓.class',
+ \ 'Xspotbugs/tests/𐌂2.class']),
+ \ }
+ " No class file for an empty source file even with "-Xpkginfo:always".
+ let results['Xspotbugs/src/tests/package-info.java'] = {
+ \ 'Sourcepath': {-> ''},
+ \ 'classfiles': [],
+ \ }
+ let results['Xspotbugs/src/tests/α/𐌂1.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/𐌂1.java',
+ \ ':p:h:h:S')},
+ \ 'classfiles': sort([
+ \ 'Xspotbugs/tests/α/𐌂1$1.class',
+ \ 'Xspotbugs/tests/α/𐌂1$1𐌉𐌉1.class',
+ \ 'Xspotbugs/tests/α/𐌂1$𐌂11$𐌉𐌉2.class',
+ \ 'Xspotbugs/tests/α/𐌂1$𐌂11.class',
+ \ 'Xspotbugs/tests/α/𐌂1.class',
+ \ 'Xspotbugs/tests/α/𐌂2$1𐌄.class',
+ \ 'Xspotbugs/tests/α/𐌂2$1𐌓.class',
+ \ 'Xspotbugs/tests/α/𐌂2.class']),
+ \ }
+ let results['Xspotbugs/src/tests/α/package-info.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/package-info.java',
+ \ ':p:h:S')},
+ \ 'classfiles': ['Xspotbugs/tests/α/package-info.class'],
+ \ }
+ let results['Xspotbugs/src/tests/α/β/𐌂1.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/β/𐌂1.java',
+ \ ':p:h:h:h:S')},
+ \ 'classfiles': sort([
+ \ 'Xspotbugs/tests/α/β/𐌂1$1.class',
+ \ 'Xspotbugs/tests/α/β/𐌂1$1𐌉𐌉1.class',
+ \ 'Xspotbugs/tests/α/β/𐌂1$𐌂11$𐌉𐌉2.class',
+ \ 'Xspotbugs/tests/α/β/𐌂1$𐌂11.class',
+ \ 'Xspotbugs/tests/α/β/𐌂1.class',
+ \ 'Xspotbugs/tests/α/β/𐌂2$1𐌄.class',
+ \ 'Xspotbugs/tests/α/β/𐌂2$1𐌓.class',
+ \ 'Xspotbugs/tests/α/β/𐌂2.class']),
+ \ }
+ let results['Xspotbugs/src/tests/α/β/package-info.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/β/package-info.java',
+ \ ':p:h:S')},
+ \ 'classfiles': ['Xspotbugs/tests/α/β/package-info.class'],
+ \ }
+ let results['Xspotbugs/src/tests/α/β/γ/𐌂1.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/β/γ/𐌂1.java',
+ \ ':p:h:h:h:h:S')},
+ \ 'classfiles': sort([
+ \ 'Xspotbugs/tests/α/β/γ/𐌂1$1.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂1$1𐌉𐌉1.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂1$𐌂11$𐌉𐌉2.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂1$𐌂11.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂1.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂2$1𐌄.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂2$1𐌓.class',
+ \ 'Xspotbugs/tests/α/β/γ/𐌂2.class']),
+ \ }
+ let results['Xspotbugs/src/tests/α/β/γ/package-info.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/β/γ/package-info.java',
+ \ ':p:h:S')},
+ \ 'classfiles': ['Xspotbugs/tests/α/β/γ/package-info.class'],
+ \ }
+ let results['Xspotbugs/src/tests/α/β/γ/δ/𐌂1.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/β/γ/δ/𐌂1.java',
+ \ ':p:h:h:h:h:h:S')},
+ \ 'classfiles': sort([
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂1$1.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂1$1𐌉𐌉1.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂1$𐌂11$𐌉𐌉2.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂1$𐌂11.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂1.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂2$1𐌄.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂2$1𐌓.class',
+ \ 'Xspotbugs/tests/α/β/γ/δ/𐌂2.class']),
+ \ }
+ let results['Xspotbugs/src/tests/α/β/γ/δ/package-info.java'] = {
+ \ 'Sourcepath': {-> fnamemodify('Xspotbugs/src/tests/α/β/γ/δ/package-info.java',
+ \ ':p:h:S')},
+ \ 'classfiles': ['Xspotbugs/tests/α/β/γ/δ/package-info.class'],
+ \ }
+
+ " MAKE CLASS FILES DISCOVERABLE!
+ let g:spotbugs_properties = {
+ \ 'sourceDirPath': ['src/tests'],
+ \ 'classDirPath': ['tests'],
+ \ }
+
+ call assert_true(has_key(s:SpotBugsParseFilterMakePrg('Xspotbugs', ''), 'sourcepath'))
+ call assert_true(has_key(s:SpotBugsParseFilterMakePrg('Xspotbugs', ''), 'classfiles'))
+
+ " Write 45 mock-up class files for 10 source files.
+ for [class_dir, src_dir, package] in [
+ \ ['Xspotbugs/tests/', 'Xspotbugs/src/tests/', ''],
+ \ ['Xspotbugs/tests/α/', 'Xspotbugs/src/tests/α/', 'package α;'],
+ \ ['Xspotbugs/tests/α/β/', 'Xspotbugs/src/tests/α/β/', 'package α.β;'],
+ \ ['Xspotbugs/tests/α/β/γ/', 'Xspotbugs/src/tests/α/β/γ/', 'package α.β.γ;'],
+ \ ['Xspotbugs/tests/α/β/γ/δ/', 'Xspotbugs/src/tests/α/β/γ/δ/', 'package α.β.γ.δ;']]
+ for class_file in ['𐌂1$1.class', '𐌂1$1𐌉𐌉1.class', '𐌂1$𐌂11$𐌉𐌉2.class',
+ \ '𐌂1$𐌂11.class', '𐌂1.class', '𐌂2$1𐌄.class', '𐌂2$1𐌓.class', '𐌂2.class']
+ call writefile(0zcafe.babe.0000.0041, class_dir .. class_file)
+ endfor
+ call writefile(0zcafe.babe.0000.0041, class_dir .. 'package-info.class')
+
+ " Write Java source files.
+ let type_file = src_dir .. '𐌂1.java'
+ call writefile(insert(copy(lines), package), type_file)
+ let package_file = src_dir .. 'package-info.java'
+ call writefile([package], src_dir .. 'package-info.java')
+
+ " Note that using "off" for the first _outer_ iteration is preferable
+ " because only then "hlexists()" may be 0 (see "compiler/spotbugs.vim").
+ for s in ['off', 'on']
+ execute 'syntax ' .. s
+
+ execute 'edit ' .. type_file
+ compiler spotbugs
+ let result = s:SpotBugsParseFilterMakePrg('Xspotbugs', &l:makeprg)
+ call assert_equal(results[type_file].Sourcepath(), result.sourcepath)
+ call assert_equal(results[type_file].classfiles, result.classfiles)
+ bwipeout
+
+ execute 'edit ' .. package_file
+ compiler spotbugs
+ let result = s:SpotBugsParseFilterMakePrg('Xspotbugs', &l:makeprg)
+ call assert_equal(results[package_file].Sourcepath(), result.sourcepath)
+ call assert_equal(results[package_file].classfiles, result.classfiles)
+ bwipeout
+ endfor
+ endfor
+
+ let &shellslash = save_shellslash
+endfunc
+
+func s:SpotBugsBeforeFileTypeTryPluginAndClearCache(state)
+ " Ponder over "extend(spotbugs#DefaultProperties(), g:spotbugs_properties)"
+ " in "ftplugin/java.vim".
+ let g:spotbugs#state = a:state
+ runtime autoload/spotbugs.vim
+endfunc
+
+func Test_compiler_spotbugs_properties()
+ let save_shellslash = &shellslash
+ set shellslash
+ setlocal makeprg=
+ filetype plugin on
+
+ call assert_true(mkdir('Xspotbugs/src', 'pR'))
+ call assert_true(mkdir('Xspotbugs/tests', 'pR'))
+ let type_file = 'Xspotbugs/src/𐌄.java'
+ let test_file = 'Xspotbugs/tests/𐌄$.java'
+ call writefile(['enum 𐌄{}'], type_file)
+ call writefile(['class 𐌄${}'], test_file)
+
+ " TEST INTEGRATION WITH A BOGUS COMPILER PLUGIN.
+ if !filereadable($VIMRUNTIME .. '/compiler/foo.vim') && !executable('foo')
+ let g:spotbugs_properties = {'compiler': 'foo'}
+ " XXX: In case this "if" block is no longer first.
+ call s:SpotBugsBeforeFileTypeTryPluginAndClearCache({
+ \ 'compiler': g:spotbugs_properties.compiler,
+ \ })
+ execute 'edit ' .. type_file
+ call assert_equal('java', &l:filetype)
+ " This variable will indefinitely keep the compiler name.
+ call assert_equal('foo', g:spotbugs#state.compiler)
+ " The "compiler" entry should be gone after FileType and default entries
+ " should only appear for a supported compiler.
+ call assert_false(has_key(g:spotbugs_properties, 'compiler'))
+ call assert_true(empty(g:spotbugs_properties))
+ " Query default implementations.
+ call assert_true(exists('*spotbugs#DefaultProperties'))
+ call assert_true(exists('*spotbugs#DefaultPreCompilerAction'))
+ call assert_true(exists('*spotbugs#DefaultPreCompilerTestAction'))
+ call assert_true(empty(spotbugs#DefaultProperties()))
+ " Get a ":message".
+ redir => out
+ call spotbugs#DefaultPreCompilerAction()
+ redir END
+ call assert_equal('Not supported: "foo"', out[stridx(out, 'Not') :])
+ " Get a ":message".
+ redir => out
+ call spotbugs#DefaultPreCompilerTestAction()
+ redir END
+ call assert_equal('Not supported: "foo"', out[stridx(out, 'Not') :])
+ " No ":autocmd"s without one of "PreCompiler*Action", "PostCompilerAction".
+ call assert_false(exists('#java_spotbugs'))
+ bwipeout
+ endif
+
+ let s:spotbugs_results = {
+ \ 'preActionDone': 0,
+ \ 'preTestActionDone': 0,
+ \ 'preTestLocalActionDone': 0,
+ \ 'postActionDone': 0,
+ \ 'preCommandArguments': '',
+ \ 'preTestCommandArguments': '',
+ \ 'postCommandArguments': '',
+ \ }
+ defer execute('unlet s:spotbugs_results')
+
+ func! g:SpotBugsPreAction() abort
+ let s:spotbugs_results.preActionDone = 1
+ " XXX: Notify the spotbugs compiler about success or failure.
+ cc
+ endfunc
+ defer execute('delfunction g:SpotBugsPreAction')
+
+ func! g:SpotBugsPreTestAction() abort
+ let s:spotbugs_results.preTestActionDone = 1
+ " XXX: Let see compilation fail.
+ throw 'Oops'
+ endfunc
+ defer execute('delfunction g:SpotBugsPreTestAction')
+
+ func! g:SpotBugsPreTestLocalAction() abort
+ let s:spotbugs_results.preTestLocalActionDone = 1
+ " XXX: Notify the spotbugs compiler about success or failure.
+ cc
+ endfunc
+ defer execute('delfunction g:SpotBugsPreTestLocalAction')
+
+ func! g:SpotBugsPostAction() abort
+ let s:spotbugs_results.postActionDone = 1
+ endfunc
+ defer execute('delfunction g:SpotBugsPostAction')
+
+ func! g:SpotBugsPreCommand(arguments) abort
+ let s:spotbugs_results.preActionDone = 1
+ let s:spotbugs_results.preCommandArguments = a:arguments
+ " XXX: Notify the spotbugs compiler about success or failure.
+ cc
+ endfunc
+ defer execute('delfunction g:SpotBugsPreCommand')
+
+ func! g:SpotBugsPreTestCommand(arguments) abort
+ let s:spotbugs_results.preTestActionDone = 1
+ let s:spotbugs_results.preTestCommandArguments = a:arguments
+ " XXX: Notify the spotbugs compiler about success or failure.
+ cc
+ endfunc
+ defer execute('delfunction g:SpotBugsPreTestCommand')
+
+ func! g:SpotBugsPostCommand(arguments) abort
+ let s:spotbugs_results.postActionDone = 1
+ let s:spotbugs_results.postCommandArguments = a:arguments
+ endfunc
+ defer execute('delfunction g:SpotBugsPostCommand')
+
+ func! g:SpotBugsPostCompilerActionExecutor(action) abort
+ try
+ " XXX: Notify the spotbugs compiler about success or failure.
+ cc
+ catch /\<E42:/
+ execute a:action
+ endtry
+ endfunc
+ defer execute('delfunction g:SpotBugsPostCompilerActionExecutor')
+
+ " TEST INTEGRATION WITH A SUPPORTED COMPILER PLUGIN.
+ if filereadable($VIMRUNTIME .. '/compiler/maven.vim')
+ let save_PATH = $PATH
+ if !executable('mvn')
+ if has('win32')
+ let $PATH = 'Xspotbugs;' .. $PATH
+ " This is what ":help executable()" suggests.
+ call writefile([], 'Xspotbugs/mvn.cmd')
+ else
+ let $PATH = 'Xspotbugs:' .. $PATH
+ call writefile([], 'Xspotbugs/mvn')
+ call setfperm('Xspotbugs/mvn', 'rwx------')
+ endif
+ endif
+
+ let g:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ 'PreCompilerAction': function('g:SpotBugsPreAction'),
+ \ 'PreCompilerTestAction': function('g:SpotBugsPreTestAction'),
+ \ 'PostCompilerAction': function('g:SpotBugsPostAction'),
+ \ }
+ " XXX: In case this is a runner-up ":edit".
+ call s:SpotBugsBeforeFileTypeTryPluginAndClearCache({
+ \ 'compiler': g:spotbugs_properties.compiler,
+ \ })
+ execute 'edit ' .. type_file
+ call assert_equal('java', &l:filetype)
+ call assert_equal('maven', g:spotbugs#state.compiler)
+ call assert_false(has_key(g:spotbugs_properties, 'compiler'))
+ call assert_false(empty(g:spotbugs_properties))
+ " Query default implementations.
+ call assert_true(exists('*spotbugs#DefaultProperties'))
+ call assert_equal(sort([
+ \ 'PreCompilerAction',
+ \ 'PreCompilerTestAction',
+ \ 'PostCompilerAction',
+ \ 'sourceDirPath',
+ \ 'classDirPath',
+ \ 'testSourceDirPath',
+ \ 'testClassDirPath',
+ \ ]),
+ \ sort(keys(spotbugs#DefaultProperties())))
+ " Some ":autocmd"s with one of "PreCompiler*Action", "PostCompilerAction".
+ call assert_true(exists('#java_spotbugs'))
+ call assert_true(exists('#java_spotbugs#Syntax'))
+ call assert_true(exists('#java_spotbugs#User'))
+ call assert_equal(2, exists(':SpotBugsDefineBufferAutocmd'))
+ " SpotBugsDefineBufferAutocmd SigUSR1 User SigUSR1 User SigUSR1 User
+ " call assert_true(exists('#java_spotbugs#SigUSR1'))
+ SpotBugsDefineBufferAutocmd Signal User Signal User Signal User
+ call assert_true(exists('#java_spotbugs#Signal'))
+ call assert_true(exists('#java_spotbugs#Syntax'))
+ call assert_true(exists('#java_spotbugs#User'))
+ call assert_equal(2, exists(':SpotBugsRemoveBufferAutocmd'))
+ " SpotBugsRemoveBufferAutocmd SigUSR1 User SigUSR1 User UserGettingBored
+ " call assert_false(exists('#java_spotbugs#SigUSR1'))
+ SpotBugsRemoveBufferAutocmd Signal User Signal User UserGettingBored
+ call assert_false(exists('#java_spotbugs#Signal'))
+ call assert_true(exists('#java_spotbugs#Syntax'))
+ call assert_true(exists('#java_spotbugs#User'))
+
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+
+ doautocmd java_spotbugs Syntax
+ call assert_false(exists('#java_spotbugs#Syntax'))
+
+ " No match: "type_file !~# 'src/main/java'".
+ call assert_false(s:spotbugs_results.preActionDone)
+ " No match: "type_file !~# 'src/test/java'".
+ call assert_false(s:spotbugs_results.preTestActionDone)
+ " No pre-match, no post-action.
+ call assert_false(s:spotbugs_results.postActionDone)
+ " Without a match, confirm that ":compiler spotbugs" has NOT run.
+ call assert_true(empty(&l:makeprg))
+
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+ " Update path entries. (Note that we cannot use just "src" because there
+ " is another "src" directory nearer the filesystem root directory, i.e.
+ " "vim/vim/src/testdir/Xspotbugs/src", and "s:DispatchAction()" (see
+ " "ftplugin/java.vim") will match "vim/vim/src/testdir/Xspotbugs/tests"
+ " against "src".)
+ let g:spotbugs_properties.sourceDirPath = ['Xspotbugs/src']
+ let g:spotbugs_properties.classDirPath = ['Xspotbugs/src']
+ let g:spotbugs_properties.testSourceDirPath = ['tests']
+ let g:spotbugs_properties.testClassDirPath = ['tests']
+
+ doautocmd java_spotbugs User
+ " No match: "type_file !~# 'src/main/java'" (with old "*DirPath" values
+ " cached).
+ call assert_false(s:spotbugs_results.preActionDone)
+ " No match: "type_file !~# 'src/test/java'" (with old "*DirPath" values
+ " cached).
+ call assert_false(s:spotbugs_results.preTestActionDone)
+ " No pre-match, no post-action.
+ call assert_false(s:spotbugs_results.postActionDone)
+ " Without a match, confirm that ":compiler spotbugs" has NOT run.
+ call assert_true(empty(&l:makeprg))
+
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+ " XXX: Re-build ":autocmd"s from scratch with new values applied.
+ doautocmd FileType
+
+ call assert_true(exists('b:spotbugs_syntax_once'))
+ doautocmd java_spotbugs User
+ " A match: "type_file =~# 'Xspotbugs/src'" (with new "*DirPath" values
+ " cached).
+ call assert_true(s:spotbugs_results.preActionDone)
+ " No match: "type_file !~# 'tests'" (with new "*DirPath" values cached).
+ call assert_false(s:spotbugs_results.preTestActionDone)
+ " For a pre-match, a post-action.
+ call assert_true(s:spotbugs_results.postActionDone)
+
+ " With a match, confirm that ":compiler spotbugs" has run.
+ if has('win32')
+ call assert_match('^spotbugs\.bat\s', &l:makeprg)
+ else
+ call assert_match('^spotbugs\s', &l:makeprg)
+ endif
+
+ bwipeout
+ setlocal makeprg=
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionDone = 0
+ let s:spotbugs_results.preTestLocalActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+
+ execute 'edit ' .. test_file
+ " Prepare a buffer-local, incomplete variant of properties, relying on
+ " "ftplugin/java.vim" to take care of merging in unique entries, if any,
+ " from "g:spotbugs_properties".
+ let b:spotbugs_properties = {
+ \ 'PreCompilerTestAction': function('g:SpotBugsPreTestLocalAction'),
+ \ }
+ call assert_equal('java', &l:filetype)
+ call assert_true(exists('#java_spotbugs'))
+ call assert_true(exists('#java_spotbugs#Syntax'))
+ call assert_true(exists('#java_spotbugs#User'))
+ call assert_fails('doautocmd java_spotbugs Syntax', 'Oops')
+ call assert_false(exists('#java_spotbugs#Syntax'))
+ " No match: "test_file !~# 'Xspotbugs/src'".
+ call assert_false(s:spotbugs_results.preActionDone)
+ " A match: "test_file =~# 'tests'".
+ call assert_true(s:spotbugs_results.preTestActionDone)
+ call assert_false(s:spotbugs_results.preTestLocalActionDone)
+ " No action after pre-failure (the thrown "Oops" doesn't qualify for ":cc").
+ call assert_false(s:spotbugs_results.postActionDone)
+ " No ":compiler spotbugs" will be run after pre-failure.
+ call assert_true(empty(&l:makeprg))
+
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionDone = 0
+ let s:spotbugs_results.preTestLocalActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+ " XXX: Re-build ":autocmd"s from scratch with buffer-local values applied.
+ doautocmd FileType
+
+ call assert_true(exists('b:spotbugs_syntax_once'))
+ doautocmd java_spotbugs User
+ " No match: "test_file !~# 'Xspotbugs/src'".
+ call assert_false(s:spotbugs_results.preActionDone)
+ " A match: "test_file =~# 'tests'".
+ call assert_true(s:spotbugs_results.preTestLocalActionDone)
+ call assert_false(s:spotbugs_results.preTestActionDone)
+ " For a pre-match, a post-action.
+ call assert_true(s:spotbugs_results.postActionDone)
+
+ " With a match, confirm that ":compiler spotbugs" has run.
+ if has('win32')
+ call assert_match('^spotbugs\.bat\s', &l:makeprg)
+ else
+ call assert_match('^spotbugs\s', &l:makeprg)
+ endif
+
+ setlocal makeprg=
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionDone = 0
+ let s:spotbugs_results.preTestLocalActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+ let s:spotbugs_results.preCommandArguments = ''
+ let s:spotbugs_results.preTestCommandArguments = ''
+ let s:spotbugs_results.postCommandArguments = ''
+ " XXX: Compose the assigned "*Command"s with the default Maven "*Action"s.
+ let b:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ 'DefaultPreCompilerTestCommand': function('g:SpotBugsPreTestCommand'),
+ \ 'DefaultPreCompilerCommand': function('g:SpotBugsPreCommand'),
+ \ 'DefaultPostCompilerCommand': function('g:SpotBugsPostCommand'),
+ \ 'PostCompilerActionExecutor': function('g:SpotBugsPostCompilerActionExecutor'),
+ \ 'augroupForPostCompilerAction': 'java_spotbugs_test',
+ \ 'sourceDirPath': ['Xspotbugs/src'],
+ \ 'classDirPath': ['Xspotbugs/src'],
+ \ 'testSourceDirPath': ['tests'],
+ \ 'testClassDirPath': ['tests'],
+ \ }
+ unlet g:spotbugs_properties
+ " XXX: Re-build ":autocmd"s from scratch with buffer-local values applied.
+ call s:SpotBugsBeforeFileTypeTryPluginAndClearCache({
+ \ 'compiler': b:spotbugs_properties.compiler,
+ \ 'commands': {
+ \ 'DefaultPreCompilerTestCommand':
+ \ b:spotbugs_properties.DefaultPreCompilerTestCommand,
+ \ 'DefaultPreCompilerCommand':
+ \ b:spotbugs_properties.DefaultPreCompilerCommand,
+ \ 'DefaultPostCompilerCommand':
+ \ b:spotbugs_properties.DefaultPostCompilerCommand,
+ \ },
+ \ })
+ doautocmd FileType
+
+ call assert_equal('maven', g:spotbugs#state.compiler)
+ call assert_equal(sort([
+ \ 'DefaultPreCompilerTestCommand',
+ \ 'DefaultPreCompilerCommand',
+ \ 'DefaultPostCompilerCommand',
+ \ ]),
+ \ sort(keys(g:spotbugs#state.commands)))
+ call assert_true(exists('b:spotbugs_syntax_once'))
+ doautocmd java_spotbugs User
+ " No match: "test_file !~# 'Xspotbugs/src'".
+ call assert_false(s:spotbugs_results.preActionDone)
+ call assert_true(empty(s:spotbugs_results.preCommandArguments))
+ " A match: "test_file =~# 'tests'".
+ call assert_true(s:spotbugs_results.preTestActionDone)
+ call assert_equal('test-compile', s:spotbugs_results.preTestCommandArguments)
+ " For a pre-match, a post-action.
+ call assert_true(s:spotbugs_results.postActionDone)
+ call assert_equal('%:S', s:spotbugs_results.postCommandArguments)
+
+ " With a match, confirm that ":compiler spotbugs" has run.
+ if has('win32')
+ call assert_match('^spotbugs\.bat\s', &l:makeprg)
+ else
+ call assert_match('^spotbugs\s', &l:makeprg)
+ endif
+
+ setlocal makeprg=
+ let s:spotbugs_results.preActionDone = 0
+ let s:spotbugs_results.preTestActionOtherDone = 0
+ let s:spotbugs_results.preTestLocalActionDone = 0
+ let s:spotbugs_results.postActionDone = 0
+ let s:spotbugs_results.preCommandArguments = ''
+ let s:spotbugs_results.preTestCommandArguments = ''
+ let s:spotbugs_results.postCommandArguments = ''
+
+ " When "PostCompilerActionExecutor", "Pre*Action" and/or "Pre*TestAction",
+ " and "Post*Action" are available, "#java_spotbugs_post" must be defined.
+ call assert_true(exists('#java_spotbugs_post'))
+ call assert_true(exists('#java_spotbugs_post#User'))
+ call assert_false(exists('#java_spotbugs_post#ShellCmdPost'))
+ call assert_false(exists('#java_spotbugs_test#ShellCmdPost'))
+
+ " Re-link a Funcref on the fly.
+ func! g:SpotBugsPreTestCommand(arguments) abort
+ let s:spotbugs_results.preTestActionOtherDone = 1
+ let s:spotbugs_results.preTestCommandArguments = a:arguments
+ " Define a once-only ":autocmd" for "#java_spotbugs_test#ShellCmdPost".
+ doautocmd java_spotbugs_post User
+ " XXX: Do NOT use ":cc" to notify the spotbugs compiler about success or
+ " failure, and assume the transfer of control to a ShellCmdPost command.
+ endfunc
+
+ doautocmd java_spotbugs User
+ " No match: "test_file !~# 'Xspotbugs/src'".
+ call assert_false(s:spotbugs_results.preActionDone)
+ call assert_true(empty(s:spotbugs_results.preCommandArguments))
+ " A match: "test_file =~# 'tests'".
+ call assert_true(s:spotbugs_results.preTestActionOtherDone)
+ call assert_equal('test-compile', s:spotbugs_results.preTestCommandArguments)
+ " For a pre-match, no post-action (without ":cc") UNLESS a ShellCmdPost
+ " event is consumed whose command will invoke "PostCompilerActionExecutor"
+ " and the latter will accept a post-compiler action argument.
+ call assert_false(s:spotbugs_results.postActionDone)
+ call assert_true(exists('#java_spotbugs_test#ShellCmdPost'))
+ doautocmd ShellCmdPost
+ call assert_false(exists('#java_spotbugs_test#ShellCmdPost'))
+ call assert_true(s:spotbugs_results.postActionDone)
+ call assert_equal('%:S', s:spotbugs_results.postCommandArguments)
+
+ " With a match, confirm that ":compiler spotbugs" has run.
+ if has('win32')
+ call assert_match('^spotbugs\.bat\s', &l:makeprg)
+ else
+ call assert_match('^spotbugs\s', &l:makeprg)
+ endif
+
+ bwipeout
+ setlocal makeprg=
+ let $PATH = save_PATH
+ endif
+
+ filetype plugin off
+ setlocal makeprg=
+ let &shellslash = save_shellslash
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_debugger.vim b/test/old/testdir/test_debugger.vim
index 3a469e8a17..5820b58247 100644
--- a/test/old/testdir/test_debugger.vim
+++ b/test/old/testdir/test_debugger.vim
@@ -6,10 +6,11 @@ source check.vim
func CheckCWD()
" Check that the longer lines don't wrap due to the length of the script name
- " in cwd
+ " in cwd. Need to subtract by 1 since Vim will still wrap the message if it
+ " just fits.
let script_len = len( getcwd() .. '/Xtest1.vim' )
let longest_line = len( 'Breakpoint in "" line 1' )
- if script_len > ( 75 - longest_line )
+ if script_len > ( 75 - longest_line - 1 )
throw 'Skipped: Your CWD has too many characters'
endif
endfunc
diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim
index 880286d329..ab64658bd0 100644
--- a/test/old/testdir/test_diffmode.vim
+++ b/test/old/testdir/test_diffmode.vim
@@ -1017,6 +1017,41 @@ func Test_diff_screen()
call WriteDiffFiles(buf, [], [0])
call VerifyBoth(buf, "Test_diff_22", "")
+ call WriteDiffFiles(buf, ['?a', '?b', '?c'], ['!b'])
+ call VerifyInternal(buf, 'Test_diff_23', " diffopt+=linematch:30")
+
+ call WriteDiffFiles(buf, ['',
+ \ 'common line',
+ \ 'common line',
+ \ '',
+ \ 'DEFabc',
+ \ 'xyz',
+ \ 'xyz',
+ \ 'xyz',
+ \ 'DEFabc',
+ \ 'DEFabc',
+ \ 'DEFabc',
+ \ 'common line',
+ \ 'common line',
+ \ 'DEF',
+ \ 'common line',
+ \ 'DEF',
+ \ 'something' ],
+ \ ['',
+ \ 'common line',
+ \ 'common line',
+ \ '',
+ \ 'ABCabc',
+ \ 'ABCabc',
+ \ 'ABCabc',
+ \ 'ABCabc',
+ \ 'common line',
+ \ 'common line',
+ \ 'common line',
+ \ 'something'])
+ call VerifyInternal(buf, 'Test_diff_24', " diffopt+=linematch:30")
+
+
" clean up
call StopVimInTerminal(buf)
call delete('Xdifile1')
@@ -1212,7 +1247,7 @@ func CloseoffSetup()
call setline(1, ['one', 'tow', 'three'])
diffthis
call assert_equal(1, &diff)
- only!
+ bw!
endfunc
func Test_diff_closeoff()
@@ -2004,6 +2039,12 @@ func Test_diff_overlapped_diff_blocks_will_be_merged()
call WriteDiffFiles3(buf, ["a", "b", "c"], ["a", "x", "c"], ["a", "b", "y", "c"])
call VerifyBoth(buf, "Test_diff_overlapped_3.37", "")
+ call WriteDiffFiles3(buf, ["a", "b", "c"], ["d", "e"], ["b", "f"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.38", "")
+
+ call WriteDiffFiles3(buf, ["a", "b", "c"], ["d", "e"], ["b"])
+ call VerifyBoth(buf, "Test_diff_overlapped_3.39", "")
+
call StopVimInTerminal(buf)
endfunc
@@ -2034,4 +2075,440 @@ func Test_diff_topline_noscroll()
call StopVimInTerminal(buf)
endfunc
+func Test_diffget_diffput_linematch()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call WriteDiffFiles(0, [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
+ call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
+
+ " enable linematch
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call WriteDiffFiles(buf, ['',
+ \ 'common line',
+ \ 'common line',
+ \ '',
+ \ 'ABCabc',
+ \ 'ABCabc',
+ \ 'ABCabc',
+ \ 'ABCabc',
+ \ 'common line',
+ \ 'common line',
+ \ 'common line',
+ \ 'something' ],
+ \ ['',
+ \ 'common line',
+ \ 'common line',
+ \ '',
+ \ 'DEFabc',
+ \ 'xyz',
+ \ 'xyz',
+ \ 'xyz',
+ \ 'DEFabc',
+ \ 'DEFabc',
+ \ 'DEFabc',
+ \ 'common line',
+ \ 'common line',
+ \ 'DEF',
+ \ 'common line',
+ \ 'DEF',
+ \ 'something'])
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_1', {})
+
+ " get from window 1 from line 5 to 9
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, ":5,9diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_2', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get from window 2 from line 5 to 10
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, ":5,10diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_3', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get all from window 2
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, ":4,17diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_4', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get all from window 1
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, ":4,12diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_5', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get from window 1 using do 1 line 5
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "5gg")
+ call term_sendkeys(buf, ":diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_6', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get from window 1 using do 2 line 6
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "6gg")
+ call term_sendkeys(buf, ":diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_7', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get from window 1 using do 2 line 7
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "7gg")
+ call term_sendkeys(buf, ":diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_8', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get from window 1 using do 2 line 11
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "11gg")
+ call term_sendkeys(buf, ":diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_9', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " get from window 1 using do 2 line 12
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "12gg")
+ call term_sendkeys(buf, ":diffget\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_10', {})
+
+ " undo the last diffget
+ call term_sendkeys(buf, "u")
+
+ " put from window 1 using dp 1 line 5
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "5gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_11', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 1 using dp 2 line 6
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "6gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_12', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 1 using dp 2 line 7
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "7gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_13', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 1 using dp 2 line 11
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "11gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_14', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 1 using dp 2 line 12
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "12gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_15', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 2 using dp line 6
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "6gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_16', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 2 using dp line 8
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "8gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_17', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 2 using dp line 9
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "9gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_18', {})
+
+ " undo the last diffput
+ call term_sendkeys(buf, "1\<c-w>w")
+ call term_sendkeys(buf, "u")
+
+ " put from window 2 using dp line 17
+ call term_sendkeys(buf, "2\<c-w>w")
+ call term_sendkeys(buf, "17gg")
+ call term_sendkeys(buf, ":diffput\<CR>")
+ call VerifyScreenDump(buf, 'Test_diff_get_put_linematch_19', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_linematch_diff()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call WriteDiffFiles(0, [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
+ call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
+
+ " enable linematch
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call WriteDiffFiles(buf, ['// abc d?',
+ \ '// d?',
+ \ '// d?' ],
+ \ ['!',
+ \ 'abc d!',
+ \ 'd!'])
+ call VerifyScreenDump(buf, 'Test_linematch_diff1', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_linematch_diff_iwhite()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call WriteDiffFiles(0, [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
+ call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
+
+ " setup a diff with 2 files and set linematch:30, with ignore white
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call WriteDiffFiles(buf, ['void testFunction () {',
+ \ ' for (int i = 0; i < 10; i++) {',
+ \ ' for (int j = 0; j < 10; j++) {',
+ \ ' }',
+ \ ' }',
+ \ '}' ],
+ \ ['void testFunction () {',
+ \ ' // for (int j = 0; j < 10; i++) {',
+ \ ' // }',
+ \ '}'])
+ call VerifyScreenDump(buf, 'Test_linematch_diff_iwhite1', {})
+ call term_sendkeys(buf, ":set diffopt+=iwhiteall\<CR>")
+ call VerifyScreenDump(buf, 'Test_linematch_diff_iwhite2', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_linematch_diff_grouping()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call WriteDiffFiles(0, [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
+ call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
+
+ " a diff that would result in multiple groups before grouping optimization
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call WriteDiffFiles(buf, ['!A',
+ \ '!B',
+ \ '!C' ],
+ \ ['?Z',
+ \ '?A',
+ \ '?B',
+ \ '?C',
+ \ '?A',
+ \ '?B',
+ \ '?B',
+ \ '?C'])
+ call VerifyScreenDump(buf, 'Test_linematch_diff_grouping1', {})
+ call WriteDiffFiles(buf, ['!A',
+ \ '!B',
+ \ '!C' ],
+ \ ['?A',
+ \ '?Z',
+ \ '?B',
+ \ '?C',
+ \ '?A',
+ \ '?B',
+ \ '?C',
+ \ '?C'])
+ call VerifyScreenDump(buf, 'Test_linematch_diff_grouping2', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_linematch_diff_scroll()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call WriteDiffFiles(0, [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
+ call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
+
+ " a diff that would result in multiple groups before grouping optimization
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call WriteDiffFiles(buf, ['!A',
+ \ '!B',
+ \ '!C' ],
+ \ ['?A',
+ \ '?Z',
+ \ '?B',
+ \ '?C',
+ \ '?A',
+ \ '?B',
+ \ '?C',
+ \ '?C'])
+ " scroll down to show calculation of top fill and scroll to correct line in
+ " both windows
+ call VerifyScreenDump(buf, 'Test_linematch_diff_grouping_scroll0', {})
+ call term_sendkeys(buf, "3\<c-e>")
+ call VerifyScreenDump(buf, 'Test_linematch_diff_grouping_scroll1', {})
+ call term_sendkeys(buf, "3\<c-e>")
+ call VerifyScreenDump(buf, 'Test_linematch_diff_grouping_scroll2', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_linematch_line_limit_exceeded()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call WriteDiffFiles(0, [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2', {})
+ call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
+
+ call term_sendkeys(buf, ":set diffopt+=linematch:10\<CR>")
+ " a diff block will not be aligned with linematch because it's contents
+ " exceed 10 lines
+ call WriteDiffFiles(buf,
+ \ ['common line',
+ \ 'HIL',
+ \ '',
+ \ 'aABCabc',
+ \ 'aABCabc',
+ \ 'aABCabc',
+ \ 'aABCabc',
+ \ 'common line',
+ \ 'HIL',
+ \ 'common line',
+ \ 'something'],
+ \ ['common line',
+ \ 'DEF',
+ \ 'GHI',
+ \ 'something',
+ \ '',
+ \ 'aDEFabc',
+ \ 'xyz',
+ \ 'xyz',
+ \ 'xyz',
+ \ 'aDEFabc',
+ \ 'aDEFabc',
+ \ 'aDEFabc',
+ \ 'common line',
+ \ 'DEF',
+ \ 'GHI',
+ \ 'something else',
+ \ 'common line',
+ \ 'something'])
+ call VerifyScreenDump(buf, 'Test_linematch_line_limit_exceeded1', {})
+ " after increasing the count to 30, the limit is not exceeded, and the
+ " alignment algorithm will run on the largest diff block here
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call VerifyScreenDump(buf, 'Test_linematch_line_limit_exceeded2', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_linematch_3diffs()
+ CheckScreendump
+ call delete('.Xdifile1.swp')
+ call delete('.Xdifile2.swp')
+ call delete('.Xdifile3.swp')
+ call WriteDiffFiles3(0, [], [], [])
+ let buf = RunVimInTerminal('-d Xdifile1 Xdifile2 Xdifile3', {})
+ call term_sendkeys(buf, "1\<c-w>w:set autoread\<CR>")
+ call term_sendkeys(buf, "2\<c-w>w:set autoread\<CR>")
+ call term_sendkeys(buf, "3\<c-w>w:set autoread\<CR>")
+ call term_sendkeys(buf, ":set diffopt+=linematch:30\<CR>")
+ call WriteDiffFiles3(buf,
+ \ ["",
+ \ " common line",
+ \ " AAA",
+ \ " AAA",
+ \ " AAA"],
+ \ ["",
+ \ " common line",
+ \ " <<<<<<< HEAD",
+ \ " AAA",
+ \ " AAA",
+ \ " AAA",
+ \ " =======",
+ \ " BBB",
+ \ " BBB",
+ \ " BBB",
+ \ " >>>>>>> branch1"],
+ \ ["",
+ \ " common line",
+ \ " BBB",
+ \ " BBB",
+ \ " BBB"])
+ call VerifyScreenDump(buf, 'Test_linematch_3diffs1', {})
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+" this used to access invalid memory
+func Test_linematch_3diffs_sanity_check()
+ CheckScreendump
+ call delete('.Xfile_linematch1.swp')
+ call delete('.Xfile_linematch2.swp')
+ call delete('.Xfile_linematch3.swp')
+ let lines =<< trim END
+ set diffopt+=linematch:60
+ call feedkeys("Aq\<esc>")
+ call feedkeys("GAklm\<esc>")
+ call feedkeys("o")
+ END
+ call writefile(lines, 'Xlinematch_3diffs.vim', 'D')
+ call writefile(['abcd', 'def', 'hij'], 'Xfile_linematch1', 'D')
+ call writefile(['defq', 'hijk', 'nopq'], 'Xfile_linematch2', 'D')
+ call writefile(['hijklm', 'nopqr', 'stuv'], 'Xfile_linematch3', 'D')
+ call WriteDiffFiles3(0, [], [], [])
+ let buf = RunVimInTerminal('-d -S Xlinematch_3diffs.vim Xfile_linematch1 Xfile_linematch2 Xfile_linematch3', {})
+ call VerifyScreenDump(buf, 'Test_linematch_3diffs2', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_digraph.vim b/test/old/testdir/test_digraph.vim
index 8fbcd4d8ca..0a71cbba99 100644
--- a/test/old/testdir/test_digraph.vim
+++ b/test/old/testdir/test_digraph.vim
@@ -40,6 +40,9 @@ func Test_digraphs()
" Quadruple prime
call Put_Dig("'4")
call assert_equal("⁗", getline('.'))
+ " APPROACHES THE LIMIT
+ call Put_Dig(".=")
+ call assert_equal("≐", getline('.'))
" Not a digraph
call Put_Dig("a\<bs>")
call Put_Dig("\<bs>a")
@@ -250,9 +253,12 @@ func Test_digraphs_option()
call Put_Dig_BS("P","=")
call assert_equal(['Р']+repeat(["₽"],2)+['П'], getline(line('.')-3,line('.')))
" Not a digraph: this is different from <c-k>!
+ let _bs = &bs
+ set bs=
call Put_Dig_BS("a","\<bs>")
call Put_Dig_BS("\<bs>","a")
call assert_equal(['','a'], getline(line('.')-1,line('.')))
+ let &bs = _bs
" Grave
call Put_Dig_BS("a","!")
call Put_Dig_BS("!","e")
@@ -604,8 +610,10 @@ func Test_digraph_getlist_function()
" of digraphs returned.
call assert_equal(digraph_getlist()->len(), digraph_getlist(0)->len())
call assert_notequal(digraph_getlist()->len(), digraph_getlist(1)->len())
+ call assert_equal(digraph_getlist()->len(), digraph_getlist(v:false)->len())
+ call assert_notequal(digraph_getlist()->len(), digraph_getlist(v:true)->len())
- call assert_fails('call digraph_getlist(0z12)', 'E974: Using a Blob as a Number')
+ call assert_fails('call digraph_getlist(0z12)', 'E1212: Bool required for argument 1')
endfunc
diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim
index 19b7d41552..9398cea786 100644
--- a/test/old/testdir/test_filetype.vim
+++ b/test/old/testdir/test_filetype.vim
@@ -108,12 +108,14 @@ func s:GetFilenameChecks() abort
\ '/etc/httpd/sites-some/file', '/etc/httpd/conf.file/conf'],
\ 'apachestyle': ['/etc/proftpd/file.config,/etc/proftpd/conf.file/file', '/etc/proftpd/conf.file/file', '/etc/proftpd/file.conf', '/etc/proftpd/file.conf-file',
\ 'any/etc/proftpd/conf.file/file', 'any/etc/proftpd/file.conf', 'any/etc/proftpd/file.conf-file', 'proftpd.conf', 'proftpd.conf-file'],
+ \ 'apkbuild': ['APKBUILD'],
\ 'applescript': ['file.scpt'],
\ 'aptconf': ['apt.conf', '/.aptitude/config', 'any/.aptitude/config'],
\ 'arch': ['.arch-inventory', '=tagging-method'],
\ 'arduino': ['file.ino', 'file.pde'],
\ 'art': ['file.art'],
\ 'asciidoc': ['file.asciidoc', 'file.adoc'],
+ \ 'asm': ['file.s', 'file.S', 'file.a', 'file.A'],
\ 'asn': ['file.asn', 'file.asn1'],
\ 'asterisk': ['asterisk/file.conf', 'asterisk/file.conf-file', 'some-asterisk/file.conf', 'some-asterisk/file.conf-file'],
\ 'astro': ['file.astro'],
@@ -144,6 +146,7 @@ func s:GetFilenameChecks() abort
\ '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', 'file.mdh', 'file.epro'],
+ \ 'c3': ['file.c3', 'file.c3i', 'file.c3t'],
\ 'cabal': ['file.cabal'],
\ 'cabalconfig': ['cabal.config', expand("$HOME/.config/cabal/config")] + s:WhenConfigHome('$XDG_CONFIG_HOME/cabal/config'),
\ 'cabalproject': ['cabal.project', 'cabal.project.local'],
@@ -151,6 +154,7 @@ func s:GetFilenameChecks() abort
\ 'calendar': ['calendar', '/.calendar/file', '/share/calendar/any/calendar.file', '/share/calendar/calendar.file', 'any/share/calendar/any/calendar.file', 'any/share/calendar/calendar.file'],
\ 'capnp': ['file.capnp'],
\ 'catalog': ['catalog', 'sgml.catalogfile', 'sgml.catalog', 'sgml.catalog-file'],
+ \ 'cdc': ['file.cdc'],
\ 'cdl': ['file.cdl'],
\ 'cdrdaoconf': ['/etc/cdrdao.conf', '/etc/defaults/cdrdao', '/etc/default/cdrdao', '.cdrdao', 'any/etc/cdrdao.conf', 'any/etc/default/cdrdao', 'any/etc/defaults/cdrdao'],
\ 'cdrtoc': ['file.toc'],
@@ -188,13 +192,13 @@ func s:GetFilenameChecks() abort
\ 'crm': ['file.crm'],
\ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'],
\ 'crystal': ['file.cr'],
- \ 'cs': ['file.cs', 'file.csx'],
+ \ 'cs': ['file.cs', 'file.csx', 'file.cake'],
\ 'csc': ['file.csc'],
\ 'csdl': ['file.csdl'],
\ 'csp': ['file.csp', 'file.fdr'],
\ 'css': ['file.css'],
- \ 'cterm': ['file.con'],
\ 'csv': ['file.csv'],
+ \ 'cterm': ['file.con'],
\ 'cucumber': ['file.feature'],
\ 'cuda': ['file.cu', 'file.cuh'],
\ 'cue': ['file.cue'],
@@ -209,11 +213,11 @@ func s:GetFilenameChecks() abort
\ 'dart': ['file.dart', 'file.drt'],
\ 'datascript': ['file.ds'],
\ 'dcd': ['file.dcd'],
+ \ 'deb822sources': ['/etc/apt/sources.list.d/file.sources', 'any/etc/apt/sources.list.d/file.sources'],
\ 'debchangelog': ['changelog.Debian', 'changelog.dch', 'NEWS.Debian', 'NEWS.dch', '/debian/changelog'],
- \ 'debcontrol': ['/debian/control', 'any/debian/control'],
+ \ 'debcontrol': ['/debian/control', 'any/debian/control', 'any/DEBIAN/control'],
\ 'debcopyright': ['/debian/copyright', 'any/debian/copyright'],
\ 'debsources': ['/etc/apt/sources.list', '/etc/apt/sources.list.d/file.list', 'any/etc/apt/sources.list', 'any/etc/apt/sources.list.d/file.list'],
- \ 'deb822sources': ['/etc/apt/sources.list.d/file.sources', 'any/etc/apt/sources.list.d/file.sources'],
\ 'def': ['file.def'],
\ 'denyhosts': ['denyhosts.conf'],
\ 'desc': ['file.desc'],
@@ -271,7 +275,7 @@ func s:GetFilenameChecks() abort
\ 'falcon': ['file.fal'],
\ 'fan': ['file.fan', 'file.fwt'],
\ 'faust': ['file.dsp', 'file.lib'],
- \ 'fennel': ['file.fnl'],
+ \ 'fennel': ['file.fnl', '.fennelrc', 'fennelrc'],
\ 'fetchmail': ['.fetchmailrc'],
\ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'],
\ 'firrtl': ['file.fir'],
@@ -295,12 +299,13 @@ func s:GetFilenameChecks() abort
\ 'gdscript': ['file.gd'],
\ 'gdshader': ['file.gdshader', 'file.shader'],
\ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'],
+ \ 'gel': ['file.gel'],
\ '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'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/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'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/config'),
- \ 'gitignore': ['file.git/info/exclude', '.gitignore', '/.config/git/ignore', 'some.git/info/exclude'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/ignore') + ['.prettierignore'],
+ \ 'gitignore': ['file.git/info/exclude', '.gitignore', '/.config/git/ignore', 'some.git/info/exclude'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/ignore') + ['.prettierignore', '.fdignore', '/.config/fd/ignore', '.ignore', '.rgignore', '.dockerignore', '.npmignore', '.vscodeignore'],
\ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'],
\ 'gitrebase': ['git-rebase-todo'],
\ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'],
@@ -349,11 +354,12 @@ func s:GetFilenameChecks() abort
\ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'],
\ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny', 'any/etc/hosts.allow', 'any/etc/hosts.deny'],
\ 'html': ['file.html', 'file.htm', 'file.cshtml', 'file.component.html'],
- \ 'http': ['file.http'],
\ 'htmlm4': ['file.html.m4'],
\ 'httest': ['file.htt', 'file.htb'],
+ \ 'http': ['file.http'],
\ 'hurl': ['file.hurl'],
- \ 'hyprlang': ['hyprlock.conf', 'hyprland.conf', 'hypridle.conf', 'hyprpaper.conf'],
+ \ 'hy': ['file.hy', '.hy-history'],
+ \ 'hyprlang': ['hyprlock.conf', 'hyprland.conf', 'hypridle.conf', 'hyprpaper.conf', '/hypr/foo.conf'],
\ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'],
\ 'ibasic': ['file.iba', 'file.ibi'],
\ 'icemenu': ['/.icewm/menu', 'any/.icewm/menu'],
@@ -372,7 +378,7 @@ func s:GetFilenameChecks() abort
\ 'jal': ['file.jal', 'file.JAL'],
\ 'jam': ['file.jpl', 'file.jpr', 'JAM-file.file', 'JAM.file', 'Prl-file.file', 'Prl.file'],
\ 'janet': ['file.janet'],
- \ 'java': ['file.java', 'file.jav'],
+ \ 'java': ['file.java', 'file.jav', 'file.jsh'],
\ 'javacc': ['file.jj', 'file.jjt'],
\ 'javascript': ['file.js', 'file.jsm', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs', '.node_repl_history', '.bun_repl_history', 'deno_history.txt'],
\ 'javascript.glimmer': ['file.gjs'],
@@ -380,18 +386,19 @@ func s:GetFilenameChecks() abort
\ 'jess': ['file.clp'],
\ 'jgraph': ['file.jgr'],
\ 'jinja': ['file.jinja'],
- \ 'jj': ['file.jjdescription'],
- \ 'jq': ['file.jq'],
+ \ 'jjdescription': ['file.jjdescription'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ '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.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', 'file.jupyterlab-settings', '.prettierrc', '.firebaserc', '.stylelintrc', '.lintstagedrc', 'file.slnf', 'file.sublime-project', 'file.sublime-settings', 'file.sublime-workspace', 'file.bd', 'file.bda', 'file.xci', 'flake.lock', 'pack.mcmeta', 'deno.lock'],
+ \ 'jq': ['file.jq'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', 'file.jupyterlab-settings', '.prettierrc', '.firebaserc', '.stylelintrc', '.lintstagedrc', 'file.slnf', 'file.sublime-project', 'file.sublime-settings', 'file.sublime-workspace', 'file.bd', 'file.bda', 'file.xci', 'flake.lock', 'pack.mcmeta', 'deno.lock', '.swcrc'],
\ 'json5': ['file.json5'],
- \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.jscsrc', '.vsconfig', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json', '.luaurc'],
+ \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.jscsrc', '.vsconfig', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json', '.luaurc', 'bun.lock', expand("$HOME/.config/VSCodium/User/settings.json")],
\ 'jsonl': ['file.jsonl'],
\ 'jsonnet': ['file.jsonnet', 'file.libsonnet'],
\ 'jsp': ['file.jsp'],
\ 'julia': ['file.jl'],
\ 'just': ['justfile', 'Justfile', '.justfile', 'config.just'],
+ \ 'karel': ['file.kl', 'file.KL'],
\ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file', 'Config.in', 'Config.in.host'],
\ 'kdl': ['file.kdl'],
\ 'kivy': ['file.kv'],
@@ -401,14 +408,15 @@ func s:GetFilenameChecks() abort
\ 'kscript': ['file.ks'],
\ 'kwt': ['file.k'],
\ 'lace': ['file.ace', 'file.ACE'],
+ \ 'lalrpop': ['file.lalrpop'],
\ 'latte': ['file.latte', 'file.lte'],
\ 'ld': ['file.ld', 'any/usr/lib/aarch64-xilinx-linux/ldscripts/aarch64elf32b.x'],
\ 'ldapconf': ['ldap.conf', '.ldaprc', 'ldaprc'],
\ 'ldif': ['file.ldif'],
\ 'lean': ['file.lean'],
\ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'],
- \ 'less': ['file.less'],
\ 'leo': ['file.leo'],
+ \ 'less': ['file.less'],
\ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'],
\ 'lf': ['lfrc'],
\ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc', 'lftp/rc', 'some-lftp/rc'],
@@ -419,13 +427,13 @@ func s:GetFilenameChecks() abort
\ 'lilo': ['lilo.conf', 'lilo.conf-file'],
\ 'lilypond': ['file.ly', 'file.ily'],
\ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf', '/etc/limits.conf', '/etc/limits.d/file.conf', '/etc/some-limits.conf', '/etc/some-limits.d/file.conf', 'any/etc/limits', 'any/etc/limits.conf', 'any/etc/limits.d/file.conf', 'any/etc/some-limits.conf', 'any/etc/some-limits.d/file.conf'],
- \ 'liquidsoap': ['file.liq'],
\ 'liquid': ['file.liquid'],
- \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc', 'file.stsg', 'any/local/share/supertux2/config'],
+ \ 'liquidsoap': ['file.liq'],
+ \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc'],
\ 'lite': ['file.lite', 'file.lt'],
\ 'litestep': ['/LiteStep/any/file.rc', 'any/LiteStep/any/file.rc'],
- \ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'],
\ 'livebook': ['file.livemd'],
+ \ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'],
\ 'loginaccess': ['/etc/login.access', 'any/etc/login.access'],
\ 'logindefs': ['/etc/login.defs', 'any/etc/login.defs'],
\ 'logtalk': ['file.lgt'],
@@ -448,9 +456,9 @@ func s:GetFilenameChecks() abort
\ 'mallard': ['file.page'],
"\ 'man': ['file.man'],
\ 'manconf': ['/etc/man.conf', 'man.config', 'any/etc/man.conf'],
- \ 'map': ['file.map'],
\ 'maple': ['file.mv', 'file.mpl', 'file.mws'],
\ 'markdown': ['file.markdown', 'file.mdown', 'file.mkd', 'file.mkdn', 'file.mdwn', 'file.md'],
+ \ 'masm': ['file.masm'],
\ 'mason': ['file.mason', 'file.mhtml'],
\ 'master': ['file.mas', 'file.master'],
\ 'matlab': ['file.m'],
@@ -496,6 +504,7 @@ func s:GetFilenameChecks() abort
\ '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'],
\ 'modula3': ['file.m3', 'file.mg', 'file.i3', 'file.ig', 'file.lm3'],
+ \ 'mojo': ['file.mojo', 'file.🔥'],
\ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'],
\ 'moo': ['file.moo'],
\ 'moonscript': ['file.moon'],
@@ -504,10 +513,9 @@ func s:GetFilenameChecks() abort
\ 'mplayerconf': ['mplayer.conf', '/.mplayer/config', 'any/.mplayer/config'],
\ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'],
\ 'msidl': ['file.odl', 'file.mof'],
- \ 'mss': ['file.mss'],
- \ 'msql': ['file.msql'],
- \ 'mojo': ['file.mojo', 'file.🔥'],
\ 'msmtp': ['.msmtprc'],
+ \ 'msql': ['file.msql'],
+ \ 'mss': ['file.mss'],
\ 'mupad': ['file.mu'],
\ 'mush': ['file.mush'],
\ 'mustache': ['file.mustache'],
@@ -527,6 +535,7 @@ func s:GetFilenameChecks() abort
\ 'n1ql': ['file.n1ql', 'file.nql'],
\ 'named': ['namedfile.conf', 'rndcfile.conf', 'named-file.conf', 'named.conf', 'rndc-file.conf', 'rndc-file.key', 'rndc.conf', 'rndc.key'],
\ 'nanorc': ['/etc/nanorc', 'file.nanorc', 'any/etc/nanorc'],
+ \ 'nasm': ['file.nasm'],
\ 'natural': ['file.NSA', 'file.NSC', 'file.NSG', 'file.NSL', 'file.NSM', 'file.NSN', 'file.NSP', 'file.NSS'],
\ 'ncf': ['file.ncf'],
\ 'neomuttlog': ['/home/user/.neomuttdebug1'],
@@ -540,6 +549,7 @@ func s:GetFilenameChecks() abort
\ 'nqc': ['file.nqc'],
\ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'],
\ 'nsis': ['file.nsi', 'file.nsh'],
+ \ 'ntriples': ['file.nt'],
\ 'nu': ['file.nu'],
\ 'obj': ['file.obj'],
\ 'objdump': ['file.objdump', 'file.cppobjdump'],
@@ -599,16 +609,18 @@ func s:GetFilenameChecks() abort
\ 'promela': ['file.pml'],
\ 'proto': ['file.proto'],
\ 'protocols': ['/etc/protocols', 'any/etc/protocols'],
+ \ 'prql': ['file.prql'],
\ 'ps1': ['file.ps1', 'file.psd1', 'file.psm1', 'file.pssc'],
\ 'ps1xml': ['file.ps1xml'],
\ 'psf': ['file.psf'],
\ 'psl': ['file.psl'],
+ \ 'ptx': ['file.ptx'],
\ 'pug': ['file.pug'],
\ 'puppet': ['file.pp'],
\ 'purescript': ['file.purs'],
\ 'pymanifest': ['MANIFEST.in'],
\ 'pyret': ['file.arr'],
- \ 'pyrex': ['file.pyx', 'file.pxd'],
+ \ 'pyrex': ['file.pyx', 'file.pxd', 'file.pxi', 'file.pyx+'],
\ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', '.python_history', '.jline-jython.history', 'file.ptl', 'file.pyi', 'SConstruct'],
\ 'ql': ['file.ql', 'file.qll'],
\ 'qml': ['file.qml', 'file.qbs'],
@@ -640,27 +652,27 @@ func s:GetFilenameChecks() abort
\ 'rnc': ['file.rnc'],
\ 'rng': ['file.rng'],
\ 'rnoweb': ['file.rnw', 'file.snw'],
- \ 'rpgle': ['file.rpgle', 'file.rpgleinc'],
\ 'robot': ['file.robot', 'file.resource'],
\ 'robots': ['robots.txt'],
\ 'roc': ['file.roc'],
\ 'ron': ['file.ron'],
\ 'routeros': ['file.rsc'],
\ 'rpcgen': ['file.x'],
+ \ 'rpgle': ['file.rpgle', 'file.rpgleinc'],
\ 'rpl': ['file.rpl'],
\ 'rrst': ['file.rrst', 'file.srst'],
\ 'rst': ['file.rst'],
\ 'rtf': ['file.rtf'],
\ 'ruby': ['.irbrc', 'irbrc', '.irb_history', 'irb_history', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile', 'Vagrantfile'],
\ 'rust': ['file.rs'],
+ \ 'sage': ['file.sage'],
\ 'salt': ['file.sls'],
\ 'samba': ['smb.conf'],
\ 'sas': ['file.sas'],
\ 'sass': ['file.sass'],
- \ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
\ 'scala': ['file.scala'],
- \ 'scheme': ['file.scm', 'file.ss', 'file.sld'],
+ \ 'scheme': ['file.scm', 'file.ss', 'file.sld', 'file.stsg', 'any/local/share/supertux2/config', '.lips_repl_history'],
\ 'scilab': ['file.sci', 'file.sce'],
\ 'screen': ['.screenrc', 'screenrc'],
\ 'scss': ['file.scss'],
@@ -673,19 +685,18 @@ func s:GetFilenameChecks() abort
\ 'setserial': ['/etc/serial.conf', 'any/etc/serial.conf'],
\ 'sexplib': ['file.sexp'],
\ 'sh': ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '.bash_history', '.bash-history',
- \ '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh',
+ \ '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh',
\ '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf', 'file.bats', '.ash_history', 'any/etc/neofetch/config.conf', '.xprofile',
\ 'user-dirs.defaults', 'user-dirs.dirs', 'makepkg.conf', '.makepkg.conf', 'file.mdd', 'file.cygport', '.env', '.envrc', 'devscripts.conf',
\ '.devscripts', 'file.lo', 'file.la', 'file.lai'],
+ \ 'shaderslang': ['file.slang'],
\ 'sieve': ['file.siv', 'file.sieve'],
\ 'sil': ['file.sil'],
\ 'simula': ['file.sim'],
\ 'sinda': ['file.sin', 'file.s85'],
\ 'sisu': ['file.sst', 'file.ssm', 'file.ssi', 'file.-sst', 'file._sst', 'file.sst.meta', 'file.-sst.meta', 'file._sst.meta'],
\ 'skill': ['file.il', 'file.ils', 'file.cdf'],
- \ 'cdc': ['file.cdc'],
\ 'slang': ['file.sl'],
- \ 'sage': ['file.sage'],
\ 'slice': ['file.ice'],
\ 'slint': ['file.slint'],
\ 'slpconf': ['/etc/slp.conf', 'any/etc/slp.conf'],
@@ -711,7 +722,6 @@ func s:GetFilenameChecks() abort
\ 'spyce': ['file.spy', 'file.spi'],
\ 'sql': ['file.tyb', 'file.tyc', 'file.pkb', 'file.pks', '.sqlite_history'],
\ 'sqlj': ['file.sqlj'],
- \ 'prql': ['file.prql'],
\ 'sqr': ['file.sqr', 'file.sqi'],
\ 'squid': ['squid.conf'],
\ 'squirrel': ['file.nut'],
@@ -777,14 +787,13 @@ func s:GetFilenameChecks() abort
\ 'any/etc/systemd/system/file.d/.#-file',
\ 'any/etc/systemd/system/file.d/file.conf'],
\ 'systemverilog': ['file.sv', 'file.svh'],
- \ 'trace32': ['file.cmm', 'file.t32'],
+ \ 'tablegen': ['file.td'],
\ 'tags': ['tags'],
\ 'tak': ['file.tak'],
\ 'tal': ['file.tal'],
\ 'taskdata': ['pending.data', 'completed.data', 'undo.data'],
\ 'taskedit': ['file.task'],
\ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc', '.tclsh-history', '.xsctcmdhistory', '.xsdbcmdhistory'],
- \ 'tablegen': ['file.td'],
\ 'teal': ['file.tl'],
\ 'templ': ['file.templ'],
\ 'template': ['file.tmpl'],
@@ -804,7 +813,9 @@ func s:GetFilenameChecks() abort
\ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf', '.tmux-file.conf', '.tmux.conf', 'tmux-file.conf', 'tmux.conf', 'tmux.conf.local'],
\ 'toml': ['file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config', '.black'],
\ 'tpp': ['file.tpp'],
+ \ 'trace32': ['file.cmm', 'file.t32'],
\ 'treetop': ['file.treetop'],
+ \ 'trig': ['file.trig'],
\ 'trustees': ['trustees.conf'],
\ 'tsalt': ['file.slt'],
\ 'tsscl': ['file.tsscl'],
@@ -816,12 +827,12 @@ func s:GetFilenameChecks() abort
\ 'typescript.glimmer': ['file.gts'],
\ 'typescriptreact': ['file.tsx'],
\ 'typespec': ['file.tsp'],
- \ 'ungrammar': ['file.ungram'],
\ 'uc': ['file.uc'],
\ 'udevconf': ['/etc/udev/udev.conf', 'any/etc/udev/udev.conf'],
\ 'udevperm': ['/etc/udev/permissions.d/file.permissions', 'any/etc/udev/permissions.d/file.permissions'],
\ 'udevrules': ['/etc/udev/rules.d/file.rules', '/usr/lib/udev/rules.d/file.rules', '/lib/udev/rules.d/file.rules'],
\ 'uil': ['file.uit', 'file.uil'],
+ \ 'ungrammar': ['file.ungram'],
\ 'unison': ['file.u', 'file.uu'],
\ 'updatedb': ['/etc/updatedb.conf', 'any/etc/updatedb.conf'],
\ 'upstart': ['/usr/share/upstart/file.conf', '/usr/share/upstart/file.override', '/etc/init/file.conf', '/etc/init/file.override', '/.init/file.conf', '/.init/file.override', '/.config/upstart/file.conf', '/.config/upstart/file.override', 'any/.config/upstart/file.conf', 'any/.config/upstart/file.override', 'any/.init/file.conf', 'any/.init/file.override', 'any/etc/init/file.conf', 'any/etc/init/file.override', 'any/usr/share/upstart/file.conf', 'any/usr/share/upstart/file.override'],
@@ -874,7 +885,7 @@ func s:GetFilenameChecks() abort
\ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui',
\ 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old',
\ 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd', 'fonts.conf', 'file.xcu', 'file.xlb', 'file.xlc', 'file.xba', 'file.xpr',
- \ 'file.xpfm', 'file.spfm', 'file.bxml', 'file.mmi'],
+ \ 'file.xpfm', 'file.spfm', 'file.bxml', 'file.mmi', 'file.slnx', 'Directory.Packages.props', 'Directory.Build.targets', 'Directory.Build.props'],
\ 'xmodmap': ['anyXmodmap', 'Xmodmap', 'some-Xmodmap', 'some-xmodmap', 'some-xmodmap-file', 'xmodmap', 'xmodmap-file'],
\ 'xpm': ['file.xpm'],
\ 'xpm2': ['file.xpm2'],
@@ -884,7 +895,7 @@ func s:GetFilenameChecks() abort
\ 'xslt': ['file.xsl', 'file.xslt'],
\ 'yacc': ['file.yy', 'file.yxx', 'file.y++'],
\ 'yaml': ['file.yaml', 'file.yml', 'file.eyaml', 'any/.bundle/config', '.clangd', '.clang-format', '.clang-tidy', 'file.mplstyle', 'matplotlibrc', 'yarn.lock',
- \ '/home/user/.kube/config'],
+ \ '/home/user/.kube/config', '.condarc', 'condarc', 'pixi.lock'],
\ 'yang': ['file.yang'],
\ 'yuck': ['file.yuck'],
\ 'z8a': ['file.z8a'],
@@ -986,6 +997,7 @@ func s:GetScriptChecks() abort
\ 'expect': [['#!/path/expect']],
\ 'execline': [['#!/sbin/execlineb -S0'], ['#!/usr/bin/execlineb']],
\ 'gnuplot': [['#!/path/gnuplot']],
+ \ 'just': [['#!/path/just']],
\ 'make': [['#!/path/make']],
\ 'nix': [['#!/path/nix-shell']],
\ 'pike': [['#!/path/pike'],
@@ -1191,6 +1203,22 @@ func Test_cfg_file()
filetype off
endfunc
+func Test_cl_file()
+ filetype on
+
+ call writefile(['/*', ' * Xfile.cl', ' */', 'int f() {}'], 'Xfile.cl')
+ split Xfile.cl
+ call assert_equal('opencl', &filetype)
+ bwipe!
+
+ call writefile(['()'], 'Xfile.cl')
+ split Xfile.cl
+ call assert_equal('lisp', &filetype)
+ bwipe!
+
+ filetype off
+endfunc
+
func Test_d_file()
filetype on
@@ -2273,6 +2301,43 @@ func Test_cls_file()
filetype off
endfunc
+func Test_cmd_file()
+ filetype on
+
+ call writefile(['--rom_model'], 'Xfile.cmd')
+ split Xfile.cmd
+ call assert_equal('lnk', &filetype)
+ bwipe!
+
+ call writefile(['/* comment */'], 'Xfile.cmd')
+ split Xfile.cmd
+ call assert_equal('rexx', &filetype)
+ bwipe!
+
+ call writefile(['REM comment'], 'Xfile.cmd')
+ split Xfile.cmd
+ call assert_equal('dosbatch', &filetype)
+ bwipe!
+
+ filetype off
+endfunc
+
+func Test_sa_file()
+ filetype on
+
+ call writefile([';* XXX-a.sa: XXX for TI C6000 DSP *;', '.no_mdep'], 'Xfile.sa')
+ split Xfile.sa
+ call assert_equal('tiasm', &filetype)
+ bwipe!
+
+ call writefile(['-- comment'], 'Xfile.sa')
+ split Xfile.sa
+ call assert_equal('sather', &filetype)
+ bwipe!
+
+ filetype off
+endfunc
+
func Test_sig_file()
filetype on
@@ -2737,6 +2802,24 @@ func Test_make_file()
filetype off
endfunc
+func Test_map_file()
+ filetype on
+
+ " TI linker map file
+ call writefile(['******************************************************************************', ' TMS320C6x Linker Unix v7.4.24 ', '******************************************************************************'], 'Xfile.map', 'D')
+ split Xfile.map
+ call assert_equal('lnkmap', &filetype)
+ bwipe!
+
+ " UMN mapserver config file
+ call writefile(['MAP', 'NAME "local-demo"', 'END'], 'Xfile.map', 'D')
+ split Xfile.map
+ call assert_equal('map', &filetype)
+ bwipe!
+
+ filetype off
+endfunc
+
func Test_org_file()
filetype on
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index 327ea98e1c..738a417b86 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -2390,6 +2390,85 @@ func Test_getchar()
call assert_equal("\<M-F2>", getchar(0))
call assert_equal(0, getchar(0))
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar())
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar(-1))
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar(-1, {}))
+ call feedkeys("\<Tab>", '')
+ call assert_equal(char2nr("\<Tab>"), getchar(-1, #{number: v:true}))
+ call assert_equal(0, getchar(0))
+ call assert_equal(0, getchar(1))
+ call assert_equal(0, getchar(0, #{number: v:true}))
+ call assert_equal(0, getchar(1, #{number: v:true}))
+
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getcharstr())
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getcharstr(-1))
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getcharstr(-1, {}))
+ call feedkeys("\<Tab>", '')
+ call assert_equal("\<Tab>", getchar(-1, #{number: v:false}))
+ call assert_equal('', getcharstr(0))
+ call assert_equal('', getcharstr(1))
+ call assert_equal('', getchar(0, #{number: v:false}))
+ call assert_equal('', getchar(1, #{number: v:false}))
+
+ " Nvim: <M-x> is never simplified
+ " for key in ["C-I", "C-X", "M-x"]
+ for key in ["C-I", "C-X"]
+ let lines =<< eval trim END
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar())
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1, {{}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1, {{'number': 1}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal(char2nr("\<{key}>"), getchar(-1, {{'simplify': 1}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<*{key}>", getchar(-1, {{'simplify': v:false}}))
+ call assert_equal(0, getchar(0))
+ call assert_equal(0, getchar(1))
+ END
+ call CheckLegacyAndVim9Success(lines)
+
+ let lines =<< eval trim END
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr())
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr(-1))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr(-1, {{}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getchar(-1, {{'number': 0}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<{key}>", getcharstr(-1, {{'simplify': 1}}))
+ call feedkeys("\<*{key}>", '')
+ call assert_equal("\<*{key}>", getcharstr(-1, {{'simplify': v:false}}))
+ call assert_equal('', getcharstr(0))
+ call assert_equal('', getcharstr(1))
+ END
+ call CheckLegacyAndVim9Success(lines)
+ endfor
+
+ call assert_fails('call getchar(1, 1)', 'E1206:')
+ call assert_fails('call getcharstr(1, 1)', 'E1206:')
+ call assert_fails('call getchar(1, #{cursor: "foo"})', 'E475:')
+ call assert_fails('call getcharstr(1, #{cursor: "foo"})', 'E475:')
+ call assert_fails('call getchar(1, #{cursor: 0z})', 'E976:')
+ call assert_fails('call getcharstr(1, #{cursor: 0z})', 'E976:')
+ call assert_fails('call getchar(1, #{simplify: 0z})', 'E974:')
+ call assert_fails('call getcharstr(1, #{simplify: 0z})', 'E974:')
+ call assert_fails('call getchar(1, #{number: []})', 'E745:')
+ call assert_fails('call getchar(1, #{number: {}})', 'E728:')
+ call assert_fails('call getcharstr(1, #{number: v:true})', 'E475:')
+ call assert_fails('call getcharstr(1, #{number: v:false})', 'E475:')
+
call setline(1, 'xxxx')
call Ntest_setmouse(1, 3)
let v:mouse_win = 9
@@ -2405,10 +2484,59 @@ func Test_getchar()
enew!
endfunc
+func Test_getchar_cursor_position()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, ['foobar', 'foobar', 'foobar'])
+ call cursor(3, 6)
+ nnoremap <F1> <Cmd>echo 1234<Bar>call getchar()<CR>
+ nnoremap <F2> <Cmd>call getchar()<CR>
+ nnoremap <F3> <Cmd>call getchar(-1, {})<CR>
+ nnoremap <F4> <Cmd>call getchar(-1, #{cursor: 'msg'})<CR>
+ nnoremap <F5> <Cmd>call getchar(-1, #{cursor: 'keep'})<CR>
+ nnoremap <F6> <Cmd>call getchar(-1, #{cursor: 'hide'})<CR>
+ END
+ call writefile(lines, 'XgetcharCursorPos', 'D')
+ let buf = RunVimInTerminal('-S XgetcharCursorPos', {'rows': 6})
+ call WaitForAssert({-> assert_equal([3, 6], term_getcursor(buf)[0:1])})
+
+ call term_sendkeys(buf, "\<F1>")
+ call WaitForAssert({-> assert_equal([6, 5], term_getcursor(buf)[0:1])})
+ call assert_true(term_getcursor(buf)[2].visible)
+ call term_sendkeys(buf, 'a')
+ call WaitForAssert({-> assert_equal([3, 6], term_getcursor(buf)[0:1])})
+ call assert_true(term_getcursor(buf)[2].visible)
+
+ for key in ["\<F2>", "\<F3>", "\<F4>"]
+ call term_sendkeys(buf, key)
+ call WaitForAssert({-> assert_equal([6, 1], term_getcursor(buf)[0:1])})
+ call assert_true(term_getcursor(buf)[2].visible)
+ call term_sendkeys(buf, 'a')
+ call WaitForAssert({-> assert_equal([3, 6], term_getcursor(buf)[0:1])})
+ call assert_true(term_getcursor(buf)[2].visible)
+ endfor
+
+ call term_sendkeys(buf, "\<F5>")
+ call TermWait(buf, 50)
+ call assert_equal([3, 6], term_getcursor(buf)[0:1])
+ call assert_true(term_getcursor(buf)[2].visible)
+ call term_sendkeys(buf, 'a')
+ call TermWait(buf, 50)
+ call assert_equal([3, 6], term_getcursor(buf)[0:1])
+ call assert_true(term_getcursor(buf)[2].visible)
+
+ call term_sendkeys(buf, "\<F6>")
+ call WaitForAssert({-> assert_false(term_getcursor(buf)[2].visible)})
+ call term_sendkeys(buf, 'a')
+ call WaitForAssert({-> assert_true(term_getcursor(buf)[2].visible)})
+ call assert_equal([3, 6], term_getcursor(buf)[0:1])
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_libcall_libcallnr()
- if !has('libcall')
- return
- endif
+ CheckFeature libcall
if has('win32')
let libc = 'msvcrt.dll'
@@ -2672,7 +2800,9 @@ endfunc
func Test_call()
call assert_equal(3, call('len', [123]))
call assert_equal(3, 'len'->call([123]))
- call assert_fails("call call('len', 123)", 'E714:')
+ call assert_equal(4, call({ x -> len(x) }, ['xxxx']))
+ call assert_equal(2, call(function('len'), ['xx']))
+ call assert_fails("call call('len', 123)", 'E1211:')
call assert_equal(0, call('', []))
call assert_equal(0, call('len', v:_null_list))
diff --git a/test/old/testdir/test_highlight.vim b/test/old/testdir/test_highlight.vim
index 0a64c63d30..56c7a9656f 100644
--- a/test/old/testdir/test_highlight.vim
+++ b/test/old/testdir/test_highlight.vim
@@ -785,8 +785,8 @@ func Test_1_highlight_Normalgroup_exists()
if !has('gui_running')
call assert_match('hi Normal\s*clear', hlNormal)
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)
+ " expect is DEFAULT_FONT of gui_gtk_x11.c (any size)
+ call assert_match('hi Normal\s*font=Monospace\>', hlNormal)
elseif has('gui_motif')
" expect is DEFAULT_FONT of gui_x11.c
call assert_match('hi Normal\s*font=7x13', hlNormal)
diff --git a/test/old/testdir/test_indent.vim b/test/old/testdir/test_indent.vim
index dcacc11663..630beed810 100644
--- a/test/old/testdir/test_indent.vim
+++ b/test/old/testdir/test_indent.vim
@@ -1,5 +1,8 @@
" Test for various indent options
+source shared.vim
+source check.vim
+
func Test_preserveindent()
new
" Test for autoindent copying indent from the previous line
@@ -276,4 +279,77 @@ func Test_formatting_keeps_first_line_indent()
bwipe!
endfunc
+" Test for indenting with large amount, causes overflow
+func Test_indent_overflow_count()
+ throw 'skipped: TODO: '
+ new
+ setl sw=8
+ call setline(1, "abc")
+ norm! V2147483647>
+ " indents by INT_MAX
+ call assert_equal(2147483647, indent(1))
+ close!
+endfunc
+
+func Test_indent_overflow_count2()
+ throw 'skipped: Nvim does not support 64-bit number options'
+ new
+ " this only works, when long is 64bits
+ try
+ setl sw=0x180000000
+ catch /^Vim\%((\a\+)\)\=:E487:/
+ throw 'Skipped: value negative on this platform'
+ endtry
+ call setline(1, "\tabc")
+ norm! <<
+ call assert_equal(0, indent(1))
+ close!
+endfunc
+
+" Test that mouse shape is restored to Normal mode after using "gq" when
+" 'indentexpr' executes :normal.
+func Test_mouse_shape_indent_norm_with_gq()
+ CheckFeature mouseshape
+ CheckCanRunGui
+
+ let lines =<< trim END
+ func Indent()
+ exe "normal! \<Ignore>"
+ return 0
+ endfunc
+
+ setlocal indentexpr=Indent()
+ END
+ call writefile(lines, 'Xindentexpr.vim', 'D')
+
+ let lines =<< trim END
+ vim9script
+ var mouse_shapes = []
+
+ setline(1, [repeat('a', 80), repeat('b', 80)])
+
+ feedkeys('ggVG')
+ timer_start(50, (_) => {
+ mouse_shapes += [getmouseshape()]
+ timer_start(50, (_) => {
+ feedkeys('gq')
+ timer_start(50, (_) => {
+ mouse_shapes += [getmouseshape()]
+ timer_start(50, (_) => {
+ writefile(mouse_shapes, 'Xmouseshapes')
+ quit!
+ })
+ })
+ })
+ })
+ END
+ call writefile(lines, 'Xmouseshape.vim', 'D')
+
+ call RunVim([], [], "-g -S Xindentexpr.vim -S Xmouseshape.vim")
+ call WaitForAssert({-> assert_equal(['rightup-arrow', 'arrow'],
+ \ readfile('Xmouseshapes'))}, 300)
+
+ call delete('Xmouseshapes')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim
index c02aa1db62..2fb1715634 100644
--- a/test/old/testdir/test_ins_complete.vim
+++ b/test/old/testdir/test_ins_complete.vim
@@ -253,6 +253,91 @@ func Test_CompleteDoneNone()
au! CompleteDone
endfunc
+func Test_CompleteDone_vevent_keys()
+ func OnDone()
+ let g:complete_word = get(v:event, 'complete_word', v:null)
+ let g:complete_type = get(v:event, 'complete_type', v:null)
+ endfunction
+
+ autocmd CompleteDone * :call OnDone()
+
+ func CompleteFunc(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return [#{word: "foo"}, #{word: "bar"}]
+ endfunc
+ set omnifunc=CompleteFunc
+ set completefunc=CompleteFunc
+ set completeopt+=menuone
+
+ new
+ call feedkeys("A\<C-X>\<C-O>\<Esc>", 'tx')
+ call assert_equal('', g:complete_word)
+ call assert_equal('omni', g:complete_type)
+
+ call feedkeys("S\<C-X>\<C-O>\<C-Y>\<Esc>", 'tx')
+ call assert_equal('foo', g:complete_word)
+ call assert_equal('omni', g:complete_type)
+
+ call feedkeys("S\<C-X>\<C-O>\<C-N>\<C-Y>\<Esc>0", 'tx')
+ call assert_equal('bar', g:complete_word)
+ call assert_equal('omni', g:complete_type)
+
+ call feedkeys("Shello vim visual v\<C-X>\<C-N>\<ESC>", 'tx')
+ call assert_equal('', g:complete_word)
+ call assert_equal('keyword', g:complete_type)
+
+ call feedkeys("Shello vim visual v\<C-X>\<C-N>\<C-Y>", 'tx')
+ call assert_equal('vim', g:complete_word)
+ call assert_equal('keyword', g:complete_type)
+
+ call feedkeys("Shello vim visual v\<C-X>\<C-N>\<C-Y>", 'tx')
+ call assert_equal('vim', g:complete_word)
+ call assert_equal('keyword', g:complete_type)
+
+ call feedkeys("Shello vim\<CR>completion test\<CR>\<C-X>\<C-l>\<C-Y>", 'tx')
+ call assert_equal('completion test', g:complete_word)
+ call assert_equal('whole_line', g:complete_type)
+
+ call feedkeys("S\<C-X>\<C-U>\<C-Y>", 'tx')
+ call assert_equal('foo', g:complete_word)
+ call assert_equal('function', g:complete_type)
+
+ inoremap <buffer> <f3> <cmd>call complete(1, ["red", "blue"])<cr>
+ call feedkeys("S\<f3>\<C-Y>", 'tx')
+ call assert_equal('red', g:complete_word)
+ call assert_equal('eval', g:complete_type)
+
+ call feedkeys("S\<C-X>\<C-V>\<C-Y>", 'tx')
+ call assert_equal('!', g:complete_word)
+ call assert_equal('cmdline', g:complete_type)
+
+ call writefile([''], 'foo_test', 'D')
+ call feedkeys("Sfoo\<C-X>\<C-F>\<C-Y>\<Esc>", 'tx')
+ call assert_equal('foo_test', g:complete_word)
+ call assert_equal('files', g:complete_type)
+
+ call writefile(['hello help'], 'test_case.txt', 'D')
+ set dictionary=test_case.txt
+ call feedkeys("ggdGSh\<C-X>\<C-K>\<C-Y>\<Esc>", 'tx')
+ call assert_equal('hello', g:complete_word)
+ call assert_equal('dictionary', g:complete_type)
+
+ set spell spelllang=en_us
+ call feedkeys("STheatre\<C-X>s\<C-Y>\<Esc>", 'tx')
+ call assert_equal('Theater', g:complete_word)
+ call assert_equal('spell', g:complete_type)
+
+ bwipe!
+ set completeopt& omnifunc& completefunc& spell& spelllang& dictionary&
+ autocmd! CompleteDone
+ delfunc OnDone
+ delfunc CompleteFunc
+ unlet g:complete_word
+ unlet g:complete_type
+endfunc
+
func Test_CompleteDoneDict()
au CompleteDonePre * :call <SID>CompleteDone_CheckCompletedItemDict(1)
au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDict(0)
@@ -1509,7 +1594,7 @@ func Test_complete_item_refresh_always()
set completefunc=Tcomplete
exe "normal! iup\<C-X>\<C-U>\<BS>\<BS>\<BS>\<BS>\<BS>"
call assert_equal('up', getline(1))
- call assert_equal(2, g:CallCount)
+ call assert_equal(6, g:CallCount)
set completeopt&
set completefunc&
bw!
@@ -2693,7 +2778,7 @@ func Test_complete_fuzzy_match()
if a:findstart
return col(".")
endif
- return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}]
+ return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}, #{word: "你好吗"}, #{word: "我好"}]
endfunc
new
@@ -2752,6 +2837,26 @@ func Test_complete_fuzzy_match()
call feedkeys("STe\<C-X>\<C-N>x\<CR>\<Esc>0", 'tx!')
call assert_equal('Tex', getline('.'))
+ " test case for nosort option
+ set cot=menuone,menu,noinsert,fuzzy,nosort
+ " "fooBaz" should have a higher score when the leader is "fb".
+ " With "nosort", "foobar" should still be shown first in the popup menu.
+ call feedkeys("S\<C-x>\<C-o>fb", 'tx')
+ call assert_equal('foobar', g:word)
+ call feedkeys("S\<C-x>\<C-o>好", 'tx')
+ call assert_equal("你好吗", g:word)
+
+ set cot+=noselect
+ call feedkeys("S\<C-x>\<C-o>好", 'tx')
+ call assert_equal(v:null, g:word)
+ call feedkeys("S\<C-x>\<C-o>好\<C-N>", 'tx')
+ call assert_equal('你好吗', g:word)
+
+ " "nosort" shouldn't enable fuzzy filtering when "fuzzy" isn't present.
+ set cot=menuone,noinsert,nosort
+ call feedkeys("S\<C-x>\<C-o>fooB\<C-Y>", 'tx')
+ call assert_equal('fooBaz', getline('.'))
+
" clean up
set omnifunc=
bw!
@@ -2785,4 +2890,203 @@ func Test_complete_fuzzy_match_tie()
set completeopt&
endfunc
+func Test_complete_info_matches()
+ let g:what = ['matches']
+ func ShownInfo()
+ let g:compl_info = complete_info(g:what)
+ return ''
+ endfunc
+ set completeopt+=noinsert
+
+ new
+ call setline(1, ['aaa', 'aab', 'aba', 'abb'])
+ inoremap <buffer><F5> <C-R>=ShownInfo()<CR>
+
+ call feedkeys("Go\<C-X>\<C-N>\<F5>\<Esc>dd", 'tx')
+ call assert_equal([
+ \ {'word': 'aaa', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'aab', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'aba', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'abb', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \], g:compl_info['matches'])
+
+ call feedkeys("Goa\<C-X>\<C-N>b\<F5>\<Esc>dd", 'tx')
+ call assert_equal([
+ \ {'word': 'aba', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'abb', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''},
+ \], g:compl_info['matches'])
+
+ " items and matches both in what
+ let g:what = ['items', 'matches']
+ call feedkeys("Goa\<C-X>\<C-N>b\<F5>\<Esc>dd", 'tx')
+ call assert_equal([
+ \ {'word': 'aaa', 'menu': '', 'user_data': '', 'match': v:false, 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'aab', 'menu': '', 'user_data': '', 'match': v:false, 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'aba', 'menu': '', 'user_data': '', 'match': v:true, 'info': '', 'kind': '', 'abbr': ''},
+ \ {'word': 'abb', 'menu': '', 'user_data': '', 'match': v:true, 'info': '', 'kind': '', 'abbr': ''},
+ \], g:compl_info['items'])
+ call assert_false(has_key(g:compl_info, 'matches'))
+
+ bw!
+ unlet g:what
+ delfunc ShownInfo
+ set cot&
+endfunc
+
+func Test_complete_info_completed()
+ func ShownInfo()
+ let g:compl_info = complete_info(['completed'])
+ return ''
+ endfunc
+ set completeopt+=noinsert
+
+ new
+ call setline(1, ['aaa', 'aab', 'aba', 'abb'])
+ inoremap <buffer><F5> <C-R>=ShownInfo()<CR>
+
+ call feedkeys("Go\<C-X>\<C-N>\<F5>\<Esc>dd", 'tx')
+ call assert_equal({'word': 'aaa', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, g:compl_info['completed'])
+
+ call feedkeys("Go\<C-X>\<C-N>\<C-N>\<F5>\<Esc>dd", 'tx')
+ call assert_equal({'word': 'aab', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, g:compl_info['completed'])
+
+ call feedkeys("Go\<C-X>\<C-N>\<C-N>\<C-N>\<C-N>\<F5>\<Esc>dd", 'tx')
+ call assert_equal({'word': 'abb', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, g:compl_info['completed'])
+
+ set completeopt+=noselect
+ call feedkeys("Go\<C-X>\<C-N>\<F5>\<Esc>dd", 'tx')
+ call assert_equal({}, g:compl_info)
+
+ bw!
+ delfunc ShownInfo
+ set cot&
+endfunc
+
+function Test_completeopt_preinsert()
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return [#{word: "fobar"}, #{word: "foobar"}, #{word: "你的"}, #{word: "你好世界"}]
+ endfunc
+ set omnifunc=Omni_test
+ set completeopt=menu,menuone,preinsert
+
+ new
+ call feedkeys("S\<C-X>\<C-O>f", 'tx')
+ call assert_equal("fobar", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("S\<C-X>\<C-O>foo", 'tx')
+ call assert_equal("foobar", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("S\<C-X>\<C-O>foo\<BS>\<BS>\<BS>", 'tx')
+ call assert_equal("", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ " delete a character and input new leader
+ call feedkeys("S\<C-X>\<C-O>foo\<BS>b", 'tx')
+ call assert_equal("fobar", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ " delete preinsert when prepare completion
+ call feedkeys("S\<C-X>\<C-O>f\<Space>", 'tx')
+ call assert_equal("f ", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("S\<C-X>\<C-O>你", 'tx')
+ call assert_equal("你的", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("S\<C-X>\<C-O>你好", 'tx')
+ call assert_equal("你好世界", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>f", 'tx')
+ call assert_equal("hello fobar wo", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>f\<BS>", 'tx')
+ call assert_equal("hello wo", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>foo", 'tx')
+ call assert_equal("hello foobar wo", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ call feedkeys("Shello wo\<Left>\<Left>\<Left>\<C-X>\<C-O>foo\<BS>b", 'tx')
+ call assert_equal("hello fobar wo", getline('.'))
+ call feedkeys("\<C-E>\<ESC>", 'tx')
+
+ " confirm
+ call feedkeys("S\<C-X>\<C-O>f\<C-Y>", 'tx')
+ call assert_equal("fobar", getline('.'))
+ call assert_equal(5, col('.'))
+
+ " cancel
+ call feedkeys("S\<C-X>\<C-O>fo\<C-E>", 'tx')
+ call assert_equal("fo", getline('.'))
+ call assert_equal(2, col('.'))
+
+ call feedkeys("S hello hero\<CR>h\<C-X>\<C-N>", 'tx')
+ call assert_equal("hello", getline('.'))
+ call assert_equal(1, col('.'))
+
+ call feedkeys("Sh\<C-X>\<C-N>\<C-Y>", 'tx')
+ call assert_equal("hello", getline('.'))
+ call assert_equal(5, col('.'))
+
+ " delete preinsert part
+ call feedkeys("S\<C-X>\<C-O>fo ", 'tx')
+ call assert_equal("fo ", getline('.'))
+ call assert_equal(3, col('.'))
+
+ call feedkeys("She\<C-X>\<C-N>\<C-U>", 'tx')
+ call assert_equal("", getline('.'))
+ call assert_equal(1, col('.'))
+
+ call feedkeys("She\<C-X>\<C-N>\<C-W>", 'tx')
+ call assert_equal("", getline('.'))
+ call assert_equal(1, col('.'))
+
+ " whole line
+ call feedkeys("Shello hero\<CR>\<C-X>\<C-L>", 'tx')
+ call assert_equal("hello hero", getline('.'))
+ call assert_equal(1, col('.'))
+
+ call feedkeys("Shello hero\<CR>he\<C-X>\<C-L>", 'tx')
+ call assert_equal("hello hero", getline('.'))
+ call assert_equal(2, col('.'))
+
+ call feedkeys("Shello hero\<CR>h\<C-X>\<C-N>er", 'tx')
+ call assert_equal("hero", getline('.'))
+ call assert_equal(3, col('.'))
+
+ " can not work with fuzzy
+ set cot+=fuzzy
+ call feedkeys("S\<C-X>\<C-O>", 'tx')
+ call assert_equal("fobar", getline('.'))
+ call assert_equal(5, col('.'))
+
+ " test for fuzzy and noinsert
+ set cot+=noinsert
+ call feedkeys("S\<C-X>\<C-O>fb", 'tx')
+ call assert_equal("fb", getline('.'))
+ call assert_equal(2, col('.'))
+
+ call feedkeys("S\<C-X>\<C-O>你", 'tx')
+ call assert_equal("你", getline('.'))
+ call assert_equal(1, col('.'))
+
+ call feedkeys("S\<C-X>\<C-O>fb\<C-Y>", 'tx')
+ call assert_equal("fobar", getline('.'))
+ call assert_equal(5, col('.'))
+
+ bw!
+ set cot&
+ set omnifunc&
+ delfunc Omni_test
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/test/old/testdir/test_let.vim b/test/old/testdir/test_let.vim
index 44852c1d38..22a3a35f87 100644
--- a/test/old/testdir/test_let.vim
+++ b/test/old/testdir/test_let.vim
@@ -436,6 +436,24 @@ func Test_let_heredoc_fails()
call assert_report('Caught exception: ' .. v:exception)
endtry
+ try
+ let v =<< trim trimm
+ trimm
+ call assert_report('No exception thrown')
+ catch /E221:/
+ catch
+ call assert_report('Caught exception: ' .. v:exception)
+ endtry
+
+ try
+ let v =<< trim trim evall
+ evall
+ call assert_report('No exception thrown')
+ catch /E221:/
+ catch
+ call assert_report('Caught exception: ' .. v:exception)
+ endtry
+
let text =<< trim END
func WrongSyntax()
let v =<< that there
diff --git a/test/old/testdir/test_matchfuzzy.vim b/test/old/testdir/test_matchfuzzy.vim
index 90f3366b23..0c982c19f1 100644
--- a/test/old/testdir/test_matchfuzzy.vim
+++ b/test/old/testdir/test_matchfuzzy.vim
@@ -23,6 +23,8 @@ func Test_matchfuzzy()
call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa'))
call assert_equal(256, matchfuzzy([repeat('a', 256)], repeat('a', 256))[0]->len())
call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257)))
+ " full match has highest score
+ call assert_equal(['Cursor', 'lCursor'], matchfuzzy(["hello", "lCursor", "Cursor"], "Cursor"))
" matches with same score should not be reordered
let l = ['abc1', 'abc2', 'abc3']
call assert_equal(l, l->matchfuzzy('abc'))
@@ -98,15 +100,15 @@ endfunc
" Test for the matchfuzzypos() function
func Test_matchfuzzypos()
- call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], matchfuzzypos(['world', 'curl'], 'rl'))
- call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], matchfuzzypos(['world', 'one', 'curl'], 'rl'))
+ call assert_equal([['curl', 'world'], [[2,3], [2,3]], [178, 177]], matchfuzzypos(['world', 'curl'], 'rl'))
+ call assert_equal([['curl', 'world'], [[2,3], [2,3]], [178, 177]], matchfuzzypos(['world', 'one', 'curl'], 'rl'))
call assert_equal([['hello', 'hello world hello world'],
- \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [275, 257]],
+ \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [500, 382]],
\ matchfuzzypos(['hello world hello world', 'hello', 'world'], 'hello'))
- call assert_equal([['aaaaaaa'], [[0, 1, 2]], [191]], matchfuzzypos(['aaaaaaa'], 'aaa'))
- call assert_equal([['a b'], [[0, 3]], [219]], matchfuzzypos(['a b'], 'a b'))
- call assert_equal([['a b'], [[0, 3]], [219]], matchfuzzypos(['a b'], 'a b'))
- call assert_equal([['a b'], [[0]], [112]], matchfuzzypos(['a b'], ' a '))
+ call assert_equal([['aaaaaaa'], [[0, 1, 2]], [266]], matchfuzzypos(['aaaaaaa'], 'aaa'))
+ call assert_equal([['a b'], [[0, 3]], [269]], matchfuzzypos(['a b'], 'a b'))
+ call assert_equal([['a b'], [[0, 3]], [269]], matchfuzzypos(['a b'], 'a b'))
+ call assert_equal([['a b'], [[0]], [137]], matchfuzzypos(['a b'], ' a '))
call assert_equal([[], [], []], matchfuzzypos(['a b'], ' '))
call assert_equal([[], [], []], matchfuzzypos(['world', 'curl'], 'ab'))
let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256))
@@ -115,33 +117,33 @@ func Test_matchfuzzypos()
call assert_equal([[], [], []], matchfuzzypos([], 'abc'))
" match in a long string
- call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]], [-135]],
+ call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]], [-60]],
\ matchfuzzypos([repeat('x', 300) .. 'abc'], 'abc'))
" preference for camel case match
- call assert_equal([['xabcxxaBc'], [[6, 7, 8]], [189]], matchfuzzypos(['xabcxxaBc'], 'abc'))
+ call assert_equal([['xabcxxaBc'], [[6, 7, 8]], [269]], matchfuzzypos(['xabcxxaBc'], 'abc'))
" preference for match after a separator (_ or space)
- call assert_equal([['xabx_ab'], [[5, 6]], [145]], matchfuzzypos(['xabx_ab'], 'ab'))
+ call assert_equal([['xabx_ab'], [[5, 6]], [195]], matchfuzzypos(['xabx_ab'], 'ab'))
" preference for leading letter match
- call assert_equal([['abcxabc'], [[0, 1]], [150]], matchfuzzypos(['abcxabc'], 'ab'))
+ call assert_equal([['abcxabc'], [[0, 1]], [200]], matchfuzzypos(['abcxabc'], 'ab'))
" preference for sequential match
- call assert_equal([['aobncedone'], [[7, 8, 9]], [158]], matchfuzzypos(['aobncedone'], 'one'))
+ call assert_equal([['aobncedone'], [[7, 8, 9]], [233]], matchfuzzypos(['aobncedone'], 'one'))
" best recursive match
- call assert_equal([['xoone'], [[2, 3, 4]], [168]], matchfuzzypos(['xoone'], 'one'))
+ call assert_equal([['xoone'], [[2, 3, 4]], [243]], matchfuzzypos(['xoone'], 'one'))
" match multiple words (separated by space)
- call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]], [369]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo'))
+ call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]], [519]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo'))
call assert_equal([[], [], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo', {'matchseq': 1}))
- call assert_equal([['foo bar baz'], [[0, 1, 2, 8, 9, 10]], [369]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('foo baz'))
- call assert_equal([['foo bar baz'], [[0, 1, 2, 3, 4, 5, 10]], [326]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('foo baz', {'matchseq': 1}))
+ call assert_equal([['foo bar baz'], [[0, 1, 2, 8, 9, 10]], [519]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('foo baz'))
+ call assert_equal([['foo bar baz'], [[0, 1, 2, 3, 4, 5, 10]], [476]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('foo baz', {'matchseq': 1}))
call assert_equal([[], [], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('one two'))
call assert_equal([[], [], []], ['foo bar']->matchfuzzypos(" \t "))
- call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]], [657]], ['grace']->matchfuzzypos('race ace grace'))
+ call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]], [1057]], ['grace']->matchfuzzypos('race ace grace'))
let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}]
- call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]],
+ call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [267]],
\ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}}))
- call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]],
+ call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [267]],
\ matchfuzzypos(l, 'cam', {'key' : 'val'}))
call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> v.val}}))
call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'key' : 'val'}))
@@ -159,6 +161,18 @@ func Test_matchfuzzypos()
" Nvim doesn't have null functions
" call assert_fails("let x = matchfuzzypos(l, 'foo', {'text_cb' : test_null_function()})", 'E475:')
+ " case match
+ call assert_equal([['Match', 'match'], [[0, 1], [0, 1]], [202, 177]], matchfuzzypos(['match', 'Match'], 'Ma'))
+ call assert_equal([['match', 'Match'], [[0, 1], [0, 1]], [202, 177]], matchfuzzypos(['Match', 'match'], 'ma'))
+ " CamelCase has high weight even case match
+ call assert_equal(['MyTestCase', 'mytestcase'], matchfuzzy(['mytestcase', 'MyTestCase'], 'mtc'))
+ call assert_equal(['MyTestCase', 'mytestcase'], matchfuzzy(['MyTestCase', 'mytestcase'], 'mtc'))
+ call assert_equal(['MyTest', 'Mytest', 'mytest', ],matchfuzzy(['Mytest', 'mytest', 'MyTest'], 'MyT'))
+ call assert_equal(['CamelCaseMatchIngAlg', 'camelCaseMatchingAlg', 'camelcasematchingalg'],
+ \ matchfuzzy(['CamelCaseMatchIngAlg', 'camelcasematchingalg', 'camelCaseMatchingAlg'], 'CamelCase'))
+ call assert_equal(['CamelCaseMatchIngAlg', 'camelCaseMatchingAlg', 'camelcasematchingalg'],
+ \ matchfuzzy(['CamelCaseMatchIngAlg', 'camelcasematchingalg', 'camelCaseMatchingAlg'], 'CamelcaseM'))
+
let l = [{'id' : 5, 'name' : 'foo'}, {'id' : 6, 'name' : []}, {'id' : 7}]
call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : 'name'})", 'E730:')
endfunc
@@ -209,12 +223,12 @@ func Test_matchfuzzypos_mbyte()
call assert_equal([['ンヹㄇヺヴ'], [[1, 3]], [88]], matchfuzzypos(['ンヹㄇヺヴ'], 'ヹヺ'))
" reverse the order of characters
call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ'))
- call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]], [222, 113]],
+ call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]], [252, 143]],
\ matchfuzzypos(['αβΩxxx', 'xαxβxΩx'], 'αβΩ'))
call assert_equal([['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'πbπ'],
- \ [[0, 1], [0, 1], [0, 1], [0, 2]], [151, 148, 145, 110]],
+ \ [[0, 1], [0, 1], [0, 1], [0, 2]], [176, 173, 170, 135]],
\ matchfuzzypos(['πbπ', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ'))
- call assert_equal([['ααααααα'], [[0, 1, 2]], [191]],
+ call assert_equal([['ααααααα'], [[0, 1, 2]], [216]],
\ matchfuzzypos(['ααααααα'], 'ααα'))
call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇ', 'ŗŝţ'], 'fffifl'))
@@ -227,10 +241,10 @@ func Test_matchfuzzypos_mbyte()
call assert_equal([[], [], []], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 돼지']->matchfuzzypos('파란 하늘'))
" match in a long string
- call assert_equal([[repeat('ぶ', 300) .. 'ẼẼẼ'], [[300, 301, 302]], [-135]],
+ call assert_equal([[repeat('ぶ', 300) .. 'ẼẼẼ'], [[300, 301, 302]], [-110]],
\ matchfuzzypos([repeat('ぶ', 300) .. 'ẼẼẼ'], 'ẼẼẼ'))
" preference for camel case match
- call assert_equal([['xѳѵҁxxѳѴҁ'], [[6, 7, 8]], [189]], matchfuzzypos(['xѳѵҁxxѳѴҁ'], 'ѳѵҁ'))
+ call assert_equal([['xѳѵҁxxѳѴҁ'], [[6, 7, 8]], [219]], matchfuzzypos(['xѳѵҁxxѳѴҁ'], 'ѳѵҁ'))
" preference for match after a separator (_ or space)
call assert_equal([['xちだx_ちだ'], [[5, 6]], [145]], matchfuzzypos(['xちだx_ちだ'], 'ちだ'))
" preference for leading letter match
diff --git a/test/old/testdir/test_messages.vim b/test/old/testdir/test_messages.vim
index ac5184645f..bfead20142 100644
--- a/test/old/testdir/test_messages.vim
+++ b/test/old/testdir/test_messages.vim
@@ -216,6 +216,7 @@ endfunc
" Test more-prompt (see :help more-prompt).
func Test_message_more()
CheckRunVimInTerminal
+
let buf = RunVimInTerminal('', {'rows': 6})
call term_sendkeys(buf, ":call setline(1, range(1, 100))\n")
@@ -611,4 +612,50 @@ func Test_cmdheight_zero()
tabonly
endfunc
+func Test_messagesopt_history()
+ " After setting 'messagesopt' "history" to 2 and outputting a message 4 times
+ " with :echomsg, is the number of output lines of :messages 2?
+ set messagesopt=hit-enter,history:2
+ echomsg 'foo'
+ echomsg 'bar'
+ echomsg 'baz'
+ echomsg 'foobar'
+ call assert_equal(['baz', 'foobar'], GetMessages())
+
+ " When the number of messages is 10 and 'messagesopt' "history" is changed to
+ " 5, is the number of output lines of :messages 5?
+ set messagesopt=hit-enter,history:10
+ for num in range(1, 10)
+ echomsg num
+ endfor
+ set messagesopt=hit-enter,history:5
+ call assert_equal(5, len(GetMessages()))
+
+ " Check empty list
+ set messagesopt=hit-enter,history:0
+ call assert_true(empty(GetMessages()))
+
+ set messagesopt&
+endfunc
+
+func Test_messagesopt_wait()
+ CheckRunVimInTerminal
+
+ let buf = RunVimInTerminal('', {'rows': 6, 'cols': 45})
+ call term_sendkeys(buf, ":set cmdheight=1\n")
+
+ " Check hit-enter prompt
+ call term_sendkeys(buf, ":set messagesopt=hit-enter,history:500\n")
+ call term_sendkeys(buf, ":echo 'foo' | echo 'bar' | echo 'baz'\n")
+ call WaitForAssert({-> assert_equal('Press ENTER or type command to continue', term_getline(buf, 6))})
+
+ " Check no hit-enter prompt when "wait:" is set
+ call term_sendkeys(buf, ":set messagesopt=wait:100,history:500\n")
+ call term_sendkeys(buf, ":echo 'foo' | echo 'bar' | echo 'baz'\n")
+ call WaitForAssert({-> assert_equal(' 0,0-1 All', term_getline(buf, 6))})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim
index c89e73bada..1d9609cbe1 100644
--- a/test/old/testdir/test_normal.vim
+++ b/test/old/testdir/test_normal.vim
@@ -1338,11 +1338,27 @@ func Test_scroll_in_ex_mode()
call writefile(['done'], 'Xdone')
qa!
END
- call writefile(lines, 'Xscript')
+ call writefile(lines, 'Xscript', 'D')
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
+
+func Test_scroll_and_paste_in_ex_mode()
+ throw 'Skipped: does not work when Nvim is run from :!'
+ " This used to crash because of moving cursor to line 0.
+ let lines =<< trim END
+ v/foo/vi|YY9PYQ
+ v/bar/vi|YY9PYQ
+ v/bar/exe line('.') == 1 ? "vi|Y\<C-B>9PYQ" : "vi|YQ"
+ call writefile(['done'], 'Xdone')
+ qa!
+ END
+ call writefile(lines, 'Xscript', 'D')
+ call assert_equal(1, RunVim([], [], '-u NONE -i NONE -n -X -Z -e -s -S Xscript'))
+ call assert_equal(['done'], readfile('Xdone'))
+
call delete('Xdone')
endfunc
@@ -4303,4 +4319,5 @@ func Test_normal_go()
bwipe!
endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim
index b6bdb1be52..2479f0ca51 100644
--- a/test/old/testdir/test_options.vim
+++ b/test/old/testdir/test_options.vim
@@ -496,7 +496,7 @@ func Test_set_completion_string_values()
" but don't exhaustively validate their results.
call assert_equal('single', getcompletion('set ambw=', 'cmdline')[0])
call assert_match('light\|dark', getcompletion('set bg=', 'cmdline')[1])
- call assert_equal('indent', getcompletion('set backspace=', 'cmdline')[0])
+ call assert_equal('indent,eol,start', getcompletion('set backspace=', 'cmdline')[0])
call assert_equal('yes', getcompletion('set backupcopy=', 'cmdline')[1])
call assert_equal('backspace', getcompletion('set belloff=', 'cmdline')[1])
call assert_equal('min:', getcompletion('set briopt=', 'cmdline')[1])
@@ -504,7 +504,8 @@ func Test_set_completion_string_values()
call assert_equal('current', getcompletion('set browsedir=', 'cmdline')[1])
endif
call assert_equal('unload', getcompletion('set bufhidden=', 'cmdline')[1])
- call assert_equal('nowrite', getcompletion('set buftype=', 'cmdline')[1])
+ "call assert_equal('nowrite', getcompletion('set buftype=', 'cmdline')[1])
+ call assert_equal('help', getcompletion('set buftype=', 'cmdline')[1])
call assert_equal('internal', getcompletion('set casemap=', 'cmdline')[1])
if exists('+clipboard')
" call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1])
@@ -644,6 +645,10 @@ func Test_set_completion_string_values()
" call feedkeys(":set hl=8b i\<Left>\<Left>\<Tab>\<C-B>\"\<CR>", 'xt')
" call assert_equal("\"set hl=8bi i", @:)
+ " messagesopt
+ call assert_equal(['history:', 'hit-enter', 'wait:'],
+ \ getcompletion('set messagesopt+=', 'cmdline')->sort())
+
"
" Test flag lists
"
@@ -706,6 +711,10 @@ func Test_set_completion_string_values()
" Test empty option
set diffopt=
call assert_equal([], getcompletion('set diffopt-=', 'cmdline'))
+ " Test all possible values
+ call assert_equal(['filler', 'context:', 'iblank', 'icase', 'iwhite', 'iwhiteall', 'iwhiteeol', 'horizontal',
+ \ 'vertical', 'closeoff', 'hiddenoff', 'foldcolumn:', 'followwrap', 'internal', 'indent-heuristic', 'algorithm:', 'linematch:'],
+ \ getcompletion('set diffopt=', 'cmdline'))
set diffopt&
" Test escaping output
@@ -743,7 +752,6 @@ func Test_set_option_errors()
call assert_fails('set backupcopy=', 'E474:')
call assert_fails('set regexpengine=3', 'E474:')
call assert_fails('set history=10001', 'E474:')
- call assert_fails('set msghistory=10001', 'E474:')
call assert_fails('set numberwidth=21', 'E474:')
call assert_fails('set colorcolumn=-a', 'E474:')
call assert_fails('set colorcolumn=a', 'E474:')
@@ -757,7 +765,6 @@ func Test_set_option_errors()
endif
call assert_fails('set helpheight=-1', 'E487:')
call assert_fails('set history=-1', 'E487:')
- call assert_fails('set msghistory=-1', 'E487:')
call assert_fails('set report=-1', 'E487:')
call assert_fails('set shiftwidth=-1', 'E487:')
call assert_fails('set sidescroll=-1', 'E487:')
@@ -2252,16 +2259,57 @@ func Test_opt_default()
call assert_equal('vt', &formatoptions)
set formatoptions&vim
call assert_equal('tcq', &formatoptions)
+
+ call assert_equal('ucs-bom,utf-8,default,latin1', &fencs)
+ set fencs=latin1
+ set fencs&
+ call assert_equal('ucs-bom,utf-8,default,latin1', &fencs)
+ set fencs=latin1
+ set all&
+ call assert_equal('ucs-bom,utf-8,default,latin1', &fencs)
endfunc
" Test for the 'cmdheight' option
-func Test_cmdheight()
+func Test_opt_cmdheight()
%bw!
let ht = &lines
set cmdheight=9999
call assert_equal(1, winheight(0))
call assert_equal(ht - 1, &cmdheight)
set cmdheight&
+
+ " The status line should be taken into account.
+ set laststatus=2
+ set cmdheight=9999
+ call assert_equal(ht - 2, &cmdheight)
+ set cmdheight& laststatus=1 " Accommodate Nvim default
+
+ " The tabline should be taken into account only non-GUI.
+ set showtabline=2
+ set cmdheight=9999
+ if has('gui_running')
+ call assert_equal(ht - 1, &cmdheight)
+ else
+ call assert_equal(ht - 2, &cmdheight)
+ endif
+ set cmdheight& showtabline&
+
+ " The 'winminheight' should be taken into account.
+ set winheight=3 winminheight=3
+ split
+ set cmdheight=9999
+ call assert_equal(ht - 8, &cmdheight)
+ %bw!
+ set cmdheight& winminheight& winheight&
+
+ " Only the windows in the current tabpage are taken into account.
+ set winheight=3 winminheight=3 showtabline=0
+ split
+ tabnew
+ set cmdheight=9999
+ call assert_equal(ht - 3, &cmdheight)
+ %bw!
+ set cmdheight& winminheight& winheight& showtabline&
endfunc
" To specify a control character as an option value, '^' can be used
@@ -2476,6 +2524,7 @@ func Test_string_option_revert_on_failure()
\ ['lispoptions', 'expr:1', 'a123'],
\ ['listchars', 'tab:->', 'tab:'],
\ ['matchpairs', '<:>', '<:'],
+ \ ['messagesopt', 'hit-enter,history:100', 'a123'],
\ ['mkspellmem', '100000,1000,100', '100000'],
\ ['mouse', 'nvi', 'z'],
\ ['mousemodel', 'extend', 'a123'],
diff --git a/test/old/testdir/test_perl.vim b/test/old/testdir/test_perl.vim
index c08a042dae..2d7f8fdc10 100644
--- a/test/old/testdir/test_perl.vim
+++ b/test/old/testdir/test_perl.vim
@@ -316,7 +316,10 @@ VIM::DoCommand('let s ..= "B"')
perl << trim eof
VIM::DoCommand('let s ..= "E"')
eof
- call assert_equal('ABCDE', s)
+ perl << trimm
+VIM::DoCommand('let s ..= "F"')
+trimm
+ call assert_equal('ABCDEF', s)
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim
index 601ba6c688..e4abf978ab 100644
--- a/test/old/testdir/test_popup.vim
+++ b/test/old/testdir/test_popup.vim
@@ -748,17 +748,11 @@ func Test_popup_and_preview_autocommand()
bw!
endfunc
-func Test_popup_and_previewwindow_dump()
+func s:run_popup_and_previewwindow_dump(lines, dumpfile)
CheckScreendump
CheckFeature quickfix
- let lines =<< trim END
- set previewheight=9
- silent! pedit
- call setline(1, map(repeat(["ab"], 10), "v:val .. v:key"))
- exec "norm! G\<C-E>\<C-E>"
- END
- call writefile(lines, 'Xscript')
+ call writefile(a:lines, 'Xscript', 'D')
let buf = RunVimInTerminal('-S Xscript', {})
" wait for the script to finish
@@ -768,11 +762,30 @@ func Test_popup_and_previewwindow_dump()
call term_sendkeys(buf, "o")
call TermWait(buf, 50)
call term_sendkeys(buf, "\<C-X>\<C-N>")
- call VerifyScreenDump(buf, 'Test_popup_and_previewwindow_01', {})
+ call VerifyScreenDump(buf, a:dumpfile, {})
call term_sendkeys(buf, "\<Esc>u")
call StopVimInTerminal(buf)
- call delete('Xscript')
+endfunc
+
+func Test_popup_and_previewwindow_dump_pedit()
+ let lines =<< trim END
+ set previewheight=9
+ silent! pedit
+ call setline(1, map(repeat(["ab"], 10), "v:val .. v:key"))
+ exec "norm! G\<C-E>\<C-E>"
+ END
+ call s:run_popup_and_previewwindow_dump(lines, 'Test_popup_and_previewwindow_pedit')
+endfunc
+
+func Test_popup_and_previewwindow_dump_pbuffer()
+ let lines =<< trim END
+ set previewheight=9
+ silent! pbuffer
+ call setline(1, map(repeat(["ab"], 10), "v:val .. v:key"))
+ exec "norm! G\<C-E>\<C-E>\<C-E>"
+ END
+ call s:run_popup_and_previewwindow_dump(lines, 'Test_popup_and_previewwindow_pbuffer')
endfunc
func Test_balloon_split()
@@ -1506,6 +1519,39 @@ func Test_pum_highlights_match()
call StopVimInTerminal(buf)
endfunc
+func Test_pum_highlights_match_with_abbr()
+ CheckScreendump
+ let lines =<< trim END
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return {
+ \ 'words': [
+ \ { 'word': 'foobar', 'abbr': "foobar\t\t!" },
+ \ { 'word': 'foobaz', 'abbr': "foobaz\t\t!" },
+ \]}
+ endfunc
+
+ set omnifunc=Omni_test
+ set completeopt=menuone,noinsert
+ hi PmenuMatchSel ctermfg=6 ctermbg=7
+ hi PmenuMatch ctermfg=4 ctermbg=225
+ END
+ call writefile(lines, 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call TermWait(buf)
+ call term_sendkeys(buf, "i\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "foo")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_19', {})
+
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call TermWait(buf)
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_pum_user_abbr_hlgroup()
CheckScreendump
let lines =<< trim END
@@ -1675,4 +1721,168 @@ func Test_pum_completeitemalign()
call StopVimInTerminal(buf)
endfunc
+func Test_pum_keep_select()
+ CheckScreendump
+ let lines =<< trim END
+ set completeopt=menu,menuone,noinsert
+ END
+ call writefile(lines, 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call TermWait(buf)
+
+ call term_sendkeys(buf, "ggSFab\<CR>Five\<CR>find\<CR>film\<CR>\<C-X>\<C-P>")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_keep_select_01', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call TermWait(buf, 50)
+
+ call term_sendkeys(buf, "S\<C-X>\<C-P>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "F")
+ call VerifyScreenDump(buf, 'Test_pum_keep_select_02', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ call TermWait(buf, 50)
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_pum_matchins_highlight()
+ CheckScreendump
+ let lines =<< trim END
+ let g:change = 0
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ if g:change == 0
+ return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}]
+ endif
+ return [#{word: "foo", info: "info"}, #{word: "bar"}, #{word: "你好"}]
+ endfunc
+ set omnifunc=Omni_test
+ hi ComplMatchIns ctermfg=red
+ END
+ call writefile(lines, 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {})
+
+ call TermWait(buf)
+ call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_01', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ call TermWait(buf)
+ call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<C-N>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_02', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ call TermWait(buf)
+ call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<C-N>\<C-N>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_03', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ " restore after accept
+ call TermWait(buf)
+ call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<C-Y>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_04', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " restore after cancel completion
+ call TermWait(buf)
+ call term_sendkeys(buf, "Sαβγ \<C-X>\<C-O>\<Space>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_05', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " text after the inserted text shouldn't be highlighted
+ call TermWait(buf)
+ call term_sendkeys(buf, "0ea \<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_07', {})
+ call term_sendkeys(buf, "\<C-P>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_08', {})
+ call term_sendkeys(buf, "\<C-P>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_09', {})
+ call term_sendkeys(buf, "\<C-Y>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_10', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call term_sendkeys(buf, ":let g:change=1\<CR>S\<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_11', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_pum_matchins_highlight_combine()
+ CheckScreendump
+ let lines =<< trim END
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}]
+ endfunc
+ set omnifunc=Omni_test
+ hi Normal ctermbg=blue
+ hi CursorLine cterm=underline ctermbg=green
+ set cursorline
+ call setline(1, 'aaa bbb')
+ END
+ call writefile(lines, 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {})
+
+ " when ComplMatchIns is not set, CursorLine applies normally
+ call term_sendkeys(buf, "0ea \<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_01', {})
+ call term_sendkeys(buf, "\<C-E>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_02', {})
+ call term_sendkeys(buf, "\<BS>\<Esc>")
+
+ " when ComplMatchIns is set, it is applied over CursorLine
+ call TermWait(buf)
+ call term_sendkeys(buf, ":hi ComplMatchIns ctermbg=red ctermfg=yellow\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "0ea \<C-X>\<C-O>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_03', {})
+ call term_sendkeys(buf, "\<C-P>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_04', {})
+ call term_sendkeys(buf, "\<C-P>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_05', {})
+ call term_sendkeys(buf, "\<C-E>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_06', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ " Does not highlight the compl leader
+ call TermWait(buf)
+ call term_sendkeys(buf, ":set cot+=menuone,noselect\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "S\<C-X>\<C-O>f\<C-N>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_07', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ call term_sendkeys(buf, ":set cot+=fuzzy\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "S\<C-X>\<C-O>f\<C-N>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_08', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call TermWait(buf)
+
+ call term_sendkeys(buf, ":set cot-=fuzzy\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "Sf\<C-N>")
+ call VerifyScreenDump(buf, 'Test_pum_matchins_combine_09', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
+" this used to crash
+func Test_popup_completion_many_ctrlp()
+ new
+ let candidates=repeat(['a0'], 99)
+ call setline(1, candidates)
+ exe ":norm! VGg\<C-A>"
+ norm! G
+ call feedkeys("o" .. repeat("\<c-p>", 100), 'tx')
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_preview.vim b/test/old/testdir/test_preview.vim
index b7b908e761..422c50ac77 100644
--- a/test/old/testdir/test_preview.vim
+++ b/test/old/testdir/test_preview.vim
@@ -15,6 +15,20 @@ func Test_Psearch()
bwipe
endfunc
+func s:goto_preview_and_close()
+ " Go to the preview window
+ wincmd P
+ call assert_equal(1, &previewwindow)
+ call assert_equal('preview', win_gettype())
+
+ " Close preview window
+ wincmd z
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, &previewwindow)
+
+ call assert_fails('wincmd P', 'E441:')
+endfunc
+
func Test_window_preview()
CheckFeature quickfix
@@ -23,17 +37,48 @@ func Test_window_preview()
call assert_equal(2, winnr('$'))
call assert_equal(0, &previewwindow)
- " Go to the preview window
- wincmd P
- call assert_equal(1, &previewwindow)
- call assert_equal('preview', win_gettype())
+ call s:goto_preview_and_close()
+endfunc
+
+func Test_window_preview_from_pbuffer()
+ CheckFeature quickfix
+
+ call writefile(['/* some C code */'], 'Xpreview.c', 'D')
+ edit Xpreview.c
+ const buf_num = bufnr('%')
+ enew
+
+ call feedkeys(":pbuffer Xpre\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"pbuffer Xpreview.c", @:)
- " Close preview window
- wincmd z
call assert_equal(1, winnr('$'))
+ exe 'pbuffer ' . buf_num
+ call assert_equal(2, winnr('$'))
call assert_equal(0, &previewwindow)
- call assert_fails('wincmd P', 'E441:')
+ call s:goto_preview_and_close()
+
+ call assert_equal(1, winnr('$'))
+ pbuffer Xpreview.c
+ call assert_equal(2, winnr('$'))
+ call assert_equal(0, &previewwindow)
+
+ call s:goto_preview_and_close()
+endfunc
+
+func Test_window_preview_terminal()
+ CheckFeature quickfix
+ " CheckFeature terminal
+
+ " term ++curwin
+ term
+ const buf_num = bufnr('$')
+ call assert_equal(1, winnr('$'))
+ exe 'pbuffer' . buf_num
+ call assert_equal(2, winnr('$'))
+ call assert_equal(0, &previewwindow)
+
+ call s:goto_preview_and_close()
endfunc
func Test_window_preview_from_help()
diff --git a/test/old/testdir/test_python3.vim b/test/old/testdir/test_python3.vim
index c9dbc0b84e..2436587100 100644
--- a/test/old/testdir/test_python3.vim
+++ b/test/old/testdir/test_python3.vim
@@ -284,7 +284,10 @@ s+='B'
python3 << trim eof
s+='E'
eof
- call assert_equal('ABCDE', pyxeval('s'))
+ python3 << trimm
+s+='F'
+trimm
+ call assert_equal('ABCDEF', pyxeval('s'))
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_pyx3.vim b/test/old/testdir/test_pyx3.vim
index 89a3cc22ff..8dfeaff807 100644
--- a/test/old/testdir/test_pyx3.vim
+++ b/test/old/testdir/test_pyx3.vim
@@ -97,7 +97,10 @@ result+='B'
pyx << trim eof
result+='E'
eof
- call assert_equal('ABCDE', pyxeval('result'))
+ pyx << trimm
+result+='F'
+trimm
+ call assert_equal('ABCDEF', pyxeval('result'))
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_ruby.vim b/test/old/testdir/test_ruby.vim
index d4a3dc3301..94941da873 100644
--- a/test/old/testdir/test_ruby.vim
+++ b/test/old/testdir/test_ruby.vim
@@ -282,7 +282,7 @@ func Test_ruby_Vim_buffer_get()
call assert_match('Xfoo1$', rubyeval('Vim::Buffer[1].name'))
call assert_match('Xfoo2$', rubyeval('Vim::Buffer[2].name'))
call assert_fails('ruby print Vim::Buffer[3].name',
- \ "NoMethodError: undefined method `name' for nil")
+ \ "NoMethodError")
%bwipe
endfunc
@@ -364,7 +364,7 @@ func Test_ruby_Vim_evaluate_dict()
redir => l:out
ruby d = Vim.evaluate("d"); print d
redir END
- call assert_equal(['{"a"=>"foo", "b"=>123}'], split(l:out, "\n"))
+ call assert_equal(['{"a"=>"foo","b"=>123}'], split(substitute(l:out, '\s', '', 'g'), "\n"))
endfunc
" Test Vim::message({msg}) (display message {msg})
@@ -384,7 +384,7 @@ func Test_ruby_print()
call assert_equal('1.23', RubyPrint('1.23'))
call assert_equal('Hello World!', RubyPrint('"Hello World!"'))
call assert_equal('[1, 2]', RubyPrint('[1, 2]'))
- call assert_equal('{"k1"=>"v1", "k2"=>"v2"}', RubyPrint('({"k1" => "v1", "k2" => "v2"})'))
+ call assert_equal('{"k1"=>"v1","k2"=>"v2"}', substitute(RubyPrint('({"k1" => "v1", "k2" => "v2"})'), '\s', '', 'g'))
call assert_equal('true', RubyPrint('true'))
call assert_equal('false', RubyPrint('false'))
call assert_equal('', RubyPrint('nil'))
@@ -439,7 +439,10 @@ Vim.command('let s ..= "B"')
ruby << trim eof
Vim.command('let s ..= "E"')
eof
- call assert_equal('ABCDE', s)
+ruby << trimm
+Vim.command('let s ..= "F"')
+trimm
+ call assert_equal('ABCDEF', s)
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_shift.vim b/test/old/testdir/test_shift.vim
index ec357dac88..f31c5a11e6 100644
--- a/test/old/testdir/test_shift.vim
+++ b/test/old/testdir/test_shift.vim
@@ -108,10 +108,809 @@ func Test_ex_shift_errors()
call assert_fails('>!', 'E477:')
call assert_fails('<!', 'E477:')
- " call assert_fails('2,1>', 'E493:')
- call assert_fails('execute "2,1>"', 'E493:')
- " call assert_fails('2,1<', 'E493:')
- call assert_fails('execute "2,1<"', 'E493:')
+ call assert_fails('2,1>', 'E493:')
+ call assert_fails('2,1<', 'E493:')
+endfunc
+
+" Test inserting a backspace at the start of a line.
+"
+" This is to verify the proper behavior of tabstop_start() as called from
+" ins_bs().
+"
+func Test_shift_ins_bs()
+ set backspace=indent,start
+ set softtabstop=11
+
+ call setline(1, repeat(" ", 33) . "word")
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 22) . "word", getline(1))
+ call setline(1, repeat(" ", 23) . "word")
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 22) . "word", getline(1))
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 11) . "word", getline(1))
+
+ set backspace& softtabstop&
+ bw!
+endfunc
+
+" Test inserting a backspace at the start of a line, with 'varsofttabstop'.
+"
+func Test_shift_ins_bs_vartabs()
+ CheckFeature vartabs
+ set backspace=indent,start
+ set varsofttabstop=13,11,7
+
+ call setline(1, repeat(" ", 44) . "word")
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 38) . "word", getline(1))
+ call setline(1, repeat(" ", 39) . "word")
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 38) . "word", getline(1))
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 31) . "word", getline(1))
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 24) . "word", getline(1))
+ exe "norm! I\<BS>"
+ call assert_equal(repeat(" ", 13) . "word", getline(1))
+ exe "norm! I\<BS>"
+ call assert_equal( "word", getline(1))
+ exe "norm! I\<BS>"
+ call assert_equal( "word", getline(1))
+
+ set backspace& varsofttabstop&
+ bw!
+endfunc
+
+" Test the >> and << normal-mode commands.
+"
+func Test_shift_norm()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=5
+ set tabstop=7
+
+ call setline(1, " word")
+
+ " Shift by 'shiftwidth' right and left.
+
+ norm! >>
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 12) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 17) . "word", getline(1))
+
+ norm! <<
+ call assert_equal(repeat(" ", 12) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, " word")
+
+ norm! >>
+ call assert_equal(repeat(" ", 9) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 16) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 23) . "word", getline(1))
+
+ norm! <<
+ call assert_equal(repeat(" ", 16) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 9) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test the >> and << normal-mode commands, with 'vartabstop'.
+"
+func Test_shift_norm_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, " word")
+
+ norm! >>
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 38) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 49) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 60) . "word", getline(1))
+
+ norm! <<
+ call assert_equal(repeat(" ", 49) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 38) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& vartabstop&
+ bw!
+endfunc
+
+" Test the >> and << normal-mode commands with 'shiftround'.
+"
+func Test_shift_norm_round()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftround
+ set shiftwidth=5
+ set tabstop=7
+
+ call setline(1, "word")
+
+ " Shift by 'shiftwidth' right and left.
+
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 5) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 20) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 25) . "word", getline(1))
+
+ norm! <<
+ call assert_equal(repeat(" ", 20) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+ exe "norm! I "
+ norm! <<
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+
+ call setline(1, repeat(" ", 7) . "word")
+ norm! <<
+ call assert_equal(repeat(" ", 5) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ call setline(1, repeat(" ", 2) . "word")
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, "word")
+
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 14) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 28) . "word", getline(1))
+ norm! >>
+ call assert_equal(repeat(" ", 35) . "word", getline(1))
+
+ norm! <<
+ call assert_equal(repeat(" ", 28) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 14) . "word", getline(1))
+ exe "norm! I "
+ norm! <<
+ call assert_equal(repeat(" ", 14) . "word", getline(1))
+
+ call setline(1, repeat(" ", 9) . "word")
+ norm! <<
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ call setline(1, repeat(" ", 2) . "word")
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftround& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test the >> and << normal-mode commands with 'shiftround' and 'vartabstop'.
+"
+func Test_shift_norm_round_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftround
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, "word")
+
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 36) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+ exe "norm! I "
+ norm! >>
+ call assert_equal(repeat(" ", 58) . "word", getline(1))
+
+ exe "norm! I "
+ norm! <<
+ call assert_equal(repeat(" ", 58) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 36) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I "
+ norm! <<
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ exe "norm! I "
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftround& shiftwidth& vartabstop&
+ bw!
+endfunc
+
+" Test the V> and V< visual-mode commands.
+"
+" See ":help v_<" and ":help v_>". See also the last paragraph of "3. Simple
+" changes", ":help simple-change", immediately above "4. Complex changes",
+" ":help complex-change".
+"
+func Test_shift_vis()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=5
+ set tabstop=7
+
+ call setline(1, " word")
+
+ " Shift by 'shiftwidth' right and left.
+
+ norm! V>
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ norm! V2>
+ call assert_equal(repeat(" ", 17) . "word", getline(1))
+ norm! V3>
+ call assert_equal(repeat(" ", 32) . "word", getline(1))
+
+ norm! V<
+ call assert_equal(repeat(" ", 27) . "word", getline(1))
+ norm! V2<
+ call assert_equal(repeat(" ", 17) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, " word")
+
+ norm! V>
+ call assert_equal(repeat(" ", 9) . "word", getline(1))
+ norm! V2>
+ call assert_equal(repeat(" ", 23) . "word", getline(1))
+ norm! V3>
+ call assert_equal(repeat(" ", 44) . "word", getline(1))
+
+ norm! V<
+ call assert_equal(repeat(" ", 37) . "word", getline(1))
+ norm! V2<
+ call assert_equal(repeat(" ", 23) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test the V> and V< visual-mode commands, with 'vartabstop'.
+"
+" See ":help v_<" and ":help v_>". See also the last paragraph of "3. Simple
+" changes", ":help simple-change", immediately above "4. Complex changes",
+" ":help complex-change".
+"
+func Test_shift_vis_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, " word")
+
+ norm! V>
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ norm! V2>
+ call assert_equal(repeat(" ", 49) . "word", getline(1))
+ norm! V3>
+ call assert_equal(repeat(" ", 82) . "word", getline(1))
+
+ norm! V<
+ call assert_equal(repeat(" ", 71) . "word", getline(1))
+ norm! V2<
+ call assert_equal(repeat(" ", 49) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& vartabstop&
+ bw!
+endfunc
+
+" Test the V> and V< visual-mode commands with 'shiftround'.
+"
+func Test_shift_vis_round()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftround
+ set shiftwidth=5
+ set tabstop=7
+
+ call setline(1, "word")
+
+ " Shift by 'shiftwidth' right and left.
+
+ exe "norm! I "
+ norm! V>
+ call assert_equal(repeat(" ", 5) . "word", getline(1))
+ exe "norm! I "
+ norm! V2>
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ exe "norm! I "
+ norm! V3>
+ call assert_equal(repeat(" ", 30) . "word", getline(1))
+
+ exe "norm! I "
+ norm! V2<
+ call assert_equal(repeat(" ", 25) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 5) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, "word")
+
+ exe "norm! I "
+ norm! V>
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ exe "norm! I "
+ norm! V2>
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ exe "norm! I "
+ norm! V3>
+ call assert_equal(repeat(" ", 42) . "word", getline(1))
+
+ exe "norm! I "
+ norm! V<
+ call assert_equal(repeat(" ", 42) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 35) . "word", getline(1))
+ norm! V2<
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ call setline(1, " word")
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+
+ set expandtab& shiftround& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test the V> and V< visual-mode commands with 'shiftround' and 'vartabstop'.
+"
+func Test_shift_vis_round_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftround
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, "word")
+
+ exe "norm! I "
+ norm! V>
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I "
+ norm! V3>
+ call assert_equal(repeat(" ", 58) . "word", getline(1))
+
+ exe "norm! I "
+ norm! V2<
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+ exe "norm! I "
+ norm! V3<
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I "
+ norm! V3<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ exe "norm! I "
+ norm! V<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftround& shiftwidth& vartabstop&
+ bw!
+endfunc
+
+" Test the :> and :< ex-mode commands.
+"
+func Test_shift_ex()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=5
+ set tabstop=7
+
+ call setline(1, " word")
+
+ " Shift by 'shiftwidth' right and left.
+
+ >
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ >>
+ call assert_equal(repeat(" ", 17) . "word", getline(1))
+ >>>
+ call assert_equal(repeat(" ", 32) . "word", getline(1))
+
+ <<<<
+ call assert_equal(repeat(" ", 12) . "word", getline(1))
+ <
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ <
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ <
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, " word")
+
+ >
+ call assert_equal(repeat(" ", 9) . "word", getline(1))
+ >>
+ call assert_equal(repeat(" ", 23) . "word", getline(1))
+ >>>
+ call assert_equal(repeat(" ", 44) . "word", getline(1))
+
+ <<<<
+ call assert_equal(repeat(" ", 16) . "word", getline(1))
+ <<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ <
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test the :> and :< ex-mode commands, with vartabstop.
+"
+func Test_shift_ex_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, " word")
+
+ >
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ >>
+ call assert_equal(repeat(" ", 49) . "word", getline(1))
+ >>>
+ call assert_equal(repeat(" ", 82) . "word", getline(1))
+
+ <<<<
+ call assert_equal(repeat(" ", 38) . "word", getline(1))
+ <<
+ call assert_equal(repeat(" ", 2) . "word", getline(1))
+ <
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& vartabstop&
+ bw!
+endfunc
+
+" Test the :> and :< ex-mode commands with 'shiftround'.
+"
+func Test_shift_ex_round()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftround
+ set shiftwidth=5
+ set tabstop=7
+
+ call setline(1, "word")
+
+ " Shift by 'shiftwidth' right and left.
+
+ exe "norm! I "
+ >
+ call assert_equal(repeat(" ", 5) . "word", getline(1))
+ exe "norm! I "
+ >>
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ exe "norm! I "
+ >>>
+ call assert_equal(repeat(" ", 30) . "word", getline(1))
+
+ exe "norm! I "
+ <<<<
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ exe "norm! I "
+ <<
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+ <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ >>
+ <<<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, "word")
+
+ exe "norm! I "
+ >
+ call assert_equal(repeat(" ", 7) . "word", getline(1))
+ exe "norm! I "
+ >>
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ exe "norm! I "
+ >>>
+ call assert_equal(repeat(" ", 42) . "word", getline(1))
+
+ exe "norm! I "
+ <<<<
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ exe "norm! I "
+ <<
+ call assert_equal(repeat(" ", 14) . "word", getline(1))
+ exe "norm! I "
+ <<<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ >>
+ <<<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftround& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test the :> and :< ex-mode commands with 'shiftround' and 'vartabstop'.
+"
+func Test_shift_ex_round_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftround
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, "word")
+
+ exe "norm! I "
+ >
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I "
+ >>
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+ >>>
+ call assert_equal(repeat(" ", 80) . "word", getline(1))
+
+ <<<<
+ call assert_equal(repeat(" ", 36) . "word", getline(1))
+ exe "norm! I "
+ <<
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I "
+ <<
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ <
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftround& shiftwidth& vartabstop&
+ bw!
+endfunc
+
+" Test shifting lines with <C-T> and <C-D>.
+"
+func Test_shift_ins()
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=5
+ set tabstop=7
+
+ " Shift by 'shiftwidth' right and left.
+
+ call setline(1, repeat(" ", 7) . "word")
+ exe "norm! 9|i\<C-T>"
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+ exe "norm! A\<C-T>"
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ exe "norm! I \<C-T>"
+ call assert_equal(repeat(" ", 20) . "word", getline(1))
+
+ exe "norm! I \<C-D>"
+ call assert_equal(repeat(" ", 20) . "word", getline(1))
+ exe "norm! I "
+ exe "norm! 24|i\<C-D>"
+ call assert_equal(repeat(" ", 20) . "word", getline(1))
+ exe "norm! A\<C-D>"
+ call assert_equal(repeat(" ", 15) . "word", getline(1))
+ exe "norm! I "
+ exe "norm! A\<C-D>\<C-D>"
+ call assert_equal(repeat(" ", 10) . "word", getline(1))
+ exe "norm! I\<C-D>\<C-D>\<C-D>"
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ exe "norm! I\<C-D>"
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ " Shift by 'tabstop' right and left.
+
+ set shiftwidth=0
+ call setline(1, "word")
+
+ call setline(1, repeat(" ", 9) . "word")
+ exe "norm! 11|i\<C-T>"
+ call assert_equal(repeat(" ", 14) . "word", getline(1))
+ exe "norm! A\<C-T>"
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ exe "norm! I \<C-T>"
+ call assert_equal(repeat(" ", 28) . "word", getline(1))
+
+ exe "norm! I \<C-D>"
+ call assert_equal(repeat(" ", 28) . "word", getline(1))
+ exe "norm! I "
+ exe "norm! 32|i\<C-D>"
+ call assert_equal(repeat(" ", 28) . "word", getline(1))
+ exe "norm! A\<C-D>"
+ call assert_equal(repeat(" ", 21) . "word", getline(1))
+ exe "norm! I "
+ exe "norm! A\<C-D>\<C-D>"
+ call assert_equal(repeat(" ", 14) . "word", getline(1))
+ exe "norm! I\<C-D>\<C-D>\<C-D>"
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ exe "norm! I\<C-D>"
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& tabstop&
+ bw!
+endfunc
+
+" Test shifting lines with <C-T> and <C-D>, with 'vartabstop'.
+"
+func Test_shift_ins_vartabs()
+ CheckFeature vartabs
+ set expandtab " Don't want to worry about tabs vs. spaces in
+ " results.
+
+ set shiftwidth=0
+ set vartabstop=19,17,11
+
+ " Shift by 'vartabstop' right and left.
+
+ call setline(1, "word")
+
+ call setline(1, repeat(" ", 9) . "word")
+ exe "norm! 11|i\<C-T>"
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! A\<C-T>"
+ call assert_equal(repeat(" ", 36) . "word", getline(1))
+ exe "norm! I \<C-T>"
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+
+ exe "norm! I \<C-D>"
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+ exe "norm! I "
+ exe "norm! 51|i\<C-D>"
+ call assert_equal(repeat(" ", 47) . "word", getline(1))
+ exe "norm! A\<C-D>"
+ call assert_equal(repeat(" ", 36) . "word", getline(1))
+ exe "norm! I "
+ exe "norm! A\<C-D>\<C-D>"
+ call assert_equal(repeat(" ", 19) . "word", getline(1))
+ exe "norm! I\<C-D>\<C-D>\<C-D>"
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+ exe "norm! I\<C-D>"
+ call assert_equal(repeat(" ", 0) . "word", getline(1))
+
+ set expandtab& shiftwidth& vartabstop&
+ bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_spell.vim b/test/old/testdir/test_spell.vim
index bdd8a673fd..a5ae653369 100644
--- a/test/old/testdir/test_spell.vim
+++ b/test/old/testdir/test_spell.vim
@@ -471,7 +471,9 @@ func Test_spellsuggest_option_number()
\ .. "Change \"baord\" to:\n"
\ .. " 1 \"board\"\n"
\ .. " 2 \"bard\"\n"
- \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ "\ Nvim: Prompt message is sent to cmdline prompt.
+ "\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ \ , a)
set spell spellsuggest=0
call assert_equal("\nSorry, no suggestions", execute('norm $z='))
@@ -509,7 +511,9 @@ func Test_spellsuggest_option_expr()
\ .. " 1 \"BARD\"\n"
\ .. " 2 \"BOARD\"\n"
\ .. " 3 \"BROAD\"\n"
- \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ "\ Nvim: Prompt message is sent to cmdline prompt.
+ "\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ \ , a)
" With verbose, z= should show the score i.e. word length with
" our SpellSuggest() function.
@@ -521,7 +525,9 @@ func Test_spellsuggest_option_expr()
\ .. " 1 \"BARD\" (4 - 0)\n"
\ .. " 2 \"BOARD\" (5 - 0)\n"
\ .. " 3 \"BROAD\" (5 - 0)\n"
- \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ "\ Nvim: Prompt message is sent to cmdline prompt.
+ "\ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a)
+ \ , a)
set spell& spellsuggest& verbose&
bwipe!
diff --git a/test/old/testdir/test_stacktrace.vim b/test/old/testdir/test_stacktrace.vim
new file mode 100644
index 0000000000..9e1f51e1f6
--- /dev/null
+++ b/test/old/testdir/test_stacktrace.vim
@@ -0,0 +1,142 @@
+" Test for getstacktrace() and v:stacktrace
+
+source vim9.vim
+
+let s:thisfile = expand('%:p')
+let s:testdir = s:thisfile->fnamemodify(':h')
+
+func Filepath(name)
+ return s:testdir .. '/' .. a:name
+endfunc
+
+func AssertStacktrace(expect, actual)
+ call assert_equal(Filepath('runtest.vim'), a:actual[0]['filepath'])
+ call assert_equal(a:expect, a:actual[-len(a:expect):])
+endfunc
+
+func Test_getstacktrace()
+ let g:stacktrace = []
+ let lines1 =<< trim [SCRIPT]
+ " Xscript1
+ source Xscript2
+ func Xfunc1()
+ " Xfunc1
+ call Xfunc2()
+ endfunc
+ [SCRIPT]
+ let lines2 =<< trim [SCRIPT]
+ " Xscript2
+ func Xfunc2()
+ " Xfunc2
+ let g:stacktrace = getstacktrace()
+ endfunc
+ [SCRIPT]
+ call writefile(lines1, 'Xscript1', 'D')
+ call writefile(lines2, 'Xscript2', 'D')
+ source Xscript1
+ call Xfunc1()
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_getstacktrace'), lnum: 37, filepath: s:thisfile},
+ \ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
+ \ ], g:stacktrace)
+ unlet g:stacktrace
+endfunc
+
+func Test_getstacktrace_event()
+ let g:stacktrace = []
+ let lines1 =<< trim [SCRIPT]
+ " Xscript1
+ func Xfunc()
+ " Xfunc
+ let g:stacktrace = getstacktrace()
+ endfunc
+ augroup test_stacktrace
+ autocmd SourcePre * call Xfunc()
+ augroup END
+ [SCRIPT]
+ let lines2 =<< trim [SCRIPT]
+ " Xscript2
+ [SCRIPT]
+ call writefile(lines1, 'Xscript1', 'D')
+ call writefile(lines2, 'Xscript2', 'D')
+ source Xscript1
+ source Xscript2
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_getstacktrace_event'), lnum: 64, filepath: s:thisfile},
+ \ #{event: 'SourcePre Autocommands for "*"', lnum: 7, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc'), lnum: 4, filepath: Filepath('Xscript1')},
+ \ ], g:stacktrace)
+ augroup test_stacktrace
+ autocmd!
+ augroup END
+ unlet g:stacktrace
+endfunc
+
+func Test_vstacktrace()
+ let lines1 =<< trim [SCRIPT]
+ " Xscript1
+ source Xscript2
+ func Xfunc1()
+ " Xfunc1
+ call Xfunc2()
+ endfunc
+ [SCRIPT]
+ let lines2 =<< trim [SCRIPT]
+ " Xscript2
+ func Xfunc2()
+ " Xfunc2
+ throw 'Exception from Xfunc2'
+ endfunc
+ [SCRIPT]
+ call writefile(lines1, 'Xscript1', 'D')
+ call writefile(lines2, 'Xscript2', 'D')
+ source Xscript1
+ call assert_equal([], v:stacktrace)
+ try
+ call Xfunc1()
+ catch
+ let stacktrace = v:stacktrace
+ try
+ call Xfunc1()
+ catch
+ let stacktrace_inner = v:stacktrace
+ endtry
+ let stacktrace_after = v:stacktrace " should be restored by the exception stack to the previous one
+ endtry
+ call assert_equal([], v:stacktrace)
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_vstacktrace'), lnum: 97, filepath: s:thisfile},
+ \ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
+ \ ], stacktrace)
+ call AssertStacktrace([
+ \ #{funcref: funcref('Test_vstacktrace'), lnum: 101, filepath: s:thisfile},
+ \ #{funcref: funcref('Xfunc1'), lnum: 5, filepath: Filepath('Xscript1')},
+ \ #{funcref: funcref('Xfunc2'), lnum: 4, filepath: Filepath('Xscript2')},
+ \ ], stacktrace_inner)
+ call assert_equal(stacktrace, stacktrace_after)
+endfunc
+
+func Test_stacktrace_vim9()
+ let lines =<< trim [SCRIPT]
+ var stacktrace = getstacktrace()
+ assert_notequal([], stacktrace)
+ for d in stacktrace
+ assert_true(has_key(d, 'lnum'))
+ endfor
+ try
+ throw 'Exception from s:Func'
+ catch
+ assert_notequal([], v:stacktrace)
+ assert_equal(len(stacktrace), len(v:stacktrace))
+ for d in v:stacktrace
+ assert_true(has_key(d, 'lnum'))
+ endfor
+ endtry
+ call assert_equal([], v:stacktrace)
+ [SCRIPT]
+ call CheckDefSuccess(lines)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_statusline.vim b/test/old/testdir/test_statusline.vim
index c8162ced07..c9f79dfef7 100644
--- a/test/old/testdir/test_statusline.vim
+++ b/test/old/testdir/test_statusline.vim
@@ -220,6 +220,10 @@ func Test_statusline()
wincmd j
call assert_match('^\[Preview\],PRV\s*$', s:get_statusline())
pclose
+ pbuffer
+ wincmd j
+ call assert_match('^\[Preview\],PRV\s*$', s:get_statusline())
+ pclose
" %y: Type of file in the buffer, e.g., "[vim]". See 'filetype'.
" %Y: Type of file in the buffer, e.g., ",VIM". See 'filetype'.
diff --git a/test/old/testdir/test_tagjump.vim b/test/old/testdir/test_tagjump.vim
index 470c5c43b4..efc5e4cebe 100644
--- a/test/old/testdir/test_tagjump.vim
+++ b/test/old/testdir/test_tagjump.vim
@@ -1231,8 +1231,10 @@ func Test_tselect_listing()
2 FS v first Xfoo
typeref:typename:char
2
-Type number and <Enter> (q or empty cancels):
[DATA]
+" Type number and <Enter> (q or empty cancels):
+" Nvim: Prompt message is sent to cmdline prompt.
+
call assert_equal(expected, l)
call delete('Xtags')
diff --git a/test/old/testdir/test_termdebug.vim b/test/old/testdir/test_termdebug.vim
index eb88ea6f5f..6ff233fff0 100644
--- a/test/old/testdir/test_termdebug.vim
+++ b/test/old/testdir/test_termdebug.vim
@@ -391,7 +391,8 @@ endfunc
function Test_termdebug_save_restore_variables()
" saved mousemodel
- let &mousemodel=''
+ "let &mousemodel=''
+ let &mousemodel='extend'
" saved keys
nnoremap K :echo "hello world!"<cr>
@@ -414,7 +415,8 @@ function Test_termdebug_save_restore_variables()
quit!
call WaitForAssert({-> assert_equal(1, winnr('$'))})
- call assert_true(empty(&mousemodel))
+ "call assert_true(empty(&mousemodel))
+ call assert_equal(&mousemodel, 'extend')
call assert_true(empty(expected_map_minus))
call assert_equal(expected_map_K.rhs, maparg('K', 'n', 0, 1).rhs)
diff --git a/test/old/testdir/test_user_func.vim b/test/old/testdir/test_user_func.vim
index 3c24412eb7..b1543c8f24 100644
--- a/test/old/testdir/test_user_func.vim
+++ b/test/old/testdir/test_user_func.vim
@@ -380,7 +380,7 @@ func Test_script_local_func()
" 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 assert_fails('let x = call("<SID>Xfunc", [])', ['E81:', 'E117:'])
:call writefile(v:errors, 'Xresult')
:qall
@@ -421,12 +421,48 @@ func Test_func_def_error()
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 writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript', 'D')
call assert_fails('source Xscript', 'E746:')
- call delete('Xscript')
" Try to list functions using an invalid search pattern
call assert_fails('function /\%(/', 'E53:')
+
+ " Use a script-local function to cover uf_name_exp.
+ func s:TestRedefine(arg1 = 1, arg2 = 10)
+ let caught_E122 = 0
+ try
+ func s:TestRedefine(arg1 = 1, arg2 = 10)
+ endfunc
+ catch /E122:/
+ let caught_E122 = 1
+ endtry
+ call assert_equal(1, caught_E122)
+
+ let caught_E127 = 0
+ try
+ func! s:TestRedefine(arg1 = 1, arg2 = 10)
+ endfunc
+ catch /E127:/
+ let caught_E127 = 1
+ endtry
+ call assert_equal(1, caught_E127)
+
+ " The failures above shouldn't cause heap-use-after-free here.
+ return [a:arg1 + a:arg2, expand('<stack>')]
+ endfunc
+
+ let stacks = []
+ " Call the function twice.
+ " Failing to redefine a function shouldn't clear its argument list.
+ for i in range(2)
+ let [val, stack] = s:TestRedefine(1000)
+ call assert_equal(1010, val)
+ call assert_match(expand('<SID>') .. 'TestRedefine\[20\]$', stack)
+ call add(stacks, stack)
+ endfor
+ call assert_equal(stacks[0], stacks[1])
+
+ delfunc s:TestRedefine
endfunc
" Test for deleting a function
@@ -910,4 +946,36 @@ func Test_func_curly_brace_invalid_name()
delfunc Fail
endfunc
+func Test_func_return_in_try_verbose()
+ func TryReturnList()
+ try
+ return [1, 2, 3]
+ endtry
+ endfunc
+ func TryReturnNumber()
+ try
+ return 123
+ endtry
+ endfunc
+ func TryReturnOverlongString()
+ try
+ return repeat('a', 9999)
+ endtry
+ endfunc
+
+ " This should not cause heap-use-after-free
+ call assert_match('\n:return \[1, 2, 3\] made pending\n',
+ \ execute('14verbose call TryReturnList()'))
+ " This should not cause stack-use-after-scope
+ call assert_match('\n:return 123 made pending\n',
+ \ execute('14verbose call TryReturnNumber()'))
+ " An overlong string is truncated
+ call assert_match('\n:return a\{100,}\.\.\.',
+ \ execute('14verbose call TryReturnOverlongString()'))
+
+ delfunc TryReturnList
+ delfunc TryReturnNumber
+ delfunc TryReturnOverlongString
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim
index e25327ddd4..328ac502bf 100644
--- a/test/old/testdir/test_visual.vim
+++ b/test/old/testdir/test_visual.vim
@@ -470,7 +470,7 @@ func Test_Visual_Block()
\ "\t{",
\ "\t}"], getline(1, '$'))
- close!
+ bw!
endfunc
" Test for 'p'ut in visual block mode
@@ -1082,7 +1082,7 @@ func Test_star_register()
delmarks < >
call assert_fails('*yank', 'E20:')
- close!
+ bw!
endfunc
" Test for changing text in visual mode with 'exclusive' selection
@@ -1098,7 +1098,7 @@ func Test_exclusive_selection()
call assert_equal('l one', getline(1))
set virtualedit&
set selection&
- close!
+ bw!
endfunc
" Test for starting linewise visual with a count.
@@ -1155,7 +1155,7 @@ func Test_visual_inner_block()
8,9d
call cursor(5, 1)
call assert_beeps('normal ViBiB')
- close!
+ bw!
endfunc
func Test_visual_put_in_block()
@@ -2718,4 +2718,68 @@ func Test_visual_block_cursor_insert_enter()
bwipe!
endfunc
+func Test_visual_block_exclusive_selection()
+ new
+ set selection=exclusive
+ call setline(1, ['asöd asdf', 'asdf asdf', 'as€d asdf', 'asdf asdf'])
+ call cursor(1, 1)
+ exe ":norm! \<c-v>eh3j~"
+ call assert_equal(['ASÖd asdf', 'ASDf asdf', 'AS€d asdf', 'ASDf asdf'], getline(1, '$'))
+ exe ":norm! 1v~"
+ call assert_equal(['asöd asdf', 'asdf asdf', 'as€d asdf', 'asdf asdf'], getline(1, '$'))
+ bwipe!
+ set selection&vim
+endfunc
+
+func Test_visual_block_exclusive_selection_adjusted()
+ new
+ " Test that the end-position of the visual selection is adjusted for exclusive selection
+ set selection=exclusive
+ call setline(1, ['asöd asdf ', 'asdf asdf ', 'as€d asdf ', 'asdf asdf '])
+ call cursor(1, 1)
+ " inclusive motion
+ exe ":norm! \<c-v>e3jy"
+ call assert_equal([0, 4, 5, 0], getpos("'>"))
+ " exclusive motion
+ exe ":norm! \<c-v>ta3jy"
+ call assert_equal([0, 4, 6, 0], getpos("'>"))
+ " another inclusive motion
+ exe ":norm! \<c-v>g_3jy"
+ call assert_equal([0, 4, 10, 0], getpos("'>"))
+
+ " Reset selection option to Vim default
+ set selection&vim
+ call cursor(1, 1)
+
+ " inclusive motion
+ exe ":norm! \<c-v>e3jy"
+ call assert_equal([0, 4, 4, 0], getpos("'>"))
+ " exclusive motion
+ exe ":norm! \<c-v>ta3jy"
+ call assert_equal([0, 4, 5, 0], getpos("'>"))
+ " another inclusive motion
+ exe ":norm! \<c-v>g_3jy"
+ call assert_equal([0, 4, 9, 0], getpos("'>"))
+ bwipe!
+ set selection&vim
+endfunc
+
+" the following caused a Heap-Overflow, because Vim was accessing outside of a
+" line end
+func Test_visual_pos_buffer_heap_overflow()
+ set virtualedit=all
+ args Xa Xb
+ all
+ call setline(1, ['', '', ''])
+ call cursor(3, 1)
+ wincmd w
+ call setline(1, 'foobar')
+ normal! $lv0
+ all
+ call setreg('"', 'baz')
+ normal! [P
+ set virtualedit=
+ bw! Xa Xb
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_window_cmd.vim b/test/old/testdir/test_window_cmd.vim
index 8048fa6ff8..24517f90cb 100644
--- a/test/old/testdir/test_window_cmd.vim
+++ b/test/old/testdir/test_window_cmd.vim
@@ -55,6 +55,26 @@ func Test_window_cmd_cmdwin_with_vsp()
set ls&vim
endfunc
+func Test_cmdheight_not_changed()
+ set cmdheight=2
+ set winminheight=0
+ augroup Maximize
+ autocmd WinEnter * wincmd _
+ augroup END
+ split
+ tabnew
+ tabfirst
+ call assert_equal(2, &cmdheight)
+
+ tabonly!
+ only
+ set winminheight& cmdheight&
+ augroup Maximize
+ au!
+ augroup END
+ augroup! Maximize
+endfunc
+
" Test for jumping to windows
func Test_window_jump()
new
@@ -1164,20 +1184,20 @@ func Run_noroom_for_newwindow_test(dir_arg)
let dir = (a:dir_arg == 'v') ? 'vert ' : ''
" Open as many windows as possible
- for i in range(500)
+ while v:true
try
exe dir . 'new'
catch /E36:/
break
endtry
- endfor
+ endwhile
- call writefile(['first', 'second', 'third'], 'Xfile1')
- call writefile([], 'Xfile2')
- call writefile([], 'Xfile3')
+ call writefile(['first', 'second', 'third'], 'Xnorfile1')
+ call writefile([], 'Xnorfile2')
+ call writefile([], 'Xnorfile3')
" Argument list related commands
- args Xfile1 Xfile2 Xfile3
+ args Xnorfile1 Xnorfile2 Xnorfile3
next
for cmd in ['sargument 2', 'snext', 'sprevious', 'sNext', 'srewind',
\ 'sfirst', 'slast']
@@ -1188,13 +1208,13 @@ func Run_noroom_for_newwindow_test(dir_arg)
" Buffer related commands
set modified
hide enew
- for cmd in ['sbuffer Xfile1', 'sbnext', 'sbprevious', 'sbNext', 'sbrewind',
+ for cmd in ['sbuffer Xnorfile1', 'sbnext', 'sbprevious', 'sbNext', 'sbrewind',
\ 'sbfirst', 'sblast', 'sball', 'sbmodified', 'sunhide']
call assert_fails(dir .. cmd, 'E36:')
endfor
" Window related commands
- for cmd in ['split', 'split Xfile2', 'new', 'new Xfile3', 'sview Xfile1',
+ for cmd in ['split', 'split Xnorfile2', 'new', 'new Xnorfile3', 'sview Xnorfile1',
\ 'sfind runtest.vim']
call assert_fails(dir .. cmd, 'E36:')
endfor
@@ -1217,7 +1237,8 @@ func Run_noroom_for_newwindow_test(dir_arg)
call assert_fails(dir .. 'lopen', 'E36:')
" Preview window
- call assert_fails(dir .. 'pedit Xfile2', 'E36:')
+ call assert_fails(dir .. 'pedit Xnorfile2', 'E36:')
+ call assert_fails(dir .. 'pbuffer', 'E36:')
call setline(1, 'abc')
call assert_fails(dir .. 'psearch abc', 'E36:')
endif
@@ -1225,15 +1246,15 @@ func Run_noroom_for_newwindow_test(dir_arg)
" Window commands (CTRL-W ^ and CTRL-W f)
if a:dir_arg == 'h'
call assert_fails('call feedkeys("\<C-W>^", "xt")', 'E36:')
- call setline(1, 'Xfile1')
+ call setline(1, 'Xnorfile1')
call assert_fails('call feedkeys("gg\<C-W>f", "xt")', 'E36:')
endif
enew!
" Tag commands (:stag, :stselect and :stjump)
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
- \ "second\tXfile1\t2",
- \ "third\tXfile1\t3",],
+ \ "second\tXnorfile1\t2",
+ \ "third\tXnorfile1\t3",],
\ 'Xtags')
set tags=Xtags
call assert_fails(dir .. 'stag second', 'E36:')
@@ -1255,9 +1276,9 @@ func Run_noroom_for_newwindow_test(dir_arg)
endif
%bwipe!
- call delete('Xfile1')
- call delete('Xfile2')
- call delete('Xfile3')
+ call delete('Xnorfile1')
+ call delete('Xnorfile2')
+ call delete('Xnorfile3')
only
endfunc
diff --git a/test/old/testdir/test_winfixbuf.vim b/test/old/testdir/test_winfixbuf.vim
index 1777bec184..f7986fdda3 100644
--- a/test/old/testdir/test_winfixbuf.vim
+++ b/test/old/testdir/test_winfixbuf.vim
@@ -2545,6 +2545,18 @@ func Test_pedit()
call assert_equal(l:other, bufnr())
endfunc
+" Allow :pbuffer because, unlike :buffer, it uses a separate window
+func Test_pbuffer()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+
+ exe 'pbuffer ' . l:other
+
+ execute "normal \<C-w>w"
+ call assert_equal(l:other, bufnr())
+endfunc
+
" Fail :pop but :pop! is allowed
func Test_pop()
call s:reset_all_buffers()
@@ -2613,7 +2625,7 @@ EOF
try
pyxdo test_winfixbuf_Test_pythonx_pyxdo_set_buffer()
- catch /pynvim\.api\.common\.NvimError: E1513:/
+ catch /pynvim\.api\.common\.NvimError: Vim:E1513:/
let l:caught = 1
endtry
@@ -2644,7 +2656,7 @@ func Test_pythonx_pyxfile()
try
pyxfile file.py
- catch /pynvim\.api\.common\.NvimError: E1513:/
+ catch /pynvim\.api\.common\.NvimError: Vim:E1513:/
let l:caught = 1
endtry
@@ -2676,7 +2688,7 @@ import vim
buffer = vim.vars["_previous_buffer"]
vim.current.buffer = vim.buffers[buffer]
EOF
- catch /pynvim\.api\.common\.NvimError: E1513:/
+ catch /pynvim\.api\.common\.NvimError: Vim:E1513:/
let l:caught = 1
endtry
diff --git a/test/testutil.lua b/test/testutil.lua
index 00b30d74d5..e69dcae120 100644
--- a/test/testutil.lua
+++ b/test/testutil.lua
@@ -22,13 +22,6 @@ local M = {
paths = Paths,
}
---- @param p string
---- @return string
-local function relpath(p)
- p = vim.fs.normalize(p)
- return (p:gsub('^' .. uv.cwd, ''))
-end
-
--- @param path string
--- @return boolean
function M.isdir(path)
@@ -45,14 +38,15 @@ end
--- (Only on Windows) Replaces yucky "\\" slashes with delicious "/" slashes in a string, or all
--- string values in a table (recursively).
---
---- @param obj string|table
---- @return any
+--- @generic T: string|table
+--- @param obj T
+--- @return T|nil
function M.fix_slashes(obj)
if not M.is_os('win') then
return obj
end
if type(obj) == 'string' then
- local ret = obj:gsub('\\', '/')
+ local ret = string.gsub(obj, '\\', '/')
return ret
elseif type(obj) == 'table' then
--- @cast obj table<any,any>
@@ -394,21 +388,35 @@ end
local sysname = uv.os_uname().sysname:lower()
---- @param s 'win'|'mac'|'freebsd'|'openbsd'|'bsd'
+--- @param s 'win'|'mac'|'linux'|'freebsd'|'openbsd'|'bsd'
--- @return boolean
function M.is_os(s)
- if not (s == 'win' or s == 'mac' or s == 'freebsd' or s == 'openbsd' or s == 'bsd') then
+ if
+ not (s == 'win' or s == 'mac' or s == 'linux' or s == 'freebsd' or s == 'openbsd' or s == 'bsd')
+ then
error('unknown platform: ' .. tostring(s))
end
return not not (
(s == 'win' and (sysname:find('windows') or sysname:find('mingw')))
or (s == 'mac' and sysname == 'darwin')
+ or (s == 'linux' and sysname == 'linux')
or (s == 'freebsd' and sysname == 'freebsd')
or (s == 'openbsd' and sysname == 'openbsd')
or (s == 'bsd' and sysname:find('bsd'))
)
end
+local architecture = uv.os_uname().machine
+
+--- @param s 'x86_64'|'arm64'
+--- @return boolean
+function M.is_arch(s)
+ if not (s == 'x86_64' or s == 'arm64') then
+ error('unknown architecture: ' .. tostring(s))
+ end
+ return s == architecture
+end
+
local tmpname_id = 0
local tmpdir = os.getenv('TMPDIR') or os.getenv('TEMP')
local tmpdir_is_local = not not (tmpdir and tmpdir:find('Xtest'))
@@ -471,7 +479,8 @@ function M.check_cores(app, force) -- luacheck: ignore
-- "./Xtest-tmpdir/" => "Xtest%-tmpdir"
local local_tmpdir = nil
if tmpdir_is_local and tmpdir then
- local_tmpdir = vim.pesc(relpath(tmpdir):gsub('^[ ./]+', ''):gsub('%/+$', ''))
+ local_tmpdir =
+ vim.pesc(vim.fs.relpath(assert(vim.uv.cwd()), tmpdir):gsub('^[ ./]+', ''):gsub('%/+$', ''))
end
local db_cmd --- @type string
diff --git a/test/unit/fixtures/multiqueue.c b/test/unit/fixtures/multiqueue.c
index 149daca893..2003bc7a5a 100644
--- a/test/unit/fixtures/multiqueue.c
+++ b/test/unit/fixtures/multiqueue.c
@@ -1,8 +1,9 @@
-#include <string.h>
#include <stdlib.h>
-#include "nvim/event/multiqueue.h"
+#include <string.h>
+
#include "multiqueue.h"
+#include "nvim/event/multiqueue.h"
void ut_multiqueue_put(MultiQueue *self, const char *str)
{
diff --git a/test/unit/fixtures/multiqueue.h b/test/unit/fixtures/multiqueue.h
index 78a3a89063..37da1d4db9 100644
--- a/test/unit/fixtures/multiqueue.h
+++ b/test/unit/fixtures/multiqueue.h
@@ -1,4 +1,4 @@
#include "nvim/event/multiqueue.h"
-void ut_multiqueue_put(MultiQueue *queue, const char *str);
-const char *ut_multiqueue_get(MultiQueue *queue);
+void ut_multiqueue_put(MultiQueue *self, const char *str);
+const char *ut_multiqueue_get(MultiQueue *self);
diff --git a/test/unit/fixtures/posix.h b/test/unit/fixtures/posix.h
index f6f24cd9dc..0d16f8aac9 100644
--- a/test/unit/fixtures/posix.h
+++ b/test/unit/fixtures/posix.h
@@ -1,8 +1,8 @@
-#include <unistd.h>
-#include <string.h>
#include <errno.h>
-#include <sys/wait.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
enum {
kPOSIXErrnoEINTR = EINTR,
diff --git a/test/unit/fixtures/vterm_test.c b/test/unit/fixtures/vterm_test.c
new file mode 100644
index 0000000000..6744305960
--- /dev/null
+++ b/test/unit/fixtures/vterm_test.c
@@ -0,0 +1,789 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/grid.h"
+#include "nvim/mbyte.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+#include "vterm_test.h"
+
+int parser_text(const char bytes[], size_t len, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "text ");
+ size_t i;
+ for (i = 0; i < len; i++) {
+ unsigned char b = (unsigned char)bytes[i];
+ if (b < 0x20 || b == 0x7f || (b >= 0x80 && b < 0xa0)) {
+ break;
+ }
+ fprintf(f, i ? ",%x" : "%x", b);
+ }
+ fprintf(f, "\n");
+ fclose(f);
+
+ return (int)i;
+}
+
+static void printchars(const char *s, size_t len, FILE *f)
+{
+ while (len--) {
+ fprintf(f, "%c", (s++)[0]);
+ }
+}
+
+int parser_csi(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "csi %02x", command);
+
+ if (leader && leader[0]) {
+ fprintf(f, " L=");
+ for (int i = 0; leader[i]; i++) {
+ fprintf(f, "%02x", leader[i]);
+ }
+ }
+
+ for (int i = 0; i < argcount; i++) {
+ char sep = i ? ',' : ' ';
+
+ if (args[i] == CSI_ARG_MISSING) {
+ fprintf(f, "%c*", sep);
+ } else {
+ fprintf(f, "%c%ld%s", sep, CSI_ARG(args[i]), CSI_ARG_HAS_MORE(args[i]) ? "+" : "");
+ }
+ }
+
+ if (intermed && intermed[0]) {
+ fprintf(f, " I=");
+ for (int i = 0; intermed[i]; i++) {
+ fprintf(f, "%02x", intermed[i]);
+ }
+ }
+
+ fprintf(f, "\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+int parser_osc(int command, VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "osc ");
+
+ if (frag.initial) {
+ if (command == -1) {
+ fprintf(f, "[");
+ } else {
+ fprintf(f, "[%d;", command);
+ }
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if (frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "dcs ");
+
+ if (frag.initial) {
+ fprintf(f, "[");
+ for (size_t i = 0; i < commandlen; i++) {
+ fprintf(f, "%c", command[i]);
+ }
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if (frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_apc(VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "apc ");
+
+ if (frag.initial) {
+ fprintf(f, "[");
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if (frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_pm(VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "pm ");
+
+ if (frag.initial) {
+ fprintf(f, "[");
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if (frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int parser_sos(VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sos ");
+
+ if (frag.initial) {
+ fprintf(f, "[");
+ }
+
+ printchars(frag.str, frag.len, f);
+
+ if (frag.final) {
+ fprintf(f, "]");
+ }
+
+ fprintf(f, "\n");
+ fclose(f);
+
+ return 1;
+}
+
+int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "selection-set mask=%04X ", mask);
+ if (frag.initial) {
+ fprintf(f, "[");
+ }
+ printchars(frag.str, frag.len, f);
+ if (frag.final) {
+ fprintf(f, "]");
+ }
+ fprintf(f, "\n");
+
+ fclose(f);
+ return 1;
+}
+
+int selection_query(VTermSelectionMask mask, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "selection-query mask=%04X\n", mask);
+
+ fclose(f);
+ return 1;
+}
+
+static void print_schar(FILE *f, schar_T schar)
+{
+ char buf[MAX_SCHAR_SIZE];
+ schar_get(buf, schar);
+ StrCharInfo ci = utf_ptr2StrCharInfo(buf);
+ bool did = false;
+ while (*ci.ptr != 0) {
+ if (did) {
+ fprintf(f, ",");
+ }
+
+ if (ci.chr.len == 1 && ci.chr.value >= 0x80) {
+ fprintf(f, "??%x", ci.chr.value);
+ } else {
+ fprintf(f, "%x", ci.chr.value);
+ }
+ did = true;
+ ci = utf_ptr2StrCharInfo(ci.ptr + ci.chr.len);
+ }
+}
+
+bool want_state_putglyph;
+int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
+{
+ if (!want_state_putglyph) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "putglyph ");
+ print_schar(f, info->schar);
+ fprintf(f, " %d %d,%d", info->width, pos.row, pos.col);
+ if (info->protected_cell) {
+ fprintf(f, " prot");
+ }
+ if (info->dwl) {
+ fprintf(f, " dwl");
+ }
+ if (info->dhl) {
+ fprintf(f, " dhl-%s", info->dhl == 1 ? "top" : info->dhl == 2 ? "bottom" : "?");
+ }
+ fprintf(f, "\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+bool want_state_movecursor;
+VTermPos state_pos;
+int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ state_pos = pos;
+
+ if (want_state_movecursor) {
+ fprintf(f, "movecursor %d,%d\n", pos.row, pos.col);
+ }
+
+ fclose(f);
+ return 1;
+}
+
+bool want_state_scrollrect;
+int state_scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+ if (!want_state_scrollrect) {
+ return 0;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+
+ fprintf(f, "scrollrect %d..%d,%d..%d => %+d,%+d\n",
+ rect.start_row, rect.end_row, rect.start_col, rect.end_col,
+ downward, rightward);
+
+ fclose(f);
+ return 1;
+}
+
+bool want_state_moverect;
+int state_moverect(VTermRect dest, VTermRect src, void *user)
+{
+ if (!want_state_moverect) {
+ return 0;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "moverect %d..%d,%d..%d -> %d..%d,%d..%d\n",
+ src.start_row, src.end_row, src.start_col, src.end_col,
+ dest.start_row, dest.end_row, dest.start_col, dest.end_col);
+
+ fclose(f);
+ return 1;
+}
+
+void print_color(const VTermColor *col)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ if (VTERM_COLOR_IS_RGB(col)) {
+ fprintf(f, "rgb(%d,%d,%d", col->rgb.red, col->rgb.green, col->rgb.blue);
+ } else if (VTERM_COLOR_IS_INDEXED(col)) {
+ fprintf(f, "idx(%d", col->indexed.idx);
+ } else {
+ fprintf(f, "invalid(%d", col->type);
+ }
+ if (VTERM_COLOR_IS_DEFAULT_FG(col)) {
+ fprintf(f, ",is_default_fg");
+ }
+ if (VTERM_COLOR_IS_DEFAULT_BG(col)) {
+ fprintf(f, ",is_default_bg");
+ }
+ fprintf(f, ")");
+ fclose(f);
+}
+
+static VTermValueType vterm_get_prop_type(VTermProp prop)
+{
+ switch (prop) {
+ case VTERM_PROP_CURSORVISIBLE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORBLINK:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_ALTSCREEN:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_TITLE:
+ return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_ICONNAME:
+ return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_REVERSE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORSHAPE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_MOUSE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_FOCUSREPORT:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_THEMEUPDATES:
+ return VTERM_VALUETYPE_BOOL;
+
+ case VTERM_N_PROPS:
+ return 0;
+ }
+ return 0; // UNREACHABLE
+}
+
+bool want_state_settermprop;
+int state_settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+ if (!want_state_settermprop) {
+ return 1;
+ }
+
+ int errcode = 0;
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+
+ VTermValueType type = vterm_get_prop_type(prop);
+ switch (type) {
+ case VTERM_VALUETYPE_BOOL:
+ fprintf(f, "settermprop %d %s\n", prop, val->boolean ? "true" : "false");
+ errcode = 1;
+ goto end;
+ case VTERM_VALUETYPE_INT:
+ fprintf(f, "settermprop %d %d\n", prop, val->number);
+ errcode = 1;
+ goto end;
+ case VTERM_VALUETYPE_STRING:
+ fprintf(f, "settermprop %d %s\"%.*s\"%s\n", prop,
+ val->string.initial ? "[" : "", (int)val->string.len, val->string.str,
+ val->string.final ? "]" : "");
+ errcode = 0;
+ goto end;
+ case VTERM_VALUETYPE_COLOR:
+ fprintf(f, "settermprop %d ", prop);
+ print_color(&val->color);
+ fprintf(f, "\n");
+ errcode = 1;
+ goto end;
+ case VTERM_N_VALUETYPES:
+ goto end;
+ }
+
+end:
+ fclose(f);
+ return errcode;
+}
+
+bool want_state_erase;
+int state_erase(VTermRect rect, int selective, void *user)
+{
+ if (!want_state_erase) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+
+ fprintf(f, "erase %d..%d,%d..%d%s\n",
+ rect.start_row, rect.end_row, rect.start_col, rect.end_col,
+ selective ? " selective" : "");
+
+ fclose(f);
+ return 1;
+}
+
+struct {
+ int bold;
+ int underline;
+ int italic;
+ int blink;
+ int reverse;
+ int conceal;
+ int strike;
+ int font;
+ int small;
+ int baseline;
+ VTermColor foreground;
+ VTermColor background;
+} state_pen;
+
+int state_setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ state_pen.bold = val->boolean;
+ break;
+ case VTERM_ATTR_UNDERLINE:
+ state_pen.underline = val->number;
+ break;
+ case VTERM_ATTR_ITALIC:
+ state_pen.italic = val->boolean;
+ break;
+ case VTERM_ATTR_BLINK:
+ state_pen.blink = val->boolean;
+ break;
+ case VTERM_ATTR_REVERSE:
+ state_pen.reverse = val->boolean;
+ break;
+ case VTERM_ATTR_CONCEAL:
+ state_pen.conceal = val->boolean;
+ break;
+ case VTERM_ATTR_STRIKE:
+ state_pen.strike = val->boolean;
+ break;
+ case VTERM_ATTR_FONT:
+ state_pen.font = val->number;
+ break;
+ case VTERM_ATTR_SMALL:
+ state_pen.small = val->boolean;
+ break;
+ case VTERM_ATTR_BASELINE:
+ state_pen.baseline = val->number;
+ break;
+ case VTERM_ATTR_FOREGROUND:
+ state_pen.foreground = val->color;
+ break;
+ case VTERM_ATTR_BACKGROUND:
+ state_pen.background = val->color;
+ break;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+bool want_state_scrollback;
+int state_sb_clear(void *user)
+{
+ if (!want_state_scrollback) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sb_clear\n");
+ fclose(f);
+
+ return 0;
+}
+
+bool want_screen_scrollback;
+int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user)
+{
+ if (!want_screen_scrollback) {
+ return 1;
+ }
+
+ int eol = cols;
+ while (eol && !cells[eol - 1].schar) {
+ eol--;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sb_pushline %d =", cols);
+ for (int c = 0; c < eol; c++) {
+ fprintf(f, " ");
+ print_schar(f, cells[c].schar);
+ }
+ fprintf(f, "\n");
+
+ fclose(f);
+
+ return 1;
+}
+
+int screen_sb_popline(int cols, VTermScreenCell *cells, void *user)
+{
+ if (!want_screen_scrollback) {
+ return 0;
+ }
+
+ // All lines of scrollback contain "ABCDE"
+ for (int col = 0; col < cols; col++) {
+ if (col < 5) {
+ cells[col].schar = schar_from_ascii((uint32_t)('A' + col));
+ } else {
+ cells[col].schar = 0;
+ }
+
+ cells[col].width = 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sb_popline %d\n", cols);
+ fclose(f);
+ return 1;
+}
+
+int screen_sb_clear(void *user)
+{
+ if (!want_screen_scrollback) {
+ return 1;
+ }
+
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "sb_clear\n");
+ fclose(f);
+ return 0;
+}
+
+void term_output(const char *s, size_t len, void *user)
+{
+ FILE *f = fopen(VTERM_TEST_FILE, "a");
+ fprintf(f, "output ");
+ for (size_t i = 0; i < len; i++) {
+ fprintf(f, "%x%s", (unsigned char)s[i], i < len - 1 ? "," : "\n");
+ }
+ fclose(f);
+}
+
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
+{
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ val->boolean = state->pen.bold;
+ return 1;
+
+ case VTERM_ATTR_UNDERLINE:
+ val->number = state->pen.underline;
+ return 1;
+
+ case VTERM_ATTR_ITALIC:
+ val->boolean = state->pen.italic;
+ return 1;
+
+ case VTERM_ATTR_BLINK:
+ val->boolean = state->pen.blink;
+ return 1;
+
+ case VTERM_ATTR_REVERSE:
+ val->boolean = state->pen.reverse;
+ return 1;
+
+ case VTERM_ATTR_CONCEAL:
+ val->boolean = state->pen.conceal;
+ return 1;
+
+ case VTERM_ATTR_STRIKE:
+ val->boolean = state->pen.strike;
+ return 1;
+
+ case VTERM_ATTR_FONT:
+ val->number = state->pen.font;
+ return 1;
+
+ case VTERM_ATTR_FOREGROUND:
+ val->color = state->pen.fg;
+ return 1;
+
+ case VTERM_ATTR_BACKGROUND:
+ val->color = state->pen.bg;
+ return 1;
+
+ case VTERM_ATTR_SMALL:
+ val->boolean = state->pen.small;
+ return 1;
+
+ case VTERM_ATTR_BASELINE:
+ val->number = state->pen.baseline;
+ return 1;
+
+ case VTERM_ATTR_URI:
+ val->number = state->pen.uri;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
+{
+ if ((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline)) {
+ return 1;
+ }
+ if ((attrs & VTERM_ATTR_URI_MASK) && (a->pen.uri != b->pen.uri)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos,
+ VTermAttrMask attrs)
+{
+ ScreenCell *target = getcell(screen, pos.row, pos.col);
+
+ // TODO(vterm): bounds check
+ extent->start_row = pos.row;
+ extent->end_row = pos.row + 1;
+
+ if (extent->start_col < 0) {
+ extent->start_col = 0;
+ }
+ if (extent->end_col < 0) {
+ extent->end_col = screen->cols;
+ }
+
+ int col;
+
+ for (col = pos.col - 1; col >= extent->start_col; col--) {
+ if (attrs_differ(attrs, target, getcell(screen, pos.row, col))) {
+ break;
+ }
+ }
+ extent->start_col = col + 1;
+
+ for (col = pos.col + 1; col < extent->end_col; col++) {
+ if (attrs_differ(attrs, target, getcell(screen, pos.row, col))) {
+ break;
+ }
+ }
+ extent->end_col = col - 1;
+
+ return 1;
+}
+
+/// Does not NUL-terminate the buffer
+size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len,
+ const VTermRect rect)
+{
+ size_t outpos = 0;
+ int padding = 0;
+
+#define PUT(bytes, thislen) \
+ if (true) { \
+ if (buffer && outpos + thislen <= len) \
+ memcpy((char *)buffer + outpos, bytes, thislen); \
+ outpos += thislen; \
+ } \
+
+ for (int row = rect.start_row; row < rect.end_row; row++) {
+ for (int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if (cell->schar == 0) {
+ // Erased cell, might need a space
+ padding++;
+ } else if (cell->schar == (uint32_t)-1) {
+ // Gap behind a double-width char, do nothing
+ } else {
+ while (padding) {
+ PUT(" ", 1);
+ padding--;
+ }
+ char buf[MAX_SCHAR_SIZE + 1];
+ size_t thislen = schar_get(buf, cell->schar);
+ PUT(buf, thislen);
+ }
+ }
+
+ if (row < rect.end_row - 1) {
+ PUT("\n", 1);
+ padding = 0;
+ }
+ }
+
+ return outpos;
+}
+
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
+{
+ // This cell is EOL if this and every cell to the right is black
+ for (; pos.col < screen->cols; pos.col++) {
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+ if (cell->schar != 0) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
+{
+ *cursorpos = state->pos;
+}
+
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
+{
+ state->bold_is_highbright = bold_is_highbright;
+}
+
+/// Compares two colours. Returns true if the colors are equal, false otherwise.
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
+{
+ // First make sure that the two colours are of the same type (RGB/Indexed)
+ if (a->type != b->type) {
+ return false;
+ }
+
+ // Depending on the type inspect the corresponding members
+ if (VTERM_COLOR_IS_INDEXED(a)) {
+ return a->indexed.idx == b->indexed.idx;
+ } else if (VTERM_COLOR_IS_RGB(a)) {
+ return (a->rgb.red == b->rgb.red)
+ && (a->rgb.green == b->rgb.green)
+ && (a->rgb.blue == b->rgb.blue);
+ }
+
+ return 0;
+}
diff --git a/test/unit/fixtures/vterm_test.h b/test/unit/fixtures/vterm_test.h
new file mode 100644
index 0000000000..ef6463af6d
--- /dev/null
+++ b/test/unit/fixtures/vterm_test.h
@@ -0,0 +1,45 @@
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "nvim/macros_defs.h"
+#include "nvim/vterm/vterm.h"
+
+EXTERN VTermPos state_pos;
+EXTERN bool want_state_putglyph INIT (=false);
+EXTERN bool want_state_movecursor INIT(= false);
+EXTERN bool want_state_erase INIT(= false);
+EXTERN bool want_state_scrollrect INIT(= false);
+EXTERN bool want_state_moverect INIT(= false);
+EXTERN bool want_state_settermprop INIT(= false);
+EXTERN bool want_state_scrollback INIT(= false);
+EXTERN bool want_screen_scrollback INIT(= false);
+int parser_text(const char bytes[], size_t len, void *user);
+int parser_csi(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user);
+int parser_osc(int command, VTermStringFragment frag, void *user);
+int parser_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+int parser_apc(VTermStringFragment frag, void *user);
+int parser_pm(VTermStringFragment frag, void *user);
+int parser_sos(VTermStringFragment frag, void *user);
+int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user);
+int selection_query(VTermSelectionMask mask, void *user);
+int state_putglyph(VTermGlyphInfo *info, VTermPos pos, void *user);
+int state_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user);
+int state_scrollrect(VTermRect rect, int downward, int rightward, void *user);
+int state_moverect(VTermRect dest, VTermRect src, void *user);
+int state_settermprop(VTermProp prop, VTermValue *val, void *user);
+int state_erase(VTermRect rect, int selective, void *user);
+int state_setpenattr(VTermAttr attr, VTermValue *val, void *user);
+int state_sb_clear(void *user);
+void print_color(const VTermColor *col);
+int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user);
+int screen_sb_popline(int cols, VTermScreenCell *cells, void *user);
+int screen_sb_clear(void *user);
+void term_output(const char *s, size_t len, void *user);
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
+size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len, VTermRect rect);
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua
index bdc111de2c..2c52aa9217 100644
--- a/test/unit/mbyte_spec.lua
+++ b/test/unit/mbyte_spec.lua
@@ -58,11 +58,11 @@ describe('mbyte', function()
lib.schar_get(buf, lib.utfc_ptr2schar(to_string(seq), firstc))
local str = ffi.string(buf)
if 1 > 2 then -- for debugging
- local tabel = {}
+ local tbl = {}
for i = 1, #str do
- table.insert(tabel, string.format('0x%02x', string.byte(str, i)))
+ table.insert(tbl, string.format('0x%02x', string.byte(str, i)))
end
- print('{ ' .. table.concat(tabel, ', ') .. ' }')
+ print('{ ' .. table.concat(tbl, ', ') .. ' }')
io.stdout:flush()
end
return { str, firstc[0] }
diff --git a/test/unit/optionstr_spec.lua b/test/unit/optionstr_spec.lua
index b9c9ceaa85..1f5b42485f 100644
--- a/test/unit/optionstr_spec.lua
+++ b/test/unit/optionstr_spec.lua
@@ -11,8 +11,8 @@ local check_ff_value = function(ff)
end
describe('check_ff_value', function()
- itp('views empty string as valid', function()
- eq(1, check_ff_value(''))
+ itp('views empty string as invalid', function()
+ eq(0, check_ff_value(''))
end)
itp('views "unix", "dos" and "mac" as valid', function()
diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua
index 25cdc27b28..2b7a4d6261 100644
--- a/test/unit/strings_spec.lua
+++ b/test/unit/strings_spec.lua
@@ -1,6 +1,7 @@
local t = require('test.unit.testutil')
local itp = t.gen_itp(it)
+local child_call_once = t.child_call_once
local cimport = t.cimport
local eq = t.eq
local ffi = t.ffi
@@ -8,6 +9,12 @@ local to_cstr = t.to_cstr
local strings = cimport('stdlib.h', './src/nvim/strings.h', './src/nvim/memory.h')
+local UVARNUM_TYPE
+
+child_call_once(function()
+ UVARNUM_TYPE = ffi.typeof('uvarnumber_T')
+end)
+
describe('vim_strsave_escaped()', function()
local vim_strsave_escaped = function(s, chars)
local res = strings.vim_strsave_escaped(to_cstr(s), to_cstr(chars))
@@ -140,13 +147,22 @@ end)
describe('vim_snprintf()', function()
local function a(expected, buf, bsize, fmt, ...)
- eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...))
+ local args = { ... }
+ local ctx = string.format('snprintf(buf, %d, "%s"', bsize, fmt)
+ for _, x in ipairs(args) do
+ ctx = ctx .. ', ' .. tostring(x)
+ end
+ ctx = ctx .. string.format(') = %s', expected)
+ eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...), ctx)
if bsize > 0 then
local actual = ffi.string(buf, math.min(#expected + 1, bsize))
eq(expected:sub(1, bsize - 1) .. '\0', actual)
end
end
+ local function uv(n)
+ return ffi.cast(UVARNUM_TYPE, n)
+ end
local function i(n)
return ffi.cast('int', n)
end
@@ -181,7 +197,7 @@ describe('vim_snprintf()', function()
a(' 1234567', buf, bsize, '%9ld', l(1234567))
a('1234567 ', buf, bsize, '%-9ld', l(1234567))
a('deadbeef', buf, bsize, '%x', u(0xdeadbeef))
- a('001100', buf, bsize, '%06b', u(12))
+ a('001100', buf, bsize, '%06b', uv(12))
a('one two', buf, bsize, '%s %s', 'one', 'two')
a('1.234000', buf, bsize, '%f', 1.234)
a('1.234000e+00', buf, bsize, '%e', 1.234)
@@ -223,10 +239,10 @@ describe('vim_snprintf()', function()
a('three one two', buf, bsize, '%3$s %1$s %2$s', 'one', 'two', 'three')
a('1234567', buf, bsize, '%1$d', i(1234567))
a('deadbeef', buf, bsize, '%1$x', u(0xdeadbeef))
- a('001100', buf, bsize, '%2$0*1$b', i(6), u(12))
- a('001100', buf, bsize, '%1$0.*2$b', u(12), i(6))
+ a('001100', buf, bsize, '%2$0*1$b', i(6), uv(12))
+ a('001100', buf, bsize, '%1$0.*2$b', uv(12), i(6))
a('one two', buf, bsize, '%1$s %2$s', 'one', 'two')
- a('001100', buf, bsize, '%06b', u(12))
+ a('001100', buf, bsize, '%06b', uv(12))
a('two one', buf, bsize, '%2$s %1$s', 'one', 'two')
a('1.234000', buf, bsize, '%1$f', 1.234)
a('1.234000e+00', buf, bsize, '%1$e', 1.234)
diff --git a/test/unit/vterm_spec.lua b/test/unit/vterm_spec.lua
index 4ea5d9c29a..c5293a21cb 100644
--- a/test/unit/vterm_spec.lua
+++ b/test/unit/vterm_spec.lua
@@ -17,7 +17,9 @@ local bit = require('bit')
--- @field VTERM_KEY_NONE integer
--- @field VTERM_KEY_TAB integer
--- @field VTERM_KEY_UP integer
---- @field VTERM_MAX_CHARS_PER_CELL integer
+--- @field VTERM_KEY_BACKSPACE integer
+--- @field VTERM_KEY_ESCAPE integer
+--- @field VTERM_KEY_DEL integer
--- @field VTERM_MOD_ALT integer
--- @field VTERM_MOD_CTRL integer
--- @field VTERM_MOD_SHIFT integer
@@ -29,6 +31,7 @@ local bit = require('bit')
--- @field parser_sos function
--- @field parser_text function
--- @field print_color function
+--- @field schar_get fun(any, any):integer
--- @field screen_sb_clear function
--- @field screen_sb_popline function
--- @field screen_sb_pushline function
@@ -44,6 +47,8 @@ local bit = require('bit')
--- @field state_setpenattr function
--- @field state_settermprop function
--- @field term_output function
+--- @field utf_ptr2char fun(any):integer
+--- @field utf_ptr2len fun(any):integer
--- @field vterm_input_write function
--- @field vterm_keyboard_end_paste function
--- @field vterm_keyboard_key function
@@ -62,7 +67,6 @@ local bit = require('bit')
--- @field vterm_screen_enable_reflow function
--- @field vterm_screen_get_attrs_extent function
--- @field vterm_screen_get_cell function
---- @field vterm_screen_get_chars fun(any, any, any, any):any
--- @field vterm_screen_get_text fun(any, any, any, any):any
--- @field vterm_screen_is_eol fun(any, any):any
--- @field vterm_screen_reset function
@@ -79,7 +83,20 @@ local bit = require('bit')
--- @field vterm_state_set_callbacks function
--- @field vterm_state_set_selection_callbacks function
--- @field vterm_state_set_unrecognised_fallbacks function
-local vterm = t.cimport('./src/vterm/vterm.h', './src/vterm/vterm_internal.h')
+local vterm = t.cimport(
+ './src/nvim/grid.h',
+ './src/nvim/mbyte.h',
+ './src/nvim/vterm/encoding.h',
+ './src/nvim/vterm/keyboard.h',
+ './src/nvim/vterm/mouse.h',
+ './src/nvim/vterm/parser.h',
+ './src/nvim/vterm/pen.h',
+ './src/nvim/vterm/screen.h',
+ './src/nvim/vterm/state.h',
+ './src/nvim/vterm/vterm.h',
+ './src/nvim/vterm/vterm_internal.h',
+ './test/unit/fixtures/vterm_test.h'
+)
--- @return string
local function read_rm()
@@ -298,16 +315,12 @@ local function screen_chars(start_row, start_col, end_row, end_col, expected, sc
rect['end_row'] = end_row
rect['end_col'] = end_col
- local len = vterm.vterm_screen_get_chars(screen, nil, 0, rect)
-
- local chars = t.ffi.new('uint32_t[?]', len)
- vterm.vterm_screen_get_chars(screen, chars, len, rect)
+ local len = vterm.vterm_screen_get_text(screen, nil, 0, rect)
- local actual = ''
- for i = 0, tonumber(len) - 1 do
- actual = actual .. string.char(chars[i])
- end
+ local text = t.ffi.new('unsigned char[?]', len)
+ vterm.vterm_screen_get_text(screen, text, len, rect)
+ local actual = t.ffi.string(text, len)
t.eq(expected, actual)
end
@@ -345,7 +358,7 @@ local function screen_row(row, expected, screen, end_col)
local text = t.ffi.new('unsigned char[?]', len)
vterm.vterm_screen_get_text(screen, text, len, rect)
- t.eq(expected, t.ffi.string(text))
+ t.eq(expected, t.ffi.string(text, len))
end
local function screen_cell(row, col, expected, screen)
@@ -353,17 +366,23 @@ local function screen_cell(row, col, expected, screen)
pos['row'] = row
pos['col'] = col
- local cell = t.ffi.new('VTermScreenCell')
+ local cell = t.ffi.new('VTermScreenCell') ---@type any
vterm.vterm_screen_get_cell(screen, pos, cell)
+ local buf = t.ffi.new('unsigned char[32]')
+ vterm.schar_get(buf, cell.schar)
+
local actual = '{'
- for i = 0, vterm.VTERM_MAX_CHARS_PER_CELL - 1 do
- if cell['chars'][i] ~= 0 then
- if i > 0 then
- actual = actual .. ','
- end
- actual = string.format('%s%02x', actual, cell['chars'][i])
+ local i = 0
+ while buf[i] > 0 do
+ local char = vterm.utf_ptr2char(buf + i)
+ local charlen = vterm.utf_ptr2len(buf + i)
+ if i > 0 then
+ actual = actual .. ','
end
+ local invalid = char >= 128 and charlen == 1
+ actual = string.format('%s%s%02x', actual, invalid and '?' or '', char)
+ i = i + charlen
end
actual = string.format('%s} width=%d attrs={', actual, cell['width'])
actual = actual .. (cell['attrs'].bold ~= 0 and 'B' or '')
@@ -489,6 +508,18 @@ local function strp_key(input_key)
return vterm.VTERM_KEY_ENTER
end
+ if input_key == 'bs' then
+ return vterm.VTERM_KEY_BACKSPACE
+ end
+
+ if input_key == 'del' then
+ return vterm.VTERM_KEY_DEL
+ end
+
+ if input_key == 'esc' then
+ return vterm.VTERM_KEY_ESCAPE
+ end
+
if input_key == 'f1' then
return vterm.VTERM_KEY_FUNCTION_0 + 1
end
@@ -958,8 +989,8 @@ describe('vterm', function()
-- Spare combining chars get truncated
reset(state, nil)
- push('e' .. string.rep('\xCC\x81', 10), vt)
- expect('putglyph 65,301,301,301,301,301 1 0,0') -- and nothing more
+ push('e' .. string.rep('\xCC\x81', 20), vt)
+ expect('putglyph 65,301,301,301,301,301,301,301,301,301,301,301,301,301,301 1 0,0') -- and nothing more
reset(state, nil)
push('e', vt)
@@ -969,6 +1000,34 @@ describe('vterm', function()
push('\xCC\x82', vt)
expect('putglyph 65,301,302 1 0,0')
+ -- emoji with ZWJ and variant selectors, as one chunk
+ reset(state, nil)
+ push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt)
+ expect([[putglyph 1f3f3,fe0f,200d,1f308 2 0,0
+putglyph 1f3f3,fe0f,200d,26a7,fe0f 2 0,2
+putglyph 1f3f4,200d,2620,fe0f 2 0,4]])
+
+ -- emoji, one code point at a time
+ reset(state, nil)
+ push('🏳', vt)
+ expect('putglyph 1f3f3 2 0,0')
+ push('\xef\xb8\x8f', vt)
+ expect('putglyph 1f3f3,fe0f 2 0,0')
+ push('\xe2\x80\x8d', vt)
+ expect('putglyph 1f3f3,fe0f,200d 2 0,0')
+ push('🌈', vt)
+ expect('putglyph 1f3f3,fe0f,200d,1f308 2 0,0')
+
+ -- modifier can change width
+ push('❤', vt)
+ expect('putglyph 2764 1 0,2')
+ push('\xef\xb8\x8f', vt)
+ expect('putglyph 2764,fe0f 2 0,2')
+
+ -- also works batched
+ push('❤️', vt)
+ expect('putglyph 2764,fe0f 2 0,4')
+
-- DECSCA protected
reset(state, nil)
push('A\x1b[1"qB\x1b[2"qC', vt)
@@ -1090,7 +1149,7 @@ describe('vterm', function()
push('\x1b[0F', vt)
cursor(0, 0, state)
- -- Cursor Horizonal Absolute
+ -- Cursor Horizontal Absolute
push('\n', vt)
cursor(1, 0, state)
push('\x1b[20G', vt)
@@ -1664,12 +1723,6 @@ describe('vterm', function()
push('#', vt)
expect('putglyph 23 1 0,0')
- -- Designate G0=UK
- reset(state, nil)
- push('\x1b(A', vt)
- push('#', vt)
- expect('putglyph a3 1 0,0')
-
-- Designate G0=DEC drawing
reset(state, nil)
push('\x1b(0', vt)
@@ -2026,6 +2079,18 @@ describe('vterm', function()
mousebtn('u', 1, vt)
expect_output('\x1b[<0;301;301m')
+ -- Button 8 on SGR extended encoding mode
+ mousebtn('d', 8, vt)
+ expect_output('\x1b[<128;301;301M')
+ mousebtn('u', 8, vt)
+ expect_output('\x1b[<128;301;301m')
+
+ -- Button 9 on SGR extended encoding mode
+ mousebtn('d', 9, vt)
+ expect_output('\x1b[<129;301;301M')
+ mousebtn('u', 9, vt)
+ expect_output('\x1b[<129;301;301m')
+
-- DECRQM on SGR extended encoding mode
push('\x1b[?1005$p', vt)
expect_output('\x1b[?1005;2$y')
@@ -2041,6 +2106,18 @@ describe('vterm', function()
mousebtn('u', 1, vt)
expect_output('\x1b[3;301;301M')
+ -- Button 8 on rxvt extended encoding mode
+ mousebtn('d', 8, vt)
+ expect_output('\x1b[128;301;301M')
+ mousebtn('u', 8, vt)
+ expect_output('\x1b[3;301;301M')
+
+ -- Button 9 on rxvt extended encoding mode
+ mousebtn('d', 9, vt)
+ expect_output('\x1b[129;301;301M')
+ mousebtn('u', 9, vt)
+ expect_output('\x1b[3;301;301M')
+
-- DECRQM on rxvt extended encoding mode
push('\x1b[?1005$p', vt)
expect_output('\x1b[?1005;2$y')
@@ -2286,65 +2363,83 @@ describe('vterm', function()
local vt = init()
local state = wantstate(vt)
+ -- Disambiguate escape codes enabled
+ push('\x1b[>1u', vt)
+
-- Unmodified ASCII
- inchar(41, vt)
- expect('output 29')
- inchar(61, vt)
- expect('output 3d')
+ inchar(0x41, vt)
+ expect_output('A')
+ inchar(0x61, vt)
+ expect_output('a')
-- Ctrl modifier on ASCII letters
- inchar(41, vt, { C = true })
- expect('output 1b,5b,34,31,3b,35,75')
- inchar(61, vt, { C = true })
- expect('output 1b,5b,36,31,3b,35,75')
+ inchar(0x41, vt, { C = true })
+ expect_output('\x1b[97;6u')
+ inchar(0x61, vt, { C = true })
+ expect_output('\x1b[97;5u')
-- Alt modifier on ASCII letters
- inchar(41, vt, { A = true })
- expect('output 1b,29')
- inchar(61, vt, { A = true })
- expect('output 1b,3d')
+ inchar(0x41, vt, { A = true })
+ expect_output('\x1b[97;4u')
+ inchar(0x61, vt, { A = true })
+ expect_output('\x1b[97;3u')
-- Ctrl-Alt modifier on ASCII letters
- inchar(41, vt, { C = true, A = true })
- expect('output 1b,5b,34,31,3b,37,75')
- inchar(61, vt, { C = true, A = true })
- expect('output 1b,5b,36,31,3b,37,75')
-
- -- Special handling of Ctrl-I
- inchar(49, vt)
- expect('output 31')
- inchar(69, vt)
- expect('output 45')
- inchar(49, vt, { C = true })
- expect('output 1b,5b,34,39,3b,35,75')
- inchar(69, vt, { C = true })
- expect('output 1b,5b,36,39,3b,35,75')
- inchar(49, vt, { A = true })
- expect('output 1b,31')
- inchar(69, vt, { A = true })
- expect('output 1b,45')
- inchar(49, vt, { A = true, C = true })
- expect('output 1b,5b,34,39,3b,37,75')
- inchar(69, vt, { A = true, C = true })
- expect('output 1b,5b,36,39,3b,37,75')
+ inchar(0x41, vt, { C = true, A = true })
+ expect_output('\x1b[97;8u')
+ inchar(0x61, vt, { C = true, A = true })
+ expect_output('\x1b[97;7u')
+
+ -- Ctrl-I is disambiguated
+ inchar(0x49, vt)
+ expect_output('I')
+ inchar(0x69, vt)
+ expect_output('i')
+ inchar(0x49, vt, { C = true })
+ expect_output('\x1b[105;6u')
+ inchar(0x69, vt, { C = true })
+ expect_output('\x1b[105;5u')
+ inchar(0x49, vt, { A = true })
+ expect_output('\x1b[105;4u')
+ inchar(0x69, vt, { A = true })
+ expect_output('\x1b[105;3u')
+ inchar(0x49, vt, { A = true, C = true })
+ expect_output('\x1b[105;8u')
+ inchar(0x69, vt, { A = true, C = true })
+ expect_output('\x1b[105;7u')
+
+ -- Ctrl+Digits
+ for i = 0, 9 do
+ local c = 0x30 + i
+ inchar(c, vt)
+ expect_output(tostring(i))
+ inchar(c, vt, { C = true })
+ expect_output(string.format('\x1b[%d;5u', c))
+ inchar(c, vt, { C = true, S = true })
+ expect_output(string.format('\x1b[%d;6u', c))
+ inchar(c, vt, { C = true, A = true })
+ expect_output(string.format('\x1b[%d;7u', c))
+ inchar(c, vt, { C = true, A = true, S = true })
+ expect_output(string.format('\x1b[%d;8u', c))
+ end
-- Special handling of Space
- inchar(20, vt)
- expect('output 14')
- inchar(20, vt, { S = true })
- expect('output 14')
- inchar(20, vt, { C = true })
- expect('output 1b,5b,32,30,3b,35,75')
- inchar(20, vt, { C = true, S = true })
- expect('output 1b,5b,32,30,3b,35,75')
- inchar(20, vt, { A = true })
- expect('output 1b,14')
- inchar(20, vt, { S = true, A = true })
- expect('output 1b,14')
- inchar(20, vt, { C = true, A = true })
- expect('output 1b,5b,32,30,3b,37,75')
- inchar(20, vt, { S = true, C = true, A = true })
- expect('output 1b,5b,32,30,3b,37,75')
+ inchar(0x20, vt)
+ expect_output(' ')
+ inchar(0x20, vt, { S = true })
+ expect_output('\x1b[32;2u')
+ inchar(0x20, vt, { C = true })
+ expect_output('\x1b[32;5u')
+ inchar(0x20, vt, { C = true, S = true })
+ expect_output('\x1b[32;6u')
+ inchar(0x20, vt, { A = true })
+ expect_output('\x1b[32;3u')
+ inchar(0x20, vt, { S = true, A = true })
+ expect_output('\x1b[32;4u')
+ inchar(0x20, vt, { C = true, A = true })
+ expect_output('\x1b[32;7u')
+ inchar(0x20, vt, { S = true, C = true, A = true })
+ expect_output('\x1b[32;8u')
-- Cursor keys in reset (cursor) mode
inkey('up', vt)
@@ -2375,21 +2470,65 @@ describe('vterm', function()
inkey('up', vt, { C = true })
expect_output('\x1b[1;5A')
- -- Shift-Tab should be different
+ -- Tab
inkey('tab', vt)
expect_output('\x09')
inkey('tab', vt, { S = true })
- expect_output('\x1b[Z')
+ expect_output('\x1b[9;2u')
inkey('tab', vt, { C = true })
expect_output('\x1b[9;5u')
inkey('tab', vt, { A = true })
- expect_output('\x1b\x09')
+ expect_output('\x1b[9;3u')
inkey('tab', vt, { C = true, A = true })
expect_output('\x1b[9;7u')
+ -- Backspace
+ inkey('bs', vt)
+ expect_output('\x7f')
+ inkey('bs', vt, { S = true })
+ expect_output('\x1b[127;2u')
+ inkey('bs', vt, { C = true })
+ expect_output('\x1b[127;5u')
+ inkey('bs', vt, { A = true })
+ expect_output('\x1b[127;3u')
+ inkey('bs', vt, { C = true, A = true })
+ expect_output('\x1b[127;7u')
+
+ -- DEL
+ inkey('del', vt)
+ expect_output('\x1b[3~')
+ inkey('del', vt, { S = true })
+ expect_output('\x1b[3;2~')
+ inkey('del', vt, { C = true })
+ expect_output('\x1b[3;5~')
+ inkey('del', vt, { A = true })
+ expect_output('\x1b[3;3~')
+ inkey('del', vt, { C = true, A = true })
+ expect_output('\x1b[3;7~')
+
+ -- ESC
+ inkey('esc', vt)
+ expect_output('\x1b[27;1u')
+ inkey('esc', vt, { S = true })
+ expect_output('\x1b[27;2u')
+ inkey('esc', vt, { C = true })
+ expect_output('\x1b[27;5u')
+ inkey('esc', vt, { A = true })
+ expect_output('\x1b[27;3u')
+ inkey('esc', vt, { C = true, A = true })
+ expect_output('\x1b[27;7u')
+
-- Enter in linefeed mode
inkey('enter', vt)
expect_output('\x0d')
+ inkey('enter', vt, { S = true })
+ expect_output('\x1b[13;2u')
+ inkey('enter', vt, { C = true })
+ expect_output('\x1b[13;5u')
+ inkey('enter', vt, { A = true })
+ expect_output('\x1b[13;3u')
+ inkey('enter', vt, { C = true, A = true })
+ expect_output('\x1b[13;7u')
-- Enter in newline mode
push('\x1b[20h', vt)
@@ -2410,7 +2549,7 @@ describe('vterm', function()
-- Keypad in DECKPNM
inkey('kp0', vt)
- expect_output('0')
+ expect_output('\x1b[57399;1u')
-- Keypad in DECKPAM
push('\x1b=', vt)
@@ -2440,6 +2579,77 @@ describe('vterm', function()
expect_output('\x1b[I')
vterm.vterm_state_focus_out(state)
expect_output('\x1b[O')
+
+ -- Disambiguate escape codes disabled
+ push('\x1b[<u', vt)
+
+ -- Unmodified ASCII
+ inchar(0x41, vt)
+ expect_output('A')
+ inchar(0x61, vt)
+ expect_output('a')
+
+ -- Ctrl modifier on ASCII letters
+ inchar(0x41, vt, { C = true })
+ expect_output('\x01')
+ inchar(0x61, vt, { C = true })
+ expect_output('\x01')
+
+ -- Alt modifier on ASCII letters
+ inchar(0x41, vt, { A = true })
+ expect_output('\x1bA')
+ inchar(0x61, vt, { A = true })
+ expect_output('\x1ba')
+
+ -- Ctrl-Alt modifier on ASCII letters
+ inchar(0x41, vt, { C = true, A = true })
+ expect_output('\x1b\x01')
+ inchar(0x61, vt, { C = true, A = true })
+ expect_output('\x1b\x01')
+
+ -- Ctrl-I is ambiguous
+ inchar(0x49, vt)
+ expect_output('I')
+ inchar(0x69, vt)
+ expect_output('i')
+ inchar(0x49, vt, { C = true })
+ expect_output('\x09')
+ inchar(0x69, vt, { C = true })
+ expect_output('\x09')
+ inchar(0x49, vt, { A = true })
+ expect_output('\x1bI')
+ inchar(0x69, vt, { A = true })
+ expect_output('\x1bi')
+ inchar(0x49, vt, { A = true, C = true })
+ expect_output('\x1b\x09')
+ inchar(0x69, vt, { A = true, C = true })
+ expect_output('\x1b\x09')
+
+ -- Ctrl+Digits
+ inchar(0x30, vt, { C = true })
+ expect_output('0')
+ inchar(0x31, vt, { C = true })
+ expect_output('1')
+ inchar(0x32, vt, { C = true })
+ expect_output('\x00')
+ inchar(0x33, vt, { C = true })
+ expect_output('\x1b')
+ inchar(0x34, vt, { C = true })
+ expect_output('\x1c')
+ inchar(0x35, vt, { C = true })
+ expect_output('\x1d')
+ inchar(0x36, vt, { C = true })
+ expect_output('\x1e')
+ inchar(0x37, vt, { C = true })
+ expect_output('\x1f')
+ inchar(0x38, vt, { C = true })
+ expect_output('\x7f')
+ inchar(0x39, vt, { C = true })
+ expect_output('9')
+
+ -- Ctrl+/
+ inchar(0x2F, vt, { C = true })
+ expect_output('\x1f')
end)
itp('26state_query', function()
@@ -3042,7 +3252,7 @@ describe('vterm', function()
screen_cell(
0,
0,
- '{65,301,302,303,304,305} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
+ '{65,301,302,303,304,305,306,307,308,309,30a} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
screen
)
@@ -3059,15 +3269,25 @@ describe('vterm', function()
screen_cell(
0,
0,
- '{65,301,301,301,301,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
+ '{65,301,301,301,301,301,301,301,301,301,301,301,301,301,301} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)',
screen
)
- -- Outputing CJK doublewidth in 80th column should wraparound to next line and not crash"
+ -- Outputting CJK doublewidth in 80th column should wraparound to next line and not crash"
reset(nil, screen)
push('\x1b[80G\xEF\xBC\x90', vt)
screen_cell(0, 79, '{} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
screen_cell(1, 0, '{ff10} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
+
+ -- Outputting emoji with ZWJ and variant selectors
+ reset(nil, screen)
+ push('🏳️‍🌈🏳️‍⚧️🏴‍☠️', vt)
+
+ -- stylua: ignore start
+ screen_cell(0, 0, '{1f3f3,fe0f,200d,1f308} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
+ screen_cell(0, 2, '{1f3f3,fe0f,200d,26a7,fe0f} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
+ screen_cell(0, 4, '{1f3f4,200d,2620,fe0f} width=2 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)', screen)
+ -- stylua: ignore end
end)
pending('62screen_damage', function() end)
@@ -3121,7 +3341,7 @@ describe('vterm', function()
screen = wantscreen(vt, { b = true })
resize(20, 80, vt)
expect(
- 'sb_pushline 80 = 54 6F 70\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 ='
+ 'sb_pushline 80 = 54 6f 70\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 =\nsb_pushline 80 ='
)
-- TODO(dundargoc): fix or remove
-- screen_row( 0 , "",screen)