aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/build.bat20
-rw-r--r--CMakeLists.txt11
-rw-r--r--runtime/autoload/clojurecomplete.vim16
-rw-r--r--runtime/autoload/health.vim6
-rw-r--r--runtime/autoload/health/nvim.vim2
-rw-r--r--runtime/autoload/health/provider.vim186
-rw-r--r--runtime/autoload/man.vim5
-rw-r--r--runtime/autoload/msgpack.vim8
-rw-r--r--runtime/autoload/netrw.vim502
-rw-r--r--runtime/autoload/provider/clipboard.vim19
-rw-r--r--runtime/autoload/provider/pythonx.vim3
-rw-r--r--runtime/autoload/shada.vim13
-rw-r--r--runtime/doc/api-funcs.txt716
-rw-r--r--runtime/doc/autocmd.txt43
-rw-r--r--runtime/doc/change.txt6
-rw-r--r--runtime/doc/editing.txt2
-rw-r--r--runtime/doc/eval.txt105
-rw-r--r--runtime/doc/if_cscop.txt4
-rw-r--r--runtime/doc/if_pyth.txt2
-rw-r--r--runtime/doc/index.txt10
-rw-r--r--runtime/doc/map.txt14
-rw-r--r--runtime/doc/mbyte.txt3
-rw-r--r--runtime/doc/motion.txt3
-rw-r--r--runtime/doc/options.txt68
-rw-r--r--runtime/doc/pattern.txt24
-rw-r--r--runtime/doc/pi_msgpack.txt5
-rw-r--r--runtime/doc/pi_netrw.txt27
-rw-r--r--runtime/doc/quickfix.txt3
-rw-r--r--runtime/doc/repeat.txt10
-rw-r--r--runtime/doc/starting.txt8
-rw-r--r--runtime/doc/syntax.txt19
-rw-r--r--runtime/doc/term.txt2
-rw-r--r--runtime/doc/usr_41.txt26
-rw-r--r--runtime/doc/vim_diff.txt27
-rw-r--r--runtime/doc/windows.txt12
-rw-r--r--runtime/filetype.vim6
-rw-r--r--runtime/ftplugin/c.vim4
-rw-r--r--runtime/ftplugin/clojure.vim20
-rw-r--r--runtime/ftplugin/python.vim15
-rw-r--r--runtime/ftplugin/spec.vim18
-rw-r--r--runtime/indent/clojure.vim206
-rw-r--r--runtime/indent/vhdl.vim21
-rw-r--r--runtime/indent/vim.vim4
-rw-r--r--runtime/keymap/armenian-eastern_utf-8.vim108
-rw-r--r--runtime/keymap/armenian-western_utf-8.vim108
-rw-r--r--runtime/plugin/health.vim9
-rw-r--r--runtime/plugin/netrwPlugin.vim2
-rw-r--r--runtime/syntax/bib.vim36
-rw-r--r--runtime/syntax/clojure.vim217
-rw-r--r--runtime/syntax/help.vim2
-rw-r--r--runtime/syntax/php.vim4
-rw-r--r--runtime/syntax/python.vim4
-rw-r--r--runtime/syntax/spec.vim10
-rw-r--r--runtime/syntax/sqloracle.vim20
-rw-r--r--runtime/syntax/vim.vim4
-rw-r--r--runtime/syntax/viminfo.vim6
-rw-r--r--scripts/gen_api_vimdoc.py514
-rw-r--r--scripts/genoptions.lua1
-rwxr-xr-xsrc/clint.py69
-rw-r--r--src/nvim/api/private/helpers.c78
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--src/nvim/auevents.lua3
-rw-r--r--src/nvim/buffer.c65
-rw-r--r--src/nvim/eval.c413
-rw-r--r--src/nvim/eval.lua3
-rw-r--r--src/nvim/eval/encode.c323
-rw-r--r--src/nvim/eval/typval_encode.c.h802
-rw-r--r--src/nvim/eval/typval_encode.h594
-rw-r--r--src/nvim/event/libuv_process.c3
-rw-r--r--src/nvim/event/process.c3
-rw-r--r--src/nvim/ex_cmds.c14
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_docmd.c69
-rw-r--r--src/nvim/ex_docmd.h15
-rw-r--r--src/nvim/ex_getln.c2
-rw-r--r--src/nvim/file_search.c56
-rw-r--r--src/nvim/fileio.c267
-rw-r--r--src/nvim/globals.h31
-rw-r--r--src/nvim/hashtab.c12
-rw-r--r--src/nvim/hashtab.h9
-rw-r--r--src/nvim/keymap.c6
-rw-r--r--src/nvim/mark.c27
-rw-r--r--src/nvim/memline.c9
-rw-r--r--src/nvim/memory.c49
-rw-r--r--src/nvim/memory.h34
-rw-r--r--src/nvim/message.c31
-rw-r--r--src/nvim/ops.c6
-rw-r--r--src/nvim/option.c31
-rw-r--r--src/nvim/options.lua6
-rw-r--r--src/nvim/os/env.c25
-rw-r--r--src/nvim/os/fs.c12
-rw-r--r--src/nvim/os/stdpaths.c28
-rw-r--r--src/nvim/os/time.c39
-rw-r--r--src/nvim/path.c47
-rw-r--r--src/nvim/po/uk.po4
-rw-r--r--src/nvim/screen.c20
-rw-r--r--src/nvim/search.c50
-rw-r--r--src/nvim/shada.c110
-rw-r--r--src/nvim/spell.c8
-rw-r--r--src/nvim/testdir/Makefile3
-rw-r--r--src/nvim/testdir/runtest.vim6
-rw-r--r--src/nvim/testdir/setup.vim7
-rw-r--r--src/nvim/testdir/test17.in14
-rw-r--r--src/nvim/testdir/test53.in59
-rw-r--r--src/nvim/testdir/test53.ok26
-rw-r--r--src/nvim/testdir/test73.in11
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_autocmd.vim136
-rw-r--r--src/nvim/testdir/test_execute_func.vim55
-rw-r--r--src/nvim/testdir/test_expr.vim83
-rw-r--r--src/nvim/testdir/test_gn.vim93
-rw-r--r--src/nvim/testdir/test_goto.vim15
-rw-r--r--src/nvim/testdir/test_syn_attr.vim2
-rw-r--r--src/nvim/testdir/test_usercommands.vim64
-rw-r--r--src/nvim/types.h1
-rw-r--r--src/nvim/version.c55
-rw-r--r--src/nvim/window.c42
-rw-r--r--test/functional/api/server_requests_spec.lua4
-rw-r--r--test/functional/autocmd/autocmd_spec.lua2
-rw-r--r--test/functional/autocmd/bufenter_spec.lua35
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua113
-rw-r--r--test/functional/autocmd/tabnew_spec.lua9
-rw-r--r--test/functional/autocmd/tabnewentered_spec.lua8
-rw-r--r--test/functional/core/job_partial_spec.lua13
-rw-r--r--test/functional/core/job_spec.lua18
-rw-r--r--test/functional/eval/execute_spec.lua91
-rw-r--r--test/functional/eval/json_functions_spec.lua6
-rw-r--r--test/functional/eval/msgpack_functions_spec.lua19
-rw-r--r--test/functional/eval/server_spec.lua17
-rw-r--r--test/functional/eval/setpos_spec.lua64
-rw-r--r--test/functional/eval/string_spec.lua92
-rw-r--r--test/functional/eval/system_spec.lua101
-rw-r--r--test/functional/ex_cmds/ctrl_c_spec.lua62
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua17
-rw-r--r--test/functional/ex_cmds/global_spec.lua74
-rw-r--r--test/functional/fixtures/CMakeLists.txt1
-rw-r--r--test/functional/fixtures/printargs-test.c9
-rw-r--r--test/functional/helpers.lua82
-rw-r--r--test/functional/legacy/autochdir_spec.lua26
-rw-r--r--test/functional/options/defaults_spec.lua35
-rw-r--r--test/functional/plugin/msgpack_spec.lua2
-rw-r--r--test/functional/plugin/shada_spec.lua37
-rw-r--r--test/functional/shada/buffers_spec.lua2
-rw-r--r--test/functional/shada/marks_spec.lua15
-rw-r--r--test/functional/shada/shada_spec.lua6
-rw-r--r--test/functional/ui/inccommand_spec.lua71
-rw-r--r--test/functional/ui/mouse_spec.lua14
-rw-r--r--test/functional/ui/screen.lua107
-rw-r--r--test/functional/ui/screen_basic_spec.lua16
-rw-r--r--test/helpers.lua40
-rw-r--r--test/unit/api/private_helpers_spec.lua17
-rw-r--r--test/unit/eval/helpers.lua286
-rw-r--r--test/unit/eval/tv_clear_spec.lua127
-rw-r--r--test/unit/formatc.lua8
-rw-r--r--test/unit/helpers.lua93
-rw-r--r--test/unit/path_spec.lua11
-rw-r--r--test/unit/preprocess.lua156
157 files changed, 6718 insertions, 2511 deletions
diff --git a/.ci/build.bat b/.ci/build.bat
index c2f560fb7c..87a171b994 100644
--- a/.ci/build.bat
+++ b/.ci/build.bat
@@ -3,11 +3,11 @@
:: in MSYS2, but we cannot build inside the MSYS2 shell.
echo on
if "%CONFIGURATION%" == "MINGW_32" (
- set ARCH=i686
- set BITS=32
+ set ARCH=i686
+ set BITS=32
) else (
- set ARCH=x86_64
- set BITS=64
+ set ARCH=x86_64
+ set BITS=64
)
:: We cannot have sh.exe in the PATH (MinGW)
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
@@ -17,7 +17,17 @@ set PATH=C:\Program Files (x86)\CMake\bin\cpack.exe;%PATH%
:: Build third-party dependencies
C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Su" || goto :error
-C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S mingw-w64-%ARCH%-cmake mingw-w64-%ARCH%-perl mingw-w64-%ARCH%-python2 mingw-w64-%ARCH%-diffutils gperf" || goto :error
+C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S mingw-w64-%ARCH%-cmake mingw-w64-%ARCH%-perl mingw-w64-%ARCH%-diffutils gperf" || goto :error
+
+:: Setup python (use AppVeyor system python)
+C:\Python27\python.exe -m pip install neovim || goto :error
+C:\Python35\python.exe -m pip install neovim || goto :error
+:: Disambiguate python3
+move c:\Python35\python.exe c:\Python35\python3.exe
+set PATH=C:\Python35;C:\Python27;%PATH%
+:: Sanity check
+python -c "import neovim; print(str(neovim))" || goto :error
+python3 -c "import neovim; print(str(neovim))" || goto :error
mkdir .deps
cd .deps
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cb8302deb7..65e4bbd672 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,8 +42,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ")
endif()
+endif()
- # Enable fixing case-insensitive filenames for Mac.
+if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ # Enable fixing case-insensitive filenames for Windows and Mac.
set(USE_FNAME_CASE TRUE)
endif()
@@ -489,10 +491,9 @@ if(BUSTED_PRG)
${CMAKE_BINARY_DIR}/test/config/paths.lua)
set(UNITTEST_PREREQS nvim-test unittest-headers)
- if(WIN32)
- set(FUNCTIONALTEST_PREREQS nvim shell-test)
- else()
- set(FUNCTIONALTEST_PREREQS nvim tty-test shell-test)
+ set(FUNCTIONALTEST_PREREQS nvim printargs-test shell-test)
+ if(NOT WIN32)
+ list(APPEND FUNCTIONALTEST_PREREQS tty-test)
endif()
set(BENCHMARK_PREREQS nvim tty-test)
diff --git a/runtime/autoload/clojurecomplete.vim b/runtime/autoload/clojurecomplete.vim
index 708bb31104..030785e901 100644
--- a/runtime/autoload/clojurecomplete.vim
+++ b/runtime/autoload/clojurecomplete.vim
@@ -1,14 +1,14 @@
" Vim completion script
-" Language: Clojure
-" Maintainer: Sung Pae <self@sungpae.com>
-" URL: https://github.com/guns/vim-clojure-static
-" License: Same as Vim
-" Last Change: 27 March 2014
+" Language: Clojure
+" Maintainer: Sung Pae <self@sungpae.com>
+" URL: https://github.com/guns/vim-clojure-static
+" License: Same as Vim
+" Last Change: 18 July 2016
" -*- COMPLETION WORDS -*-
-" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-010/clj/src/vim_clojure_static/generate.clj
-" Clojure version 1.6.0
-let s:words = ["*","*'","*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-readably*","*read-eval*","*source-path*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","+","+'","-","-'","->","->>","->ArrayChunk","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods",".","..","/","<","<=","=","==",">",">=","EMPTY-NODE","accessor","aclone","add-classpath","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","amap","ancestors","and","apply","areduce","array-map","as->","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assert","assoc!","assoc","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","binding","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","booleans","bound-fn","bound-fn*","bound?","butlast","byte","byte-array","bytes","case","cast","catch","char","char-array","char-escape-string","char-name-string","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","coll?","comment","commute","comp","comparator","compare","compare-and-set!","compile","complement","concat","cond","cond->","cond->>","condp","conj!","conj","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","declare","def","default-data-readers","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype","delay","delay?","deliver","denominator","deref","derive","descendants","destructure","disj!","disj","dissoc!","dissoc","distinct","distinct?","do","doall","dorun","doseq","dosync","dotimes","doto","double","double-array","doubles","drop","drop-last","drop-while","empty","empty?","ensure","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-data","ex-info","extend","extend-protocol","extend-type","extenders","extends?","false?","ffirst","file-seq","filter","filterv","finally","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn","fn","fn?","fnext","fnil","for","force","format","frequencies","future","future-call","future-cancel","future-cancelled?","future-done?","future?","gen-class","gen-interface","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","identical?","identity","if","if-let","if-not","if-some","ifn?","import","in-ns","inc","inc'","init-proxy","instance?","int","int-array","integer?","interleave","intern","interpose","into","into-array","ints","io!","isa?","iterate","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","lazy-cat","lazy-seq","let","let","letfn","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","locking","long","long-array","longs","loop","loop","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-indexed","map?","mapcat","mapv","max","max-key","memfn","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","monitor-enter","monitor-exit","munge","name","namespace","namespace-munge","neg?","new","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","or","parents","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop!","pop","pop-thread-bindings","pos?","pr","pr-str","prefer-method","prefers","primitives-classnames","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy","proxy-call-with-super","proxy-mappings","proxy-name","proxy-super","push-thread-bindings","pvalues","quot","quote","rand","rand-int","rand-nth","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read-line","read-string","realized?","record?","recur","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","refer-clojure","reify","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-watch","repeat","repeatedly","replace","replicate","require","reset!","reset-meta!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","satisfies?","second","select-keys","send","send-off","send-via","seq","seq?","seque","sequence","sequential?","set!","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","slurp","some","some->","some->>","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","symbol","symbol?","sync","take","take-last","take-nth","take-while","test","the-ns","thread-bound?","throw","time","to-array","to-array-2d","trampoline","transient","tree-seq","true?","try","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unquote","unquote-splicing","unsigned-bit-shift-right","update-in","update-proxy","use","val","vals","var","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","when","when-first","when-let","when-not","when-some","while","with-bindings","with-bindings*","with-in-str","with-loading-context","with-local-vars","with-meta","with-open","with-out-str","with-precision","with-redefs","with-redefs-fn","xml-seq","zero?","zipmap"]
+" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-011/clj/src/vim_clojure_static/generate.clj
+" Clojure version 1.8.0
+let s:words = ["*","*'","*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-readably*","*read-eval*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","+","+'","-","-'","->","->>","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods",".","..","/","<","<=","=","==",">",">=","EMPTY-NODE","Throwable->map","accessor","aclone","add-classpath","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","amap","ancestors","and","apply","areduce","array-map","as->","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assert","assoc!","assoc","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","binding","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","booleans","bound-fn","bound-fn*","bound?","butlast","byte","byte-array","bytes","case","cast","cat","catch","char","char-array","char-escape-string","char-name-string","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","coll?","comment","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","cond","cond->","cond->>","condp","conj!","conj","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","declare","dedupe","def","default-data-readers","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype","delay","delay?","deliver","denominator","deref","derive","descendants","destructure","disj!","disj","dissoc!","dissoc","distinct","distinct?","do","doall","dorun","doseq","dosync","dotimes","doto","double","double-array","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-data","ex-info","extend","extend-protocol","extend-type","extenders","extends?","false?","ffirst","file-seq","filter","filterv","finally","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn","fn","fn?","fnext","fnil","for","force","format","frequencies","future","future-call","future-cancel","future-cancelled?","future-done?","future?","gen-class","gen-interface","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","identical?","identity","if","if-let","if-not","if-some","ifn?","import","in-ns","inc","inc'","init-proxy","instance?","int","int-array","integer?","interleave","intern","interpose","into","into-array","ints","io!","isa?","iterate","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","lazy-cat","lazy-seq","let","let","letfn","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","locking","long","long-array","longs","loop","loop","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memfn","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","monitor-enter","monitor-exit","munge","name","namespace","namespace-munge","neg?","new","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","or","parents","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop!","pop","pop-thread-bindings","pos?","pr","pr-str","prefer-method","prefers","primitives-classnames","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy","proxy-call-with-super","proxy-mappings","proxy-name","proxy-super","push-thread-bindings","pvalues","quot","quote","rand","rand-int","rand-nth","random-sample","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","recur","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","refer-clojure","reify","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-watch","repeat","repeatedly","replace","replicate","require","reset!","reset-meta!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq?","seque","sequence","sequential?","set!","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","slurp","some","some->","some->>","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","symbol","symbol?","sync","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","test","the-ns","thread-bound?","throw","time","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true?","try","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unquote","unquote-splicing","unreduced","unsigned-bit-shift-right","update","update-in","update-proxy","use","val","vals","var","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","vswap!","when","when-first","when-let","when-not","when-some","while","with-bindings","with-bindings*","with-in-str","with-loading-context","with-local-vars","with-meta","with-open","with-out-str","with-precision","with-redefs","with-redefs-fn","xml-seq","zero?","zipmap"]
" Simple word completion for special forms and public vars in clojure.core
function! clojurecomplete#Complete(findstart, base)
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim
index a63eebae13..93ca4dfc54 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -39,7 +39,7 @@ function! health#check(plugin_names) abort
tabnew
setlocal wrap breakindent
- setlocal filetype=markdown bufhidden=wipe
+ setlocal filetype=markdown
setlocal conceallevel=2 concealcursor=nc
setlocal keywordprg=:help
call s:enhance_syntax()
@@ -152,8 +152,8 @@ function! health#report_error(msg, ...) abort " {{{
endfunction " }}}
function! s:filepath_to_function(name) abort
- return substitute(substitute(substitute(a:name, ".*autoload/", "", ""),
- \ "\\.vim", "#check", ""), "/", "#", "g")
+ return substitute(substitute(substitute(a:name, '.*autoload[\/]', '', ''),
+ \ '\.vim', '#check', ''), '[\/]', '#', 'g')
endfunction
function! s:discover_health_checks() abort
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
index f5dacfebcc..e2ad9f7ccb 100644
--- a/runtime/autoload/health/nvim.vim
+++ b/runtime/autoload/health/nvim.vim
@@ -41,7 +41,7 @@ function! s:check_rplugin_manifest() abort
\ + glob(python_dir.'/*/__init__.py', 1, 1)
let contents = join(readfile(script))
if contents =~# '\<\%(from\|import\)\s\+neovim\>'
- if script =~# '/__init__\.py$'
+ if script =~# '[\/]__init__\.py$'
let script = fnamemodify(script, ':h')
endif
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim
index 9cf540ba09..417426c101 100644
--- a/runtime/autoload/health/provider.vim
+++ b/runtime/autoload/health/provider.vim
@@ -37,6 +37,7 @@ endfunction
function! s:system(cmd, ...) abort
let stdin = a:0 ? a:1 : ''
let ignore_stderr = a:0 > 1 ? a:2 : 0
+ let ignore_error = a:0 > 2 ? a:3 : 0
let opts = {
\ 'output': '',
\ 'on_stdout': function('s:system_handler'),
@@ -63,7 +64,7 @@ function! s:system(cmd, ...) abort
call health#report_error(printf('Command timed out: %s',
\ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd))
call jobstop(jobid)
- elseif s:shell_error != 0
+ elseif s:shell_error != 0 && !ignore_error
call health#report_error(printf("Command error (%d) %s: %s", jobid,
\ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd,
\ opts.output))
@@ -83,8 +84,8 @@ endfunction
" Fetch the contents of a URL.
function! s:download(url) abort
if executable('curl')
- let rv = s:system(['curl', '-sL', a:url])
- return s:shell_error ? 'curl error: '.s:shell_error : rv
+ let rv = s:system(['curl', '-sL', a:url], '', 1, 1)
+ return s:shell_error ? 'curl error with '.a:url.': '.s:shell_error : rv
elseif executable('python')
let script = "
\try:\n
@@ -110,7 +111,7 @@ function! s:check_clipboard() abort
let clipboard_tool = provider#clipboard#Executable()
if empty(clipboard_tool)
call health#report_warn(
- \ "No clipboard tool found. Using the system clipboard won't work.",
+ \ "No clipboard tool found. Clipboard registers will not work.",
\ ['See ":help clipboard".'])
else
call health#report_ok('Clipboard tool found: '. clipboard_tool)
@@ -155,13 +156,10 @@ function! s:version_info(python) abort
endif
let nvim_path = s:trim(s:system([
- \ a:python,
- \ '-c',
- \ 'import neovim; print(neovim.__file__)']))
- let nvim_path = s:shell_error ? '' : nvim_path
-
- if empty(nvim_path)
- return [python_version, 'unable to find nvim executable', pypi_version, 'unable to get nvim executable']
+ \ a:python, '-c', 'import neovim; print(neovim.__file__)']))
+ if s:shell_error || empty(nvim_path)
+ return [python_version, 'unable to load neovim Python module', pypi_version,
+ \ nvim_path]
endif
" Assuming that multiple versions of a package are installed, sort them
@@ -172,24 +170,34 @@ function! s:version_info(python) abort
return a == b ? 0 : a > b ? 1 : -1
endfunction
- let nvim_version = 'unable to find nvim version'
- let base = fnamemodify(nvim_path, ':h')
- let metas = glob(base.'-*/METADATA', 1, 1) + glob(base.'-*/PKG-INFO', 1, 1)
- let metas = sort(metas, 's:compare')
-
- if !empty(metas)
- for meta_line in readfile(metas[0])
- if meta_line =~# '^Version:'
- let nvim_version = matchstr(meta_line, '^Version: \zs\S\+')
- break
- endif
- endfor
+ " Try to get neovim.VERSION (added in 0.1.11dev).
+ let nvim_version = s:system(['python', '-c',
+ \ 'from neovim import VERSION as v; '.
+ \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'],
+ \ '', 1, 1)
+ if empty(nvim_version)
+ let nvim_version = 'unable to find neovim Python module version'
+ let base = fnamemodify(nvim_path, ':h')
+ let metas = glob(base.'-*/METADATA', 1, 1)
+ \ + glob(base.'-*/PKG-INFO', 1, 1)
+ \ + glob(base.'.egg-info/PKG-INFO', 1, 1)
+ let metas = sort(metas, 's:compare')
+
+ if !empty(metas)
+ for meta_line in readfile(metas[0])
+ if meta_line =~# '^Version:'
+ let nvim_version = matchstr(meta_line, '^Version: \zs\S\+')
+ break
+ endif
+ endfor
+ endif
endif
- let version_status = 'unknown'
+ let nvim_path_base = fnamemodify(nvim_path, ':~:h')
+ let version_status = 'unknown; '.nvim_path_base
if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version)
if s:version_cmp(nvim_version, pypi_version) == -1
- let version_status = 'outdated'
+ let version_status = 'outdated; from '.nvim_path_base
else
let version_status = 'up to date'
endif
@@ -264,7 +272,7 @@ function! s:check_python(version) abort
let python_bin = s:trim(s:system([pyenv, 'which', python_bin_name], '', 1))
if empty(python_bin)
- call health#report_warn(printf('pyenv couldn''t find %s.', python_bin_name))
+ call health#report_warn(printf('pyenv could not find %s.', python_bin_name))
endif
endif
@@ -283,15 +291,15 @@ function! s:check_python(version) abort
if len(python_multiple)
" This is worth noting since the user may install something
" that changes $PATH, like homebrew.
- call health#report_info(printf('There are multiple %s executables found. '
- \ . 'Set "g:%s" to avoid surprises.', python_bin_name, host_prog_var))
+ call health#report_info(printf('Multiple %s executables found. '
+ \ . 'Set `g:%s` to avoid surprises.', python_bin_name, host_prog_var))
endif
if python_bin =~# '\<shims\>'
- call health#report_warn(printf('"%s" appears to be a pyenv shim.', python_bin), [
- \ 'The "pyenv" executable is not in $PATH,',
+ call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_bin), [
+ \ 'The `pyenv` executable is not in $PATH,',
\ 'Your pyenv installation is broken. You should set '
- \ . '"g:'.host_prog_var.'" to avoid surprises.',
+ \ . '`g:'.host_prog_var.'` to avoid surprises.',
\ ])
endif
endif
@@ -302,9 +310,9 @@ function! s:check_python(version) abort
if empty(venv) && !empty(pyenv) && !exists('g:'.host_prog_var)
\ && !empty(pyenv_root) && resolve(python_bin) !~# '^'.pyenv_root.'/'
call health#report_warn('pyenv is not set up optimally.', [
- \ printf('Suggestion: Create a virtualenv specifically '
- \ . 'for Neovim using pyenv and use "g:%s". This will avoid '
- \ . 'the need to install Neovim''s Python client in each '
+ \ printf('Create a virtualenv specifically '
+ \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid '
+ \ . 'the need to install Neovim''s Python module in each '
\ . 'version/virtualenv.', host_prog_var)
\ ])
elseif !empty(venv) && exists('g:'.host_prog_var)
@@ -316,9 +324,9 @@ function! s:check_python(version) abort
if resolve(python_bin) !~# '^'.venv_root.'/'
call health#report_warn('Your virtualenv is not set up optimally.', [
- \ printf('Suggestion: Create a virtualenv specifically '
- \ . 'for Neovim and use "g:%s". This will avoid '
- \ . 'the need to install Neovim''s Python client in each '
+ \ printf('Create a virtualenv specifically '
+ \ . 'for Neovim and use `g:%s`. This will avoid '
+ \ . 'the need to install Neovim''s Python module in each '
\ . 'virtualenv.', host_prog_var)
\ ])
endif
@@ -327,7 +335,7 @@ function! s:check_python(version) abort
if empty(python_bin) && !empty(python_bin_name)
" An error message should have already printed.
- call health#report_error(printf('"%s" was not found.', python_bin_name))
+ call health#report_error(printf('`%s` was not found.', python_bin_name))
elseif !empty(python_bin) && !s:check_bin(python_bin)
let python_bin = ''
endif
@@ -347,13 +355,10 @@ function! s:check_python(version) abort
endif
if virtualenv_inactive
- let suggestions = [
- \ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229',
- \ ]
call health#report_warn(
- \ '$VIRTUAL_ENV exists but appears to be inactive. '
- \ . 'This could lead to unexpected results.',
- \ suggestions)
+ \ '$VIRTUAL_ENV exists but appears to be inactive. '
+ \ . 'This could lead to unexpected results.',
+ \ [ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229' ])
endif
" Diagnostic output
@@ -367,7 +372,7 @@ function! s:check_python(version) abort
if !empty(python_bin)
let [pyversion, current, latest, status] = s:version_info(python_bin)
if a:version != str2nr(pyversion)
- call health#report_warn('Got an unexpected version of Python.' .
+ call health#report_warn('Unexpected Python version.' .
\ ' This could lead to confusing error messages.')
endif
if a:version == 3 && str2float(pyversion) < 3.3
@@ -375,27 +380,25 @@ function! s:check_python(version) abort
endif
call health#report_info('Python'.a:version.' version: ' . pyversion)
- call health#report_info(printf('%s-neovim version: %s', python_bin_name, current))
+ if s:is_bad_response(status)
+ call health#report_info(printf('%s-neovim version: %s (%s)', python_bin_name, current, status))
+ else
+ call health#report_info(printf('%s-neovim version: %s', python_bin_name, current))
+ endif
if s:is_bad_response(current)
- let suggestions = [
- \ 'Error found was: ' . current,
- \ 'Use the command `$ pip' . a:version . ' install neovim`',
- \ ]
call health#report_error(
- \ 'Neovim Python client is not installed.',
- \ suggestions)
+ \ "Neovim Python client is not installed.\nError: ".current,
+ \ ['Run in shell: pip' . a:version . ' install neovim'])
endif
if s:is_bad_response(latest)
- call health#report_warn('Unable to contact PyPI.')
+ call health#report_warn('Could not contact PyPI to get latest version.')
call health#report_error('HTTP request failed: '.latest)
- endif
-
- if s:is_bad_response(status)
+ elseif s:is_bad_response(status)
call health#report_warn(printf('Latest %s-neovim is NOT installed: %s',
\ python_bin_name, latest))
- elseif !s:is_bad_response(latest)
+ elseif !s:is_bad_response(current)
call health#report_ok(printf('Latest %s-neovim is installed: %s',
\ python_bin_name, latest))
endif
@@ -405,38 +408,49 @@ endfunction
function! s:check_ruby() abort
call health#report_start('Ruby provider')
- let ruby_version = 'not found'
- if executable('ruby')
- let ruby_version = s:systemlist('ruby -v')[0]
+
+ if !executable('ruby') || !executable('gem')
+ call health#report_warn(
+ \ "`ruby` and `gem` must be in $PATH.",
+ \ ["Install Ruby and verify that `ruby` and `gem` commands work."])
+ return
endif
- let ruby_prog = provider#ruby#Detect()
- let suggestions =
- \ ['Install or upgrade the neovim RubyGem using `gem install neovim`.']
-
- if empty(ruby_prog)
- let ruby_prog = 'not found'
- let prog_vers = 'not found'
- call health#report_error('Missing Neovim RubyGem', suggestions)
- else
- silent let latest_gem = get(split(s:system(['gem', 'list', '-ra', '^neovim$']),
- \ ' (\|, \|)$' ), 1, 'not found')
- let latest_desc = ' (latest: ' . latest_gem . ')'
-
- silent let prog_vers = s:systemlist(ruby_prog . ' --version')[0]
- if s:shell_error
- let prog_vers = 'not found' . latest_desc
- call health#report_warn('Neovim RubyGem is not up-to-date.', suggestions)
- elseif s:version_cmp(prog_vers, latest_gem) == -1
- let prog_vers .= latest_desc
- call health#report_warn('Neovim RubyGem is not up-to-date.', suggestions)
- else
- call health#report_ok('Found up-to-date neovim RubyGem')
- endif
+ call health#report_info('Ruby: '. s:system('ruby -v'))
+
+ let host = provider#ruby#Detect()
+ if empty(host)
+ call health#report_warn('Missing "neovim" gem.',
+ \ ['Run in shell: gem install neovim'])
+ return
+ endif
+ call health#report_info('Host: '. host)
+
+ let latest_gem_cmd = 'gem list -ra ^neovim$'
+ let latest_gem = s:system(split(latest_gem_cmd))
+ if s:shell_error || empty(latest_gem)
+ call health#report_error('Failed to run: '. latest_gem_cmd,
+ \ ["Make sure you're connected to the internet.",
+ \ "Are you behind a firewall or proxy?"])
+ return
+ endif
+ let latest_gem = get(split(latest_gem, ' (\|, \|)$' ), 1, 'not found')
+
+ let current_gem_cmd = host .' --version'
+ let current_gem = s:system(current_gem_cmd)
+ if s:shell_error
+ call health#report_error('Failed to run: '. current_gem_cmd,
+ \ ["Report this issue with the output of: ", current_gem_cmd])
+ return
endif
- call health#report_info('Ruby Version: ' . ruby_version)
- call health#report_info('Host Executable: ' . ruby_prog)
- call health#report_info('Host Version: ' . prog_vers)
+ if s:version_cmp(current_gem, latest_gem) == -1
+ call health#report_warn(
+ \ printf('Gem "neovim" is out-of-date. Installed: %s, latest: %s',
+ \ current_gem, latest_gem),
+ \ ['Run in shell: gem update neovim'])
+ else
+ call health#report_ok('Gem "neovim" is up-to-date: '. current_gem)
+ endif
endfunction
function! health#provider#check() abort
diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim
index 6fed4a54e3..251e6eee41 100644
--- a/runtime/autoload/man.vim
+++ b/runtime/autoload/man.vim
@@ -113,10 +113,11 @@ function! s:system(cmd, ...) abort
endfunction
function! s:get_page(path) abort
+ " Respect $MANWIDTH or default to window width.
+ let manwidth = empty($MANWIDTH) ? winwidth(0) : $MANWIDTH
" Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db).
" http://comments.gmane.org/gmane.editors.vim.devel/29085
- " Respect $MANWIDTH, or default to window width.
- return s:system(['env', 'MANPAGER=cat', (empty($MANWIDTH) ? 'MANWIDTH='.winwidth(0) : ''), 'man', a:path])
+ return s:system(['env', 'MANPAGER=cat', 'MANWIDTH='.manwidth, 'man', a:path])
endfunction
function! s:put_page(page) abort
diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim
index 2e2697c57f..a10ac32469 100644
--- a/runtime/autoload/msgpack.vim
+++ b/runtime/autoload/msgpack.vim
@@ -665,11 +665,15 @@ function msgpack#eval(s, special_objs) abort
call add(expr, ']}')
let s = s[1:]
elseif s[0] is# ''''
- let char = matchstr(s, '\m\C^''\zs.\ze''')
+ let char = matchstr(s, '\v\C^\''\zs%(\\\d+|.)\ze\''')
if empty(char)
throw 'char-invalid:Invalid integer character literal format: ' . s
endif
- call add(expr, char2nr(char))
+ if char[0] is# '\'
+ call add(expr, +char[1:])
+ else
+ call add(expr, char2nr(char))
+ endif
let s = s[len(char) + 2:]
else
throw 'unknown:Invalid non-space character: ' . s
diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim
index 64c08e98fa..de85844d5d 100644
--- a/runtime/autoload/netrw.vim
+++ b/runtime/autoload/netrw.vim
@@ -1,7 +1,7 @@
" netrw.vim: Handles file transfer and remote directory listing across
" AUTOLOAD SECTION
-" Date: Feb 16, 2016
-" Version: 155 ASTRO-ONLY
+" Date: Apr 20, 2016
+" Version: 156
" Maintainer: Charles E Campbell <NdrOchip@ScampbellPfamily.AbizM-NOSPAM>
" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim
" Copyright: Copyright (C) 2016 Charles E. Campbell {{{1
@@ -13,7 +13,7 @@
" 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.
-"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore(,'~'.expand("<slnum>"))
+"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)
@@ -30,7 +30,7 @@ if v:version < 704 || !has("patch213")
let s:needpatch213= 1
finish
endif
-let g:loaded_netrw = "v155"
+let g:loaded_netrw = "v156"
if !exists("s:NOTE")
let s:NOTE = 0
let s:WARNING = 1
@@ -444,7 +444,7 @@ 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_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")
@@ -490,6 +490,7 @@ if !exists("g:netrw_sort_sequence")
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_suppress_gx_mesg", 1)
call s:NetrwInit("g:netrw_use_noswf" , 1)
call s:NetrwInit("g:netrw_sizestyle" ,"b")
" Default values - t-w ---------- {{{3
@@ -526,6 +527,7 @@ if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4'
else
let s:treedepthstring= "| "
endif
+call s:NetrwInit("s:netrw_nbcd",'{}')
" BufEnter event ignored by decho when following variable is true
" Has a side effect that doau BufReadPost doesn't work, so
@@ -551,7 +553,7 @@ if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists(
endif
au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif
-if g:netrw_keepj =~ "keepj"
+if g:netrw_keepj =~# "keepj"
com! -nargs=* NetrwKeepj keepj <args>
else
let g:netrw_keepj= ""
@@ -821,7 +823,7 @@ fun! netrw#Explore(indx,dosplit,style,...)
let dirname= curfiledir
" call Decho("..empty dirname, using current file's directory<".dirname.">",'~'.expand("<slnum>"))
endif
- if dirname =~ '^scp://' || dirname =~ '^ftp://'
+ if dirname =~# '^scp://' || dirname =~ '^ftp://'
call netrw#Nread(2,dirname)
else
if dirname == ""
@@ -1560,15 +1562,7 @@ fun! s:NetrwOptionRestore(vt)
" call Dfunc("s:NetrwOptionRestore(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$"))
" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>"))
if !exists("{a:vt}netrw_optionsave")
- if exists("s:nbcd_curpos_{bufnr('%')}")
-" call Decho("restoring posn to s:nbcd_curpos_".bufnr('%')."<".string(s:nbcd_curpos_{bufnr('%')}).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(s:nbcd_curpos_{bufnr('%')})
-" call Decho("win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$"),'~'.expand("<slnum>"))
-" call Decho("unlet s:nbcd_curpos_".bufnr('%'),'~'.expand("<slnum>"))
- unlet s:nbcd_curpos_{bufnr('%')}
- else
-" call Decho("no previous position",'~'.expand("<slnum>"))
- endif
+ call s:RestorePosn(s:netrw_nbcd)
" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>"))
" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
" call Dret("s:NetrwOptionRestore : ".a:vt."netrw_optionsave doesn't exist")
@@ -1656,16 +1650,7 @@ fun! s:NetrwOptionRestore(vt)
if exists("{a:vt}netrw_regstar") |sil! let @*= {a:vt}netrw_regstar |unlet {a:vt}netrw_regstar |endif
endif
if exists("{a:vt}netrw_regslash")|sil! let @/= {a:vt}netrw_regslash|unlet {a:vt}netrw_regslash|endif
- if exists("s:nbcd_curpos_{bufnr('%')}")
-" call Decho("restoring posn to s:nbcd_curpos_".bufnr('%')."<".string(s:nbcd_curpos_{bufnr('%')}).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(s:nbcd_curpos_{bufnr('%')})
-" call Decho("unlet s:nbcd_curpos_".bufnr('%'),'~'.expand("<slnum>"))
- if exists("s:nbcd_curpos_".bufnr('%'))
- unlet s:nbcd_curpos_{bufnr('%')}
- endif
- else
-" call Decho("no previous position",'~'.expand("<slnum>"))
- endif
+ call s:RestorePosn(s:netrw_nbcd)
" call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("<slnum>"))
" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist"),'~'.expand("<slnum>"))
@@ -3016,10 +3001,10 @@ fun! s:NetrwMethod(choice)
if exists("s:netrw_hup[host]")
call NetUserPass("ftp:".host)
- elseif (has("win32") || has("win95") || has("win64") || has("win16")) && s:netrw_ftp_cmd =~ '-[sS]:'
+ elseif (has("win32") || has("win95") || has("win64") || has("win16")) && 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\>'
+ 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
@@ -3583,7 +3568,7 @@ fun! s:NetrwBrowse(islocal,dirname)
" 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("#")
-" call Decho("setting w:netrw_rexfile<".w:netrw_rexfile."> win#".winnr())
+" call Decho("setting w:netrw_rexfile<".w:netrw_rexfile."> win#".winnr(),'~'.expand("<slnum>"))
endif
" s:NetrwBrowse : initialize history {{{3
@@ -3773,7 +3758,7 @@ fun! s:NetrwBrowse(islocal,dirname)
" analyze dirname and g:netrw_list_cmd {{{3
" call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist")."> dirname<".dirname.">",'~'.expand("<slnum>"))
- if dirname =~ "^NetrwTreeListing\>"
+ if dirname =~# "^NetrwTreeListing\>"
let dirname= b:netrw_curdir
" call Decho("(dirname was <NetrwTreeListing>) dirname<".dirname.">",'~'.expand("<slnum>"))
elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")
@@ -3854,15 +3839,15 @@ endfun
" directory is used.
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'))
-" call Decho("g:netrw_cygwin =".(exists("g:netrw_cygwin")? g:netrw_cygwin : 'n/a'))
-" call Decho("g:netrw_liststyle=".(exists("g:netrw_liststyle")? g:netrw_liststyle : 'n/a'))
-" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'))
+" 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.">")
+" call Decho("clean up any leading treedepthstring: fname<".fname.">",'~'.expand("<slnum>"))
else
let fname= a:fname
endif
@@ -3897,6 +3882,8 @@ fun! s:NetrwFile(fname)
" 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)
@@ -3910,9 +3897,9 @@ fun! s:NetrwFileInfo(islocal,fname)
let ykeep= @@
if a:islocal
let lsopt= "-lsad"
- if g:netrw_sizestyle =~ 'H'
+ if g:netrw_sizestyle =~# 'H'
let lsopt= "-lsadh"
- elseif g:netrw_sizestyle =~ 'h'
+ elseif g:netrw_sizestyle =~# 'h'
let lsopt= "-lsadh --si"
endif
if (has("unix") || has("macunix")) && executable("/bin/ls")
@@ -3944,7 +3931,7 @@ fun! s:NetrwFileInfo(islocal,fname)
endif
let t = getftime(s:NetrwFile(fname))
let sz = getfsize(s:NetrwFile(fname))
- if g:netrw_sizestyle =~ "[hH]"
+ if g:netrw_sizestyle =~# "[hH]"
let sz= s:NetrwHumanReadable(sz)
endif
echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname)))
@@ -3958,108 +3945,49 @@ fun! s:NetrwFileInfo(islocal,fname)
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: {{{2
" returns 0=cleared buffer
" 1=re-used buffer (buffer not cleared)
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,'~'.expand("<slnum>"))
+" call Decho("netrwbuf dictionary=".string(s:netrwbuf),'~'.expand("<slnum>"))
let dirname= a:dirname
" re-use buffer if possible {{{3
" call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>"))
- if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST
- " find NetrwTreeList buffer if there is one
-" call Decho("case liststyle=treelist: find NetrwTreeList buffer if there is one",'~'.expand("<slnum>"))
- if exists("w:netrw_treebufnr") && w:netrw_treebufnr > 0
-" call Decho(" re-using w:netrw_treebufnr=".w:netrw_treebufnr,'~'.expand("<slnum>"))
- let eikeep= &ei
- setl ei=all
- exe "sil! keepj noswapfile keepalt b ".w:netrw_treebufnr
- let &ei= eikeep
- setl ma
- 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<buffer cleared> : bufnum#".w:netrw_treebufnr."<NetrwTreeListing>")
- return 0
+ if !exists("s:netrwbuf")
+ let s:netrwbuf= {}
+ endif
+ if 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)
+ if !bufexists(bufnum)
+ call remove(s:netrwbuf,s:NetrwFullPath(dirname))
+ let bufnum= -1
endif
- let bufnum= -1
-" call Decho(" liststyle=TREE but w:netrw_treebufnr doesn't exist",'~'.expand("<slnum>"))
-
else
- " find buffer number of buffer named precisely the same as dirname {{{3
-" call Decho("case listtyle not treelist: find buffer numnber of buffer named precisely the same as dirname--",'~'.expand("<slnum>"))
-" call Dredir("(NetrwGetBuffer) ls!","ls!")
-
- " get dirname and associated buffer number
- let bufnum = bufnr(escape(dirname,'\'))
-" call Decho(" find buffer<".dirname.">'s number ",'~'.expand("<slnum>"))
-" call Decho(" bufnr(dirname<".escape(dirname,'\').">)=".bufnum,'~'.expand("<slnum>"))
-
- if bufnum < 0 && dirname !~ '/$'
- " try appending a trailing /
-" call Decho(" try appending a trailing / to dirname<".dirname.">",'~'.expand("<slnum>"))
- let bufnum= bufnr(escape(dirname.'/','\'))
- if bufnum > 0
- let dirname= dirname.'/'
- endif
- endif
-
- if bufnum < 0 && dirname =~ '/$'
- " try removing a trailing /
-" call Decho(" try removing a trailing / from dirname<".dirname.">",'~'.expand("<slnum>"))
- let bufnum= bufnr(escape(substitute(dirname,'/$','',''),'\'))
- if bufnum > 0
- let dirname= substitute(dirname,'/$','','')
- endif
- endif
-
-" call Decho(" findbuf1: bufnum=bufnr('".dirname."')=".bufnum." bufname(".bufnum.")<".bufname(bufnum)."> (initial)",'~'.expand("<slnum>"))
- " note: !~ was used just below, but that means using ../ to go back would match (ie. abc/def/ and abc/ matches)
- if bufnum > 0 && bufname(bufnum) != dirname && bufname(bufnum) != '.'
- " handle approximate matches
-" call Decho(" handling approx match: bufnum#".bufnum.">0 AND bufname<".bufname(bufnum).">!=dirname<".dirname."> AND bufname(".bufnum.")!='.'",'~'.expand("<slnum>"))
- let ibuf = 1
- let buflast = bufnr("$")
-" call Decho(" findbuf2: buflast=bufnr($)=".buflast,'~'.expand("<slnum>"))
- while ibuf <= buflast
- let bname= substitute(bufname(ibuf),'\\','/','g')
- let bname= substitute(bname,'.\zs/$','','')
-" call Decho(" findbuf3: while [ibuf=",ibuf."]<=[buflast=".buflast."]: dirname<".dirname."> bname=bufname(".ibuf.")<".bname.">",'~'.expand("<slnum>"))
- if bname != '' && dirname =~ '/'.bname.'/\=$' && dirname !~ '^/'
- " bname is not empty
- " dirname ends with bname,
- " dirname doesn't start with /, so its not a absolute path
-" call Decho(" findbuf3a: passes test 1 : dirname<".dirname.'> =~ /'.bname.'/\=$ && dirname !~ ^/','~'.expand("<slnum>"))
- break
- endif
- if bname =~ '^'.dirname.'/\=$'
- " bname begins with dirname
-" call Decho(' findbuf3b: passes test 2 : bname<'.bname.'>=~^'.dirname.'/\=$','~'.expand("<slnum>"))
- break
- endif
- if dirname =~ '^'.bname.'/$'
-" call Decho(' findbuf3c: passes test 3 : dirname<'.dirname.'>=~^'.bname.'/$','~'.expand("<slnum>"))
- break
- endif
- if bname != '' && dirname =~ '/'.bname.'$' && bname == bufname("%") && line("$") == 1
-" call Decho(' findbuf3d: passes test 4 : dirname<'.dirname.'>=~ /'.bname.'$','~'.expand("<slnum>"))
- break
- endif
- let ibuf= ibuf + 1
- endwhile
- if ibuf > buflast
- let bufnum= -1
- else
- let bufnum= ibuf
- endif
-" call Decho(" findbuf4: bufnum=".bufnum." (ibuf=".ibuf." buflast=".buflast.")",'~'.expand("<slnum>"))
- endif
+" call Decho("lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."] not a key")
+ let bufnum= -1
endif
" get enew buffer and name it -or- re-use buffer {{{3
- if bufnum < 0 || !bufexists(bufnum) " get enew buffer and name it
+ 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>"))
@@ -4093,6 +4021,10 @@ fun! s:NetrwGetBuffer(islocal,dirname)
" let v:errmsg= "" " Decho
exe 'sil! keepj keepalt file '.escdirname
" call Decho(" errmsg<".v:errmsg."> bufnr(".escdirname.")=".bufnr(escdirname)."<".bufname(bufnr(escdirname)).">",'~'.expand("<slnum>"))
+ " 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>"))
@@ -4100,7 +4032,7 @@ fun! s:NetrwGetBuffer(islocal,dirname)
" call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>"))
let eikeep= &ei
setl ei=all
- if getline(2) =~ '^" Netrw Directory Listing'
+ if getline(2) =~# '^" Netrw Directory Listing'
" call Decho(" getline(2)<".getline(2).'> matches "Netrw Directory Listing" : using keepalt b '.bufnum,'~'.expand("<slnum>"))
exe "sil! NetrwKeepj noswapfile keepalt b ".bufnum
else
@@ -4215,20 +4147,20 @@ fun! s:NetrwGetWord()
let dirname= "./"
let curline= getline('.')
- if curline =~ '"\s*Sorted by\s'
+ if curline =~# '"\s*Sorted by\s'
NetrwKeepj norm s
let s:netrw_skipbrowse= 1
echo 'Pressing "s" also works'
- elseif curline =~ '"\s*Sort sequence:'
+ elseif curline =~# '"\s*Sort sequence:'
let s:netrw_skipbrowse= 1
echo 'Press "S" to edit sorting sequence'
- elseif curline =~ '"\s*Quick Help:'
+ elseif curline =~# '"\s*Quick Help:'
NetrwKeepj norm ?
let s:netrw_skipbrowse= 1
- elseif curline =~ '"\s*\%(Hiding\|Showing\):'
+ elseif curline =~# '"\s*\%(Hiding\|Showing\):'
NetrwKeepj norm a
let s:netrw_skipbrowse= 1
echo 'Pressing "a" also works'
@@ -4471,10 +4403,10 @@ fun! s:NetrwBookmark(del,...)
let i = 1
while i <= a:0
if islocal
- if v:version == 704 && has("patch656")
- let mbfiles= glob(a:{i},0,1,1)
+ if v:version > 704 || (v:version == 704 && has("patch656"))
+ let mbfiles= glob(fnameescape(a:{i}),0,1,1)
else
- let mbfiles= glob(a:{i},0,1)
+ let mbfiles= glob(fnameescape(a:{i}),0,1)
endif
else
let mbfiles= [a:{i}]
@@ -4578,14 +4510,13 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
" call Dret("s:NetrwBrowseChgDir")
return
endif
+" call Decho("b:netrw_curdir<".b:netrw_curdir.">")
" NetrwBrowseChgDir: save options and initialize {{{3
" call Decho("saving options",'~'.expand("<slnum>"))
+ call s:SavePosn(s:netrw_nbcd)
NetrwKeepj call s:NetrwOptionSave("s:")
NetrwKeepj call s:NetrwSafeOptions()
- let nbcd_curpos = winsaveview()
-" call Decho("saving posn to nbcd_curpos<".string(nbcd_curpos).">",'~'.expand("<slnum>"))
- let s:nbcd_curpos_{bufnr('%')} = nbcd_curpos
if (has("win32") || has("win95") || has("win64") || has("win16"))
let dirname = substitute(b:netrw_curdir,'\\','/','ge')
else
@@ -4601,15 +4532,14 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
if g:netrw_banner
" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." line(.)#".line('.')." line($)#".line("#"),'~'.expand("<slnum>"))
if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt
- if getline(".") =~ 'Quick Help'
+ if getline(".") =~# 'Quick Help'
" call Decho("#1: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp)
" call Decho("#2: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
setl ma noro nowrap
NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp])
setl noma nomod nowrap
-" call Decho("restoring posn to nbcd_curpos<".string(nbcd_curpos).">",'~'.expand("<slnum>"))
- NetrwKeepj call winrestview(nbcd_curpos)
+ call s:RestorePosn(s:netrw_nbcd)
NetrwKeepj call s:NetrwOptionRestore("s:")
" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>"))
endif
@@ -4633,7 +4563,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
" call Decho("adjusting dirname<".dirname.'> (put trailing "/" back)','~'.expand("<slnum>"))
endif
-" " call Decho("[newdir<".newdir."> ".((newdir =~ dirpat)? "=~" : "!~")." dirpat<".dirpat.">] && [islocal=".a:islocal."] && [newdir is ".(isdirectory(s:NetrwFile(newdir))? "" : "not ")."a directory]",'~'.expand("<slnum>"))
+" call Decho("[newdir<".newdir."> ".((newdir =~ dirpat)? "=~" : "!~")." dirpat<".dirpat.">] && [islocal=".a:islocal."] && [newdir is ".(isdirectory(s:NetrwFile(newdir))? "" : "not ")."a directory]",'~'.expand("<slnum>"))
if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir))))
" ------------------------------
" NetrwBrowseChgDir: edit a file {{{3
@@ -4658,7 +4588,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
" call Decho("edit-a-file: dirname<".dirname.">",'~'.expand("<slnum>"))
" call Decho("edit-a-file: tree listing",'~'.expand("<slnum>"))
elseif newdir =~ '^\(/\|\a:\)'
-" call Decho("edit-a-file: handle an url or path starting with /: <".newdir.">")
+" call Decho("edit-a-file: handle an url or path starting with /: <".newdir.">",'~'.expand("<slnum>"))
let dirname= newdir
else
let dirname= s:ComposePath(dirname,newdir)
@@ -4685,7 +4615,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
if !&ea
keepalt wincmd _
endif
- call s:SetRexDir(a:islocal,b:netrw_curdir)
+ call s:SetRexDir(a:islocal,curdir)
elseif g:netrw_browse_split == 2
" vertically splitting the window first
" call Decho("edit-a-file: vertically splitting window prior to edit",'~'.expand("<slnum>"))
@@ -4693,12 +4623,15 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
if !&ea
keepalt wincmd |
endif
- call s:SetRexDir(a:islocal,b:netrw_curdir)
+ call s:SetRexDir(a:islocal,curdir)
elseif g:netrw_browse_split == 3
" open file in new tab
" call Decho("edit-a-file: opening new tab prior to edit",'~'.expand("<slnum>"))
keepalt tabnew
- call s:SetRexDir(a:islocal,b:netrw_curdir)
+ 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)
" call Decho("edit-a-file: use previous window for edit",'~'.expand("<slnum>"))
@@ -4707,7 +4640,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
" call Dret("s:NetrwBrowseChgDir")
return
endif
- call s:SetRexDir(a:islocal,b:netrw_curdir)
+ call s:SetRexDir(a:islocal,curdir)
else
" handling a file, didn't split, so remove menu
" call Decho("edit-a-file: handling a file+didn't split, so remove menu",'~'.expand("<slnum>"))
@@ -4860,7 +4793,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
endif
let treedir = s:NetrwTreeDir(a:islocal)
" call Decho("tree-list: treedir<".treedir.">",'~'.expand("<slnum>"))
- let s:treecurpos = nbcd_curpos
+ let s:treecurpos = winsaveview()
let haskey = 0
" call Decho("tree-list: w:netrw_treedict<".string(w:netrw_treedict).">",'~'.expand("<slnum>"))
@@ -4936,6 +4869,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...)
" else " Decho
" call Decho("skipping option restore (dorestore==0): hidden=".&hidden." bufhidden=".&bufhidden." mod=".&mod,'~'.expand("<slnum>"))
endif
+ call s:RestorePosn(s:netrw_nbcd)
if dolockout && dorestore
" call Decho("restore: filewritable(dirname<".dirname.">)=".filewritable(dirname),'~'.expand("<slnum>"))
if filewritable(dirname)
@@ -4971,6 +4905,10 @@ fun! s:NetrwBrowseUpDir(islocal)
return
endif
+ if !exists("w:netrw_liststyle") || w:netrw_liststyle != s:TREELIST
+ call s:SavePosn(s:netrw_nbcd)
+ endif
+
norm! 0
if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict")
" call Decho("case: treestyle",'~'.expand("<slnum>"))
@@ -5010,7 +4948,9 @@ fun! s:NetrwBrowseUpDir(islocal)
else
call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../'))
endif
- if exists("w:netrw_bannercnt")
+ if has_key(s:netrw_nbcd,bufnr("%"))
+ call s:RestorePosn(s:netrw_nbcd)
+ elseif exists("w:netrw_bannercnt")
" call Decho("moving to line#".w:netrw_bannercnt,'~'.expand("<slnum>"))
exe w:netrw_bannercnt
else
@@ -5106,17 +5046,20 @@ fun! netrw#BrowseX(fname,remote)
" call Decho("fname<".fname.">",'~'.expand("<slnum>"))
" call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten),'~'.expand("<slnum>"))
- " set up redirection
- if &srr =~ "%s"
- if (has("win32") || has("win95") || has("win64") || has("win16"))
- let redir= substitute(&srr,"%s","nul","")
+ " set up redirection (avoids browser messages)
+ " by default, g:netrw_suppress_gx_mesg is true
+ if g:netrw_suppress_gx_mesg
+ if &srr =~ "%s"
+ if (has("win32") || has("win95") || has("win64") || has("win16"))
+ let redir= substitute(&srr,"%s","nul","")
+ else
+ let redir= substitute(&srr,"%s","/dev/null","")
+ endif
+ elseif (has("win32") || has("win95") || has("win64") || has("win16"))
+ let redir= &srr . "nul"
else
- let redir= substitute(&srr,"%s","/dev/null","")
+ let redir= &srr . "/dev/null"
endif
- elseif (has("win32") || has("win95") || has("win64") || has("win16"))
- let redir= &srr . "nul"
- else
- let redir= &srr . "/dev/null"
endif
" call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("<slnum>"))
@@ -5377,8 +5320,12 @@ endfun
" ---------------------------------------------------------------------
" s:NetrwGlob: does glob() if local, remote listing otherwise {{{2
-fun! s:NetrwGlob(direntry,expr)
-" call Dfunc("s:NetrwGlob(direntry<".a:direntry."> expr<".a:expr.">)")
+" 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
@@ -5393,9 +5340,16 @@ fun! s:NetrwGlob(direntry,expr)
let filelist= w:netrw_treedict[a:direntry]
endif
let w:netrw_liststyle= keep_liststyle
+ elseif v:version > 704 || (v:version == 704 && has("patch656"))
+ let filelist= glob(s:ComposePath(fnameescape(a:direntry),a:expr),0,1,1)
+ if a:pare
+ let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")')
+ endif
else
- let filelist= glob(s:ComposePath(a:direntry,a:expr),0,1,1)
- let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")')
+ let filelist= glob(s:ComposePath(fnameescape(a:direntry),a:expr),0,1)
+ if a:pare
+ let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")')
+ endif
endif
" call Dret("s:NetrwGlob ".string(filelist))
return filelist
@@ -6087,7 +6041,7 @@ fun! s:NetrwMaps(islocal)
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> 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,'./'))<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,'./'))<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>
@@ -6140,7 +6094,7 @@ fun! s:NetrwMaps(islocal)
" inoremap <buffer> <silent> <nowait> qf <c-o>:<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr>
" inoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr>
" inoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(1,getloclist(v:count))<cr>
-" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr>
+" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr>
" inoremap <buffer> <silent> <nowait> s <c-o>:call <SID>NetrwSortStyle(1)<cr>
" inoremap <buffer> <silent> <nowait> S <c-o>:call <SID>NetSortSequence(1)<cr>
" inoremap <buffer> <silent> <nowait> t <c-o>:call <SID>NetrwSplit(4)<cr>
@@ -6261,7 +6215,7 @@ fun! s:NetrwMaps(islocal)
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,'./'))<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,'./'))<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>
@@ -6311,7 +6265,7 @@ fun! s:NetrwMaps(islocal)
" inoremap <buffer> <silent> <nowait> qf <c-o>:<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr>
" inoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr>
" inoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(0,getloclist(v:count))<cr>
-" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr>
+" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr>
" inoremap <buffer> <silent> <nowait> s <c-o>:call <SID>NetrwSortStyle(0)<cr>
" inoremap <buffer> <silent> <nowait> S <c-o>:call <SID>NetSortSequence(0)<cr>
" inoremap <buffer> <silent> <nowait> t <c-o>:call <SID>NetrwSplit(1)<cr>
@@ -6415,10 +6369,10 @@ fun! s:NetrwMarkFiles(islocal,...)
let i = 1
while i <= a:0
if a:islocal
- if v:version == 704 && has("patch656")
- let mffiles= glob(a:{i},0,1,1)
+ if v:version > 704 || (v:version == 704 && has("patch656"))
+ let mffiles= glob(fnameescape(a:{i}),0,1,1)
else
- let mffiles= glob(a:{i},0,1)
+ let mffiles= glob(fnameescape(a:{i}),0,1)
endif
else
let mffiles= [a:{i}]
@@ -6894,6 +6848,8 @@ fun! s:NetrwMarkFileCopy(islocal,...)
" 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>"))
@@ -7373,7 +7329,7 @@ fun! s:NetrwMarkFileGrep(islocal)
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$'
+ if pat =~# nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$'
call s:NetrwMarkFileQFEL(a:islocal,getqflist())
endif
endif
@@ -7571,7 +7527,7 @@ fun! s:NetrwMarkFileRegexp(islocal)
" 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 && has("patch656")
+ if v:version > 704 || (v:version == 704 && has("patch656"))
let files = glob(s:ComposePath(dirname,regexp),0,0,1)
else
let files = glob(s:ComposePath(dirname,regexp),0,0)
@@ -7788,7 +7744,7 @@ fun! s:NetrwMarkFileTgt(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 Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse.", so refreshing all local netrw buffers")
+" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse.", so refreshing all local netrw buffers",'~'.expand("<slnum>"))
call s:LocalBrowseRefresh()
endif
" call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./'))
@@ -7821,10 +7777,10 @@ fun! s:NetrwGetCurdir(islocal)
" 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")
+" 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)
+" call Decho("g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>"))
if g:netrw_keepdir == 0
call s:NetrwLcd(curdir)
endif
@@ -8125,7 +8081,7 @@ fun! s:NetrwMenu(domenu)
elseif !a:domenu
let s:netrwcnt = 0
let curwin = winnr()
- windo if getline(2) =~ "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif
+ windo if getline(2) =~# "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif
exe curwin."wincmd w"
if s:netrwcnt <= 1
@@ -8708,7 +8664,7 @@ fun! s:NetrwSortStyle(islocal)
let svpos= winsaveview()
" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
- let g:netrw_sort_by= (g:netrw_sort_by =~ '^n')? 'time' : (g:netrw_sort_by =~ '^t')? 'size' : (g:netrw_sort_by =~ '^siz')? 'exten' : 'name'
+ 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,'./'))
" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))
@@ -8811,7 +8767,6 @@ fun! s:NetrwSplit(mode)
let s:didsplit= 1
NetrwKeepj call s:RestoreWinVars()
NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord()))
- "call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord()))
unlet s:didsplit
else
@@ -8890,9 +8845,9 @@ endfun
" (full path directory with trailing slash returned)
fun! s:NetrwTreeDir(islocal)
" call Dfunc("s:NetrwTreeDir(islocal=".a:islocal.") getline(".line(".").")"."<".getline('.')."> b:netrw_curdir<".b:netrw_curdir."> tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> ft=".&ft)
-" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a'))
-" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'))
-" call Decho("w:netrw_treetop =".(exists("w:netrw_treetop")? w:netrw_treetop : 'n/a'))
+" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a'),'~'.expand("<slnum>"))
+" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>"))
+" call Decho("w:netrw_treetop =".(exists("w:netrw_treetop")? w:netrw_treetop : 'n/a'),'~'.expand("<slnum>"))
if exists("s:treedir")
" s:NetrwPrevWinOpen opens a "previous" window -- and thus needs to and does call s:NetrwTreeDir early
@@ -8933,7 +8888,7 @@ fun! s:NetrwTreeDir(islocal)
" detect user attempting to close treeroot
" call Decho("check if user is attempting to close treeroot",'~'.expand("<slnum>"))
" call Decho(".win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>"))
-" call Decho(".getline(".line(".").")<".getline('.').'> '.((getline('.') =~ '^'.s:treedepthstring)? '=~' : '!~').' ^'.s:treedepthstring,'~'.expand("<slnum>"))
+" call Decho(".getline(".line(".").")<".getline('.').'> '.((getline('.') =~# '^'.s:treedepthstring)? '=~#' : '!~').' ^'.s:treedepthstring,'~'.expand("<slnum>"))
if curline !~ '^'.s:treedepthstring && getline('.') != '..'
" call Decho(".user may have attempted to close treeroot",'~'.expand("<slnum>"))
" now force a refresh
@@ -9040,24 +8995,24 @@ fun! s:NetrwRefreshTreeDict(dir)
if entry =~ '/$' && has_key(w:netrw_treedict,direntry)
" call Decho("<".direntry."> is a key in treedict - display subtree for it",'~'.expand("<slnum>"))
NetrwKeepj call s:NetrwRefreshTreeDict(direntry)
- let liststar = s:NetrwGlob(direntry,'*')
- let listdotstar = s:NetrwGlob(direntry,'.*')
+ let liststar = s:NetrwGlob(direntry,'*',1)
+ let listdotstar = s:NetrwGlob(direntry,'.*',1)
let w:netrw_treedict[direntry] = liststar + listdotstar
" call Decho("updating w:netrw_treedict[".direntry.']='.string(w:netrw_treedict[direntry]),'~'.expand("<slnum>"))
elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/')
" call Decho("<".direntry."/> is a key in treedict - display subtree for it",'~'.expand("<slnum>"))
NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/')
- let liststar = s:NetrwGlob(direntry.'/','*')
- let listdotstar= s:NetrwGlob(direntry.'/','.*')
+ let liststar = s:NetrwGlob(direntry.'/','*',1)
+ let listdotstar= s:NetrwGlob(direntry.'/','.*',1)
let w:netrw_treedict[direntry]= liststar + listdotstar
" call Decho("updating w:netrw_treedict[".direntry.']='.string(w:netrw_treedict[direntry]),'~'.expand("<slnum>"))
elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@')
" call Decho("<".direntry."/> is a key in treedict - display subtree for it",'~'.expand("<slnum>"))
NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/')
- let liststar = s:NetrwGlob(direntry.'/','*')
- let listdotstar= s:NetrwGlob(direntry.'/','.*')
+ let liststar = s:NetrwGlob(direntry.'/','*',1)
+ let listdotstar= s:NetrwGlob(direntry.'/','.*',1)
" call Decho("updating w:netrw_treedict[".direntry.']='.string(w:netrw_treedict[direntry]),'~'.expand("<slnum>"))
else
@@ -9315,14 +9270,14 @@ fun! s:PerformListing(islocal)
" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>"))
let sortby= g:netrw_sort_by
- if g:netrw_sort_direction =~ "^r"
+ 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"
+ if g:netrw_sort_by =~# "^n"
" call Decho("directories will be sorted by name",'~'.expand("<slnum>"))
" sorted by name
NetrwKeepj put ='\" Sorted by '.sortby
@@ -9419,13 +9374,13 @@ fun! s:PerformListing(islocal)
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"
+ if g:netrw_sort_by =~# "^n"
" sort by name
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'
+ if g:netrw_sort_direction =~# 'n'
" normal direction sorting
exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
else
@@ -9438,7 +9393,7 @@ fun! s:PerformListing(islocal)
exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e'
NetrwKeepj call histdel("/",-1)
- elseif g:netrw_sort_by =~ "^ext"
+ elseif g:netrw_sort_by =~# "^ext"
" sort by extension
exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/'
NetrwKeepj call histdel("/",-1)
@@ -9448,7 +9403,7 @@ fun! s:PerformListing(islocal)
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'
+ if g:netrw_sort_direction =~# 'n'
" normal direction sorting
exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options
else
@@ -9462,7 +9417,7 @@ fun! s:PerformListing(islocal)
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'
+ 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
@@ -9474,7 +9429,7 @@ fun! s:PerformListing(islocal)
endif
endif
- elseif g:netrw_sort_direction =~ 'r'
+ 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
@@ -9790,9 +9745,9 @@ fun! s:NetrwRemoteListing()
" 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'
+ if g:netrw_sort_by =~# '^t'
let listcmd= g:netrw_ftp_timelist_cmd
- elseif g:netrw_sort_by =~ '^s'
+ 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>"))
@@ -9899,7 +9854,7 @@ fun! s:NetrwRemoteListing()
if s:method == "ftp"
" cleanup
exe "sil! NetrwKeepj ".w:netrw_bannercnt
- while getline('.') =~ g:netrw_ftp_browse_reject
+ while getline('.') =~# g:netrw_ftp_browse_reject
sil! NetrwKeepj d
endwhile
" if there's no ../ listed, then put ../ in
@@ -9952,9 +9907,9 @@ fun! s:NetrwRemoteRm(usrhost,path) range
" call Decho("remove all marked files with bufnr#".bufnr("%"),'~'.expand("<slnum>"))
for fname in s:netrwmarkfilelist_{bufnr("%")}
let ok= s:NetrwRemoteRmFile(a:path,fname,all)
- if ok =~ 'q\%[uit]'
+ if ok =~# 'q\%[uit]'
break
- elseif ok =~ 'a\%[ll]'
+ elseif ok =~# 'a\%[ll]'
let all= 1
endif
endfor
@@ -9973,9 +9928,9 @@ fun! s:NetrwRemoteRm(usrhost,path) range
while ctr <= a:lastline
exe "NetrwKeepj ".ctr
let ok= s:NetrwRemoteRmFile(a:path,s:NetrwGetWord(),all)
- if ok =~ 'q\%[uit]'
+ if ok =~# 'q\%[uit]'
break
- elseif ok =~ 'a\%[ll]'
+ elseif ok =~# 'a\%[ll]'
let all= 1
endif
let ctr= ctr + 1
@@ -10014,12 +9969,12 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all)
let ok="no"
endif
let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
- if ok =~ 'a\%[ll]'
+ if ok =~# 'a\%[ll]'
let all= 1
endif
endif
- if all || ok =~ 'y\%[es]' || ok == ""
+ 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>"))
@@ -10054,13 +10009,13 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all)
else
call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60)
endif
- else if ret != 0
+ 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]'
+ elseif ok =~# 'q\%[uit]'
" call Decho("ok==".ok,'~'.expand("<slnum>"))
endif
@@ -10075,12 +10030,12 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all)
let ok="no"
endif
let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e')
- if ok =~ 'a\%[ll]'
+ if ok =~# 'a\%[ll]'
let all= 1
endif
endif
- if all || ok =~ 'y\%[es]' || ok == ""
+ 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
@@ -10103,7 +10058,7 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all)
endif
endif
- elseif ok =~ 'q\%[uit]'
+ elseif ok =~# 'q\%[uit]'
" call Decho("ok==".ok,'~'.expand("<slnum>"))
endif
endif
@@ -10257,7 +10212,7 @@ fun! netrw#LocalBrowseCheck(dirname)
" 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("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>"))
" call Dredir("ls!","ls!")
" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>"))
@@ -10281,7 +10236,6 @@ fun! netrw#LocalBrowseCheck(dirname)
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
@@ -10361,7 +10315,7 @@ fun! s:LocalBrowseRefresh()
" refresh any netrw buffer
" call Decho("refresh buf#".ibuf.'-> win#'.bufwinnr(ibuf),'~'.expand("<slnum>"))
exe bufwinnr(ibuf)."wincmd w"
- if getline(".") =~ 'Quick Help'
+ 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
@@ -10467,15 +10421,8 @@ fun! s:LocalListing()
" get the list of files contained in the current directory
let dirname = b:netrw_curdir
let dirnamelen = strlen(b:netrw_curdir)
- if v:version == 704 && has("patch656")
-" call Decho("using glob with patch656",'~'.expand("<slnum>"))
- let filelist = glob(s:ComposePath(dirname,"*"),0,1,1)
- let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1,1)
- else
-" call Decho("using glob without patch656",'~'.expand("<slnum>"))
- let filelist = glob(s:ComposePath(dirname,"*"),0,1)
- let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1)
- endif
+ let filelist = s:NetrwGlob(dirname,"*",0)
+ let filelist = filelist + s:NetrwGlob(dirname,".*",0)
" call Decho("filelist=".string(filelist),'~'.expand("<slnum>"))
if g:netrw_cygwin == 0 && (has("win32") || has("win95") || has("win64") || has("win16"))
@@ -10487,9 +10434,9 @@ fun! s:LocalListing()
" call Decho("filelist=".string(filelist),'~'.expand("<slnum>"))
endif
-" call Decho("before while: dirname<".dirname.">",'~'.expand("<slnum>"))
+" call Decho("before while: dirname <".dirname.">",'~'.expand("<slnum>"))
" call Decho("before while: dirnamelen<".dirnamelen.">",'~'.expand("<slnum>"))
-" call Decho("before while: filelist=".string(filelist),'~'.expand("<slnum>"))
+" call Decho("before while: filelist =".string(filelist),'~'.expand("<slnum>"))
if get(g:, 'netrw_dynamic_maxfilenamelen', 0)
let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")')
@@ -10559,15 +10506,15 @@ fun! s:LocalListing()
if w:netrw_liststyle == s:LONGLIST
let sz = getfsize(filename)
- if g:netrw_sizestyle =~ "[hH]"
+ if g:netrw_sizestyle =~# "[hH]"
let sz= s:NetrwHumanReadable(sz)
endif
let fsz = strpart(" ",1,15-strlen(sz)).sz
let pfile= pfile."\t".fsz." ".strftime(g:netrw_timefmt,getftime(filename))
-" call Decho("sz=".sz." fsz=".fsz,'~'.expand("<slnum>"))
+" call Decho("longlist support: sz=".sz." fsz=".fsz,'~'.expand("<slnum>"))
endif
- if g:netrw_sort_by =~ "^t"
+ if g:netrw_sort_by =~# "^t"
" sort by time (handles time up to 1 quintillion seconds, US)
" call Decho("getftime(".filename.")=".getftime(filename),'~'.expand("<slnum>"))
let t = getftime(filename)
@@ -10580,7 +10527,7 @@ fun! s:LocalListing()
" sort by size (handles file sizes up to 1 quintillion bytes, US)
" call Decho("getfsize(".filename.")=".getfsize(filename),'~'.expand("<slnum>"))
let sz = getfsize(filename)
- if g:netrw_sizestyle =~ "[hH]"
+ if g:netrw_sizestyle =~# "[hH]"
let sz= s:NetrwHumanReadable(sz)
endif
let fsz = strpart("000000000000000000",1,18-strlen(sz)).sz
@@ -10733,9 +10680,9 @@ fun! s:NetrwLocalRm(path) range
" call Decho("remove all marked files",'~'.expand("<slnum>"))
for fname in s:netrwmarkfilelist_{bufnr("%")}
let ok= s:NetrwLocalRmFile(a:path,fname,all)
- if ok =~ 'q\%[uit]' || ok == "no"
+ if ok =~# 'q\%[uit]' || ok == "no"
break
- elseif ok =~ 'a\%[ll]'
+ elseif ok =~# 'a\%[ll]'
let all= 1
endif
endfor
@@ -10762,9 +10709,9 @@ fun! s:NetrwLocalRm(path) range
continue
endif
let ok= s:NetrwLocalRmFile(a:path,curword,all)
- if ok =~ 'q\%[uit]' || ok == "no"
+ if ok =~# 'q\%[uit]' || ok == "no"
break
- elseif ok =~ 'a\%[ll]'
+ elseif ok =~# 'a\%[ll]'
let all= 1
endif
let ctr= ctr + 1
@@ -10811,12 +10758,12 @@ fun! s:NetrwLocalRmFile(path,fname,all)
" 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]'
+ if ok =~# 'a\%[ll]'
let all= 1
endif
endif
- if all || ok =~ 'y\%[es]' || ok == ""
+ if all || ok =~# 'y\%[es]' || ok == ""
let ret= s:NetrwDelete(rmfile)
" call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>"))
endif
@@ -10832,13 +10779,13 @@ fun! s:NetrwLocalRmFile(path,fname,all)
if ok == ""
let ok="no"
endif
- if ok =~ 'a\%[ll]'
+ if ok =~# 'a\%[ll]'
let all= 1
endif
endif
let rmfile= substitute(rmfile,'[\/]$','','e')
- if all || ok =~ 'y\%[es]' || ok == ""
+ if all || ok =~# 'y\%[es]' || ok == ""
if v:version < 704 || !has("patch1109")
" " call Decho("1st attempt: system(netrw#WinPath(".g:netrw_localrmdir.') '.s:ShellEscape(rmfile).')','~'.expand("<slnum>"))
call system(netrw#WinPath(g:netrw_localrmdir).' '.s:ShellEscape(rmfile))
@@ -10879,6 +10826,17 @@ endfun
" Support Functions: {{{1
" ---------------------------------------------------------------------
+" s:WinNames: COMBAK {{{2
+fun! s:WinNames(id)
+ let curwin= winnr()
+ 1wincmd w
+" call Decho("--- Windows By Name --- #".a:id)
+" windo call Decho("win#".winnr()."<".expand("%").">")
+" call Decho("--- --- --- --- --- ---")
+ exe curwin."wincmd w"
+endfun
+
+" ---------------------------------------------------------------------
" 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
@@ -10905,8 +10863,6 @@ fun! netrw#Call(funcname,...)
" call Dret("netrw#Call")
endfun
-" ------------------------------------------------------------------------
-
" ---------------------------------------------------------------------
" netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2
" I expect this function to be used in
@@ -10914,18 +10870,22 @@ endfun
" for example.
fun! netrw#Expose(varname)
" call Dfunc("netrw#Expose(varname<".a:varname.">)")
- exe "let retval= s:".a:varname
- if exists("g:netrw_pchk")
- 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
+ if exists("s:".a:varname)
+ exe "let retval= s:".a:varname
+ if exists("g:netrw_pchk")
+ 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))
+ return string(retval)
endif
-" call Dret("netrw#Expose ".string(retval))
- return string(retval)
+ else
+ let retval= "n/a"
endif
" call Dret("netrw#Expose ".string(retval))
@@ -11018,9 +10978,9 @@ fun! s:ComposePath(base,subdir)
" call Decho("amiga",'~'.expand("<slnum>"))
let ec = a:base[s:Strlen(a:base)-1]
if ec != '/' && ec != ':'
- let ret = a:base . "/" . a:subdir
+ let ret = a:base."/" . a:subdir
else
- let ret = a:base . a:subdir
+ let ret = a:base.a:subdir
endif
elseif a:subdir =~ '^\a:[/\\][^/\\]' && (has("win32") || has("win95") || has("win64") || has("win16"))
@@ -11248,10 +11208,10 @@ fun! s:NetrwBMShow()
redir END
let bmshowlist = split(bmshowraw,'\n')
if bmshowlist != []
- let bmshowfuncs= filter(bmshowlist,'v:val =~ "<SNR>\\d\\+_BMShow()"')
+ let bmshowfuncs= filter(bmshowlist,'v:val =~# "<SNR>\\d\\+_BMShow()"')
if bmshowfuncs != []
let bmshowfunc = substitute(bmshowfuncs[0],'^.*:\(call.*BMShow()\).*$','\1','')
- if bmshowfunc =~ '^call.*BMShow()'
+ if bmshowfunc =~# '^call.*BMShow()'
exe "sil! NetrwKeepj ".bmshowfunc
endif
endif
@@ -11395,11 +11355,12 @@ fun! s:NetrwEnew(...)
" call Decho("generate a buffer with NetrwKeepj keepalt enew!",'~'.expand("<slnum>"))
" when tree listing uses file TreeListing... a new buffer is made.
" Want the old buffer to be unlisted.
- setl nobl
+ " COMBAK: this causes a problem, see P43
+" setl nobl
let netrw_keepdiff= &l:diff
noswapfile NetrwKeepj keepalt enew!
let &l:diff= netrw_keepdiff
-" call Decho("bufnr($)=".bufnr("$")." winnr($)=".winnr("$"),'~'.expand("<slnum>"))
+" call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>"))
NetrwKeepj call s:NetrwOptionSave("w:")
" copy function-local-variables to buffer variable equivalents
@@ -11458,8 +11419,8 @@ endfun
" ---------------------------------------------------------------------
" s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2
fun! s:NetrwInsureWinVars()
-" call Dfunc("s:NetrwInsureWinVars() win#".winnr())
if !exists("w:netrw_liststyle")
+" call Dfunc("s:NetrwInsureWinVars() win#".winnr())
let curbuf = bufnr("%")
let curwin = winnr()
let iwin = 1
@@ -11479,8 +11440,8 @@ fun! s:NetrwInsureWinVars()
let w:{k}= winvars[k]
endfor
endif
+" call Dret("s:NetrwInsureWinVars win#".winnr())
endif
-" call Dret("s:NetrwInsureWinVars win#".winnr())
endfun
" ---------------------------------------------------------------------
@@ -11685,7 +11646,7 @@ endfun
" s:SetRexDir() sets up <2-leftmouse> maps (if g:netrw_retmap
" is true) and a command, :Rexplore, which call this function.
"
-" s:nbcd_curpos_{bufnr('%')} is set up by s:NetrwBrowseChgDir()
+" s:netrw_nbcd is set up by s:NetrwBrowseChgDir()
"
" s:rexposn_BUFNR used to save/restore cursor position
fun! s:NetrwRexplore(islocal,dirname)
@@ -11766,6 +11727,29 @@ fun! 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("%").">")
+
+ let a:posndict[bufnr("%")]= winsaveview()
+" call Decho("saving posn: posndict[".bufnr("%")."]=".string(winsaveview()),'~'.expand("<slnum>"))
+
+" 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 has_key(a:posndict,bufnr("%"))
+ call winrestview(a:posndict[bufnr("%")])
+" call Decho("restoring posn: posndict[".bufnr("%")."]=".string(a:posndict[bufnr("%")]),'~'.expand("<slnum>"))
+ endif
+" call Dret("s:RestorePosn")
+endfun
+
+" ---------------------------------------------------------------------
" s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2
fun! s:SaveWinVars()
" call Dfunc("s:SaveWinVars() win#".winnr())
@@ -11819,10 +11803,10 @@ fun! s:SetRexDir(islocal,dirname)
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)
-" call Decho("setting w:netrw_rexlocal=".w:netrw_rexlocal)
+" 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 SavePosn",'~'.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
@@ -11956,7 +11940,7 @@ fun! s:TreeListMove(dir)
" call Decho("regfile srch back: ".nl,'~'.expand("<slnum>"))
elseif a:dir == '[]' && nxtline != ''
NetrwKeepj norm! 0
-" call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>')
+" call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>','~'.expand("<slnum>"))
let nl = search('^\%('.curindent.'\)\@!','We') " search forwards
if nl != 0
NetrwKeepj norm! k
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index f63ad5730b..36232b99db 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -22,14 +22,23 @@ function! s:try_cmd(cmd, ...)
let argv = split(a:cmd, " ")
let out = a:0 ? systemlist(argv, a:1, 1) : systemlist(argv, [''], 1)
if v:shell_error
- echohl WarningMsg
- echo "clipboard: error: ".(len(out) ? out[0] : '')
- echohl None
+ if !exists('s:did_error_try_cmd')
+ echohl WarningMsg
+ echomsg "clipboard: error: ".(len(out) ? out[0] : '')
+ echohl None
+ let s:did_error_try_cmd = 1
+ endif
return 0
endif
return out
endfunction
+" Returns TRUE if `cmd` exits with success, else FALSE.
+function! s:cmd_ok(cmd)
+ call system(a:cmd)
+ return v:shell_error == 0
+endfunction
+
let s:cache_enabled = 1
let s:err = ''
@@ -38,14 +47,14 @@ function! provider#clipboard#Error() abort
endfunction
function! provider#clipboard#Executable() abort
- if executable('pbcopy')
+ if has('mac') && executable('pbcopy')
let s:copy['+'] = 'pbcopy'
let s:paste['+'] = 'pbpaste'
let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+']
let s:cache_enabled = 0
return 'pbcopy'
- elseif exists('$DISPLAY') && executable('xsel')
+ elseif exists('$DISPLAY') && executable('xsel') && s:cmd_ok('xsel -o -b')
let s:copy['+'] = 'xsel --nodetach -i -b'
let s:paste['+'] = 'xsel -o -b'
let s:copy['*'] = 'xsel --nodetach -i -p'
diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim
index f46c260fa3..08a0f39b01 100644
--- a/runtime/autoload/provider/pythonx.vim
+++ b/runtime/autoload/provider/pythonx.vim
@@ -57,7 +57,8 @@ function! provider#pythonx#Detect(major_ver) abort
if exists('g:python3_host_prog')
return [g:python3_host_prog, '']
else
- let progs = ['python3', 'python3.5', 'python3.4', 'python3.3', 'python']
+ let progs = ['python3', 'python3.7', 'python3.6', 'python3.5',
+ \ 'python3.4', 'python3.3', 'python']
endif
endif
diff --git a/runtime/autoload/shada.vim b/runtime/autoload/shada.vim
index 9be85b6f2e..cf27ee608a 100644
--- a/runtime/autoload/shada.vim
+++ b/runtime/autoload/shada.vim
@@ -241,8 +241,6 @@ function s:shada_check_type(type, val) abort
if msg isnot# 0
return msg
endif
- if a:val > 0 || a:val < 1
- endif
return 0
elseif a:type is# 'binarray'
if type isnot# 'array'
@@ -359,9 +357,14 @@ function s:shada_string(type, v) abort
if (has_key(s:SHADA_ENUMS, a:type) && type(a:v) == type(0)
\&& has_key(s:SHADA_REV_ENUMS[a:type], a:v))
return s:SHADA_REV_ENUMS[a:type][a:v]
- elseif (a:type is# 'intchar' && type(a:v) == type(0)
- \&& strtrans(nr2char(a:v)) is# nr2char(a:v))
- return "'" . nr2char(a:v) . "'"
+ " Restricting a:v to be <= 127 is not necessary, but intchar constants are
+ " normally expected to be either ASCII printable characters or NUL.
+ elseif a:type is# 'intchar' && type(a:v) == type(0) && a:v >= 0 && a:v <= 127
+ if a:v > 0 && strtrans(nr2char(a:v)) is# nr2char(a:v)
+ return "'" . nr2char(a:v) . "'"
+ else
+ return "'\\" . a:v . "'"
+ endif
else
return msgpack#string(a:v)
endif
diff --git a/runtime/doc/api-funcs.txt b/runtime/doc/api-funcs.txt
new file mode 100644
index 0000000000..706941fd12
--- /dev/null
+++ b/runtime/doc/api-funcs.txt
@@ -0,0 +1,716 @@
+*api-funcs.txt* Neovim API Function Reference {Nvim}
+
+Note: This documentation is generated from Neovim's API source code.
+
+Contents:
+
+1. Global Functions |api-global|
+2. Buffer Functions |api-buffer|
+3. Window Functions |api-window|
+4. Tabpage Functions |api-tabpage|
+5. UI Functions |api-ui|
+
+==============================================================================
+1. Global Functions *api-global*
+
+nvim_command({command}) *nvim_command()*
+ Executes an ex-command. On VimL error: Returns the VimL error;
+ v:errmsg is not updated.
+
+ Parameters:~
+ {command} Ex-command string
+
+nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()*
+ Passes input keys to Nvim. On VimL error: Does not fail, but
+ updates v:errmsg.
+
+ Parameters:~
+ {keys} to be typed
+ {mode} mapping options
+ {escape_csi} If true, escape K_SPECIAL/CSI bytes in
+ `keys`
+
+nvim_input({keys}) *nvim_input()*
+ Passes keys to Nvim as raw user-input. On VimL error: Does not
+ fail, but updates v:errmsg.
+
+ Unlike `nvim_feedkeys`, this uses a lower-level input buffer
+ and the call is not deferred. This is the most reliable way to
+ emulate real user input.
+
+ Parameters:~
+ {keys} to be typed
+
+ Return:~
+ Number of bytes actually written (can be fewer than
+ requested if the buffer becomes full).
+
+ *nvim_replace_termcodes()*
+nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
+ Replaces any terminal codes with the internal representation
+
+nvim_command_output({str}) *nvim_command_output()*
+ TODO: Documentation
+
+nvim_eval({expr}) *nvim_eval()*
+ Evaluates a VimL expression (:help expression). Dictionaries
+ and Lists are recursively expanded. On VimL error: Returns a
+ generic error; v:errmsg is not updated.
+
+ Parameters:~
+ {expr} VimL expression string
+
+ Return:~
+ Evaluation result or expanded object
+
+nvim_call_function({fname}, {args}) *nvim_call_function()*
+ Calls a VimL function with the given arguments. On VimL error:
+ Returns a generic error; v:errmsg is not updated.
+
+ Parameters:~
+ {fname} Function to call
+ {args} Function arguments packed in an Array
+
+ Return:~
+ Result of the function call
+
+nvim_strwidth({str}) *nvim_strwidth()*
+ Calculates the number of display cells occupied by `text`.
+ <Tab> counts as one cell.
+
+ Parameters:~
+ {text} Some text
+
+ Return:~
+ Number of cells
+
+nvim_list_runtime_paths() *nvim_list_runtime_paths()*
+ Gets the paths contained in 'runtimepath'.
+
+ Return:~
+ List of paths
+
+nvim_set_current_dir({dir}) *nvim_set_current_dir()*
+ Changes the global working directory.
+
+ Parameters:~
+ {dir} Directory path
+
+nvim_get_current_line() *nvim_get_current_line()*
+ Gets the current line
+
+ Parameters:~
+
+ Return:~
+ Current line string
+
+nvim_set_current_line({line}) *nvim_set_current_line()*
+ Sets the current line
+
+ Parameters:~
+ {line} Line contents
+
+nvim_del_current_line() *nvim_del_current_line()*
+ Deletes the current line
+
+ Parameters:~
+
+nvim_get_var({name}) *nvim_get_var()*
+ Gets a global (g:) variable
+
+ Parameters:~
+ {name} Variable name
+
+ Return:~
+ Variable value
+
+nvim_set_var({name}, {value}) *nvim_set_var()*
+ Sets a global (g:) variable
+
+ Parameters:~
+ {name} Variable name
+ {value} Variable value
+
+nvim_del_var({name}) *nvim_del_var()*
+ Removes a global (g:) variable
+
+ Parameters:~
+ {name} Variable name
+
+nvim_get_vvar({name}) *nvim_get_vvar()*
+ Gets a v: variable
+
+ Parameters:~
+ {name} Variable name
+
+ Return:~
+ Variable value
+
+nvim_get_option({name}) *nvim_get_option()*
+ Gets an option value string
+
+ Parameters:~
+ {name} Option name
+
+ Return:~
+ Option value
+
+nvim_set_option({name}, {value}) *nvim_set_option()*
+ Sets an option value
+
+ Parameters:~
+ {name} Option name
+ {value} New option value
+
+nvim_out_write({str}) *nvim_out_write()*
+ Writes a message to vim output buffer
+
+ Parameters:~
+ {str} Message
+
+nvim_err_write({str}) *nvim_err_write()*
+ Writes a message to vim error buffer
+
+ Parameters:~
+ {str} Message
+
+nvim_err_writeln({str}) *nvim_err_writeln()*
+ Writes a message to vim error buffer. Appends a linefeed to
+ ensure all contents are written.
+
+ Parameters:~
+ {str} Message
+
+nvim_list_bufs() *nvim_list_bufs()*
+ Gets the current list of buffer handles
+
+ Return:~
+ List of buffer handles
+
+nvim_get_current_buf() *nvim_get_current_buf()*
+ Gets the current buffer
+
+ Return:~
+ Buffer handle
+
+nvim_set_current_buf({buffer}) *nvim_set_current_buf()*
+ Sets the current buffer
+
+ Parameters:~
+ {id} Buffer handle
+
+nvim_list_wins() *nvim_list_wins()*
+ Gets the current list of window handles
+
+ Return:~
+ List of window handles
+
+nvim_get_current_win() *nvim_get_current_win()*
+ Gets the current window
+
+ Return:~
+ Window handle
+
+nvim_set_current_win({window}) *nvim_set_current_win()*
+ Sets the current window
+
+ Parameters:~
+ {handle} Window handle
+
+nvim_list_tabpages() *nvim_list_tabpages()*
+ Gets the current list of tabpage handles
+
+ Return:~
+ List of tabpage handles
+
+nvim_get_current_tabpage() *nvim_get_current_tabpage()*
+ Gets the current tabpage
+
+ Return:~
+ Tabpage handle
+
+nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()*
+ Sets the current tabpage
+
+ Parameters:~
+ {handle} Tabpage handle
+
+nvim_subscribe({event}) *nvim_subscribe()*
+ Subscribes to event broadcasts
+
+ Parameters:~
+ {event} Event type string
+
+nvim_unsubscribe({event}) *nvim_unsubscribe()*
+ Unsubscribes to event broadcasts
+
+ Parameters:~
+ {event} Event type string
+
+nvim_get_color_by_name({name}) *nvim_get_color_by_name()*
+ TODO: Documentation
+
+nvim_get_color_map() *nvim_get_color_map()*
+ TODO: Documentation
+
+nvim_get_api_info() *nvim_get_api_info()*
+ TODO: Documentation
+
+nvim_call_atomic({calls}) *nvim_call_atomic()*
+ Call many api methods atomically
+
+ This has two main usages: Firstly, to perform several requests
+ from an async context atomically, i.e. without processing
+ requests from other rpc clients or redrawing or allowing user
+ interaction in between. Note that api methods that could fire
+ autocommands or do event processing still might do so. For
+ instance invoking the :sleep command might call timer
+ callbacks. Secondly, it can be used to reduce rpc overhead
+ (roundtrips) when doing many requests in sequence.
+
+ Parameters:~
+ {calls} an array of calls, where each call is described
+ by an array with two elements: the request name,
+ and an array of arguments.
+
+ Return:~
+ an array with two elements. The first is an array of
+ return values. The second is NIL if all calls succeeded.
+ If a call resulted in an error, it is a three-element
+ array with the zero-based index of the call which resulted
+ in an error, the error type and the error message. If an
+ error ocurred, the values from all preceding calls will
+ still be returned.
+
+
+==============================================================================
+2. Buffer Functions *api-buffer*
+
+nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
+ Gets the buffer line count
+
+ Parameters:~
+ {buffer} Buffer handle
+
+ Return:~
+ Line count
+
+ *nvim_buf_get_lines()*
+nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
+ Retrieves a line range from the buffer
+
+ Indexing is zero-based, end-exclusive. Negative indices are
+ interpreted as length+1+index, i e -1 refers to the index past
+ the end. So to get the last element set start=-2 and end=-1.
+
+ Out-of-bounds indices are clamped to the nearest valid value,
+ unless `strict_indexing` is set.
+
+ Parameters:~
+ {buffer} Buffer handle
+ {start} First line index
+ {end} Last line index (exclusive)
+ {strict_indexing} Whether out-of-bounds should be an
+ error.
+
+ Return:~
+ Array of lines
+
+ *nvim_buf_set_lines()*
+nvim_buf_set_lines({buffer}, {start}, {end}, {strict_indexing},
+ {replacement})
+ Replaces line range on the buffer
+
+ Indexing is zero-based, end-exclusive. Negative indices are
+ interpreted as length+1+index, i e -1 refers to the index past
+ the end. So to change or delete the last element set start=-2
+ and end=-1.
+
+ To insert lines at a given index, set both start and end to
+ the same index. To delete a range of lines, set replacement to
+ an empty array.
+
+ Out-of-bounds indices are clamped to the nearest valid value,
+ unless `strict_indexing` is set.
+
+ Parameters:~
+ {buffer} Buffer handle
+ {start} First line index
+ {end} Last line index (exclusive)
+ {strict_indexing} Whether out-of-bounds should be an
+ error.
+ {replacement} Array of lines to use as replacement
+
+nvim_buf_get_var({buffer}, {name}) *nvim_buf_get_var()*
+ Gets a buffer-scoped (b:) variable.
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Variable name
+
+ Return:~
+ Variable value
+
+nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
+ Sets a buffer-scoped (b:) variable
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Variable name
+ {value} Variable value
+
+nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
+ Removes a buffer-scoped (b:) variable
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Variable name
+
+nvim_buf_get_option({buffer}, {name}) *nvim_buf_get_option()*
+ Gets a buffer option value
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Option name
+
+ Return:~
+ Option value
+
+nvim_buf_set_option({buffer}, {name}, {value}) *nvim_buf_set_option()*
+ Sets a buffer option value. Passing 'nil' as value deletes the
+ option (only works if there's a global fallback)
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Option name
+ {value} Option value
+
+nvim_buf_get_number({buffer}) *nvim_buf_get_number()*
+ Gets the buffer number
+
+ Parameters:~
+ {buffer} Buffer handle
+
+ Return:~
+ Buffer number
+
+nvim_buf_get_name({buffer}) *nvim_buf_get_name()*
+ Gets the full file name for the buffer
+
+ Parameters:~
+ {buffer} Buffer handle
+
+ Return:~
+ Buffer name
+
+nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()*
+ Sets the full file name for a buffer
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Buffer name
+
+nvim_buf_is_valid({buffer}) *nvim_buf_is_valid()*
+ Checks if a buffer is valid
+
+ Parameters:~
+ {buffer} Buffer handle
+
+ Return:~
+ true if the buffer is valid, false otherwise
+
+nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
+ Return a tuple (row,col) representing the position of the
+ named mark
+
+ Parameters:~
+ {buffer} Buffer handle
+ {name} Mark name
+
+ Return:~
+ (row, col) tuple
+
+ *nvim_buf_add_highlight()*
+nvim_buf_add_highlight({buffer}, {src_id}, {hl_group}, {line},
+ {col_start}, {col_end})
+ Adds a highlight to buffer.
+
+ This can be used for plugins which 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.
+
+ "src_id" is useful for batch deletion/updating of a set of
+ highlights. When called with src_id = 0, an unique source id
+ is generated and returned. Succesive calls can pass in it as
+ "src_id" to add new highlights to the same source group. All
+ highlights in the same group can then be cleared with
+ nvim_buf_clear_highlight. If the highlight never will be
+ manually deleted pass in -1 for "src_id".
+
+ If "hl_group" is the empty string no highlight is added, but a
+ new src_id is still returned. This is useful for an external
+ plugin to synchrounously request an unique src_id at
+ initialization, and later asynchronously add and clear
+ highlights in response to buffer changes.
+
+ Parameters:~
+ {buffer} Buffer handle
+ {src_id} Source group to use or 0 to use a new group,
+ or -1 for ungrouped highlight
+ {hl_group} Name of the highlight group to use
+ {line} Line to highlight
+ {col_start} Start of range of columns to highlight
+ {col_end} End of range of columns to highlight, or -1
+ to highlight to end of line
+
+ Return:~
+ The src_id that was used
+
+ *nvim_buf_clear_highlight()*
+nvim_buf_clear_highlight({buffer}, {src_id}, {line_start}, {line_end})
+ Clears highlights from a given source group and a range of
+ lines
+
+ To clear a source group in the entire buffer, pass in 1 and -1
+ to line_start and line_end respectively.
+
+ Parameters:~
+ {buffer} Buffer handle
+ {src_id} Highlight source group to clear, or -1 to
+ clear all.
+ {line_start} Start of range of lines to clear
+ {line_end} End of range of lines to clear (exclusive)
+ or -1 to clear to end of file.
+
+
+==============================================================================
+3. Window Functions *api-window*
+
+nvim_win_get_buf({window}) *nvim_win_get_buf()*
+ Gets the current buffer in a window
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ Buffer handle
+
+nvim_win_get_cursor({window}) *nvim_win_get_cursor()*
+ Gets the cursor position in the window
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ (row, col) tuple
+
+nvim_win_set_cursor({window}, {pos}) *nvim_win_set_cursor()*
+ Sets the cursor position in the window
+
+ Parameters:~
+ {window} Window handle
+ {pos} (row, col) tuple representing the new position
+
+nvim_win_get_height({window}) *nvim_win_get_height()*
+ Gets the window height
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ Height as a count of rows
+
+nvim_win_set_height({window}, {height}) *nvim_win_set_height()*
+ Sets the window height. This will only succeed if the screen
+ is split horizontally.
+
+ Parameters:~
+ {window} Window handle
+ {height} Height as a count of rows
+
+nvim_win_get_width({window}) *nvim_win_get_width()*
+ Gets the window width
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ Width as a count of columns
+
+nvim_win_set_width({window}, {width}) *nvim_win_set_width()*
+ Sets the window width. This will only succeed if the screen is
+ split vertically.
+
+ Parameters:~
+ {window} Window handle
+ {width} Width as a count of columns
+
+nvim_win_get_var({window}, {name}) *nvim_win_get_var()*
+ Gets a window-scoped (w:) variable
+
+ Parameters:~
+ {window} Window handle
+ {name} Variable name
+
+ Return:~
+ Variable value
+
+nvim_win_set_var({window}, {name}, {value}) *nvim_win_set_var()*
+ Sets a window-scoped (w:) variable
+
+ Parameters:~
+ {window} Window handle
+ {name} Variable name
+ {value} Variable value
+
+nvim_win_del_var({window}, {name}) *nvim_win_del_var()*
+ Removes a window-scoped (w:) variable
+
+ Parameters:~
+ {window} Window handle
+ {name} Variable name
+
+nvim_win_get_option({window}, {name}) *nvim_win_get_option()*
+ Gets a window option value
+
+ Parameters:~
+ {window} Window handle
+ {name} Option name
+
+ Return:~
+ Option value
+
+nvim_win_set_option({window}, {name}, {value}) *nvim_win_set_option()*
+ Sets a window option value. Passing 'nil' as value deletes the
+ option(only works if there's a global fallback)
+
+ Parameters:~
+ {window} Window handle
+ {name} Option name
+ {value} Option value
+
+nvim_win_get_position({window}) *nvim_win_get_position()*
+ Gets the window position in display cells. First position is
+ zero.
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ (row, col) tuple with the window position
+
+nvim_win_get_tabpage({window}) *nvim_win_get_tabpage()*
+ Gets the window tabpage
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ Tabpage that contains the window
+
+nvim_win_get_number({window}) *nvim_win_get_number()*
+ Gets the window number
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ Window number
+
+nvim_win_is_valid({window}) *nvim_win_is_valid()*
+ Checks if a window is valid
+
+ Parameters:~
+ {window} Window handle
+
+ Return:~
+ true if the window is valid, false otherwise
+
+
+==============================================================================
+4. Tabpage Functions *api-tabpage*
+
+nvim_tabpage_list_wins({tabpage}) *nvim_tabpage_list_wins()*
+ Gets the windows in a tabpage
+
+ Parameters:~
+ {tabpage} Tabpage
+
+ Return:~
+ List of windows in tabpage
+
+nvim_tabpage_get_var({tabpage}, {name}) *nvim_tabpage_get_var()*
+ Gets a tab-scoped (t:) variable
+
+ Parameters:~
+ {tabpage} Tabpage handle
+ {name} Variable name
+
+ Return:~
+ Variable value
+
+nvim_tabpage_set_var({tabpage}, {name}, {value}) *nvim_tabpage_set_var()*
+ Sets a tab-scoped (t:) variable
+
+ Parameters:~
+ {tabpage} Tabpage handle
+ {name} Variable name
+ {value} Variable value
+
+nvim_tabpage_del_var({tabpage}, {name}) *nvim_tabpage_del_var()*
+ Removes a tab-scoped (t:) variable
+
+ Parameters:~
+ {tabpage} Tabpage handle
+ {name} Variable name
+
+nvim_tabpage_get_win({tabpage}) *nvim_tabpage_get_win()*
+ Gets the current window in a tabpage
+
+ Parameters:~
+ {tabpage} Tabpage handle
+
+ Return:~
+ Window handle
+
+nvim_tabpage_get_number({tabpage}) *nvim_tabpage_get_number()*
+ Gets the tabpage number
+
+ Parameters:~
+ {tabpage} Tabpage handle
+
+ Return:~
+ Tabpage number
+
+nvim_tabpage_is_valid({tabpage}) *nvim_tabpage_is_valid()*
+ Checks if a tabpage is valid
+
+ Parameters:~
+ {tabpage} Tabpage handle
+
+ Return:~
+ true if the tabpage is valid, false otherwise
+
+
+==============================================================================
+5. UI Functions *api-ui*
+
+remote_ui_disconnect() *remote_ui_disconnect()*
+ TODO: Documentation
+
+nvim_ui_attach({width}, {height}, {options}) *nvim_ui_attach()*
+ TODO: Documentation
+
+nvim_ui_detach() *nvim_ui_detach()*
+ TODO: Documentation
+
+nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()*
+ TODO: Documentation
+
+nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
+ TODO: Documentation
+
+ vim:tw=78:ts=8:ft=help:norl: \ No newline at end of file
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index 4f23aee83d..180127cd52 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -1,4 +1,4 @@
-*autocmd.txt* For Vim version 7.4. Last change: 2016 Mar 26
+*autocmd.txt* For Vim version 7.4. Last change: 2016 Jun 09
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -49,9 +49,6 @@ effects. Be careful not to destroy your text.
==============================================================================
2. Defining autocommands *autocmd-define*
-Note: The ":autocmd" command cannot be followed by another command, since any
-'|' is considered part of the command.
-
*:au* *:autocmd*
:au[tocmd] [group] {event} {pat} [nested] {cmd}
Add {cmd} to the list of commands that Vim will
@@ -64,6 +61,12 @@ Note: The ":autocmd" command cannot be followed by another command, since any
The special pattern <buffer> or <buffer=N> defines a buffer-local autocommand.
See |autocmd-buflocal|.
+Note: The ":autocmd" command can only be followed by another command when the
+'|' appears before {cmd}. This works: >
+ :augroup mine | au! BufRead | augroup END
+But this sees "augroup" as part of the defined command: >
+ :augroup mine | au BufRead * set tw=70 | augroup END
+
Note that special characters (e.g., "%", "<cword>") in the ":autocmd"
arguments are not expanded when the autocommand is defined. These will be
expanded when the Event is recognized, and the {cmd} is executed. The only
@@ -270,8 +273,10 @@ Name triggered by ~
|VimLeave| before exiting Vim, after writing the shada file
Various
+|DirChanged| after the |current-directory| was changed
+
|FileChangedShell| Vim notices that a file changed since editing started
-|FileChangedShellPost| After handling a file changed since editing started
+|FileChangedShellPost| after handling a file changed since editing started
|FileChangedRO| before making the first change to a read-only file
|ShellCmdPost| after executing a shell command
@@ -291,6 +296,7 @@ Name triggered by ~
|CursorMoved| the cursor was moved in Normal mode
|CursorMovedI| the cursor was moved in Insert mode
+|WinNew| after creating a new window
|WinEnter| after entering another window
|WinLeave| before leaving a window
|TabEnter| after entering another tab page
@@ -541,6 +547,9 @@ CursorHold When the user doesn't press a key for the time
*CursorHoldI*
CursorHoldI Just like CursorHold, but in Insert mode.
+ Not triggered when waiting for another key,
+ e.g. after CTRL-V, and not when in CTRL-X mode
+ |insert_expand|.
*CursorMoved*
CursorMoved After the cursor was moved in Normal or Visual
@@ -556,6 +565,12 @@ CursorMoved After the cursor was moved in Normal or Visual
CursorMovedI After the cursor was moved in Insert mode.
Not triggered when the popup menu is visible.
Otherwise the same as CursorMoved.
+ *DirChanged*
+DirChanged After the |current-directory| was changed.
+ Sets these |v:event| keys:
+ cwd: current working directory
+ scope: "global", "tab", "window"
+ Recursion is ignored.
*FileAppendCmd*
FileAppendCmd Before appending to a file. Should do the
appending to the file. Use the '[ and ']
@@ -720,13 +735,12 @@ InsertCharPre When a character is typed in Insert mode,
*TextYankPost*
TextYankPost Just after a |yank| or |deleting| command, but not
if the black hole register |quote_| is used nor
- for |setreg()|. Pattern must be * because its
- meaning may change in the future.
+ for |setreg()|. Pattern must be *.
Sets these |v:event| keys:
- operator
- regcontents
- regname
- regtype
+ operator
+ regcontents
+ regname
+ regtype
Recursion is ignored.
It is not allowed to change the text |textlock|.
*InsertEnter*
@@ -986,6 +1000,11 @@ WinLeave Before leaving a window. If the window to be
WinLeave autocommands (but not for ":new").
Not used for ":qa" or ":q" when exiting Vim.
+ *WinNew*
+WinNew When a new window was created. Not done for
+ the fist window, when Vim has just started.
+ Before a WinEnter event.
+
==============================================================================
6. Patterns *autocmd-patterns* *{pat}*
@@ -1223,6 +1242,8 @@ option will not cause any commands to be executed.
argument is present. You probably want to use
<nomodeline> for events that are not used when loading
a buffer, such as |User|.
+ Processing modelines is also skipped when no
+ matching autocommands were executed.
*:doautoa* *:doautoall*
:doautoa[ll] [<nomodeline>] [group] {event} [fname]
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 2ccb9188a9..31a46f53bb 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -1,4 +1,4 @@
-*change.txt* For Vim version 7.4. Last change: 2016 Mar 08
+*change.txt* For Vim version 7.4. Last change: 2016 Apr 12
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -813,7 +813,7 @@ Examples: >
:s/abcde/abc^Mde/ modifies "abcde" to "abc", "de" (two lines)
:s/$/\^M/ modifies "abcde" to "abcde^M"
:s/\w\+/\u\0/g modifies "bla bla" to "Bla Bla"
- :s/\w\+/\L\u/g modifies "BLA bla" to "Bla Bla"
+ :s/\w\+/\L\u\0/g modifies "BLA bla" to "Bla Bla"
Note: "\L\u" can be used to capitalize the first letter of a word. This is
not compatible with Vi and older versions of Vim, where the "\u" would cancel
@@ -1164,7 +1164,7 @@ which does not specify a register. Additionally you can access it with the
name '"'. This means you have to type two double quotes. Writing to the ""
register writes to register "0.
-2. Numbered registers "0 to "9 *quote_number* *quote0* *quote1*
+2. Numbered registers "0 to "9 *quote_number* *quote0* *quote1*
*quote2* *quote3* *quote4* *quote9*
Vim fills these registers with text from yank and delete commands.
Numbered register 0 contains the text from the most recent yank command,
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
index 06dd21de06..5bfffac1f1 100644
--- a/runtime/doc/editing.txt
+++ b/runtime/doc/editing.txt
@@ -1152,7 +1152,7 @@ Examples: >
If you want to always use ":confirm", set the 'confirm' option.
- *:browse* *:bro* *E338* *E614* *E615* *E616* *E578*
+ *:browse* *:bro* *E338* *E614* *E615* *E616*
:bro[wse] {command} Open a file selection dialog for an argument to
{command}. At present this works for |:e|, |:w|,
|:wall|, |:wq|, |:wqall|, |:x|, |:xall|, |:exit|,
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 0a5a51a0e1..69c8d0285a 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -783,7 +783,7 @@ A |Dictionary| can only be compared with a |Dictionary| and only "equal", "not
equal" and "is" can be used. This compares the key/values of the |Dictionary|
recursively. Ignoring case means case is ignored when comparing item values.
- *E693* *E694*
+ *E694*
A |Funcref| can only be compared with a |Funcref| and only "equal" and "not
equal" can be used. Case is never ignored.
@@ -1470,7 +1470,7 @@ v:exception The value of the exception most recently caught and not
*v:false* *false-variable*
v:false Special value used to put "false" in JSON and msgpack. See
- |json_encode()|. This value is converted to "false" when used
+ |json_encode()|. This value is converted to "v:false" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
@@ -1620,7 +1620,7 @@ v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()|
*v:null* *null-variable*
v:null Special value used to put "null" in JSON and NIL in msgpack.
- See |json_encode()|. This value is converted to "null" when
+ See |json_encode()|. This value is converted to "v:null" when
used as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
@@ -1807,7 +1807,7 @@ v:throwpoint The point where the exception most recently caught and not
*v:true* *true-variable*
v:true Special value used to put "true" in JSON and msgpack. See
- |json_encode()|. This value is converted to "true" when used
+ |json_encode()|. This value is converted to "v:true" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to one when used as a Number (e.g. in |expr5| or
|expr7| when used with numeric operators).
@@ -2174,14 +2174,17 @@ sqrt({expr}) Float square root of {expr}
str2float({expr}) Float convert String to Float
str2nr({expr} [, {base}]) Number convert String to Number
strchars({expr} [, {skipcc}]) Number character length of the String {expr}
+strcharpart({str}, {start}[, {len}])
+ String {len} characters of {str} at {start}
strdisplaywidth({expr} [, {col}]) Number display length of the String {expr}
strftime({format}[, {time}]) String time in specified format
+strgetchar({str}, {index}) Number get char {index} from {str}
stridx({haystack}, {needle}[, {start}])
Number index of {needle} in {haystack}
string({expr}) String String representation of {expr} value
strlen({expr}) Number length of the String {expr}
-strpart({src}, {start}[, {len}])
- String {len} characters of {src} at {start}
+strpart({str}, {start}[, {len}])
+ String {len} characters of {str} at {start}
strridx({haystack}, {needle} [, {start}])
Number last index of {needle} in {haystack}
strtrans({expr}) String translate string to make it printable
@@ -2591,7 +2594,9 @@ byteidx({expr}, {nr}) *byteidx()*
same: >
let s = strpart(str, byteidx(str, 3))
echo strpart(s, 0, byteidx(s, 1))
-< If there are less than {nr} characters -1 is returned.
+< Also see |strgetchar()| and |strcharpart()|.
+
+ If there are less than {nr} characters -1 is returned.
If there are exactly {nr} characters the length of the string
in bytes is returned.
@@ -3065,7 +3070,7 @@ executable({expr}) *executable()*
0 does not exist
-1 not implemented on this system
-execute({command}) *execute()*
+execute({command} [, {silent}]) *execute()*
Execute {command} and capture its output.
If {command} is a |String|, returns {command} output.
If {command} is a |List|, returns concatenated outputs.
@@ -3074,10 +3079,17 @@ execute({command}) *execute()*
< foo >
echo execute(['echon "foo"', 'echon "bar"'])
< foobar
+
+ The optional {silent} argument can have these values:
+ "" no `:silent` used
+ "silent" `:silent` used
+ "silent!" `:silent!` used
+ The default is 'silent'. Note that with "silent!", unlike
+ `:redir`, error messages are dropped.
+
This function is not available in the |sandbox|.
- Note: {command} executes as if prepended with |:silent|
- (output is collected but not displayed). If nested, an outer
- execute() will not observe output of the inner calls.
+ Note: If nested, an outer execute() will not observe output of
+ the inner calls.
Note: Text attributes (highlights) are not captured.
exepath({expr}) *exepath()*
@@ -3330,6 +3342,10 @@ feedkeys({string} [, {mode}]) *feedkeys()*
will behave as if <Esc> is typed, to avoid getting
stuck, waiting for a character to be typed before the
script continues.
+ '!' When used with 'x' will not end Insert mode. Can be
+ used in a test when a timer is set to exit Insert mode
+ a little later. Useful for testing CursorHoldI.
+
Return value is always 0.
filereadable({file}) *filereadable()*
@@ -3525,7 +3541,7 @@ foreground() Move the Vim window to the foreground. Useful when sent from
{only in the Win32 GUI and console version}
- *function()* *E700* *E922* *E929*
+ *function()* *E700* *E922* *E923*
function({name} [, {arglist}] [, {dict}])
Return a |Funcref| variable that refers to function {name}.
{name} can be a user defined function or an internal function.
@@ -4088,16 +4104,21 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()*
The result is a String, which is the contents of register
{regname}. Example: >
:let cliptext = getreg('*')
-< getreg('=') returns the last evaluated value of the expression
+< When {regname} was not set the result is a empty string.
+
+ getreg('=') returns the last evaluated value of the expression
register. (For use in maps.)
getreg('=', 1) returns the expression itself, so that it can
be restored with |setreg()|. For other registers the extra
argument is ignored, thus you can always give it.
- If {list} is present and non-zero result type is changed to
- |List|. Each list item is one text line. Use it if you care
+
+ If {list} is present and non-zero, the result type is changed
+ to |List|. Each list item is one text line. Use it if you care
about zero bytes possibly present inside register: without
third argument both NLs and zero bytes are represented as NLs
(see |NL-used-for-Nul|).
+ When the register was not set an empty list is returned.
+
If {regname} is not specified, |v:register| is used.
@@ -4645,11 +4666,16 @@ jobstart({cmd}[, {opts}]) {Nvim} *jobstart()*
Spawns {cmd} as a job. If {cmd} is a |List| it is run
directly. If {cmd} is a |String| it is processed like this: >
:call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
-< NOTE: read |shell-unquoting| before constructing any lists
- with 'shell' or 'shellcmdflag' options. The above call is
- only written to show the idea, one needs to perform unquoting
- and do split taking quotes into account.
-
+< NOTE: This only shows the idea; see |shell-unquoting| before
+ constructing lists with 'shell' or 'shellcmdflag'.
+
+ NOTE: On Windows if {cmd} is a List, cmd[0] must be a valid
+ executable (.exe, .com). If the executable is in $PATH it can
+ be called by name, with or without an extension: >
+ :call jobstart(['ping', 'neovim.io'])
+< If it is a path (not a name), it must include the extension: >
+ :call jobstart(['System32\ping.exe', 'neovim.io'])
+<
{opts} is a dictionary with these keys:
on_stdout: stdout event handler (function name or |Funcref|)
on_stderr: stderr event handler (function name or |Funcref|)
@@ -5635,7 +5661,6 @@ pumvisible() *pumvisible()*
This can be used to avoid some things that would remove the
popup menu.
- *E860*
py3eval({expr}) *py3eval()*
Evaluate Python expression {expr} and return its result
converted to Vim data structures.
@@ -6761,7 +6786,6 @@ strchars({expr} [, {skipcc}]) *strchars()*
counted separately.
When {skipcc} set to 1, Composing characters are ignored.
Also see |strlen()|, |strdisplaywidth()| and |strwidth()|.
-
{skipcc} is only available after 7.4.755. For backward
compatibility, you can define a wrapper function: >
@@ -6779,6 +6803,13 @@ strchars({expr} [, {skipcc}]) *strchars()*
endfunction
endif
<
+strcharpart({src}, {start}[, {len}]) *strcharpart()*
+ Like |strpart()| but using character index and length instead
+ of byte index and length.
+ When a character index is used where a character does not
+ exist it is assumed to be one byte. For example: >
+ strcharpart('abc', -1, 2)
+< results in 'a'.
strdisplaywidth({expr}[, {col}]) *strdisplaywidth()*
The result is a Number, which is the number of display cells
@@ -6812,6 +6843,12 @@ strftime({format} [, {time}]) *strftime()*
< Not available on all systems. To check use: >
:if exists("*strftime")
+strgetchar({str}, {index}) *strgetchar()*
+ Get character {index} from {str}. This uses a character
+ index, not a byte index. Composing characters are considered
+ separate characters here.
+ Also see |strcharpart()| and |strchars()|.
+
stridx({haystack}, {needle} [, {start}]) *stridx()*
The result is a Number, which gives the byte index in
{haystack} of the first occurrence of the String {needle}.
@@ -6866,14 +6903,17 @@ strlen({expr}) The result is a Number, which is the length of the String
strpart({src}, {start}[, {len}]) *strpart()*
The result is a String, which is part of {src}, starting from
byte {start}, with the byte length {len}.
- When non-existing bytes are included, this doesn't result in
- an error, the bytes are simply omitted.
+ To count characters instead of bytes use |strcharpart()|.
+
+ When bytes are selected which do not exist, this doesn't
+ result in an error, the bytes are simply omitted.
If {len} is missing, the copy continues from {start} till the
end of the {src}. >
strpart("abcdefg", 3, 2) == "de"
strpart("abcdefg", -2, 4) == "ab"
strpart("abcdefg", 5, 4) == "fg"
strpart("abcdefg", 3) == "defg"
+
< Note: To get the first character, {start} must be 0. For
example, to get three bytes under and after the cursor: >
strpart(getline("."), col(".") - 1, 3)
@@ -6979,9 +7019,9 @@ synID({lnum}, {col}, {trans}) *synID()*
that's where the cursor can be in Insert mode, synID() returns
zero.
- When {trans} is non-zero, transparent items are reduced to the
+ When {trans} is |TRUE|, transparent items are reduced to the
item that they reveal. This is useful when wanting to know
- the effective color. When {trans} is zero, the transparent
+ the effective color. When {trans} is |FALSE|, the transparent
item is returned. This is useful when wanting to know which
syntax item is effective (e.g. inside parens).
Warning: This function can be very slow. Best speed is
@@ -7064,9 +7104,8 @@ synstack({lnum}, {col}) *synstack()*
valid positions.
system({cmd} [, {input}]) *system()* *E677*
- Get the output of the shell command {cmd} as a |string|. {cmd}
- will be run the same as in |jobstart()|. See |systemlist()|
- to get the output as a |List|.
+ Get the output of {cmd} as a |string| (use |systemlist()| to
+ get a |List|). {cmd} is treated exactly as in |jobstart()|.
Not to be used for interactive commands.
If {input} is a string it is written to a pipe and passed as
@@ -8499,14 +8538,6 @@ This does NOT work: >
endfor
< Note that reordering the list (e.g., with sort() or
reverse()) may have unexpected effects.
- Note that the type of each list item should be
- identical to avoid errors for the type of {var}
- changing. Unlet the variable at the end of the loop
- to allow multiple item types: >
- for item in ["foo", ["bar"]]
- echo item
- unlet item " E706 without this
- endfor
:for [{var1}, {var2}, ...] in {listlist}
:endfo[r]
diff --git a/runtime/doc/if_cscop.txt b/runtime/doc/if_cscop.txt
index 99d1fe42e1..7482f5eebb 100644
--- a/runtime/doc/if_cscop.txt
+++ b/runtime/doc/if_cscop.txt
@@ -96,8 +96,8 @@ command does the same and also splits the window (short: "scs").
The available subcommands are:
- *E563* *E564* *E566* *E568* *E569* *E622* *E623*
- *E625* *E626* *E609*
+ *E563* *E564* *E566* *E568* *E622* *E623* *E625*
+ *E626* *E609*
add : Add a new cscope database/connection.
USAGE :cs add {file|dir} [pre-path] [flags]
diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt
index 8946dd2e5a..b6fe234de4 100644
--- a/runtime/doc/if_pyth.txt
+++ b/runtime/doc/if_pyth.txt
@@ -699,7 +699,7 @@ if the `:py3` command is working: >
:py3 print("Hello")
< *:py3file*
The `:py3file` command works similar to `:pyfile`.
- *:py3do* *E863*
+ *:py3do*
The `:py3do` command works similar to `:pydo`.
*E880*
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 1f4557fe30..7388652f16 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1,4 +1,4 @@
-*index.txt* For Vim version 7.4. Last change: 2016 Mar 12
+*index.txt* For Vim version 7.4. Last change: 2016 Jun 12
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -48,6 +48,7 @@ tag char action in Insert mode ~
|i_CTRL-G_k| CTRL-G k line up, to column where inserting started
|i_CTRL-G_k| CTRL-G <Up> line up, to column where inserting started
|i_CTRL-G_u| CTRL-G u start new undoable edit
+|i_CTRL-G_U| CTRL-G U don't break undo with next cursor movement
|i_<BS>| <BS> delete character before the cursor
|i_digraph| {char1}<BS>{char2}
enter digraph (only when 'digraph' option set)
@@ -857,6 +858,7 @@ tag command note action in Visual mode ~
------------------------------------------------------------------------------
|v_CTRL-\_CTRL-N| CTRL-\ CTRL-N stop Visual mode
|v_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to mode specified with 'insertmode'
+|v_CTRL-A| CTRL-A 2 add N to number in highlighted text
|v_CTRL-C| CTRL-C stop Visual mode
|v_CTRL-G| CTRL-G toggle between Visual mode and Select mode
|v_<BS>| <BS> 2 Select mode: delete highlighted area
@@ -865,6 +867,7 @@ tag command note action in Visual mode ~
command
|v_CTRL-V| CTRL-V make Visual mode blockwise or stop Visual
mode
+|v_CTRL-X| CTRL-X 2 subtract N from number in highlighted text
|v_<Esc>| <Esc> stop Visual mode
|v_CTRL-]| CTRL-] jump to highlighted tag
|v_!| !{filter} 2 filter the highlighted lines through the
@@ -921,6 +924,8 @@ tag command note action in Visual mode ~
|v_a}| a} same as aB
|v_c| c 2 delete highlighted area and start insert
|v_d| d 2 delete highlighted area
+|v_g_CTRL-A| g CTRL-A 2 add N to number in highlighted text
+|v_g_CTRL-X| g CTRL-X 2 subtract N from number in highlighted text
|v_gJ| gJ 2 join the highlighted lines without
inserting spaces
|v_gq| gq 2 format the highlighted lines
@@ -1147,8 +1152,9 @@ tag command action ~
|:chdir| :chd[ir] change directory
|:checkpath| :che[ckpath] list included files
|:checktime| :checkt[ime] check timestamp of loaded buffers
-|:clist| :cl[ist] list all errors
|:clast| :cla[st] go to the specified error, default last one
+|:clearjumps| :cle[arjumps] clear the jump list
+|:clist| :cl[ist] list all errors
|:close| :clo[se] close current window
|:cmap| :cm[ap] like ":map" but for Command-line mode
|:cmapclear| :cmapc[lear] clear all mappings for Command-line mode
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index 4561020d22..991ecf8fb2 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -1,4 +1,4 @@
-*map.txt* For Vim version 7.4. Last change: 2016 Jan 10
+*map.txt* For Vim version 7.4. Last change: 2016 Jun 10
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1352,7 +1352,7 @@ Possible attributes are:
Note that -range=N and -count=N are mutually exclusive - only one should be
specified.
- *E889* *:command-addr*
+ *:command-addr*
It is possible that the special characters in the range like `.`, `$` or `%`
which by default correspond to the current line, last line and the whole
buffer, relate to arguments, (loaded) buffers, windows or tab pages.
@@ -1408,11 +1408,11 @@ The valid escape sequences are
expands to nothing.
*<mods>*
<mods> The command modifiers, if specified. Otherwise, expands to
- nothing. Supported modifiers are |aboveleft|, |belowright|,
- |botright|, |browse|, |confirm|, |hide|, |keepalt|,
- |keepjumps|, |keepmarks|, |keeppatterns|, |lockmarks|,
- |noswapfile|, |silent|, |tab|, |topleft|, |verbose|, and
- |vertical|.
+ nothing. Supported modifiers are |:aboveleft|, |:belowright|,
+ |:botright|, |:browse|, |:confirm|, |:hide|, |:keepalt|,
+ |:keepjumps|, |:keepmarks|, |:keeppatterns|, |:lockmarks|,
+ |:noswapfile|, |:silent|, |:tab|, |:topleft|, |:verbose|, and
+ |:vertical|.
Examples: >
command! -nargs=+ -complete=file MyEdit
\ for f in expand(<q-args>, 0, 1) |
diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt
index 3bdb682a31..355a1da423 100644
--- a/runtime/doc/mbyte.txt
+++ b/runtime/doc/mbyte.txt
@@ -1,4 +1,4 @@
-*mbyte.txt* For Vim version 7.4. Last change: 2013 May 18
+*mbyte.txt* For Vim version 7.4. Last change: 2016 Jul 21
VIM REFERENCE MANUAL by Bram Moolenaar et al.
@@ -849,6 +849,7 @@ text, you can use the 'keymap' option. This will translate one or more
(English) characters to another (non-English) character. This only happens
when typing text, not when typing Vim commands. This avoids having to switch
between two keyboard settings.
+{only available when compiled with the |+keymap| feature}
The value of the 'keymap' option specifies a keymap file to use. The name of
this file is one of these two:
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 4d6f0719e3..606fd53fee 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -1013,6 +1013,9 @@ CTRL-I Go to [count] newer cursor position in jump list
*:ju* *:jumps*
:ju[mps] Print the jump list (not a motion command).
+ *:cle* *:clearjumps*
+:cle[arjumps] Clear the jump list of the current window.
+
*jumplist*
Jumps are remembered in a jump list. With the CTRL-O and CTRL-I command you
can go to cursor positions before older jumps, and back again. Thus you can
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index e15fb9dc84..d332e0053a 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2682,6 +2682,40 @@ A jump table for the options with a short description can be found at |Q_op|.
It is not allowed to change text or jump to another window while
evaluating 'foldtext' |textlock|.
+ *'formatexpr'* *'fex'*
+'formatexpr' 'fex' string (default "")
+ local to buffer
+ {not available when compiled without the |+eval|
+ feature}
+ Expression which is evaluated to format a range of lines for the |gq|
+ operator or automatic formatting (see 'formatoptions'). When this
+ option is empty 'formatprg' is used.
+
+ The |v:lnum| variable holds the first line to be formatted.
+ The |v:count| variable holds the number of lines to be formatted.
+ The |v:char| variable holds the character that is going to be
+ inserted if the expression is being evaluated due to
+ automatic formatting. This can be empty. Don't insert
+ it yet!
+
+ Example: >
+ :set formatexpr=mylang#Format()
+< This will invoke the mylang#Format() function in the
+ autoload/mylang.vim file in 'runtimepath'. |autoload|
+
+ The expression is also evaluated when 'textwidth' is set and adding
+ text beyond that limit. This happens under the same conditions as
+ when internal formatting is used. Make sure the cursor is kept in the
+ same spot relative to the text then! The |mode()| function will
+ return "i" or "R" in this situation.
+
+ When the expression evaluates to non-zero Vim will fall back to using
+ the internal format mechanism.
+
+ The expression will be evaluated in the |sandbox| when set from a
+ modeline, see |sandbox-option|. That stops the option from working,
+ since changing the buffer text is not allowed.
+
*'formatoptions'* *'fo'*
'formatoptions' 'fo' string (default: "tcqj", Vi default: "vt")
local to buffer
@@ -2720,40 +2754,6 @@ A jump table for the options with a short description can be found at |Q_op|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
- *'formatexpr'* *'fex'*
-'formatexpr' 'fex' string (default "")
- local to buffer
- {not available when compiled without the |+eval|
- feature}
- Expression which is evaluated to format a range of lines for the |gq|
- operator or automatic formatting (see 'formatoptions'). When this
- option is empty 'formatprg' is used.
-
- The |v:lnum| variable holds the first line to be formatted.
- The |v:count| variable holds the number of lines to be formatted.
- The |v:char| variable holds the character that is going to be
- inserted if the expression is being evaluated due to
- automatic formatting. This can be empty. Don't insert
- it yet!
-
- Example: >
- :set formatexpr=mylang#Format()
-< This will invoke the mylang#Format() function in the
- autoload/mylang.vim file in 'runtimepath'. |autoload|
-
- The expression is also evaluated when 'textwidth' is set and adding
- text beyond that limit. This happens under the same conditions as
- when internal formatting is used. Make sure the cursor is kept in the
- same spot relative to the text then! The |mode()| function will
- return "i" or "R" in this situation.
-
- When the expression evaluates to non-zero Vim will fall back to using
- the internal format mechanism.
-
- The expression will be evaluated in the |sandbox| when set from a
- modeline, see |sandbox-option|. That stops the option from working,
- since changing the buffer text is not allowed.
-
*'fsync'* *'fs'*
'fsync' 'fs' boolean (default on)
global
diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt
index 8e50a67847..f3f5bcbd66 100644
--- a/runtime/doc/pattern.txt
+++ b/runtime/doc/pattern.txt
@@ -1,4 +1,4 @@
-*pattern.txt* For Vim version 7.4. Last change: 2016 Apr 26
+*pattern.txt* For Vim version 7.4. Last change: 2016 Jun 08
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -573,7 +573,7 @@ An atom can be followed by an indication of how many times the atom can be
matched and in what way. This is called a multi. See |/multi| for an
overview.
- */star* */\star* *E56*
+ */star* */\star*
* (use \* when 'magic' is not set)
Matches 0 or more of the preceding atom, as many as possible.
Example 'nomagic' matches ~
@@ -593,7 +593,7 @@ overview.
the end of the file and then tries matching "END", backing up one
character at a time.
- */\+* *E57*
+ */\+*
\+ Matches 1 or more of the preceding atom, as many as possible.
Example matches ~
^.\+$ any non-empty line
@@ -608,7 +608,7 @@ overview.
\? Just like \=. Cannot be used when searching backwards with the "?"
command.
- */\{* *E58* *E60* *E554* *E870*
+ */\{* *E60* *E554* *E870*
\{n,m} Matches n to m of the preceding atom, as many as possible
\{n} Matches n of the preceding atom
\{n,} Matches at least n of the preceding atom, as many as possible
@@ -947,14 +947,18 @@ $ At end of pattern or in front of "\|", "\)" or "\n" ('magic' on):
< When 'hlsearch' is set and you move the cursor around and make changes
this will clearly show when the match is updated or not.
To match the text up to column 17: >
- /.*\%17v
-< Column 17 is included, because that's where the "\%17v" matches,
- even though this is a |/zero-width| match. Adding a dot to match the
- next character has the same result: >
- /.*\%17v.
+ /^.*\%17v
+< Column 17 is not included, because this is a |/zero-width| match. To
+ include the column use: >
+ /^.*\%17v.
< This command does the same thing, but also matches when there is no
character in column 17: >
- /.*\%<18v.
+ /^.*\%<18v.
+< Note that without the "^" to anchor the match in the first column,
+ this will also highlight column 17: >
+ /.*\%17v
+< Column 17 is highlighted by 'hlsearch' because there is another match
+ where ".*" matches zero characters.
<
Character classes:
diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt
index 95d6ff7467..d695ac42cb 100644
--- a/runtime/doc/pi_msgpack.txt
+++ b/runtime/doc/pi_msgpack.txt
@@ -128,6 +128,11 @@ msgpack#eval({string}, {dict}) *msgpack#eval()*
always evaluated to |msgpack-special-dict| values, as well as
hexadecimal digits. When evaluating maps order of keys is preserved.
+ Note that in addition to regular integer representations that may be
+ obtained using |msgpack#string()| msgpack#eval() also supports C-style
+ “character” integer constants like `'/'` (equivalent to
+ `char2nr('/')`: `47`). This also allows `'\0'` (number is decimal).
+
*msgpack#equal*
msgpack#equal({msgpack-value}, {msgpack-value}) *msgpack#equal()*
Returns 1 if given values are equal, 0 otherwise. When comparing
diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt
index 2c240fe41f..f740143c61 100644
--- a/runtime/doc/pi_netrw.txt
+++ b/runtime/doc/pi_netrw.txt
@@ -1,4 +1,4 @@
-*pi_netrw.txt* For Vim version 7.4. Last change: 2016 Feb 16
+*pi_netrw.txt* For Vim version 7.4. Last change: 2016 Apr 20
------------------------------------------------
NETRW REFERENCE MANUAL by Charles E. Campbell
@@ -1523,6 +1523,7 @@ the |'isfname'| option (which is global, so netrw doesn't modify it).
Associated setting variables:
|g:netrw_gx| control how gx picks up the text under the cursor
|g:netrw_nogx| prevent gx map while editing
+ |g:netrw_suppress_gx_mesg| controls gx's suppression of browser messages
*netrw_filehandler*
@@ -2929,6 +2930,13 @@ your browsing preferences. (see also: |netrw-settings|)
such as listing, file removal, etc.
default: ssh
+ *g:netrw_suppress_gx_mesg* =1 : browsers sometimes produce messages
+ which are normally unwanted intermixed
+ with the page.
+ However, when using links, for example,
+ those messages are what the browser produces.
+ By setting this option to 0, netrw will not
+ suppress browser messages.
*g:netrw_tmpfile_escape* =' &;'
escape() is applied to all temporary files
@@ -3755,6 +3763,23 @@ netrw:
==============================================================================
12. History *netrw-history* {{{1
+ 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
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index cbb2a23a48..e94723f337 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -474,6 +474,9 @@ expression.
The BufWinEnter event is also triggered, again using "quickfix" for the buffer
name.
+Note: When adding to an existing quickfix list the autocommand are not
+triggered.
+
Note: Making changes in the quickfix window has no effect on the list of
errors. 'modifiable' is off to avoid making changes. If you delete or insert
lines anyway, the relation between the text and the error number is messed up.
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index b44761cdb5..ef98556260 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -1,4 +1,4 @@
-*repeat.txt* For Vim version 7.4. Last change: 2016 Apr 05
+*repeat.txt* For Vim version 7.4. Last change: 2016 Jul 21
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -286,7 +286,9 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
... not converted ...
< When conversion isn't supported by the system, there
- is no error message and no conversion is done.
+ is no error message and no conversion is done. When a
+ line can't be converted there is no error and the
+ original line is kept.
Don't use "ucs-2" or "ucs-4", scripts cannot be in
these encodings (they would contain NUL bytes).
@@ -527,7 +529,7 @@ whether Vim supports a feature or a dependency is missing.
You can also load an optional plugin at startup, by putting this command in
your |.vimrc|: >
:packadd! foodebug
-The extra "!" is so that the plugin isn't loaded with Vim was started with
+The extra "!" is so that the plugin isn't loaded if Vim was started with
|--noplugin|.
It is perfectly normal for a package to only have files in the "opt"
@@ -606,7 +608,7 @@ the command after changing the plugin help: >
Dependencies between plugins ~
*packload-two-steps*
-Suppose you have a two plugins that depend on the same functionality. You can
+Suppose you have two plugins that depend on the same functionality. You can
put the common functionality in an autoload directory, so that it will be
found automatically. Your package would have these files:
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index bbc0260ffa..b58b3c7853 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -1,4 +1,4 @@
-*starting.txt* For Vim version 7.4. Last change: 2016 Apr 05
+*starting.txt* For Vim version 7.4. Last change: 2016 Jun 12
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -1135,7 +1135,7 @@ files for different types of files (e.g., C code) and load them based on the
file name, using the ":autocmd" command (see |:autocmd|). More information on
ShaDa file format is contained in |shada-format| section.
- *E136* *E138* *shada-error-handling*
+ *E136* *E929* *shada-error-handling*
Some errors make Neovim leave temporary file named `{basename}.tmp.X` (X is
any free letter from `a` to `z`) while normally it will create this file,
write to it and then rename `{basename}.tmp.X` to `{basename}`. Such errors
@@ -1155,7 +1155,7 @@ include:
Do not forget to remove the temporary file or replace the target file with
temporary one after getting one of the above errors or all attempts to create
-a ShaDa file may fail with |E138|. If you got one of them when using
+a ShaDa file may fail with |E929|. If you got one of them when using
|:wshada| (and not when exiting Neovim: i.e. when you have Neovim session
running) you have additional options:
@@ -1187,7 +1187,7 @@ running) you have additional options:
internal info is written (also disables safety checks
described in |shada-error-handling|). If 'shada' is
empty, marks for up to 100 files will be written.
- When you get error "E138: All .tmp.X files exist,
+ When you get error "E929: All .tmp.X files exist,
cannot write ShaDa file!" check that no old temp files
were left behind (e.g.
~/.local/share/nvim/shada/main.shada.tmp*).
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 4886828c82..e59f567826 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -1040,6 +1040,21 @@ This works immediately.
CLOJURE *ft-clojure-syntax*
+The default syntax groups can be augmented through the
+*g:clojure_syntax_keywords* and *b:clojure_syntax_keywords* variables. The
+value should be a |Dictionary| of syntax group names to a |List| of custom
+identifiers:
+>
+ let g:clojure_syntax_keywords = {
+ \ 'clojureMacro': ["defproject", "defcustom"],
+ \ 'clojureFunc': ["string/join", "string/replace"]
+ \ }
+<
+Refer to the Clojure syntax script for valid syntax group names.
+
+If the |buffer-variable| *b:clojure_syntax_without_core_keywords* is set, only
+language constants and special forms are matched.
+
Setting *g:clojure_fold* enables folding Clojure code via the syntax engine.
Any list, vector, or map that extends over more than one line can be folded
using the standard Vim |fold-commands|.
@@ -4755,6 +4770,10 @@ font={font-name} *highlight-font*
All fonts used, except for Menu and Tooltip, should be of the same
character size as the default font! Otherwise redrawing problems will
occur.
+ To use a font name with an embedded space or other special character,
+ put it in single quotes. The single quote cannot be used then.
+ Example: >
+ :hi comment font='Monospace 10'
guifg={color-name} *highlight-guifg*
guibg={color-name} *highlight-guibg*
diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt
index 7d47368ba3..08ffee7a2f 100644
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -473,7 +473,7 @@ correct values.
One command can be used to set the screen size:
- *:mod* *:mode* *E359* *E362*
+ *:mod* *:mode*
:mod[e]
Detects the screen size and redraws the screen.
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index 49fcdf0bb7..b0e5386224 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -592,7 +592,7 @@ String manipulation: *string-functions*
match() position where a pattern matches in a string
matchend() position where a pattern match ends in a string
matchstr() match of a pattern in a string
- matchstrpos() match and postions of a pattern in a string
+ matchstrpos() match and positions of a pattern in a string
matchlist() like matchstr() and also return submatches
stridx() first index of a short string in a long string
strridx() last index of a short string in a long string
@@ -602,7 +602,9 @@ String manipulation: *string-functions*
strdisplaywidth() size of string when displayed, deals with tabs
substitute() substitute a pattern match with a string
submatch() get a specific match in ":s" and substitute()
- strpart() get part of a string
+ strpart() get part of a string using byte index
+ strcharpart() get part of a string using char index
+ strgetchar() get character from a string using char index
expand() expand special keywords
iconv() convert text from one encoding to another
byteidx() byte index of a character in a string
@@ -732,11 +734,14 @@ Working with text in the current buffer: *text-functions*
searchpair() find the other end of a start/skip/end
searchpairpos() find the other end of a start/skip/end
searchdecl() search for the declaration of a name
+ getcharsearch() return character search information
+ setcharsearch() set character search information
*system-functions* *file-functions*
System functions and manipulation of files:
glob() expand wildcards
globpath() expand wildcards in a number of directories
+ glob2regpat() convert a glob pattern into a search pattern
findfile() find a file in a list of directories
finddir() find a directory in a list of directories
resolve() find out where a shortcut points to
@@ -748,6 +753,7 @@ System functions and manipulation of files:
filereadable() check if a file can be read
filewritable() check if a file can be written to
getfperm() get the permissions of a file
+ setfperm() set the permissions of a file
getftype() get the kind of a file
isdirectory() check if a directory exists
getfsize() get the size of a file
@@ -786,9 +792,15 @@ Buffers, windows and the argument list:
tabpagenr() get the number of a tab page
tabpagewinnr() like winnr() for a specified tab page
winnr() get the window number for the current window
+ bufwinid() get the window ID of a specific buffer
bufwinnr() get the window number of a specific buffer
winbufnr() get the buffer number of a specific window
getbufline() get a list of lines from the specified buffer
+ win_findbuf() find windows containing a buffer
+ win_getid() get window ID of a window
+ win_gotoid() go to window with ID
+ win_id2tabwin() get tab and window nr from window ID
+ win_id2win() get window nr from window ID
getbufinfo() get a list with buffer information
gettabinfo() get a list with tab page information
getwininfo() get a list with window information
@@ -799,6 +811,7 @@ Command line: *command-line-functions*
setcmdpos() set position of the cursor in the command line
getcmdtype() return the current command-line type
getcmdwintype() return the current command-line window type
+ getcompletion() list of command-line completion matches
Quickfix and location lists: *quickfix-functions*
getqflist() list of quickfix errors
@@ -896,6 +909,7 @@ Mappings: *mapping-functions*
Testing: *test-functions*
assert_equal() assert that two expressions values are equal
assert_notequal() assert that two expressions values are not equal
+ assert_inrange() assert that an expression is inside a range
assert_match() assert that a pattern matches the value
assert_notmatch() assert that a pattern does not match the value
assert_false() assert that an expression is false
@@ -903,6 +917,10 @@ Testing: *test-functions*
assert_exception() assert that a command throws an exception
assert_fails() assert that a function call fails
+Timers: *timer-functions*
+ timer_start() create a timer
+ timer_stop() stop a timer
+
Various: *various-functions*
mode() get current editing mode
visualmode() last visual mode used
@@ -1399,9 +1417,9 @@ Now we can instantiate a Dutch translation object: >
And a German translator: >
:let uk2de = copy(transdict)
- :let uk2de.words = {'one': 'ein', 'two': 'zwei', 'three': 'drei'}
+ :let uk2de.words = {'one': 'eins', 'two': 'zwei', 'three': 'drei'}
:echo uk2de.translate('three one')
-< drei ein ~
+< drei eins ~
You see that the copy() function is used to make a copy of the "transdict"
Dictionary and then the copy is changed to add the words. The original
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index cdd616b6c1..ae0bd5a867 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -77,12 +77,15 @@ Clipboard integration |provider-clipboard|
USER EXPERIENCE ~
-A major goal of Nvim is to work intuitively and consistently. For example,
-Nvim does not have `-X`, a platform-specific option available in some Vim
-builds (with potential surprises: http://stackoverflow.com/q/14635295). Nvim
-avoids features that cannot be provided on all platforms--instead that is
-delegated to external plugins/extensions.
+Working intuitively and consistently is a major goal of Nvim. Examples:
+- Nvim does not have `-X`, a platform-specific option "sometimes" available in
+ Vim (with potential surprises: http://stackoverflow.com/q/14635295). Nvim
+ avoids features that cannot be provided on all platforms--instead that is
+ delegated to external plugins/extensions.
+
+- Test-only globals and functions such as test_autochdir(), test_settime(),
+ etc., are not exposed (because they don't exist).
ARCHITECTURE ~
@@ -126,6 +129,7 @@ Functions:
|msgpackdump()|, |msgpackparse()| provide msgpack de/serialization
Events:
+ |DirChanged|
|TabNewEntered|
|TermClose|
|TermOpen|
@@ -271,6 +275,19 @@ MS-DOS support:
Highlight groups:
|hl-VisualNOS|
+Test functions:
+ test_alloc_fail()
+ test_autochdir()
+ test_disable_char_avail()
+ test_garbagecollect_now()
+ test_null_channel()
+ test_null_dict()
+ test_null_job()
+ test_null_list()
+ test_null_partial()
+ test_null_string()
+ test_settime()
+
Other options:
'antialias'
'cpoptions' ("g", "w", "H", "*", "-", "j", and all POSIX flags were removed)
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index d5c7db992e..5b94626e36 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -1,4 +1,4 @@
-*windows.txt* For Vim version 7.4. Last change: 2016 Feb 01
+*windows.txt* For Vim version 7.4. Last change: 2016 Jun 10
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -70,6 +70,16 @@ places where a Normal mode command can't be used or is inconvenient.
The main Vim window can hold several split windows. There are also tab pages
|tab-page|, each of which can hold multiple windows.
+Each window has a unique identifier called the window ID. This identifier
+will not change within a Vim session. The |win_getid()| and |win_id2tabwin()|
+functions can be used to convert between the window/tab number and the
+identifier. There is also the window number, which may change whenever
+windows are opened or closed, see |winnr()|.
+
+Each buffer has a unique number and the number will not change within a Vim
+session. The |bufnr()| and |bufname()| functions can be used to convert
+between a buffer name and the buffer number.
+
==============================================================================
2. Starting Vim *windows-starting*
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index bfb45a3720..93886d5379 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1,7 +1,7 @@
" Vim support file to detect file types
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2016 Jul 12
+" Last Change: 2016 Jul 21
" Listen very carefully, I will say this only once
if exists("did_load_filetypes")
@@ -530,7 +530,7 @@ au BufNewFile,BufRead configure.in,configure.ac setf config
au BufNewFile,BufRead *.cu setf cuda
" Dockerfile
-au BufNewFile,BufRead Dockerfile setf dockerfile
+au BufNewFile,BufRead Dockerfile,*.Dockerfile setf dockerfile
" WildPackets EtherPeek Decoder
au BufNewFile,BufRead *.dcd setf dcd
@@ -1143,7 +1143,7 @@ au BufNewFile,BufRead *.ist,*.mst setf ist
au BufNewFile,BufRead *.page setf mallard
" Manpage
-au BufNewFile,BufRead *.man setf man
+au BufNewFile,BufRead *.man setf nroff
" Man config
au BufNewFile,BufRead */etc/man.conf,man.config setf manconf
diff --git a/runtime/ftplugin/c.vim b/runtime/ftplugin/c.vim
index d1b2a4941e..3717ea92a9 100644
--- a/runtime/ftplugin/c.vim
+++ b/runtime/ftplugin/c.vim
@@ -1,7 +1,7 @@
" Vim filetype plugin file
" Language: C
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2012 Jul 10
+" Last Change: 2016 Jun 12
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -32,7 +32,7 @@ setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
" When the matchit plugin is loaded, this makes the % command skip parens and
" braces in comments.
let b:match_words = &matchpairs . ',^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>'
-let b:match_skip = 's:comment\|string\|character'
+let b:match_skip = 's:comment\|string\|character\|special'
" Win32 can filter files in the browse dialog
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
diff --git a/runtime/ftplugin/clojure.vim b/runtime/ftplugin/clojure.vim
index 10fcb9f205..217711f26e 100644
--- a/runtime/ftplugin/clojure.vim
+++ b/runtime/ftplugin/clojure.vim
@@ -1,11 +1,11 @@
" Vim filetype plugin file
-" Language: Clojure
-" Author: Meikel Brandmeyer <mb@kotka.de>
+" Language: Clojure
+" Author: Meikel Brandmeyer <mb@kotka.de>
"
-" Maintainer: Sung Pae <self@sungpae.com>
-" URL: https://github.com/guns/vim-clojure-static
-" License: Same as Vim
-" Last Change: 27 March 2014
+" Maintainer: Sung Pae <self@sungpae.com>
+" URL: https://github.com/guns/vim-clojure-static
+" License: Same as Vim
+" Last Change: 18 July 2016
if exists("b:did_ftplugin")
finish
@@ -43,7 +43,7 @@ setlocal commentstring=;\ %s
" specially and hence are not indented specially.
"
" -*- LISPWORDS -*-
-" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-010/clj/src/vim_clojure_static/generate.clj
+" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-011/clj/src/vim_clojure_static/generate.clj
setlocal lispwords=as->,binding,bound-fn,case,catch,cond->,cond->>,condp,def,definline,definterface,defmacro,defmethod,defmulti,defn,defn-,defonce,defprotocol,defrecord,defstruct,deftest,deftest-,deftype,doseq,dotimes,doto,extend,extend-protocol,extend-type,fn,for,if,if-let,if-not,if-some,let,letfn,locking,loop,ns,proxy,reify,set-test,testing,when,when-first,when-let,when-not,when-some,while,with-bindings,with-in-str,with-local-vars,with-open,with-precision,with-redefs,with-redefs-fn,with-test
" Provide insert mode completions for special forms and clojure.core. As
@@ -82,9 +82,9 @@ endif
" Win32 can filter files in the browse dialog
if has("gui_win32") && !exists("b:browsefilter")
let b:browsefilter = "Clojure Source Files (*.clj)\t*.clj\n" .
- \ "ClojureScript Source Files (*.cljs)\t*.cljs\n" .
- \ "Java Source Files (*.java)\t*.java\n" .
- \ "All Files (*.*)\t*.*\n"
+ \ "ClojureScript Source Files (*.cljs)\t*.cljs\n" .
+ \ "Java Source Files (*.java)\t*.java\n" .
+ \ "All Files (*.*)\t*.*\n"
let b:undo_ftplugin .= ' | unlet! b:browsefilter'
endif
diff --git a/runtime/ftplugin/python.vim b/runtime/ftplugin/python.vim
index 75c7e87996..9e2c5a763e 100644
--- a/runtime/ftplugin/python.vim
+++ b/runtime/ftplugin/python.vim
@@ -1,8 +1,9 @@
" Vim filetype plugin file
" Language: python
-" Maintainer: Johannes Zellner <johannes@zellner.org>
-" Last Change: 2014 Feb 09
-" Last Change By Johannes: Wed, 21 Apr 2004 13:13:08 CEST
+" Maintainer: James Sully <sullyj3@gmail.com>
+" Previous Maintainer: Johannes Zellner <johannes@zellner.org>
+" Last Change: Fri, 10 June 2016
+" https://github.com/sullyj3/vim-ftplugin-python
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
@@ -21,10 +22,10 @@ setlocal omnifunc=pythoncomplete#Complete
set wildignore+=*.pyc
-nnoremap <silent> <buffer> ]] :call <SID>Python_jump('/^\(class\\|def\)')<cr>
-nnoremap <silent> <buffer> [[ :call <SID>Python_jump('?^\(class\\|def\)')<cr>
-nnoremap <silent> <buffer> ]m :call <SID>Python_jump('/^\s*\(class\\|def\)')<cr>
-nnoremap <silent> <buffer> [m :call <SID>Python_jump('?^\s*\(class\\|def\)')<cr>
+nnoremap <silent> <buffer> ]] :call <SID>Python_jump('/^\(class\\|def\)\>')<cr>
+nnoremap <silent> <buffer> [[ :call <SID>Python_jump('?^\(class\\|def\)\>')<cr>
+nnoremap <silent> <buffer> ]m :call <SID>Python_jump('/^\s*\(class\\|def\)\>')<cr>
+nnoremap <silent> <buffer> [m :call <SID>Python_jump('?^\s*\(class\\|def\)\>')<cr>
if !exists('*<SID>Python_jump')
fun! <SID>Python_jump(motion) range
diff --git a/runtime/ftplugin/spec.vim b/runtime/ftplugin/spec.vim
index 6d5bf4b806..2a961f8244 100644
--- a/runtime/ftplugin/spec.vim
+++ b/runtime/ftplugin/spec.vim
@@ -36,10 +36,11 @@ except ImportError:
else:
specfile = vim.current.buffer.name
if specfile:
+ rpm.delMacro("dist")
spec = rpm.spec(specfile)
- headers = spec.packages[0].header
- version = headers['Version']
- release = ".".join(headers['Release'].split(".")[:-1])
+ headers = spec.sourceHeader
+ version = headers["Version"]
+ release = headers["Release"]
vim.command("let ver = " + version)
vim.command("let rel = " + release)
PYEND
@@ -113,7 +114,10 @@ if !exists("*s:SpecChangelog")
endif
endif
if (chgline != -1)
+ let tmptime = v:lc_time
+ language time C
let parsed_format = "* ".strftime(format)." - ".ver."-".rel
+ execute "language time" tmptime
let release_info = "+ ".name."-".ver."-".rel
let wrong_format = 0
let wrong_release = 0
@@ -179,12 +183,8 @@ if !exists("*s:ParseRpmVars")
endif
let varname = strpart(a:str, start+2, end-(start+2))
execute a:strline
- let definestr = "^[ \t]*%define[ \t]\\+" . varname . "[ \t]\\+\\(.*\\)$"
+ let definestr = "^[ \t]*%(?:global|define)[ \t]\\+" . varname . "[ \t]\\+\\(.*\\)$"
let linenum = search(definestr, "bW")
- if (linenum == 0)
- let definestr = substitute(definestr, "%define", "%global", "")
- let linenum = search(definestr, "bW")
- endif
if (linenum != -1)
let ret = ret . substitute(getline(linenum), definestr, "\\1", "")
else
@@ -201,7 +201,7 @@ endif
let b:match_ignorecase = 0
let b:match_words =
- \ '^Name:^%description:^%clean:^%setup:^%build:^%install:^%files:' .
+ \ '^Name:^%description:^%clean:^%(?:auto)?setup:^%build:^%install:^%files:' .
\ '^%package:^%preun:^%postun:^%changelog'
let &cpo = s:cpo_save
diff --git a/runtime/indent/clojure.vim b/runtime/indent/clojure.vim
index 476ac1de1c..7592b10d7d 100644
--- a/runtime/indent/clojure.vim
+++ b/runtime/indent/clojure.vim
@@ -1,17 +1,12 @@
" Vim indent file
-" Language: Clojure
-" Author: Meikel Brandmeyer <mb@kotka.de>
-" URL: http://kotka.de/projects/clojure/vimclojure.html
+" Language: Clojure
+" Author: Meikel Brandmeyer <mb@kotka.de>
+" URL: http://kotka.de/projects/clojure/vimclojure.html
"
-" Maintainer: Sung Pae <self@sungpae.com>
-" URL: https://github.com/guns/vim-clojure-static
-" License: Same as Vim
-" Last Change: 27 March 2014
-
-" TODO: Indenting after multibyte characters is broken:
-" (let [Δ (if foo
-" bar ; Indent error
-" baz)])
+" Maintainer: Sung Pae <self@sungpae.com>
+" URL: https://github.com/guns/vim-clojure-static
+" License: Same as Vim
+" Last Change: 18 July 2016
if exists("b:did_indent")
finish
@@ -57,36 +52,39 @@ if exists("*searchpairpos")
let g:clojure_align_subforms = 0
endif
- function! s:SynIdName()
+ function! s:syn_id_name()
return synIDattr(synID(line("."), col("."), 0), "name")
endfunction
- function! s:CurrentChar()
+ function! s:ignored_region()
+ return s:syn_id_name() =~? '\vstring|regex|comment|character'
+ endfunction
+
+ function! s:current_char()
return getline('.')[col('.')-1]
endfunction
- function! s:CurrentWord()
+ function! s:current_word()
return getline('.')[col('.')-1 : searchpos('\v>', 'n', line('.'))[1]-2]
endfunction
- function! s:IsParen()
- return s:CurrentChar() =~# '\v[\(\)\[\]\{\}]' &&
- \ s:SynIdName() !~? '\vstring|regex|comment|character'
+ function! s:is_paren()
+ return s:current_char() =~# '\v[\(\)\[\]\{\}]' && !s:ignored_region()
endfunction
" Returns 1 if string matches a pattern in 'patterns', which may be a
" list of patterns, or a comma-delimited string of implicitly anchored
" patterns.
- function! s:MatchesOne(patterns, string)
+ function! s:match_one(patterns, string)
let list = type(a:patterns) == type([])
- \ ? a:patterns
- \ : map(split(a:patterns, ','), '"^" . v:val . "$"')
+ \ ? a:patterns
+ \ : map(split(a:patterns, ','), '"^" . v:val . "$"')
for pat in list
if a:string =~# pat | return 1 | endif
endfor
endfunction
- function! s:MatchPairs(open, close, stopat)
+ function! s:match_pairs(open, close, stopat)
" Stop only on vector and map [ resp. {. Ignore the ones in strings and
" comments.
if a:stopat == 0
@@ -95,11 +93,11 @@ if exists("*searchpairpos")
let stopat = a:stopat
endif
- let pos = searchpairpos(a:open, '', a:close, 'bWn', "!s:IsParen()", stopat)
- return [pos[0], virtcol(pos)]
+ let pos = searchpairpos(a:open, '', a:close, 'bWn', "!s:is_paren()", stopat)
+ return [pos[0], col(pos)]
endfunction
- function! s:ClojureCheckForStringWorker()
+ function! s:clojure_check_for_string_worker()
" Check whether there is the last character of the previous line is
" highlighted as a string. If so, we check whether it's a ". In this
" case we have to check also the previous character. The " might be the
@@ -113,17 +111,17 @@ if exists("*searchpairpos")
call cursor(nb, 0)
call cursor(0, col("$") - 1)
- if s:SynIdName() !~? "string"
+ if s:syn_id_name() !~? "string"
return -1
endif
" This will not work for a " in the first column...
- if s:CurrentChar() == '"'
+ if s:current_char() == '"'
call cursor(0, col("$") - 2)
- if s:SynIdName() !~? "string"
+ if s:syn_id_name() !~? "string"
return -1
endif
- if s:CurrentChar() != '\\'
+ if s:current_char() != '\\'
return -1
endif
call cursor(0, col("$") - 1)
@@ -138,40 +136,40 @@ if exists("*searchpairpos")
return indent(".")
endfunction
- function! s:CheckForString()
+ function! s:check_for_string()
let pos = getpos('.')
try
- let val = s:ClojureCheckForStringWorker()
+ let val = s:clojure_check_for_string_worker()
finally
call setpos('.', pos)
endtry
return val
endfunction
- function! s:StripNamespaceAndMacroChars(word)
+ function! s:strip_namespace_and_macro_chars(word)
return substitute(a:word, "\\v%(.*/|[#'`~@^,]*)(.*)", '\1', '')
endfunction
- function! s:ClojureIsMethodSpecialCaseWorker(position)
+ function! s:clojure_is_method_special_case_worker(position)
" Find the next enclosing form.
call search('\S', 'Wb')
" Special case: we are at a '(('.
- if s:CurrentChar() == '('
+ if s:current_char() == '('
return 0
endif
call cursor(a:position)
- let nextParen = s:MatchPairs('(', ')', 0)
+ let next_paren = s:match_pairs('(', ')', 0)
" Special case: we are now at toplevel.
- if nextParen == [0, 0]
+ if next_paren == [0, 0]
return 0
endif
- call cursor(nextParen)
+ call cursor(next_paren)
call search('\S', 'W')
- let w = s:StripNamespaceAndMacroChars(s:CurrentWord())
+ let w = s:strip_namespace_and_macro_chars(s:current_word())
if g:clojure_special_indent_words =~# '\V\<' . w . '\>'
return 1
endif
@@ -179,27 +177,43 @@ if exists("*searchpairpos")
return 0
endfunction
- function! s:IsMethodSpecialCase(position)
+ function! s:is_method_special_case(position)
let pos = getpos('.')
try
- let val = s:ClojureIsMethodSpecialCaseWorker(a:position)
+ let val = s:clojure_is_method_special_case_worker(a:position)
finally
call setpos('.', pos)
endtry
return val
endfunction
- function! GetClojureIndent()
+ " Check if form is a reader conditional, that is, it is prefixed by #?
+ " or @#?
+ function! s:is_reader_conditional_special_case(position)
+ if getline(a:position[0])[a:position[1] - 3 : a:position[1] - 2] == "#?"
+ return 1
+ endif
+
+ return 0
+ endfunction
+
+ " Returns 1 for opening brackets, -1 for _anything else_.
+ function! s:bracket_type(char)
+ return stridx('([{', a:char) > -1 ? 1 : -1
+ endfunction
+
+ " Returns: [opening-bracket-lnum, indent]
+ function! s:clojure_indent_pos()
" Get rid of special case.
if line(".") == 1
- return 0
+ return [0, 0]
endif
" We have to apply some heuristics here to figure out, whether to use
" normal lisp indenting or not.
- let i = s:CheckForString()
+ let i = s:check_for_string()
if i > -1
- return i + !!g:clojure_align_multiline_strings
+ return [0, i + !!g:clojure_align_multiline_strings]
endif
call cursor(0, 1)
@@ -207,28 +221,28 @@ if exists("*searchpairpos")
" Find the next enclosing [ or {. We can limit the second search
" to the line, where the [ was found. If no [ was there this is
" zero and we search for an enclosing {.
- let paren = s:MatchPairs('(', ')', 0)
- let bracket = s:MatchPairs('\[', '\]', paren[0])
- let curly = s:MatchPairs('{', '}', bracket[0])
+ let paren = s:match_pairs('(', ')', 0)
+ let bracket = s:match_pairs('\[', '\]', paren[0])
+ let curly = s:match_pairs('{', '}', bracket[0])
" In case the curly brace is on a line later then the [ or - in
" case they are on the same line - in a higher column, we take the
" curly indent.
if curly[0] > bracket[0] || curly[1] > bracket[1]
if curly[0] > paren[0] || curly[1] > paren[1]
- return curly[1]
+ return curly
endif
endif
" If the curly was not chosen, we take the bracket indent - if
" there was one.
if bracket[0] > paren[0] || bracket[1] > paren[1]
- return bracket[1]
+ return bracket
endif
" There are neither { nor [ nor (, ie. we are at the toplevel.
if paren == [0, 0]
- return 0
+ return paren
endif
" Now we have to reimplement lispindent. This is surprisingly easy, as
@@ -246,58 +260,120 @@ if exists("*searchpairpos")
" - In any other case we use the column of the end of the word + 2.
call cursor(paren)
- if s:IsMethodSpecialCase(paren)
- return paren[1] + &shiftwidth - 1
+ if s:is_method_special_case(paren)
+ return [paren[0], paren[1] + &shiftwidth - 1]
+ endif
+
+ if s:is_reader_conditional_special_case(paren)
+ return paren
endif
" In case we are at the last character, we use the paren position.
if col("$") - 1 == paren[1]
- return paren[1]
+ return paren
endif
" In case after the paren is a whitespace, we search for the next word.
call cursor(0, col('.') + 1)
- if s:CurrentChar() == ' '
+ if s:current_char() == ' '
call search('\v\S', 'W')
endif
" If we moved to another line, there is no word after the (. We
" use the ( position for indent.
if line(".") > paren[0]
- return paren[1]
+ return paren
endif
" We still have to check, whether the keyword starts with a (, [ or {.
" In that case we use the ( position for indent.
- let w = s:CurrentWord()
- if stridx('([{', w[0]) > -1
- return paren[1]
+ let w = s:current_word()
+ if s:bracket_type(w[0]) == 1
+ return paren
endif
" Test words without namespace qualifiers and leading reader macro
" metacharacters.
"
" e.g. clojure.core/defn and #'defn should both indent like defn.
- let ww = s:StripNamespaceAndMacroChars(w)
+ let ww = s:strip_namespace_and_macro_chars(w)
if &lispwords =~# '\V\<' . ww . '\>'
- return paren[1] + &shiftwidth - 1
+ return [paren[0], paren[1] + &shiftwidth - 1]
endif
if g:clojure_fuzzy_indent
- \ && !s:MatchesOne(g:clojure_fuzzy_indent_blacklist, ww)
- \ && s:MatchesOne(g:clojure_fuzzy_indent_patterns, ww)
- return paren[1] + &shiftwidth - 1
+ \ && !s:match_one(g:clojure_fuzzy_indent_blacklist, ww)
+ \ && s:match_one(g:clojure_fuzzy_indent_patterns, ww)
+ return [paren[0], paren[1] + &shiftwidth - 1]
endif
call search('\v\_s', 'cW')
call search('\v\S', 'W')
if paren[0] < line(".")
- return paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)
+ return [paren[0], paren[1] + (g:clojure_align_subforms ? 0 : &shiftwidth - 1)]
endif
call search('\v\S', 'bW')
- return virtcol(".") + 1
+ return [line('.'), col('.') + 1]
+ endfunction
+
+ function! GetClojureIndent()
+ let lnum = line('.')
+ let orig_lnum = lnum
+ let orig_col = col('.')
+ let [opening_lnum, indent] = s:clojure_indent_pos()
+
+ " Account for multibyte characters
+ if opening_lnum > 0
+ let indent -= indent - virtcol([opening_lnum, indent])
+ endif
+
+ " Return if there are no previous lines to inherit from
+ if opening_lnum < 1 || opening_lnum >= lnum - 1
+ call cursor(orig_lnum, orig_col)
+ return indent
+ endif
+
+ let bracket_count = 0
+
+ " Take the indent of the first previous non-white line that is
+ " at the same sexp level. cf. src/misc1.c:get_lisp_indent()
+ while 1
+ let lnum = prevnonblank(lnum - 1)
+ let col = 1
+
+ if lnum <= opening_lnum
+ break
+ endif
+
+ call cursor(lnum, col)
+
+ " Handle bracket counting edge case
+ if s:is_paren()
+ let bracket_count += s:bracket_type(s:current_char())
+ endif
+
+ while 1
+ if search('\v[(\[{}\])]', '', lnum) < 1
+ break
+ elseif !s:ignored_region()
+ let bracket_count += s:bracket_type(s:current_char())
+ endif
+ endwhile
+
+ if bracket_count == 0
+ " Check if this is part of a multiline string
+ call cursor(lnum, 1)
+ if s:syn_id_name() !~? '\vstring|regex'
+ call cursor(orig_lnum, orig_col)
+ return indent(lnum)
+ endif
+ endif
+ endwhile
+
+ call cursor(orig_lnum, orig_col)
+ return indent
endfunction
setlocal indentexpr=GetClojureIndent()
diff --git a/runtime/indent/vhdl.vim b/runtime/indent/vhdl.vim
index 3e847b9575..6982859670 100644
--- a/runtime/indent/vhdl.vim
+++ b/runtime/indent/vhdl.vim
@@ -1,8 +1,8 @@
" VHDL indent ('93 syntax)
" Language: VHDL
" Maintainer: Gerald Lai <laigera+vim?gmail.com>
-" Version: 1.58
-" Last Change: 2011 Sep 27
+" Version: 1.60
+" Last Change: 2016 Feb 26
" URL: http://www.vim.org/scripts/script.php?script_id=1450
" only load this indent file when no other was loaded
@@ -104,7 +104,7 @@ function GetVHDLindent()
let pn = prevnonblank(pn - 1)
let ps = getline(pn)
endwhile
- if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*('))
+ if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\((.*)\)*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\s\+\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*('))
" align closing ")" with opening "("
if curs =~ '^\s*)'
return ind2 + stridx(prevs_noi, '(')
@@ -412,11 +412,22 @@ function GetVHDLindent()
" ****************************************************************************************
" indent: maintain indent of previous opening statement
- " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + "in", "out", "inout", "buffer", "linkage", variable & ":="
+ " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$
" where: start of current line
- if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)'
+ if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$'
return ind2
endif
+ " ****************************************************************************************
+ " indent: maintain indent of previous opening statement, corner case which
+ " does not end in ;, but is part of a mapping
+ " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=", never + ;$ and
+ " prevline without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$
+ " where: start of current line
+ if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*[^;].*$'
+ if prevs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$'
+ return ind2
+ endif
+ endif
" return leftover filtered indent
return ind
diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim
index 31b76b8c0c..7ec7df849e 100644
--- a/runtime/indent/vim.vim
+++ b/runtime/indent/vim.vim
@@ -1,7 +1,7 @@
" Vim indent file
" Language: Vim script
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2016 Jan 24
+" Last Change: 2016 Apr 19
" Only load this indent file when no other was loaded.
if exists("b:did_indent")
@@ -60,7 +60,7 @@ function GetVimIndentIntern()
else
let ind = ind + shiftwidth() * 3
endif
- elseif prev_text =~ '^\s*aug\%[roup]' && prev_text !~ '^\s*aug\%[roup]\s*!\=\s\+END'
+ elseif prev_text =~ '^\s*aug\%[roup]' && prev_text !~ '^\s*aug\%[roup]\s*!\=\s\+[eE][nN][dD]'
let ind = ind + shiftwidth()
else
" A line starting with :au does not increment/decrement indent.
diff --git a/runtime/keymap/armenian-eastern_utf-8.vim b/runtime/keymap/armenian-eastern_utf-8.vim
new file mode 100644
index 0000000000..7b03c5ac73
--- /dev/null
+++ b/runtime/keymap/armenian-eastern_utf-8.vim
@@ -0,0 +1,108 @@
+" Maintainer: Benjamin Linskey <vim@benlinskey.com>
+" Last Changed: 2016 July 20
+" URL: https://github.com/blinskey/vim-armenian-keymaps
+
+let b:keymap_name = "hy"
+
+loadkeymap
+
+" Capital letters
+A Ա
+B Բ
+C Գ
+D Դ
+Y Ե
+Z Զ
+E Է
+U Ը
+: Թ
++ Ժ
+I Ի
+L Լ
+Q Խ
+? Ծ
+K Կ
+H Հ
+@ Ձ
+> Ղ
+J Ճ
+M Մ
+# Յ
+N Ն
+< Շ
+O Ո
+{ Չ
+P Պ
+} Ջ
+_ Ռ
+S Ս
+V Վ
+T Տ
+R Ր
+X Ց
+W Ւ
+\" Փ
+G Ք
+) Օ
+F Ֆ
+
+" Lowercase letters
+a ա
+b բ
+c գ
+d դ
+y ե
+z զ
+e է
+u ը
+; թ
+= ժ
+i ի
+l լ
+q խ
+/ ծ
+k կ
+h հ
+2 ձ
+. ղ
+j ճ
+m մ
+3 յ
+n ն
+, շ
+o ո
+[ չ
+p պ
+] ջ
+- ռ
+s ս
+v վ
+t տ
+r ր
+x ց
+w ւ
+' փ
+g ք
+0 օ
+f ֆ
+
+& և
+
+" Punctuation
+` ՝
+~ ՜
+1 ։
+4 ՛
+5 ,
+6 -
+7 .
+8 «
+9 »
+\\ '
+| ՞
+
+" Numbers
+! 1
+$ 3
+% 4
+^ 9
diff --git a/runtime/keymap/armenian-western_utf-8.vim b/runtime/keymap/armenian-western_utf-8.vim
new file mode 100644
index 0000000000..e02485ed8c
--- /dev/null
+++ b/runtime/keymap/armenian-western_utf-8.vim
@@ -0,0 +1,108 @@
+" Maintainer: Benjamin Linskey <vim@benlinskey.com>
+" Last Changed: 2016 July 20
+" URL: https://github.com/blinskey/vim-armenian-keymaps
+
+let b:keymap_name = "hy"
+
+loadkeymap
+
+" Capital letters
+A Ա
+P Բ
+C Գ
+T Դ
+Y Ե
+Z Զ
+E Է
+U Ը
+: Թ
++ Ժ
+I Ի
+L Լ
+Q Խ
+? Ծ
+G Կ
+H Հ
+@ Ձ
+> Ղ
+J Ճ
+M Մ
+# Յ
+N Ն
+< Շ
+O Ո
+{ Չ
+B Պ
+} Ջ
+_ Ռ
+S Ս
+W Վ
+D Տ
+R Ր
+X Ց
+V Ւ
+\" Փ
+K Ք
+) Օ
+F Ֆ
+
+" Lowercase letters
+a ա
+p բ
+c գ
+t դ
+y ե
+z զ
+e է
+u ը
+; թ
+= ժ
+i ի
+l լ
+q խ
+/ ծ
+g կ
+h հ
+2 ձ
+. ղ
+j ճ
+m մ
+3 յ
+n ն
+, շ
+o ո
+[ չ
+b պ
+] ջ
+- ռ
+s ս
+w վ
+d տ
+r ր
+x ց
+v ւ
+' փ
+k ք
+0 օ
+f ֆ
+
+& և
+
+" Punctuation
+` ՝
+~ ՜
+1 ։
+4 ՛
+5 ,
+6 -
+7 .
+8 «
+9 »
+\\ '
+| ՞
+
+" Numbers
+! 1
+$ 3
+% 4
+^ 9
diff --git a/runtime/plugin/health.vim b/runtime/plugin/health.vim
index 3c8e509acd..e3482cb0fe 100644
--- a/runtime/plugin/health.vim
+++ b/runtime/plugin/health.vim
@@ -1 +1,8 @@
-command! -nargs=* CheckHealth call health#check([<f-args>])
+function! s:complete(lead, _line, _pos) abort
+ return sort(filter(map(globpath(&runtimepath, 'autoload/health/*', 1, 1),
+ \ 'fnamemodify(v:val, ":t:r")'),
+ \ 'empty(a:lead) || v:val[:strlen(a:lead)-1] ==# a:lead'))
+endfunction
+
+command! -nargs=* -complete=customlist,s:complete CheckHealth
+ \ call health#check([<f-args>])
diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim
index 69902b1f19..28e1c3ecf8 100644
--- a/runtime/plugin/netrwPlugin.vim
+++ b/runtime/plugin/netrwPlugin.vim
@@ -20,7 +20,7 @@
if &cp || exists("g:loaded_netrwPlugin")
finish
endif
-let g:loaded_netrwPlugin = "v155"
+let g:loaded_netrwPlugin = "v156"
let s:keepcpo = &cpo
set cpo&vim
"DechoRemOn
diff --git a/runtime/syntax/bib.vim b/runtime/syntax/bib.vim
index f84d5ca95a..8bd0528e1e 100644
--- a/runtime/syntax/bib.vim
+++ b/runtime/syntax/bib.vim
@@ -2,7 +2,7 @@
" Language: BibTeX (bibliographic database format for (La)TeX)
" Maintainer: Bernd Feige <Bernd.Feige@gmx.net>
" Filenames: *.bib
-" Last Change: 2014 Mar 26
+" Last Change: 2016 May 31
" Thanks to those who pointed out problems with this file or supplied fixes!
@@ -35,8 +35,40 @@ syn keyword bibEntryKw contained crossref edition editor howpublished
syn keyword bibEntryKw contained institution journal key month note
syn keyword bibEntryKw contained number organization pages publisher
syn keyword bibEntryKw contained school series title type volume year
+
+" biblatex keywords, cf. http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/biblatex.pdf
+syn keyword bibType contained mvbook bookinbook suppbook collection mvcollection suppcollection
+syn keyword bibType contained online patent periodical suppperiodical mvproceedings reference
+syn keyword bibType contained mvreference inreference report set thesis xdata customa customb
+syn keyword bibType contained customc customd custome customf electronic www artwork audio bibnote
+syn keyword bibType contained commentary image jurisdiction legislation legal letter movie music
+syn keyword bibType contained performance review software standard video
+
+syn keyword bibEntryKw contained abstract isbn issn keywords url
+syn keyword bibEntryKw contained addendum afterwordannotation annotation annotator authortype
+syn keyword bibEntryKw contained bookauthor bookpagination booksubtitle booktitleaddon
+syn keyword bibEntryKw contained commentator date doi editora editorb editorc editortype
+syn keyword bibEntryKw contained editoratype editorbtype editorctype eid entrysubtype
+syn keyword bibEntryKw contained eprint eprintclass eprinttype eventdate eventtitle
+syn keyword bibEntryKw contained eventtitleaddon file foreword holder indextitle
+syn keyword bibEntryKw contained introduction isan ismn isrn issue issuesubtitle
+syn keyword bibEntryKw contained issuetitle iswc journalsubtitle journaltitle label
+syn keyword bibEntryKw contained language library location mainsubtitle maintitle
+syn keyword bibEntryKw contained maintitleaddon nameaddon origdate origlanguage
+syn keyword bibEntryKw contained origlocation origpublisher origtitle pagetotal
+syn keyword bibEntryKw contained pagination part pubstate reprinttitle shortauthor
+syn keyword bibEntryKw contained shorteditor shorthand shorthandintro shortjournal
+syn keyword bibEntryKw contained shortseries shorttitle subtitle titleaddon translator
+syn keyword bibEntryKw contained urldate venue version volumes entryset execute gender
+syn keyword bibEntryKw contained langid langidopts ids indexsorttitle options presort
+syn keyword bibEntryKw contained related relatedoptions relatedtype relatedstring
+syn keyword bibEntryKw contained sortkey sortname sortshorthand sorttitle sortyear xdata
+syn keyword bibEntryKw contained xref namea nameb namec nameatype namebtype namectype
+syn keyword bibEntryKw contained lista listb listc listd liste listf usera userb userc
+syn keyword bibEntryKw contained userd usere userf verba verbb verbc archiveprefix pdf
+syn keyword bibEntryKw contained primaryclass
+
" Non-standard:
-syn keyword bibNSEntryKw contained abstract isbn issn keywords url
" AMS mref http://www.ams.org/mref
syn keyword bibNSEntryKw contained mrclass mrnumber mrreviewer fjournal coden
diff --git a/runtime/syntax/clojure.vim b/runtime/syntax/clojure.vim
index 4dc1cde05c..2b48146b5a 100644
--- a/runtime/syntax/clojure.vim
+++ b/runtime/syntax/clojure.vim
@@ -1,37 +1,68 @@
" Vim syntax file
-" Language: Clojure
-" Authors: Toralf Wittner <toralf.wittner@gmail.com>
-" modified by Meikel Brandmeyer <mb@kotka.de>
-" URL: http://kotka.de/projects/clojure/vimclojure.html
+" Language: Clojure
+" Authors: Toralf Wittner <toralf.wittner@gmail.com>
+" modified by Meikel Brandmeyer <mb@kotka.de>
+" URL: http://kotka.de/projects/clojure/vimclojure.html
"
-" Contributors: Joel Holdbrooks <cjholdbrooks@gmail.com> (Regexp support, bug fixes)
+" Contributors: Joel Holdbrooks <cjholdbrooks@gmail.com> (Regexp support, bug fixes)
"
-" Maintainer: Sung Pae <self@sungpae.com>
-" URL: https://github.com/guns/vim-clojure-static
-" License: Same as Vim
-" Last Change: 27 March 2014
+" Maintainer: Sung Pae <self@sungpae.com>
+" URL: https://github.com/guns/vim-clojure-static
+" License: Same as Vim
+" Last Change: 18 July 2016
if exists("b:current_syntax")
finish
endif
+let s:cpo_sav = &cpo
+set cpo&vim
+
if has("folding") && exists("g:clojure_fold") && g:clojure_fold > 0
setlocal foldmethod=syntax
endif
" -*- KEYWORDS -*-
-" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-010/clj/src/vim_clojure_static/generate.clj
-" Clojure version 1.6.0
-syntax keyword clojureConstant nil
-syntax keyword clojureBoolean false true
-syntax keyword clojureSpecial . catch clojure.core/fn clojure.core/let clojure.core/loop def do finally fn if let loop monitor-enter monitor-exit new quote recur set! throw try var
-syntax keyword clojureException catch finally throw try
-syntax keyword clojureCond case clojure.core/case clojure.core/cond clojure.core/cond-> clojure.core/cond->> clojure.core/condp clojure.core/if-let clojure.core/if-not clojure.core/if-some clojure.core/when clojure.core/when-first clojure.core/when-let clojure.core/when-not clojure.core/when-some cond cond-> cond->> condp if-let if-not if-some when when-first when-let when-not when-some
-syntax keyword clojureRepeat clojure.core/doseq clojure.core/dotimes clojure.core/while doseq dotimes while
-syntax keyword clojureDefine clojure.core/definline clojure.core/definterface clojure.core/defmacro clojure.core/defmethod clojure.core/defmulti clojure.core/defn clojure.core/defn- clojure.core/defonce clojure.core/defprotocol clojure.core/defrecord clojure.core/defstruct clojure.core/deftype definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype
-syntax keyword clojureMacro -> ->> .. amap and areduce as-> assert binding bound-fn clojure.core/-> clojure.core/->> clojure.core/.. clojure.core/amap clojure.core/and clojure.core/areduce clojure.core/as-> clojure.core/assert clojure.core/binding clojure.core/bound-fn clojure.core/comment clojure.core/declare clojure.core/delay clojure.core/dosync clojure.core/doto clojure.core/extend-protocol clojure.core/extend-type clojure.core/for clojure.core/future clojure.core/gen-class clojure.core/gen-interface clojure.core/import clojure.core/io! clojure.core/lazy-cat clojure.core/lazy-seq clojure.core/letfn clojure.core/locking clojure.core/memfn clojure.core/ns clojure.core/or clojure.core/proxy clojure.core/proxy-super clojure.core/pvalues clojure.core/refer-clojure clojure.core/reify clojure.core/some-> clojure.core/some->> clojure.core/sync clojure.core/time clojure.core/with-bindings clojure.core/with-in-str clojure.core/with-loading-context clojure.core/with-local-vars clojure.core/with-open clojure.core/with-out-str clojure.core/with-precision clojure.core/with-redefs comment declare delay dosync doto extend-protocol extend-type for future gen-class gen-interface import io! lazy-cat lazy-seq letfn locking memfn ns or proxy proxy-super pvalues refer-clojure reify some-> some->> sync time with-bindings with-in-str with-loading-context with-local-vars with-open with-out-str with-precision with-redefs
-syntax keyword clojureFunc * *' + +' - -' ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods / < <= = == > >= accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root ancestors apply array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn* bound? butlast byte byte-array bytes cast char char-array char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version clojure.core/* clojure.core/*' clojure.core/+ clojure.core/+' clojure.core/- clojure.core/-' clojure.core/->ArrayChunk clojure.core/->Vec clojure.core/->VecNode clojure.core/->VecSeq clojure.core/-cache-protocol-fn clojure.core/-reset-methods clojure.core// clojure.core/< clojure.core/<= clojure.core/= clojure.core/== clojure.core/> clojure.core/>= clojure.core/accessor clojure.core/aclone clojure.core/add-classpath clojure.core/add-watch clojure.core/agent clojure.core/agent-error clojure.core/agent-errors clojure.core/aget clojure.core/alength clojure.core/alias clojure.core/all-ns clojure.core/alter clojure.core/alter-meta! clojure.core/alter-var-root clojure.core/ancestors clojure.core/apply clojure.core/array-map clojure.core/aset clojure.core/aset-boolean clojure.core/aset-byte clojure.core/aset-char clojure.core/aset-double clojure.core/aset-float clojure.core/aset-int clojure.core/aset-long clojure.core/aset-short clojure.core/assoc clojure.core/assoc! clojure.core/assoc-in clojure.core/associative? clojure.core/atom clojure.core/await clojure.core/await-for clojure.core/await1 clojure.core/bases clojure.core/bean clojure.core/bigdec clojure.core/bigint clojure.core/biginteger clojure.core/bit-and clojure.core/bit-and-not clojure.core/bit-clear clojure.core/bit-flip clojure.core/bit-not clojure.core/bit-or clojure.core/bit-set clojure.core/bit-shift-left clojure.core/bit-shift-right clojure.core/bit-test clojure.core/bit-xor clojure.core/boolean clojure.core/boolean-array clojure.core/booleans clojure.core/bound-fn* clojure.core/bound? clojure.core/butlast clojure.core/byte clojure.core/byte-array clojure.core/bytes clojure.core/cast clojure.core/char clojure.core/char-array clojure.core/char? clojure.core/chars clojure.core/chunk clojure.core/chunk-append clojure.core/chunk-buffer clojure.core/chunk-cons clojure.core/chunk-first clojure.core/chunk-next clojure.core/chunk-rest clojure.core/chunked-seq? clojure.core/class clojure.core/class? clojure.core/clear-agent-errors clojure.core/clojure-version clojure.core/coll? clojure.core/commute clojure.core/comp clojure.core/comparator clojure.core/compare clojure.core/compare-and-set! clojure.core/compile clojure.core/complement clojure.core/concat clojure.core/conj clojure.core/conj! clojure.core/cons clojure.core/constantly clojure.core/construct-proxy clojure.core/contains? clojure.core/count clojure.core/counted? clojure.core/create-ns clojure.core/create-struct clojure.core/cycle clojure.core/dec clojure.core/dec' clojure.core/decimal? clojure.core/delay? clojure.core/deliver clojure.core/denominator clojure.core/deref clojure.core/derive clojure.core/descendants clojure.core/destructure clojure.core/disj clojure.core/disj! clojure.core/dissoc clojure.core/dissoc! clojure.core/distinct clojure.core/distinct? clojure.core/doall clojure.core/dorun clojure.core/double clojure.core/double-array clojure.core/doubles clojure.core/drop clojure.core/drop-last clojure.core/drop-while clojure.core/empty clojure.core/empty? clojure.core/ensure clojure.core/enumeration-seq clojure.core/error-handler clojure.core/error-mode clojure.core/eval clojure.core/even? clojure.core/every-pred clojure.core/every? clojure.core/ex-data clojure.core/ex-info clojure.core/extend clojure.core/extenders clojure.core/extends? clojure.core/false? clojure.core/ffirst clojure.core/file-seq clojure.core/filter clojure.core/filterv clojure.core/find clojure.core/find-keyword clojure.core/find-ns clojure.core/find-protocol-impl clojure.core/find-protocol-method clojure.core/find-var clojure.core/first clojure.core/flatten clojure.core/float clojure.core/float-array clojure.core/float? clojure.core/floats clojure.core/flush clojure.core/fn? clojure.core/fnext clojure.core/fnil clojure.core/force clojure.core/format clojure.core/frequencies clojure.core/future-call clojure.core/future-cancel clojure.core/future-cancelled? clojure.core/future-done? clojure.core/future? clojure.core/gensym clojure.core/get clojure.core/get-in clojure.core/get-method clojure.core/get-proxy-class clojure.core/get-thread-bindings clojure.core/get-validator clojure.core/group-by clojure.core/hash clojure.core/hash-combine clojure.core/hash-map clojure.core/hash-ordered-coll clojure.core/hash-set clojure.core/hash-unordered-coll clojure.core/identical? clojure.core/identity clojure.core/ifn? clojure.core/in-ns clojure.core/inc clojure.core/inc' clojure.core/init-proxy clojure.core/instance? clojure.core/int clojure.core/int-array clojure.core/integer? clojure.core/interleave clojure.core/intern clojure.core/interpose clojure.core/into clojure.core/into-array clojure.core/ints clojure.core/isa? clojure.core/iterate clojure.core/iterator-seq clojure.core/juxt clojure.core/keep clojure.core/keep-indexed clojure.core/key clojure.core/keys clojure.core/keyword clojure.core/keyword? clojure.core/last clojure.core/line-seq clojure.core/list clojure.core/list* clojure.core/list? clojure.core/load clojure.core/load-file clojure.core/load-reader clojure.core/load-string clojure.core/loaded-libs clojure.core/long clojure.core/long-array clojure.core/longs clojure.core/macroexpand clojure.core/macroexpand-1 clojure.core/make-array clojure.core/make-hierarchy clojure.core/map clojure.core/map-indexed clojure.core/map? clojure.core/mapcat clojure.core/mapv clojure.core/max clojure.core/max-key clojure.core/memoize clojure.core/merge clojure.core/merge-with clojure.core/meta clojure.core/method-sig clojure.core/methods clojure.core/min clojure.core/min-key clojure.core/mix-collection-hash clojure.core/mod clojure.core/munge clojure.core/name clojure.core/namespace clojure.core/namespace-munge clojure.core/neg? clojure.core/newline clojure.core/next clojure.core/nfirst clojure.core/nil? clojure.core/nnext clojure.core/not clojure.core/not-any? clojure.core/not-empty clojure.core/not-every? clojure.core/not= clojure.core/ns-aliases clojure.core/ns-imports clojure.core/ns-interns clojure.core/ns-map clojure.core/ns-name clojure.core/ns-publics clojure.core/ns-refers clojure.core/ns-resolve clojure.core/ns-unalias clojure.core/ns-unmap clojure.core/nth clojure.core/nthnext clojure.core/nthrest clojure.core/num clojure.core/number? clojure.core/numerator clojure.core/object-array clojure.core/odd? clojure.core/parents clojure.core/partial clojure.core/partition clojure.core/partition-all clojure.core/partition-by clojure.core/pcalls clojure.core/peek clojure.core/persistent! clojure.core/pmap clojure.core/pop clojure.core/pop! clojure.core/pop-thread-bindings clojure.core/pos? clojure.core/pr clojure.core/pr-str clojure.core/prefer-method clojure.core/prefers clojure.core/print clojure.core/print-ctor clojure.core/print-dup clojure.core/print-method clojure.core/print-simple clojure.core/print-str clojure.core/printf clojure.core/println clojure.core/println-str clojure.core/prn clojure.core/prn-str clojure.core/promise clojure.core/proxy-call-with-super clojure.core/proxy-mappings clojure.core/proxy-name clojure.core/push-thread-bindings clojure.core/quot clojure.core/rand clojure.core/rand-int clojure.core/rand-nth clojure.core/range clojure.core/ratio? clojure.core/rational? clojure.core/rationalize clojure.core/re-find clojure.core/re-groups clojure.core/re-matcher clojure.core/re-matches clojure.core/re-pattern clojure.core/re-seq clojure.core/read clojure.core/read-line clojure.core/read-string clojure.core/realized? clojure.core/record? clojure.core/reduce clojure.core/reduce-kv clojure.core/reduced clojure.core/reduced? clojure.core/reductions clojure.core/ref clojure.core/ref-history-count clojure.core/ref-max-history clojure.core/ref-min-history clojure.core/ref-set clojure.core/refer clojure.core/release-pending-sends clojure.core/rem clojure.core/remove clojure.core/remove-all-methods clojure.core/remove-method clojure.core/remove-ns clojure.core/remove-watch clojure.core/repeat clojure.core/repeatedly clojure.core/replace clojure.core/replicate clojure.core/require clojure.core/reset! clojure.core/reset-meta! clojure.core/resolve clojure.core/rest clojure.core/restart-agent clojure.core/resultset-seq clojure.core/reverse clojure.core/reversible? clojure.core/rseq clojure.core/rsubseq clojure.core/satisfies? clojure.core/second clojure.core/select-keys clojure.core/send clojure.core/send-off clojure.core/send-via clojure.core/seq clojure.core/seq? clojure.core/seque clojure.core/sequence clojure.core/sequential? clojure.core/set clojure.core/set-agent-send-executor! clojure.core/set-agent-send-off-executor! clojure.core/set-error-handler! clojure.core/set-error-mode! clojure.core/set-validator! clojure.core/set? clojure.core/short clojure.core/short-array clojure.core/shorts clojure.core/shuffle clojure.core/shutdown-agents clojure.core/slurp clojure.core/some clojure.core/some-fn clojure.core/some? clojure.core/sort clojure.core/sort-by clojure.core/sorted-map clojure.core/sorted-map-by clojure.core/sorted-set clojure.core/sorted-set-by clojure.core/sorted? clojure.core/special-symbol? clojure.core/spit clojure.core/split-at clojure.core/split-with clojure.core/str clojure.core/string? clojure.core/struct clojure.core/struct-map clojure.core/subs clojure.core/subseq clojure.core/subvec clojure.core/supers clojure.core/swap! clojure.core/symbol clojure.core/symbol? clojure.core/take clojure.core/take-last clojure.core/take-nth clojure.core/take-while clojure.core/test clojure.core/the-ns clojure.core/thread-bound? clojure.core/to-array clojure.core/to-array-2d clojure.core/trampoline clojure.core/transient clojure.core/tree-seq clojure.core/true? clojure.core/type clojure.core/unchecked-add clojure.core/unchecked-add-int clojure.core/unchecked-byte clojure.core/unchecked-char clojure.core/unchecked-dec clojure.core/unchecked-dec-int clojure.core/unchecked-divide-int clojure.core/unchecked-double clojure.core/unchecked-float clojure.core/unchecked-inc clojure.core/unchecked-inc-int clojure.core/unchecked-int clojure.core/unchecked-long clojure.core/unchecked-multiply clojure.core/unchecked-multiply-int clojure.core/unchecked-negate clojure.core/unchecked-negate-int clojure.core/unchecked-remainder-int clojure.core/unchecked-short clojure.core/unchecked-subtract clojure.core/unchecked-subtract-int clojure.core/underive clojure.core/unsigned-bit-shift-right clojure.core/update-in clojure.core/update-proxy clojure.core/use clojure.core/val clojure.core/vals clojure.core/var-get clojure.core/var-set clojure.core/var? clojure.core/vary-meta clojure.core/vec clojure.core/vector clojure.core/vector-of clojure.core/vector? clojure.core/with-bindings* clojure.core/with-meta clojure.core/with-redefs-fn clojure.core/xml-seq clojure.core/zero? clojure.core/zipmap coll? commute comp comparator compare compare-and-set! compile complement concat conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn? fnext fnil force format frequencies future-call future-cancel future-cancelled? future-done? future? gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-ordered-coll hash-set hash-unordered-coll identical? identity ifn? in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last line-seq list list* list? load load-file load-reader load-string loaded-libs long long-array longs macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memoize merge merge-with meta method-sig methods min min-key mix-collection-hash mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy-call-with-super proxy-mappings proxy-name push-thread-bindings quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? record? reduce reduce-kv reduced reduced? reductions ref ref-history-count ref-max-history ref-min-history ref-set refer release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off send-via seq seq? seque sequence sequential? set set-agent-send-executor! set-agent-send-off-executor! set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn some? sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? take take-last take-nth take-while test the-ns thread-bound? to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unsigned-bit-shift-right update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? with-bindings* with-meta with-redefs-fn xml-seq zero? zipmap
-syntax keyword clojureVariable *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *default-data-reader-fn* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* EMPTY-NODE char-escape-string char-name-string clojure.core/*1 clojure.core/*2 clojure.core/*3 clojure.core/*agent* clojure.core/*allow-unresolved-vars* clojure.core/*assert* clojure.core/*clojure-version* clojure.core/*command-line-args* clojure.core/*compile-files* clojure.core/*compile-path* clojure.core/*compiler-options* clojure.core/*data-readers* clojure.core/*default-data-reader-fn* clojure.core/*e clojure.core/*err* clojure.core/*file* clojure.core/*flush-on-newline* clojure.core/*fn-loader* clojure.core/*in* clojure.core/*math-context* clojure.core/*ns* clojure.core/*out* clojure.core/*print-dup* clojure.core/*print-length* clojure.core/*print-level* clojure.core/*print-meta* clojure.core/*print-readably* clojure.core/*read-eval* clojure.core/*source-path* clojure.core/*unchecked-math* clojure.core/*use-context-classloader* clojure.core/*verbose-defrecords* clojure.core/*warn-on-reflection* clojure.core/EMPTY-NODE clojure.core/char-escape-string clojure.core/char-name-string clojure.core/default-data-readers clojure.core/primitives-classnames clojure.core/unquote clojure.core/unquote-splicing default-data-readers primitives-classnames unquote unquote-splicing
+" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-011/clj/src/vim_clojure_static/generate.clj
+" Clojure version 1.8.0
+let s:clojure_syntax_keywords = {
+ \ 'clojureBoolean': ["false","true"]
+ \ , 'clojureCond': ["case","clojure.core/case","clojure.core/cond","clojure.core/cond->","clojure.core/cond->>","clojure.core/condp","clojure.core/if-let","clojure.core/if-not","clojure.core/if-some","clojure.core/when","clojure.core/when-first","clojure.core/when-let","clojure.core/when-not","clojure.core/when-some","cond","cond->","cond->>","condp","if-let","if-not","if-some","when","when-first","when-let","when-not","when-some"]
+ \ , 'clojureConstant': ["nil"]
+ \ , 'clojureDefine': ["clojure.core/definline","clojure.core/definterface","clojure.core/defmacro","clojure.core/defmethod","clojure.core/defmulti","clojure.core/defn","clojure.core/defn-","clojure.core/defonce","clojure.core/defprotocol","clojure.core/defrecord","clojure.core/defstruct","clojure.core/deftype","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype"]
+ \ , 'clojureException': ["catch","finally","throw","try"]
+ \ , 'clojureFunc': ["*","*'","+","+'","-","-'","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods","/","<","<=","=","==",">",">=","Throwable->map","accessor","aclone","add-classpath","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","ancestors","apply","array-map","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assoc","assoc!","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","booleans","bound-fn*","bound?","butlast","byte","byte-array","bytes","cast","cat","char","char-array","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","clojure.core/*","clojure.core/*'","clojure.core/+","clojure.core/+'","clojure.core/-","clojure.core/-'","clojure.core/->ArrayChunk","clojure.core/->Eduction","clojure.core/->Vec","clojure.core/->VecNode","clojure.core/->VecSeq","clojure.core/-cache-protocol-fn","clojure.core/-reset-methods","clojure.core//","clojure.core/<","clojure.core/<=","clojure.core/=","clojure.core/==","clojure.core/>","clojure.core/>=","clojure.core/Throwable->map","clojure.core/accessor","clojure.core/aclone","clojure.core/add-classpath","clojure.core/add-watch","clojure.core/agent","clojure.core/agent-error","clojure.core/agent-errors","clojure.core/aget","clojure.core/alength","clojure.core/alias","clojure.core/all-ns","clojure.core/alter","clojure.core/alter-meta!","clojure.core/alter-var-root","clojure.core/ancestors","clojure.core/apply","clojure.core/array-map","clojure.core/aset","clojure.core/aset-boolean","clojure.core/aset-byte","clojure.core/aset-char","clojure.core/aset-double","clojure.core/aset-float","clojure.core/aset-int","clojure.core/aset-long","clojure.core/aset-short","clojure.core/assoc","clojure.core/assoc!","clojure.core/assoc-in","clojure.core/associative?","clojure.core/atom","clojure.core/await","clojure.core/await-for","clojure.core/await1","clojure.core/bases","clojure.core/bean","clojure.core/bigdec","clojure.core/bigint","clojure.core/biginteger","clojure.core/bit-and","clojure.core/bit-and-not","clojure.core/bit-clear","clojure.core/bit-flip","clojure.core/bit-not","clojure.core/bit-or","clojure.core/bit-set","clojure.core/bit-shift-left","clojure.core/bit-shift-right","clojure.core/bit-test","clojure.core/bit-xor","clojure.core/boolean","clojure.core/boolean-array","clojure.core/booleans","clojure.core/bound-fn*","clojure.core/bound?","clojure.core/butlast","clojure.core/byte","clojure.core/byte-array","clojure.core/bytes","clojure.core/cast","clojure.core/cat","clojure.core/char","clojure.core/char-array","clojure.core/char?","clojure.core/chars","clojure.core/chunk","clojure.core/chunk-append","clojure.core/chunk-buffer","clojure.core/chunk-cons","clojure.core/chunk-first","clojure.core/chunk-next","clojure.core/chunk-rest","clojure.core/chunked-seq?","clojure.core/class","clojure.core/class?","clojure.core/clear-agent-errors","clojure.core/clojure-version","clojure.core/coll?","clojure.core/commute","clojure.core/comp","clojure.core/comparator","clojure.core/compare","clojure.core/compare-and-set!","clojure.core/compile","clojure.core/complement","clojure.core/completing","clojure.core/concat","clojure.core/conj","clojure.core/conj!","clojure.core/cons","clojure.core/constantly","clojure.core/construct-proxy","clojure.core/contains?","clojure.core/count","clojure.core/counted?","clojure.core/create-ns","clojure.core/create-struct","clojure.core/cycle","clojure.core/dec","clojure.core/dec'","clojure.core/decimal?","clojure.core/dedupe","clojure.core/delay?","clojure.core/deliver","clojure.core/denominator","clojure.core/deref","clojure.core/derive","clojure.core/descendants","clojure.core/destructure","clojure.core/disj","clojure.core/disj!","clojure.core/dissoc","clojure.core/dissoc!","clojure.core/distinct","clojure.core/distinct?","clojure.core/doall","clojure.core/dorun","clojure.core/double","clojure.core/double-array","clojure.core/doubles","clojure.core/drop","clojure.core/drop-last","clojure.core/drop-while","clojure.core/eduction","clojure.core/empty","clojure.core/empty?","clojure.core/ensure","clojure.core/ensure-reduced","clojure.core/enumeration-seq","clojure.core/error-handler","clojure.core/error-mode","clojure.core/eval","clojure.core/even?","clojure.core/every-pred","clojure.core/every?","clojure.core/ex-data","clojure.core/ex-info","clojure.core/extend","clojure.core/extenders","clojure.core/extends?","clojure.core/false?","clojure.core/ffirst","clojure.core/file-seq","clojure.core/filter","clojure.core/filterv","clojure.core/find","clojure.core/find-keyword","clojure.core/find-ns","clojure.core/find-protocol-impl","clojure.core/find-protocol-method","clojure.core/find-var","clojure.core/first","clojure.core/flatten","clojure.core/float","clojure.core/float-array","clojure.core/float?","clojure.core/floats","clojure.core/flush","clojure.core/fn?","clojure.core/fnext","clojure.core/fnil","clojure.core/force","clojure.core/format","clojure.core/frequencies","clojure.core/future-call","clojure.core/future-cancel","clojure.core/future-cancelled?","clojure.core/future-done?","clojure.core/future?","clojure.core/gensym","clojure.core/get","clojure.core/get-in","clojure.core/get-method","clojure.core/get-proxy-class","clojure.core/get-thread-bindings","clojure.core/get-validator","clojure.core/group-by","clojure.core/hash","clojure.core/hash-combine","clojure.core/hash-map","clojure.core/hash-ordered-coll","clojure.core/hash-set","clojure.core/hash-unordered-coll","clojure.core/identical?","clojure.core/identity","clojure.core/ifn?","clojure.core/in-ns","clojure.core/inc","clojure.core/inc'","clojure.core/init-proxy","clojure.core/instance?","clojure.core/int","clojure.core/int-array","clojure.core/integer?","clojure.core/interleave","clojure.core/intern","clojure.core/interpose","clojure.core/into","clojure.core/into-array","clojure.core/ints","clojure.core/isa?","clojure.core/iterate","clojure.core/iterator-seq","clojure.core/juxt","clojure.core/keep","clojure.core/keep-indexed","clojure.core/key","clojure.core/keys","clojure.core/keyword","clojure.core/keyword?","clojure.core/last","clojure.core/line-seq","clojure.core/list","clojure.core/list*","clojure.core/list?","clojure.core/load","clojure.core/load-file","clojure.core/load-reader","clojure.core/load-string","clojure.core/loaded-libs","clojure.core/long","clojure.core/long-array","clojure.core/longs","clojure.core/macroexpand","clojure.core/macroexpand-1","clojure.core/make-array","clojure.core/make-hierarchy","clojure.core/map","clojure.core/map-entry?","clojure.core/map-indexed","clojure.core/map?","clojure.core/mapcat","clojure.core/mapv","clojure.core/max","clojure.core/max-key","clojure.core/memoize","clojure.core/merge","clojure.core/merge-with","clojure.core/meta","clojure.core/method-sig","clojure.core/methods","clojure.core/min","clojure.core/min-key","clojure.core/mix-collection-hash","clojure.core/mod","clojure.core/munge","clojure.core/name","clojure.core/namespace","clojure.core/namespace-munge","clojure.core/neg?","clojure.core/newline","clojure.core/next","clojure.core/nfirst","clojure.core/nil?","clojure.core/nnext","clojure.core/not","clojure.core/not-any?","clojure.core/not-empty","clojure.core/not-every?","clojure.core/not=","clojure.core/ns-aliases","clojure.core/ns-imports","clojure.core/ns-interns","clojure.core/ns-map","clojure.core/ns-name","clojure.core/ns-publics","clojure.core/ns-refers","clojure.core/ns-resolve","clojure.core/ns-unalias","clojure.core/ns-unmap","clojure.core/nth","clojure.core/nthnext","clojure.core/nthrest","clojure.core/num","clojure.core/number?","clojure.core/numerator","clojure.core/object-array","clojure.core/odd?","clojure.core/parents","clojure.core/partial","clojure.core/partition","clojure.core/partition-all","clojure.core/partition-by","clojure.core/pcalls","clojure.core/peek","clojure.core/persistent!","clojure.core/pmap","clojure.core/pop","clojure.core/pop!","clojure.core/pop-thread-bindings","clojure.core/pos?","clojure.core/pr","clojure.core/pr-str","clojure.core/prefer-method","clojure.core/prefers","clojure.core/print","clojure.core/print-ctor","clojure.core/print-dup","clojure.core/print-method","clojure.core/print-simple","clojure.core/print-str","clojure.core/printf","clojure.core/println","clojure.core/println-str","clojure.core/prn","clojure.core/prn-str","clojure.core/promise","clojure.core/proxy-call-with-super","clojure.core/proxy-mappings","clojure.core/proxy-name","clojure.core/push-thread-bindings","clojure.core/quot","clojure.core/rand","clojure.core/rand-int","clojure.core/rand-nth","clojure.core/random-sample","clojure.core/range","clojure.core/ratio?","clojure.core/rational?","clojure.core/rationalize","clojure.core/re-find","clojure.core/re-groups","clojure.core/re-matcher","clojure.core/re-matches","clojure.core/re-pattern","clojure.core/re-seq","clojure.core/read","clojure.core/read-line","clojure.core/read-string","clojure.core/reader-conditional","clojure.core/reader-conditional?","clojure.core/realized?","clojure.core/record?","clojure.core/reduce","clojure.core/reduce-kv","clojure.core/reduced","clojure.core/reduced?","clojure.core/reductions","clojure.core/ref","clojure.core/ref-history-count","clojure.core/ref-max-history","clojure.core/ref-min-history","clojure.core/ref-set","clojure.core/refer","clojure.core/release-pending-sends","clojure.core/rem","clojure.core/remove","clojure.core/remove-all-methods","clojure.core/remove-method","clojure.core/remove-ns","clojure.core/remove-watch","clojure.core/repeat","clojure.core/repeatedly","clojure.core/replace","clojure.core/replicate","clojure.core/require","clojure.core/reset!","clojure.core/reset-meta!","clojure.core/resolve","clojure.core/rest","clojure.core/restart-agent","clojure.core/resultset-seq","clojure.core/reverse","clojure.core/reversible?","clojure.core/rseq","clojure.core/rsubseq","clojure.core/run!","clojure.core/satisfies?","clojure.core/second","clojure.core/select-keys","clojure.core/send","clojure.core/send-off","clojure.core/send-via","clojure.core/seq","clojure.core/seq?","clojure.core/seque","clojure.core/sequence","clojure.core/sequential?","clojure.core/set","clojure.core/set-agent-send-executor!","clojure.core/set-agent-send-off-executor!","clojure.core/set-error-handler!","clojure.core/set-error-mode!","clojure.core/set-validator!","clojure.core/set?","clojure.core/short","clojure.core/short-array","clojure.core/shorts","clojure.core/shuffle","clojure.core/shutdown-agents","clojure.core/slurp","clojure.core/some","clojure.core/some-fn","clojure.core/some?","clojure.core/sort","clojure.core/sort-by","clojure.core/sorted-map","clojure.core/sorted-map-by","clojure.core/sorted-set","clojure.core/sorted-set-by","clojure.core/sorted?","clojure.core/special-symbol?","clojure.core/spit","clojure.core/split-at","clojure.core/split-with","clojure.core/str","clojure.core/string?","clojure.core/struct","clojure.core/struct-map","clojure.core/subs","clojure.core/subseq","clojure.core/subvec","clojure.core/supers","clojure.core/swap!","clojure.core/symbol","clojure.core/symbol?","clojure.core/tagged-literal","clojure.core/tagged-literal?","clojure.core/take","clojure.core/take-last","clojure.core/take-nth","clojure.core/take-while","clojure.core/test","clojure.core/the-ns","clojure.core/thread-bound?","clojure.core/to-array","clojure.core/to-array-2d","clojure.core/trampoline","clojure.core/transduce","clojure.core/transient","clojure.core/tree-seq","clojure.core/true?","clojure.core/type","clojure.core/unchecked-add","clojure.core/unchecked-add-int","clojure.core/unchecked-byte","clojure.core/unchecked-char","clojure.core/unchecked-dec","clojure.core/unchecked-dec-int","clojure.core/unchecked-divide-int","clojure.core/unchecked-double","clojure.core/unchecked-float","clojure.core/unchecked-inc","clojure.core/unchecked-inc-int","clojure.core/unchecked-int","clojure.core/unchecked-long","clojure.core/unchecked-multiply","clojure.core/unchecked-multiply-int","clojure.core/unchecked-negate","clojure.core/unchecked-negate-int","clojure.core/unchecked-remainder-int","clojure.core/unchecked-short","clojure.core/unchecked-subtract","clojure.core/unchecked-subtract-int","clojure.core/underive","clojure.core/unreduced","clojure.core/unsigned-bit-shift-right","clojure.core/update","clojure.core/update-in","clojure.core/update-proxy","clojure.core/use","clojure.core/val","clojure.core/vals","clojure.core/var-get","clojure.core/var-set","clojure.core/var?","clojure.core/vary-meta","clojure.core/vec","clojure.core/vector","clojure.core/vector-of","clojure.core/vector?","clojure.core/volatile!","clojure.core/volatile?","clojure.core/vreset!","clojure.core/with-bindings*","clojure.core/with-meta","clojure.core/with-redefs-fn","clojure.core/xml-seq","clojure.core/zero?","clojure.core/zipmap","coll?","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","conj","conj!","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","dedupe","delay?","deliver","denominator","deref","derive","descendants","destructure","disj","disj!","dissoc","dissoc!","distinct","distinct?","doall","dorun","double","double-array","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-data","ex-info","extend","extenders","extends?","false?","ffirst","file-seq","filter","filterv","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn?","fnext","fnil","force","format","frequencies","future-call","future-cancel","future-cancelled?","future-done?","future?","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","identical?","identity","ifn?","in-ns","inc","inc'","init-proxy","instance?","int","int-array","integer?","interleave","intern","interpose","into","into-array","ints","isa?","iterate","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","long","long-array","longs","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","munge","name","namespace","namespace-munge","neg?","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","parents","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop","pop!","pop-thread-bindings","pos?","pr","pr-str","prefer-method","prefers","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy-call-with-super","proxy-mappings","proxy-name","push-thread-bindings","quot","rand","rand-int","rand-nth","random-sample","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-watch","repeat","repeatedly","replace","replicate","require","reset!","reset-meta!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq?","seque","sequence","sequential?","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","slurp","some","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","symbol","symbol?","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","test","the-ns","thread-bound?","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true?","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unreduced","unsigned-bit-shift-right","update","update-in","update-proxy","use","val","vals","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","with-bindings*","with-meta","with-redefs-fn","xml-seq","zero?","zipmap"]
+ \ , 'clojureMacro': ["->","->>","..","amap","and","areduce","as->","assert","binding","bound-fn","clojure.core/->","clojure.core/->>","clojure.core/..","clojure.core/amap","clojure.core/and","clojure.core/areduce","clojure.core/as->","clojure.core/assert","clojure.core/binding","clojure.core/bound-fn","clojure.core/comment","clojure.core/declare","clojure.core/delay","clojure.core/dosync","clojure.core/doto","clojure.core/extend-protocol","clojure.core/extend-type","clojure.core/for","clojure.core/future","clojure.core/gen-class","clojure.core/gen-interface","clojure.core/import","clojure.core/io!","clojure.core/lazy-cat","clojure.core/lazy-seq","clojure.core/letfn","clojure.core/locking","clojure.core/memfn","clojure.core/ns","clojure.core/or","clojure.core/proxy","clojure.core/proxy-super","clojure.core/pvalues","clojure.core/refer-clojure","clojure.core/reify","clojure.core/some->","clojure.core/some->>","clojure.core/sync","clojure.core/time","clojure.core/vswap!","clojure.core/with-bindings","clojure.core/with-in-str","clojure.core/with-loading-context","clojure.core/with-local-vars","clojure.core/with-open","clojure.core/with-out-str","clojure.core/with-precision","clojure.core/with-redefs","comment","declare","delay","dosync","doto","extend-protocol","extend-type","for","future","gen-class","gen-interface","import","io!","lazy-cat","lazy-seq","letfn","locking","memfn","ns","or","proxy","proxy-super","pvalues","refer-clojure","reify","some->","some->>","sync","time","vswap!","with-bindings","with-in-str","with-loading-context","with-local-vars","with-open","with-out-str","with-precision","with-redefs"]
+ \ , 'clojureRepeat': ["clojure.core/doseq","clojure.core/dotimes","clojure.core/while","doseq","dotimes","while"]
+ \ , 'clojureSpecial': [".","clojure.core/fn","clojure.core/let","clojure.core/loop","def","do","fn","if","let","loop","monitor-enter","monitor-exit","new","quote","recur","set!","var"]
+ \ , 'clojureVariable': ["*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-readably*","*read-eval*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","EMPTY-NODE","char-escape-string","char-name-string","clojure.core/*1","clojure.core/*2","clojure.core/*3","clojure.core/*agent*","clojure.core/*allow-unresolved-vars*","clojure.core/*assert*","clojure.core/*clojure-version*","clojure.core/*command-line-args*","clojure.core/*compile-files*","clojure.core/*compile-path*","clojure.core/*compiler-options*","clojure.core/*data-readers*","clojure.core/*default-data-reader-fn*","clojure.core/*e","clojure.core/*err*","clojure.core/*file*","clojure.core/*flush-on-newline*","clojure.core/*fn-loader*","clojure.core/*in*","clojure.core/*math-context*","clojure.core/*ns*","clojure.core/*out*","clojure.core/*print-dup*","clojure.core/*print-length*","clojure.core/*print-level*","clojure.core/*print-meta*","clojure.core/*print-readably*","clojure.core/*read-eval*","clojure.core/*source-path*","clojure.core/*suppress-read*","clojure.core/*unchecked-math*","clojure.core/*use-context-classloader*","clojure.core/*verbose-defrecords*","clojure.core/*warn-on-reflection*","clojure.core/EMPTY-NODE","clojure.core/char-escape-string","clojure.core/char-name-string","clojure.core/default-data-readers","clojure.core/primitives-classnames","clojure.core/unquote","clojure.core/unquote-splicing","default-data-readers","primitives-classnames","unquote","unquote-splicing"]
+ \ }
+
+function! s:syntax_keyword(dict)
+ for key in keys(a:dict)
+ execute 'syntax keyword' key join(a:dict[key], ' ')
+ endfor
+endfunction
+
+if exists('b:clojure_syntax_without_core_keywords') && b:clojure_syntax_without_core_keywords
+ " Only match language specials and primitives
+ for s:key in ['clojureBoolean', 'clojureConstant', 'clojureException', 'clojureSpecial']
+ execute 'syntax keyword' s:key join(s:clojure_syntax_keywords[s:key], ' ')
+ endfor
+else
+ call s:syntax_keyword(s:clojure_syntax_keywords)
+endif
+
+if exists('g:clojure_syntax_keywords')
+ call s:syntax_keyword(g:clojure_syntax_keywords)
+endif
+
+if exists('b:clojure_syntax_keywords')
+ call s:syntax_keyword(b:clojure_syntax_keywords)
+endif
+
+unlet! s:key
+delfunction s:syntax_keyword
" Keywords are symbols:
" static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?([\\D&&[^/]][^/]*)");
@@ -43,7 +74,7 @@ syntax match clojureKeyword "\v<:{1,2}%([^ \n\r\t()\[\]{}";@^`~\\%/]+/)*[^ \n\r\
syntax match clojureStringEscape "\v\\%([\\btnfr"]|u\x{4}|[0-3]\o{2}|\o{1,2})" contained
-syntax region clojureString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=clojureStringEscape,@Spell
+syntax region clojureString matchgroup=clojureStringDelimiter start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=clojureStringEscape,@Spell
syntax match clojureCharacter "\\."
syntax match clojureCharacter "\\o\%([0-3]\o\{2\}\|\o\{1,2\}\)"
@@ -77,7 +108,7 @@ syntax match clojureMeta "\^"
syntax match clojureDeref "@"
syntax match clojureDispatch "\v#[\^'=<_]?"
-" Clojure permits no more than 20 params.
+" Clojure permits no more than 20 anonymous params.
syntax match clojureAnonArg "%\(20\|1\d\|[1-9]\|&\)\?"
syntax match clojureRegexpEscape "\v\\%([\\tnrfae.()\[\]{}^$*?+]|c\u|0[0-3]?\o{1,2}|x%(\x{2}|\{\x{1,6}\})|u\x{4})" contained display
@@ -85,27 +116,27 @@ syntax region clojureRegexpQuoted start=/\\Q/ms=e+1 skip=/\\\\\|\\"/ end=/\\E/me
syntax region clojureRegexpQuote start=/\\Q/ skip=/\\\\\|\\"/ end=/\\E/ end=/"/me=s-1 contains=clojureRegexpQuoted keepend contained
" -*- CHARACTER PROPERTY CLASSES -*-
-" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-010/clj/src/vim_clojure_static/generate.clj
-" Java version 1.7.0_51
+" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-011/clj/src/vim_clojure_static/generate.clj
+" Java version 1.8.0_92
syntax match clojureRegexpPosixCharClass "\v\\[pP]\{%(Cntrl|A%(l%(pha|num)|SCII)|Space|Graph|Upper|P%(rint|unct)|Blank|XDigit|Digit|Lower)\}" contained display
syntax match clojureRegexpJavaCharClass "\v\\[pP]\{java%(Whitespace|JavaIdentifier%(Part|Start)|SpaceChar|Mirrored|TitleCase|I%(SOControl|de%(ographic|ntifierIgnorable))|D%(efined|igit)|U%(pperCase|nicodeIdentifier%(Part|Start))|L%(etter%(OrDigit)?|owerCase)|Alphabetic)\}" contained display
-syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\cIs%(l%(owercase|etter)|hex%(digit|_digit)|w%(hite%(_space|space)|ord)|noncharacter%(_code_point|codepoint)|p%(rint|unctuation)|ideographic|graph|a%(l%(num|phabetic)|ssigned)|uppercase|titlecase|blank|digit|control)\}" contained display
+syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\cIs%(l%(owercase|etter)|hex%(digit|_digit)|w%(hite%(_space|space)|ord)|noncharacter%(_code_point|codepoint)|p%(rint|unctuation)|ideographic|graph|a%(l%(num|phabetic)|ssigned)|uppercase|join%(control|_control)|titlecase|blank|digit|control)\}" contained display
syntax match clojureRegexpUnicodeCharClass "\v\\[pP][NSCMZPL]" contained display
syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{%(N[dlo]?|P[dcifeos]?|C[ncfos]?|M[nce]?|Z[lsp]?|S[mcko]?|L[muCDlto]?)\}" contained display
syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{%(Is|gc\=|general_category\=)?%(N[dlo]?|P[dcifeos]?|C[ncfos]?|M[nce]?|Z[lsp]?|S[mcko]?|L[muCDlto]?)\}" contained display
-syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\c%(Is|sc\=|script\=)%(l%(epc%(ha)?|y%([dc]i%(an)?)|a%(t%(n|in)|na|oo?)|i%(n%(b|ear_b)|mbu?|su))|vaii?|d%(srt|e%(seret|va%(nagari)?))|g%(lag%(olitic)?|eor%(gian)?|oth%(ic)?|re%(k|ek)|u%(j%(arati|r)|r%(u|mukhi)))|u%(gar%(itic)?|nknown)|a%(r%(ab%(ic)?|m%([ni]|enian))|v%(st|estan))|e%(thi%(opic)?|gyp%(tian_hieroglyphs)?)|z%(inh|yyy|zzz)|r%(un%(ic|r)|ejang|jng)|m%(a%(nd%(aic)?|layalam)|lym|y%(anmar|mr)|tei|ong%(olian)?|eetei_mayek)|c%(a%(n%(adian_aboriginal|s)|ri%(an)?)|y%(priot|r%(l|illic))|prt|uneiform|o%(pt%(ic)?|mmon)|h%(er%(okee)?|am))|i%(n%(scriptional_pa%(rthian|hlavi)|herited)|mperial_aramaic|tal)|b%(eng%(ali)?|a%(t%(ak|k)|li%(nese)?|mum?)|ra%(i%(lle)?|h%(mi)?)|opo%(mofo)?|u%(gi%(nese)?|h%(d|id)))|o%(g%(am|ham)|r%(iya|kh|ya)|sma%(nya)?|l%(d_%(south_arabian|persian|italic|turkic)|ck|_chiki))|p%(rti|h%(oenician|li|ag%(s_pa)?|nx))|k%(h%(m%(r|er)|ar%(oshthi)?)|nda|a%(li|n%(a|nada)|takana|yah_li|ithi)|thi)|yi%(ii)?|t%(elu%(gu)?|i%(finagh|b%(t|etan))|ha%(i|a%(na)?)|a%(i_%(le|tham|viet)|g%(alog|b%(anwa)?)|vt|l[ue]|m%(il|l))|fng|glg)|x%(peo|sux)|n%(ew_tai_lue|koo?)|h%(ira%(gana)?|an%([io]|unoo|g%(ul)?)?|ebr%(ew)?)|java%(nese)?|s%(inh%(ala)?|ha%(vian|w)|a%(ur%(ashtra)?|m%(r|aritan)|rb)|y%(r%(c|iac)|lo%(ti_nagri)?)|und%(anese)?))\}" contained display
-syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\c%(In|blk\=|block\=)%(javanese|h%(a%(lfwidth%( and fullwidth forms|andfullwidthforms|_and_fullwidth_forms)|n%(unoo|gul%(compatibilityjamo|syllables|jamo%(extended\-[ab])?|_%(syllables|jamo%(_extended_[ab])?|compatibility_jamo)| %(syllables|compatibility jamo|jamo%( extended\-[ab])?))))|i%(ragana|gh%( %(private use surrogates|surrogates)|_%(private_use_surrogates|surrogates)|surrogates|privateusesurrogates))|ebrew)|i%(pa%([ _]extensions|extensions)|deographic%( description characters|_description_characters|descriptioncharacters)|nscriptional%(%([ _]pa%(rthian|hlavi))|pa%(rthian|hlavi))|mperial%(aramaic|[_ ]aramaic))|l%(e%(tterlike%([_ ]symbols|symbols)|pcha)|ow%([_ ]surrogates|surrogates)|i%(mbu|near%(_b_%(ideograms|syllabary)|b%(ideograms|syllabary)| b %(ideograms|syllabary))|su)|a%(tin%(extended%(additional|\-[dacb])| extended%( additional|\-[dacb])|\-1%(supplement| supplement)|_%(extended_%([dcb]|a%(dditional)?)|1_supplement))|o)|y[cd]ian)|b%(u%(ginese|hid)|ra%(hmi|ille%(patterns|[_ ]patterns))|o%(x%([ _]drawing|drawing)|pomofo%([ _]extended|extended)?)|lock%([ _]elements|elements)|yzantine%( musical symbols|musicalsymbols|_musical_symbols)|engali|a%(linese|mum%(supplement|[ _]supplement)?|tak|sic%([ _]latin|latin)))|e%(gyptian%([ _]hieroglyphs|hieroglyphs)|moticons|nclosed%( %(cjk letters and months|ideographic supplement|alphanumeric%( supplement|s))|cjklettersandmonths|_%(ideographic_supplement|alphanumeric%(_supplement|s)|cjk_letters_and_months)|alphanumerics%(upplement)?|ideographicsupplement)|thiopic%(supplement|_%(supplement|extended%(_a)?)| %(supplement|extended%(\-a)?)|extended%(\-a)?)?)|k%(h%(aroshthi|mer%([_ ]symbols|symbols)?)|a%(takana%(_phonetic_extensions|phoneticextensions| phonetic extensions)?|n%(gxi%([_ ]radicals|radicals)|a%(supplement|[ _]supplement)|bun|nada)|ithi|yah%([ _]li|li)))|r%(u%(nic|mi%(numeralsymbols| numeral symbols|_numeral_symbols))|ejang)|n%(umber%(forms|[ _]forms)|ko|ew%(_tai_lue|tailue| tai lue))|m%(iscellaneous%(technical|symbols%(and%(pictographs|arrows))?|mathematicalsymbols\-[ab]| %(technical|mathematical symbols\-[ab]|symbols%( and %(pictographs|arrows))?)|_%(technical|symbols%(_and_%(pictographs|arrows))?|mathematical_symbols_[ab]))|usical%([_ ]symbols|symbols)|eetei%(mayek|[_ ]mayek)|a%(ndaic|hjong%([ _]tiles|tiles)|layalam|thematical%(alphanumericsymbols| %(alphanumeric symbols|operators)|_%(alphanumeric_symbols|operators)|operators))|yanmar%(extended\-a|_extended_a| extended\-a)?|o%(difier%(_tone_letters| tone letters|toneletters)|ngolian))|d%(e%(seret|vanagari%([ _]extended|extended)?)|ingbats|omino%([ _]tiles|tiles))|yi%(syllables|%([_ ]%(syllables|radicals))|radicals|jing%(hexagramsymbols| hexagram symbols|_hexagram_symbols))|s%(havian|mall%( form variants|formvariants|_form_variants)|p%(acing%(_modifier_letters| modifier letters|modifierletters)|ecials)|a%(maritan|urashtra)|u%(p%(erscripts%(_and_subscripts|andsubscripts| and subscripts)|plementa%(ry%(_private_use_area_[ab]|privateusearea\-[ab]| private use area\-[ab])|l%(_%(arrows_[ab]|mathematical_operators|punctuation)| %(mathematical operators|punctuation|arrows\-[ab])|mathematicaloperators|punctuation|arrows\-[ab])))|rrogates_area|ndanese)|inhala|y%(riac|loti%([_ ]nagri|nagri)))|p%(h%(o%(enician|netic%( extensions%( supplement)?|extensions%(supplement)?|_extensions%(_supplement)?))|a%(istos%([ _]disc|disc)|gs[_\-]pa))|laying%(cards|[_ ]cards)|rivate%(usearea| use area|_use_area))|o%(smanya|l%([ _]chiki|d%( %(south arabian|persian|italic|turkic)|southarabian|_%(south_arabian|persian|italic|turkic)|persian|italic|turkic)|chiki)|riya|ptical%( character recognition|_character_recognition|characterrecognition)|gham)|g%(u%(jarati|rmukhi)|othic|lagolitic|e%(o%(rgian%(supplement|[ _]supplement)?|metric%(shapes|[ _]shapes))|neral%([_ ]punctuation|punctuation))|reek%( %(and coptic|extended)|andcoptic|_extended|extended)?)|c%(o%(ntrol%(pictures|[ _]pictures)|m%(bining%(diacriticalmarks%(supplement|forsymbols)?|halfmarks| %(diacritical marks%( %(supplement|for symbols))?|half marks|marks for symbols)|marksforsymbols|_%(marks_for_symbols|half_marks|diacritical_marks%(_supplement)?))|mon%(_indic_number_forms|indicnumberforms| indic number forms))|ptic|unting%( rod numerals|_rod_numerals|rodnumerals))|y%(rillic%(extended\-[ab]|_%(extended_[ab]|supplementary)|supplement%(ary)?| %(extended\-[ab]|supplement%(ary)?))?|priot%(syllabary|[ _]syllabary))|u%(rrency%([_ ]symbols|symbols)|neiform%(_numbers_and_punctuation|numbersandpunctuation| numbers and punctuation)?)|arian|h%(erokee|am)|jk%(s%(ymbolsandpunctuation|trokes)|compatibility%(forms|ideographs%(supplement)?)?|radicalssupplement| %(compatibility%( %(ideographs%( supplement)?|forms))?|radicals supplement|unified ideographs%( extension [dacb])?|s%(ymbols and punctuation|trokes))|_%(s%(trokes|ymbols_and_punctuation)|radicals_supplement|compatibility%(_%(forms|ideographs%(_supplement)?))?|unified_ideographs%(_extension_[dacb])?)|unifiedideographs%(extension[dacb])?))|t%(i%(betan|finagh)|elugu|a%(mil|i%(xuanjingsymbols|_%(le|xuan_jing_symbols|tham|viet)|le| %(xuan jing symbols|le|tham|viet)|tham|viet)|g%(alog|s|banwa))|ransport%( and map symbols|_and_map_symbols|andmapsymbols)|ha%(i|ana))|a%(l%(chemical%([_ ]symbols|symbols)|phabetic%( presentation forms|_presentation_forms|presentationforms))|ncient%(_%(greek_%(musical_notation|numbers)|symbols)|greek%(numbers|musicalnotation)| %(greek %(numbers|musical notation)|symbols)|symbols)|egean%(numbers|[ _]numbers)|vestan|r%(abic%( %(supplement|presentation forms\-[ab])|supplement|_%(presentation_forms_[ab]|supplement)|presentationforms\-[ab])?|menian|rows))|u%(garitic|nified%(canadianaboriginalsyllabics%(extended)?|_canadian_aboriginal_syllabics%(_extended)?| canadian aboriginal syllabics%( extended)?))|v%(a%(i|riation%( selectors%( supplement)?|selectors%(supplement)?|_selectors%(_supplement)?))|e%(rtical%(forms|[ _]forms)|dic%([ _]extensions|extensions))))\}" contained display
-
-syntax match clojureRegexpPredefinedCharClass "\v%(\\[dDsSwW]|\.)" contained display
-syntax cluster clojureRegexpCharPropertyClasses contains=clojureRegexpPosixCharClass,clojureRegexpJavaCharClass,clojureRegexpUnicodeCharClass
-syntax cluster clojureRegexpCharClasses contains=clojureRegexpPredefinedCharClass,clojureRegexpCharClass,@clojureRegexpCharPropertyClasses
-syntax region clojureRegexpCharClass start="\[" skip=/\\\\\|\\]/ end="]" contained contains=clojureRegexpPredefinedCharClass,@clojureRegexpCharPropertyClasses
-syntax match clojureRegexpBoundary "\\[bBAGZz]" contained display
-syntax match clojureRegexpBoundary "[$^]" contained display
-syntax match clojureRegexpQuantifier "[?*+][?+]\=" contained display
-syntax match clojureRegexpQuantifier "\v\{\d+%(,|,\d+)?}\??" contained display
-syntax match clojureRegexpOr "|" contained display
-syntax match clojureRegexpBackRef "\v\\%([1-9]\d*|k\<[a-zA-z]+\>)" contained display
+syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\c%(Is|sc\=|script\=)%(l%(epc%(ha)?|y%([dc]i%(an)?)|a%(t%(n|in)|na|oo?)|i%(n%(b|ear_b)|mbu?|su))|p%(rti|lrd|h%(oenician|li|ag%(s_pa)?|nx))|vaii?|d%(srt|e%(seret|va%(nagari)?))|g%(lag%(olitic)?|eor%(gian)?|oth%(ic)?|re%(k|ek)|u%(j%(arati|r)|r%(u|mukhi)))|u%(gar%(itic)?|nknown)|a%(r%(ab%(ic)?|m%([ni]|enian))|v%(st|estan))|e%(thi%(opic)?|gyp%(tian_hieroglyphs)?)|z%(inh|yyy|zzz)|r%(un%(ic|r)|ejang|jng)|s%(inh%(ala)?|h%(rd|a%(vian|rada|w))|a%(ur%(ashtra)?|m%(r|aritan)|rb)|y%(r%(c|iac)|lo%(ti_nagri)?)|und%(anese)?|ora%(_sompeng)?)|i%(n%(scriptional_pa%(rthian|hlavi)|herited)|mperial_aramaic|tal)|b%(eng%(ali)?|a%(t%(ak|k)|li%(nese)?|mum?)|ra%(i%(lle)?|h%(mi)?)|opo%(mofo)?|u%(gi%(nese)?|h%(d|id)))|o%(g%(am|ham)|r%(iya|kh|ya)|sma%(nya)?|l%(d_%(south_arabian|persian|italic|turkic)|ck|_chiki))|k%(h%(m%(r|er)|ar%(oshthi)?)|nda|a%(li|n%(a|nada)|takana|yah_li|ithi)|thi)|m%(a%(nd%(aic)?|layalam)|lym|y%(anmar|mr)|tei|e%(r%(c|o%(itic_%(hieroglyphs|cursive))?)|etei_mayek)|ong%(olian)?|iao)|yi%(ii)?|x%(peo|sux)|n%(ew_tai_lue|koo?)|h%(ira%(gana)?|an%([io]|unoo|g%(ul)?)?|ebr%(ew)?)|c%(y%(priot|r%(l|illic))|a%(km|n%(adian_aboriginal|s)|ri%(an)?)|prt|h%(er%(okee)?|a%(m|kma))|uneiform|o%(pt%(ic)?|mmon))|t%(elu%(gu)?|i%(finagh|b%(t|etan))|ha%(i|a%(na)?)|a%(i_%(le|tham|viet)|g%(alog|b%(anwa)?)|vt|kri?|l[ue]|m%(il|l))|fng|glg)|java%(nese)?)\}" contained display
+syntax match clojureRegexpUnicodeCharClass "\v\\[pP]\{\c%(In|blk\=|block\=)%(javanese|h%(a%(lfwidth%( and fullwidth forms|andfullwidthforms|_and_fullwidth_forms)|n%(unoo|gul%(compatibilityjamo|syllables|jamo%(extended\-[ab])?|_%(syllables|jamo%(_extended_[ab])?|compatibility_jamo)| %(syllables|compatibility jamo|jamo%( extended\-[ab])?))))|i%(ragana|gh%( %(private use surrogates|surrogates)|_%(private_use_surrogates|surrogates)|surrogates|privateusesurrogates))|ebrew)|i%(pa%([ _]extensions|extensions)|deographic%( description characters|_description_characters|descriptioncharacters)|nscriptional%(%([ _]pa%(rthian|hlavi))|pa%(rthian|hlavi))|mperial%(aramaic|[_ ]aramaic))|l%(e%(tterlike%([_ ]symbols|symbols)|pcha)|ow%([_ ]surrogates|surrogates)|i%(mbu|near%(_b_%(ideograms|syllabary)|b%(ideograms|syllabary)| b %(ideograms|syllabary))|su)|a%(tin%(extended%(additional|\-[dacb])| extended%( additional|\-[dacb])|\-1%(supplement| supplement)|_%(extended_%([dcb]|a%(dditional)?)|1_supplement))|o)|y[cd]ian)|b%(u%(ginese|hid)|ra%(hmi|ille%(patterns|[_ ]patterns))|o%(x%([ _]drawing|drawing)|pomofo%([ _]extended|extended)?)|lock%([ _]elements|elements)|yzantine%( musical symbols|musicalsymbols|_musical_symbols)|engali|a%(linese|mum%(supplement|[ _]supplement)?|tak|sic%([ _]latin|latin)))|e%(gyptian%([ _]hieroglyphs|hieroglyphs)|moticons|nclosed%( %(cjk letters and months|ideographic supplement|alphanumeric%( supplement|s))|cjklettersandmonths|_%(ideographic_supplement|alphanumeric%(_supplement|s)|cjk_letters_and_months)|alphanumerics%(upplement)?|ideographicsupplement)|thiopic%(supplement|_%(supplement|extended%(_a)?)| %(supplement|extended%(\-a)?)|extended%(\-a)?)?)|k%(h%(aroshthi|mer%([_ ]symbols|symbols)?)|a%(takana%(_phonetic_extensions|phoneticextensions| phonetic extensions)?|n%(gxi%([_ ]radicals|radicals)|a%(supplement|[ _]supplement)|bun|nada)|ithi|yah%([ _]li|li)))|m%(i%(ao|scellaneous%(technical|symbols%(and%(pictographs|arrows))?|mathematicalsymbols\-[ab]| %(technical|mathematical symbols\-[ab]|symbols%( and %(pictographs|arrows))?)|_%(technical|symbols%(_and_%(pictographs|arrows))?|mathematical_symbols_[ab])))|usical%([_ ]symbols|symbols)|e%(etei%(mayek%(extensions)?|_mayek%(_extensions)?| mayek%( extensions)?)|roitic%(hieroglyphs|%([_ ]%(hieroglyphs|cursive))|cursive))|a%(ndaic|hjong%([ _]tiles|tiles)|layalam|thematical%(alphanumericsymbols| %(alphanumeric symbols|operators)|_%(alphanumeric_symbols|operators)|operators))|yanmar%(extended\-a|_extended_a| extended\-a)?|o%(difier%(_tone_letters| tone letters|toneletters)|ngolian))|r%(u%(nic|mi%(numeralsymbols| numeral symbols|_numeral_symbols))|ejang)|n%(umber%(forms|[ _]forms)|ko|ew%(_tai_lue|tailue| tai lue))|c%(o%(ntrol%(pictures|[ _]pictures)|m%(bining%(diacriticalmarks%(supplement|forsymbols)?|halfmarks| %(diacritical marks%( %(supplement|for symbols))?|half marks|marks for symbols)|marksforsymbols|_%(marks_for_symbols|half_marks|diacritical_marks%(_supplement)?))|mon%(_indic_number_forms|indicnumberforms| indic number forms))|ptic|unting%( rod numerals|_rod_numerals|rodnumerals))|y%(rillic%(extended\-[ab]|_%(extended_[ab]|supplementary)|supplement%(ary)?| %(extended\-[ab]|supplement%(ary)?))?|priot%(syllabary|[ _]syllabary))|u%(rrency%([_ ]symbols|symbols)|neiform%(_numbers_and_punctuation|numbersandpunctuation| numbers and punctuation)?)|h%(a%(m|kma)|erokee)|arian|jk%(s%(ymbolsandpunctuation|trokes)|compatibility%(forms|ideographs%(supplement)?)?|radicalssupplement| %(compatibility%( %(ideographs%( supplement)?|forms))?|radicals supplement|unified ideographs%( extension [dacb])?|s%(ymbols and punctuation|trokes))|_%(s%(trokes|ymbols_and_punctuation)|radicals_supplement|compatibility%(_%(forms|ideographs%(_supplement)?))?|unified_ideographs%(_extension_[dacb])?)|unifiedideographs%(extension[dacb])?))|d%(e%(seret|vanagari%([ _]extended|extended)?)|ingbats|omino%([ _]tiles|tiles))|yi%(syllables|%([_ ]%(syllables|radicals))|radicals|jing%(hexagramsymbols| hexagram symbols|_hexagram_symbols))|s%(mall%( form variants|formvariants|_form_variants)|p%(acing%(_modifier_letters| modifier letters|modifierletters)|ecials)|ora%(sompeng|[ _]sompeng)|ha%(vian|rada)|a%(maritan|urashtra)|inhala|y%(riac|loti%([_ ]nagri|nagri))|u%(ndanese%(supplement|[ _]supplement)?|p%(erscripts%(_and_subscripts|andsubscripts| and subscripts)|plementa%(ry%(_private_use_area_[ab]|privateusearea\-[ab]| private use area\-[ab])|l%(_%(arrows_[ab]|mathematical_operators|punctuation)| %(mathematical operators|punctuation|arrows\-[ab])|mathematicaloperators|punctuation|arrows\-[ab])))|rrogates_area))|p%(h%(o%(enician|netic%( extensions%( supplement)?|extensions%(supplement)?|_extensions%(_supplement)?))|a%(istos%([ _]disc|disc)|gs[_\-]pa))|laying%(cards|[_ ]cards)|rivate%(usearea| use area|_use_area))|o%(smanya|l%([ _]chiki|d%( %(south arabian|persian|italic|turkic)|southarabian|_%(south_arabian|persian|italic|turkic)|persian|italic|turkic)|chiki)|riya|ptical%( character recognition|_character_recognition|characterrecognition)|gham)|g%(u%(jarati|rmukhi)|othic|lagolitic|e%(o%(rgian%(supplement|[ _]supplement)?|metric%(shapes|[ _]shapes))|neral%([_ ]punctuation|punctuation))|reek%( %(and coptic|extended)|andcoptic|_extended|extended)?)|t%(i%(betan|finagh)|elugu|ransport%( and map symbols|_and_map_symbols|andmapsymbols)|a%(mil|kri|i%(xuanjingsymbols|_%(le|xuan_jing_symbols|tham|viet)|le| %(xuan jing symbols|le|tham|viet)|tham|viet)|g%(alog|s|banwa))|ha%(i|ana))|a%(l%(chemical%([_ ]symbols|symbols)|phabetic%( presentation forms|_presentation_forms|presentationforms))|r%(menian|abic%(extended\-a|mathematicalalphabeticsymbols|supplement|_%(presentation_forms_[ab]|supplement|extended_a|mathematical_alphabetic_symbols)| %(extended\-a|mathematical alphabetic symbols|supplement|presentation forms\-[ab])|presentationforms\-[ab])?|rows)|ncient%(_%(greek_%(musical_notation|numbers)|symbols)|greek%(numbers|musicalnotation)| %(greek %(numbers|musical notation)|symbols)|symbols)|egean%(numbers|[ _]numbers)|vestan)|u%(garitic|nified%(canadianaboriginalsyllabics%(extended)?|_canadian_aboriginal_syllabics%(_extended)?| canadian aboriginal syllabics%( extended)?))|v%(a%(i|riation%( selectors%( supplement)?|selectors%(supplement)?|_selectors%(_supplement)?))|e%(rtical%(forms|[ _]forms)|dic%([ _]extensions|extensions))))\}" contained display
+
+syntax match clojureRegexpPredefinedCharClass "\v%(\\[dDsSwW]|\.)" contained display
+syntax cluster clojureRegexpCharPropertyClasses contains=clojureRegexpPosixCharClass,clojureRegexpJavaCharClass,clojureRegexpUnicodeCharClass
+syntax cluster clojureRegexpCharClasses contains=clojureRegexpPredefinedCharClass,clojureRegexpCharClass,@clojureRegexpCharPropertyClasses
+syntax region clojureRegexpCharClass start="\[" skip=/\\\\\|\\]/ end="]" contained contains=clojureRegexpPredefinedCharClass,@clojureRegexpCharPropertyClasses
+syntax match clojureRegexpBoundary "\\[bBAGZz]" contained display
+syntax match clojureRegexpBoundary "[$^]" contained display
+syntax match clojureRegexpQuantifier "[?*+][?+]\=" contained display
+syntax match clojureRegexpQuantifier "\v\{\d+%(,|,\d+)?}\??" contained display
+syntax match clojureRegexpOr "|" contained display
+syntax match clojureRegexpBackRef "\v\\%([1-9]\d*|k\<[a-zA-z]+\>)" contained display
" Mode modifiers, mode-modified spans, lookaround, regular and atomic
" grouping, and named-capturing.
@@ -123,66 +154,70 @@ syntax match clojureComment ";.*$" contains=clojureCommentTodo,@Spell
syntax match clojureComment "#!.*$"
" -*- TOP CLUSTER -*-
-" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-010/clj/src/vim_clojure_static/generate.clj
+" Generated from https://github.com/guns/vim-clojure-static/blob/vim-release-011/clj/src/vim_clojure_static/generate.clj
syntax cluster clojureTop contains=@Spell,clojureAnonArg,clojureBoolean,clojureCharacter,clojureComment,clojureCond,clojureConstant,clojureDefine,clojureDeref,clojureDispatch,clojureError,clojureException,clojureFunc,clojureKeyword,clojureMacro,clojureMap,clojureMeta,clojureNumber,clojureQuote,clojureRegexp,clojureRepeat,clojureSexp,clojureSpecial,clojureString,clojureSymbol,clojureUnquote,clojureVarArg,clojureVariable,clojureVector
-syntax region clojureSexp matchgroup=clojureParen start="(" matchgroup=clojureParen end=")" contains=@clojureTop fold
-syntax region clojureVector matchgroup=clojureParen start="\[" matchgroup=clojureParen end="]" contains=@clojureTop fold
-syntax region clojureMap matchgroup=clojureParen start="{" matchgroup=clojureParen end="}" contains=@clojureTop fold
+syntax region clojureSexp matchgroup=clojureParen start="(" end=")" contains=@clojureTop fold
+syntax region clojureVector matchgroup=clojureParen start="\[" end="]" contains=@clojureTop fold
+syntax region clojureMap matchgroup=clojureParen start="{" end="}" contains=@clojureTop fold
" Highlight superfluous closing parens, brackets and braces.
syntax match clojureError "]\|}\|)"
syntax sync fromstart
-highlight default link clojureConstant Constant
-highlight default link clojureBoolean Boolean
-highlight default link clojureCharacter Character
-highlight default link clojureKeyword Keyword
-highlight default link clojureNumber Number
-highlight default link clojureString String
-highlight default link clojureStringEscape Character
-
-highlight default link clojureRegexp Constant
-highlight default link clojureRegexpEscape Character
-highlight default link clojureRegexpCharClass SpecialChar
-highlight default link clojureRegexpPosixCharClass clojureRegexpCharClass
-highlight default link clojureRegexpJavaCharClass clojureRegexpCharClass
-highlight default link clojureRegexpUnicodeCharClass clojureRegexpCharClass
-highlight default link clojureRegexpPredefinedCharClass clojureRegexpCharClass
-highlight default link clojureRegexpBoundary SpecialChar
-highlight default link clojureRegexpQuantifier SpecialChar
-highlight default link clojureRegexpMod SpecialChar
-highlight default link clojureRegexpOr SpecialChar
-highlight default link clojureRegexpBackRef SpecialChar
-highlight default link clojureRegexpGroup clojureRegexp
-highlight default link clojureRegexpQuoted clojureString
-highlight default link clojureRegexpQuote clojureRegexpBoundary
-
-highlight default link clojureVariable Identifier
-highlight default link clojureCond Conditional
-highlight default link clojureDefine Define
-highlight default link clojureException Exception
-highlight default link clojureFunc Function
-highlight default link clojureMacro Macro
-highlight default link clojureRepeat Repeat
-
-highlight default link clojureSpecial Special
-highlight default link clojureVarArg Special
-highlight default link clojureQuote SpecialChar
-highlight default link clojureUnquote SpecialChar
-highlight default link clojureMeta SpecialChar
-highlight default link clojureDeref SpecialChar
-highlight default link clojureAnonArg SpecialChar
-highlight default link clojureDispatch SpecialChar
-
-highlight default link clojureComment Comment
-highlight default link clojureCommentTodo Todo
-
-highlight default link clojureError Error
-
-highlight default link clojureParen Delimiter
+highlight default link clojureConstant Constant
+highlight default link clojureBoolean Boolean
+highlight default link clojureCharacter Character
+highlight default link clojureKeyword Keyword
+highlight default link clojureNumber Number
+highlight default link clojureString String
+highlight default link clojureStringDelimiter String
+highlight default link clojureStringEscape Character
+
+highlight default link clojureRegexp Constant
+highlight default link clojureRegexpEscape Character
+highlight default link clojureRegexpCharClass SpecialChar
+highlight default link clojureRegexpPosixCharClass clojureRegexpCharClass
+highlight default link clojureRegexpJavaCharClass clojureRegexpCharClass
+highlight default link clojureRegexpUnicodeCharClass clojureRegexpCharClass
+highlight default link clojureRegexpPredefinedCharClass clojureRegexpCharClass
+highlight default link clojureRegexpBoundary SpecialChar
+highlight default link clojureRegexpQuantifier SpecialChar
+highlight default link clojureRegexpMod SpecialChar
+highlight default link clojureRegexpOr SpecialChar
+highlight default link clojureRegexpBackRef SpecialChar
+highlight default link clojureRegexpGroup clojureRegexp
+highlight default link clojureRegexpQuoted clojureString
+highlight default link clojureRegexpQuote clojureRegexpBoundary
+
+highlight default link clojureVariable Identifier
+highlight default link clojureCond Conditional
+highlight default link clojureDefine Define
+highlight default link clojureException Exception
+highlight default link clojureFunc Function
+highlight default link clojureMacro Macro
+highlight default link clojureRepeat Repeat
+
+highlight default link clojureSpecial Special
+highlight default link clojureVarArg Special
+highlight default link clojureQuote SpecialChar
+highlight default link clojureUnquote SpecialChar
+highlight default link clojureMeta SpecialChar
+highlight default link clojureDeref SpecialChar
+highlight default link clojureAnonArg SpecialChar
+highlight default link clojureDispatch SpecialChar
+
+highlight default link clojureComment Comment
+highlight default link clojureCommentTodo Todo
+
+highlight default link clojureError Error
+
+highlight default link clojureParen Delimiter
let b:current_syntax = "clojure"
-" vim:sts=8:sw=8:ts=8:noet:smc=0
+let &cpo = s:cpo_sav
+unlet! s:cpo_sav
+
+" vim:sts=8:sw=8:ts=8:noet
diff --git a/runtime/syntax/help.vim b/runtime/syntax/help.vim
index b3c7f2a63e..41bb0b1938 100644
--- a/runtime/syntax/help.vim
+++ b/runtime/syntax/help.vim
@@ -59,7 +59,7 @@ syn match helpSpecial "\[N]"
syn match helpSpecial "N N"he=s+1
syn match helpSpecial "Nth"me=e-2
syn match helpSpecial "N-1"me=e-2
-syn match helpSpecial "{[-a-zA-Z0-9'"*+/:%#=[\]<>.,]\+}"
+syn match helpSpecial "{[-_a-zA-Z0-9'"*+/:%#=[\]<>.,]\+}"
syn match helpSpecial "\s\[[-a-z^A-Z0-9_]\{2,}]"ms=s+1
syn match helpSpecial "<[-a-zA-Z0-9_]\+>"
syn match helpSpecial "<[SCM]-.>"
diff --git a/runtime/syntax/php.vim b/runtime/syntax/php.vim
index 4e1a84651c..fc257418d0 100644
--- a/runtime/syntax/php.vim
+++ b/runtime/syntax/php.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: php PHP 3/4/5
" Maintainer: Jason Woofenden <jason@jasonwoof.com>
-" Last Change: Dec 26, 2015
+" Last Change: Apr 18, 2016
" URL: https://jasonwoof.com/gitweb/?p=vim-syntax.git;a=blob;f=php.vim;hb=HEAD
" Former Maintainers: Peter Hodge <toomuchphp-vim@yahoo.com>
" Debian VIM Maintainers <pkg-vim-maintainers@lists.alioth.debian.org>
@@ -136,7 +136,7 @@ syn keyword phpConstant __LINE__ __FILE__ __FUNCTION__ __METHOD__ __CLASS__ __D
" Function and Methods ripped from php_manual_de.tar.gz Jan 2003
syn keyword phpFunctions apache_child_terminate apache_get_modules apache_get_version apache_getenv apache_lookup_uri apache_note apache_request_headers apache_response_headers apache_setenv ascii2ebcdic ebcdic2ascii getallheaders virtual contained
-syn keyword phpFunctions array_change_key_case array_chunk array_combine array_count_values array_diff_assoc array_diff_uassoc array_diff array_fill array_filter array_flip array_intersect_assoc array_intersect array_key_exists array_keys array_map array_merge_recursive array_merge array_multisort array_pad array_pop array_push array_rand array_reduce array_reverse array_search array_shift array_slice array_splice array_sum array_udiff_assoc array_udiff_uassoc array_udiff array_unique array_unshift array_values array_walk array arsort asort compact count current each end extract in_array key krsort ksort list natcasesort natsort next pos prev range reset rsort shuffle sizeof sort uasort uksort usort contained
+syn keyword phpFunctions array_change_key_case array_chunk array_column array_combine array_count_values array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_diff array_fill_keys array_fill array_filter array_flip array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey array_intersect array_key_exists array_keys array_map array_merge_recursive array_merge array_multisort array_pad array_pop array_product array_push array_rand array_reduce array_replace_recursive array_replace array_reverse array_search array_shift array_slice array_splice array_sum array_udiff_assoc array_udiff_uassoc array_udiff array_uintersect_assoc array_uintersect_uassoc array_uintersect array_unique array_unshift array_values array_walk_recursive array_walk arsort asort count current each end in_array key_exists key krsort ksort natcasesort natsort next pos prev range reset rsort shuffle sizeof sort uasort uksort usort contained
syn keyword phpFunctions aspell_check aspell_new aspell_suggest contained
syn keyword phpFunctions bcadd bccomp bcdiv bcmod bcmul bcpow bcpowmod bcscale bcsqrt bcsub contained
syn keyword phpFunctions bzclose bzcompress bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite contained
diff --git a/runtime/syntax/python.vim b/runtime/syntax/python.vim
index 2a0ea5de2b..fa42b3e2d2 100644
--- a/runtime/syntax/python.vim
+++ b/runtime/syntax/python.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Python
" Maintainer: Zvezdan Petkovic <zpetkovic@acm.org>
-" Last Change: 2016 Feb 20
+" Last Change: 2016 Jul 21
" Credits: Neil Schemenauer <nas@python.ca>
" Dmitry Vasiliev
"
@@ -72,7 +72,7 @@ set cpo&vim
" built-in below (use 'from __future__ import print_function' in 2)
" - async and await were added in Python 3.5 and are soft keywords.
"
-syn keyword pythonStatement False, None, True
+syn keyword pythonStatement False None True
syn keyword pythonStatement as assert break continue del exec global
syn keyword pythonStatement lambda nonlocal pass print return with yield
syn keyword pythonStatement class def nextgroup=pythonFunction skipwhite
diff --git a/runtime/syntax/spec.vim b/runtime/syntax/spec.vim
index 5d96b57a8a..9952bd2548 100644
--- a/runtime/syntax/spec.vim
+++ b/runtime/syntax/spec.vim
@@ -3,7 +3,7 @@
" Language: SPEC: Build/install scripts for Linux RPM packages
" Maintainer: Igor Gnatenko i.gnatenko.brain@gmail.com
" Former Maintainer: Donovan Rebbechi elflord@panix.com (until March 2014)
-" Last Change: Sun Mar 2 10:33 MSK 2014 Igor Gnatenko
+" Last Change: Sat Apr 9 15:30 2016 Filip Szymański
" For version 5.x: Clear all syntax items
" For version 6.x: Quit when a syntax file was already loaded
@@ -83,8 +83,8 @@ syn keyword specMacroNameLocal contained _arch _binary_payload _bindir _build _b
"One line macros - valid in all ScriptAreas
"tip: remember do include new items on specScriptArea's skip section
-syn region specSectionMacroArea oneline matchgroup=specSectionMacro start='^%\(define\|global\|patch\d*\|setup\|configure\|GNUconfigure\|find_lang\|makeinstall\|make_install\|include\)\>' end='$' contains=specCommandOpts,specMacroIdentifier
-syn region specSectionMacroBracketArea oneline matchgroup=specSectionMacro start='^%{\(configure\|GNUconfigure\|find_lang\|makeinstall\|make_install\)}' end='$' contains=specCommandOpts,specMacroIdentifier
+syn region specSectionMacroArea oneline matchgroup=specSectionMacro start='^%\(define\|global\|patch\d*\|setup\|autosetup\|autopatch\|configure\|GNUconfigure\|find_lang\|make_build\|makeinstall\|make_install\|include\)\>' end='$' contains=specCommandOpts,specMacroIdentifier
+syn region specSectionMacroBracketArea oneline matchgroup=specSectionMacro start='^%{\(configure\|GNUconfigure\|find_lang\|make_build\|makeinstall\|make_install\)}' end='$' contains=specCommandOpts,specMacroIdentifier
"%% Files Section %%
"TODO %config valid parameters: missingok\|noreplace
@@ -105,7 +105,7 @@ syn case ignore
"%% PreAmble Section %%
"Copyright and Serial were deprecated by License and Epoch
syn region specPreAmbleDeprecated oneline matchgroup=specError start='^\(Copyright\|Serial\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier
-syn region specPreAmble oneline matchgroup=specCommand start='^\(Prereq\|Summary\|Name\|Version\|Packager\|Requires\|Icon\|URL\|Source\d*\|Patch\d*\|Prefix\|Packager\|Group\|License\|Release\|BuildRoot\|Distribution\|Vendor\|Provides\|ExclusiveArch\|ExcludeArch\|ExclusiveOS\|Obsoletes\|BuildArch\|BuildArchitectures\|BuildRequires\|BuildConflicts\|BuildPreReq\|Conflicts\|AutoRequires\|AutoReq\|AutoReqProv\|AutoProv\|Epoch\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier
+syn region specPreAmble oneline matchgroup=specCommand start='^\(Prereq\|Summary\|Name\|Version\|Packager\|Requires\|Recommends\|Suggests\|Supplements\|Enhances\|Icon\|URL\|Source\d*\|Patch\d*\|Prefix\|Packager\|Group\|License\|Release\|BuildRoot\|Distribution\|Vendor\|Provides\|ExclusiveArch\|ExcludeArch\|ExclusiveOS\|Obsoletes\|BuildArch\|BuildArchitectures\|BuildRequires\|BuildConflicts\|BuildPreReq\|Conflicts\|AutoRequires\|AutoReq\|AutoReqProv\|AutoProv\|Epoch\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier
"%% Description Section %%
syn region specDescriptionArea matchgroup=specSection start='^%description' end='^%'me=e-1 contains=specDescriptionOpts,specEmail,specURL,specNumber,specMacroIdentifier,specComment
@@ -114,7 +114,7 @@ syn region specDescriptionArea matchgroup=specSection start='^%description' end=
syn region specPackageArea matchgroup=specSection start='^%package' end='^%'me=e-1 contains=specPackageOpts,specPreAmble,specComment
"%% Scripts Section %%
-syn region specScriptArea matchgroup=specSection start='^%\(prep\|build\|install\|clean\|pre\|postun\|preun\|post\|posttrans\)\>' skip='^%{\|^%\(define\|patch\d*\|configure\|GNUconfigure\|setup\|find_lang\|makeinstall\|make_install\)\>' end='^%'me=e-1 contains=specSpecialVariables,specVariables,@specCommands,specVariables,shDo,shFor,shCaseEsac,specNoNumberHilite,specCommandOpts,shComment,shIf,specSpecialChar,specMacroIdentifier,specSectionMacroArea,specSectionMacroBracketArea,shOperator,shQuote1,shQuote2
+syn region specScriptArea matchgroup=specSection start='^%\(prep\|build\|install\|clean\|pre\|postun\|preun\|post\|posttrans\)\>' skip='^%{\|^%\(define\|patch\d*\|configure\|GNUconfigure\|setup\|autosetup\|autopatch\|find_lang\|make_build\|makeinstall\|make_install\)\>' end='^%'me=e-1 contains=specSpecialVariables,specVariables,@specCommands,specVariables,shDo,shFor,shCaseEsac,specNoNumberHilite,specCommandOpts,shComment,shIf,specSpecialChar,specMacroIdentifier,specSectionMacroArea,specSectionMacroBracketArea,shOperator,shQuote1,shQuote2
"%% Changelog Section %%
syn region specChangelogArea matchgroup=specSection start='^%changelog' end='^%'me=e-1 contains=specEmail,specURL,specWeekday,specMonth,specNumber,specComment,specLicense
diff --git a/runtime/syntax/sqloracle.vim b/runtime/syntax/sqloracle.vim
index 8afe2686bf..f9e24af98c 100644
--- a/runtime/syntax/sqloracle.vim
+++ b/runtime/syntax/sqloracle.vim
@@ -4,7 +4,12 @@
" Repository: https://github.com/chrisbra/vim-sqloracle-syntax
" License: Vim
" Previous Maintainer: Paul Moore
-" Last Change: 2015 Nov 24
+" Last Change: 2016 Jul 22
+
+" Changes:
+" 02.04.2016: Support for when keyword
+" 03.04.2016: Support for join related keywords
+" 22.07.2016: Support Oracle Q-Quote-Syntax
if exists("b:current_syntax")
finish
@@ -24,10 +29,11 @@ syn keyword sqlKeyword index initial initrans into is level link logging loop
syn keyword sqlKeyword maxextents maxtrans mode modify monitoring
syn keyword sqlKeyword nocache nocompress nologging noparallel nowait of offline on online start
syn keyword sqlKeyword parallel successful synonym table tablespace then to trigger uid
-syn keyword sqlKeyword unique user validate values view whenever
+syn keyword sqlKeyword unique user validate values view when whenever
syn keyword sqlKeyword where with option order pctfree pctused privileges procedure
syn keyword sqlKeyword public resource return row rowlabel rownum rows
syn keyword sqlKeyword session share size smallint type using
+syn keyword sqlKeyword join cross inner outer left right
syn keyword sqlOperator not and or
syn keyword sqlOperator in any some all between exists
@@ -47,8 +53,13 @@ syn keyword sqlType boolean char character date float integer long
syn keyword sqlType mlslabel number raw rowid varchar varchar2 varray
" Strings:
-syn region sqlString start=+"+ skip=+\\\\\|\\"+ end=+"+
-syn region sqlString start=+'+ skip=+\\\\\|\\'+ end=+'+
+syn region sqlString matchgroup=Quote start=+"+ skip=+\\\\\|\\"+ end=+"+
+syn region sqlString matchgroup=Quote start=+'+ skip=+\\\\\|\\'+ end=+'+
+syn region sqlString matchgroup=Quote start=+n\?q'\z([^[(<{]\)+ end=+\z1'+
+syn region sqlString matchgroup=Quote start=+n\?q'<+ end=+>'+
+syn region sqlString matchgroup=Quote start=+n\?q'{+ end=+}'+
+syn region sqlString matchgroup=Quote start=+n\?q'(+ end=+)'+
+syn region sqlString matchgroup=Quote start=+n\?q'\[+ end=+]'+
" Numbers:
syn match sqlNumber "-\=\<\d*\.\=[0-9_]\>"
@@ -118,6 +129,7 @@ syn keyword sqlTodo TODO FIXME XXX DEBUG NOTE contained
" Define the default highlighting.
command -nargs=+ HiLink hi def link <args>
+HiLink Quote Special
HiLink sqlComment Comment
HiLink sqlFunction Function
HiLink sqlKeyword sqlSpecial
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index cd96a55eda..63618e902e 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -637,7 +637,9 @@ if !filereadable(s:perlpath)
endif
if g:vimsyn_embed =~# 'p' && filereadable(s:perlpath)
unlet! b:current_syntax
+ let s:foldmethod = &l:foldmethod
exe "syn include @vimPerlScript ".s:perlpath
+ let &l:foldmethod = s:foldmethod
VimFoldp syn region vimPerlRegion matchgroup=vimScriptDelim start=+pe\%[rl]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimPerlScript
VimFoldp syn region vimPerlRegion matchgroup=vimScriptDelim start=+pe\%[rl]\s*<<\s*$+ end=+\.$+ contains=@vimPerlScript
syn cluster vimFuncBodyList add=vimPerlRegion
@@ -659,7 +661,9 @@ if !filereadable(s:rubypath)
endif
if g:vimsyn_embed =~# 'r' && filereadable(s:rubypath)
unlet! b:current_syntax
+ let s:foldmethod = &l:foldmethod
exe "syn include @vimRubyScript ".s:rubypath
+ let &l:foldmethod = s:foldmethod
VimFoldr syn region vimRubyRegion matchgroup=vimScriptDelim start=+rub[y]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimRubyScript
syn region vimRubyRegion matchgroup=vimScriptDelim start=+rub[y]\s*<<\s*$+ end=+\.$+ contains=@vimRubyScript
syn cluster vimFuncBodyList add=vimRubyRegion
diff --git a/runtime/syntax/viminfo.vim b/runtime/syntax/viminfo.vim
index 7af3b89ae0..667e1bab2a 100644
--- a/runtime/syntax/viminfo.vim
+++ b/runtime/syntax/viminfo.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Vim .viminfo file
" Maintainer: Bram Moolenaar <Bram@vim.org>
-" Last Change: 2012 Feb 03
+" Last Change: 2016 Jun 05
" Quit when a (custom) syntax file was already loaded
if exists("b:current_syntax")
@@ -30,11 +30,15 @@ syn match viminfoOptionName "\*\a*"ms=s+1 contained
" Comments
syn match viminfoComment "^#.*"
+" New style lines. TODO: highlight numbers and strings.
+syn match viminfoNew "^|.*"
+
" Define the default highlighting.
" Only used when an item doesn't have highlighting yet
hi def link viminfoComment Comment
hi def link viminfoError Error
hi def link viminfoStatement Statement
+hi def link viminfoNew String
let b:current_syntax = "viminfo"
diff --git a/scripts/gen_api_vimdoc.py b/scripts/gen_api_vimdoc.py
new file mode 100644
index 0000000000..d7165187f4
--- /dev/null
+++ b/scripts/gen_api_vimdoc.py
@@ -0,0 +1,514 @@
+#!/usr/bin/env python
+"""Parses Doxygen XML output to generate Neovim's API documentation.
+
+This would be easier using lxml and XSLT, but:
+
+ 1. This should avoid needing Python dependencies, especially ones that are
+ C modules that have library dependencies (lxml requires libxml and
+ libxslt).
+ 2. I wouldn't know how to deal with nested indentation in <para> tags using
+ XSLT.
+
+Each function documentation is formatted with the following rules:
+
+ - Maximum width of 78 characters (`text_width`).
+ - Spaces for indentation.
+ - Function signature and helptag are on the same line.
+ - Helptag is right aligned.
+ - Signature and helptag must have a minimum of 8 spaces between them.
+ - If the signature is too long, it is placed on the line after the
+ helptag. The signature wraps at `text_width - 8` characters with
+ subsequent lines indented to the open parenthesis.
+ - Documentation body will be indented by 16 spaces.
+ - Subsection bodies are indented an additional 4 spaces.
+ - Documentation body consists of the function description, parameter details,
+ return description, and C declaration.
+ - Parameters are omitted for the `void` and `Error *` types, or if the
+ parameter is marked as [out].
+ - Each function documentation is separated by a single line.
+
+The C declaration is added to the end to show actual argument types.
+"""
+import os
+import re
+import sys
+import shutil
+import textwrap
+import subprocess
+
+from xml.dom import minidom
+
+# Text at the top of the doc file.
+preamble = '''
+Note: This documentation is generated from Neovim's API source code.
+'''
+
+doc_filename = 'api-funcs.txt'
+
+# Section name overrides.
+section_name = {
+ 'vim.c': 'Global',
+}
+
+# Section ordering.
+section_order = (
+ 'vim.c',
+ 'buffer.c',
+ 'window.c',
+ 'tabpage.c',
+ 'ui.c',
+)
+
+param_exclude = (
+ 'channel_id',
+)
+
+text_width = 78
+script_path = os.path.abspath(__file__)
+base_dir = os.path.dirname(os.path.dirname(script_path))
+src_dir = os.path.join(base_dir, 'src/nvim/api')
+out_dir = os.path.join(base_dir, 'tmp/api_doc')
+filter_cmd = '%s %s' % (sys.executable, script_path)
+seen_funcs = set()
+
+# Tracks `xrefsect` titles. As of this writing, used only for separating
+# deprecated functions.
+xrefs = set()
+
+
+# XML Parsing Utilities {{{
+def find_first(parent, name):
+ """Finds the first matching node within parent."""
+ sub = parent.getElementsByTagName(name)
+ if not sub:
+ return None
+ return sub[0]
+
+
+def get_children(parent, name):
+ """Yield matching child nodes within parent."""
+ for child in parent.childNodes:
+ if child.nodeType == child.ELEMENT_NODE and child.nodeName == name:
+ yield child
+
+
+def get_child(parent, name):
+ """Get the first matching child node."""
+ for child in get_children(parent, name):
+ return child
+ return None
+
+
+def clean_text(text):
+ """Cleans text.
+
+ Only cleans superfluous whitespace at the moment.
+ """
+ return ' '.join(text.split()).strip()
+
+
+def clean_lines(text):
+ """Removes superfluous lines.
+
+ The beginning and end of the string is trimmed. Empty lines are collapsed.
+ """
+ return re.sub(r'\A\n\s*\n*|\n\s*\n*\Z', '', re.sub(r'(\n\s*\n+)+', '\n\n', text))
+
+
+def get_text(parent):
+ """Combine all text in a node."""
+ if parent.nodeType == parent.TEXT_NODE:
+ return parent.data
+
+ out = ''
+ for node in parent.childNodes:
+ if node.nodeType == node.TEXT_NODE:
+ out += clean_text(node.data)
+ elif node.nodeType == node.ELEMENT_NODE:
+ out += ' ' + get_text(node)
+ return out
+
+
+def doc_wrap(text, prefix='', width=70, func=False):
+ """Wraps text to `width`.
+
+ The first line is prefixed with `prefix`, and subsequent lines are aligned.
+ If `func` is True, only wrap at commas.
+ """
+ if not width:
+ return text
+
+ indent_space = ' ' * len(prefix)
+
+ if func:
+ lines = [prefix]
+ for part in text.split(', '):
+ if part[-1] not in ');':
+ part += ', '
+ if len(lines[-1]) + len(part) > width:
+ lines.append(indent_space)
+ lines[-1] += part
+ return '\n'.join(x.rstrip() for x in lines).rstrip()
+
+ return '\n'.join(textwrap.wrap(text.strip(), width=width,
+ initial_indent=prefix,
+ subsequent_indent=indent_space))
+
+
+def parse_params(parent, width=62):
+ """Parse Doxygen `parameterlist`."""
+ name_length = 0
+ items = []
+ for child in parent.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ continue
+
+ name_node = find_first(child, 'parametername')
+ if name_node.getAttribute('direction') == 'out':
+ continue
+
+ name = get_text(name_node)
+ if name in param_exclude:
+ continue
+
+ name = '{%s}' % name
+ name_length = max(name_length, len(name) + 2)
+
+ desc = ''
+ desc_node = get_child(child, 'parameterdescription')
+ if desc_node:
+ desc = parse_parblock(desc_node, width=None)
+ items.append((name.strip(), desc.strip()))
+
+ out = 'Parameters:~\n'
+ for name, desc in items:
+ name = ' %s' % name.ljust(name_length)
+ out += doc_wrap(desc, prefix=name, width=width) + '\n'
+ return out.strip()
+
+
+def parse_para(parent, width=62):
+ """Parse doxygen `para` tag.
+
+ I assume <para> is a paragraph block or "a block of text". It can contain
+ text nodes, or other tags.
+ """
+ line = ''
+ lines = []
+ for child in parent.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ line += child.data
+ elif child.nodeName == 'computeroutput':
+ line += '`%s`' % get_text(child)
+ else:
+ if line:
+ lines.append(doc_wrap(line, width=width))
+ line = ''
+
+ if child.nodeName == 'parameterlist':
+ lines.append(parse_params(child, width=width))
+ elif child.nodeName == 'xrefsect':
+ title = get_text(get_child(child, 'xreftitle'))
+ xrefs.add(title)
+ xrefdesc = parse_para(get_child(child, 'xrefdescription'))
+ lines.append(doc_wrap(xrefdesc, prefix='%s: ' % title,
+ width=width) + '\n')
+ elif child.nodeName == 'simplesect':
+ kind = child.getAttribute('kind')
+ if kind == 'return':
+ lines.append('%s:~' % kind.title())
+ lines.append(doc_wrap(parse_para(child),
+ prefix=' ',
+ width=width))
+ else:
+ lines.append(get_text(child))
+
+ if line:
+ lines.append(doc_wrap(line, width=width))
+ return clean_lines('\n'.join(lines).strip())
+
+
+def parse_parblock(parent, width=62):
+ """Parses a nested block of `para` tags.
+
+ Named after the \parblock command, but not directly related.
+ """
+ paragraphs = []
+ for child in parent.childNodes:
+ if child.nodeType == child.TEXT_NODE:
+ paragraphs.append(doc_wrap(child.data, width=width))
+ elif child.nodeName == 'para':
+ paragraphs.append(parse_para(child, width=width))
+ else:
+ paragraphs.append(doc_wrap(get_text(child), width=width))
+ paragraphs.append('')
+ return clean_lines('\n'.join(paragraphs).strip())
+# }}}
+
+
+def parse_source_xml(filename):
+ """Collects API functions.
+
+ This returns two strings:
+ 1. The API functions
+ 2. The deprecated API functions
+
+ The caller decides what to do with the deprecated documentation.
+ """
+ global xrefs
+ xrefs = set()
+ functions = []
+ deprecated_functions = []
+
+ dom = minidom.parse(filename)
+ for member in dom.getElementsByTagName('memberdef'):
+ if member.getAttribute('static') == 'yes' or \
+ member.getAttribute('kind') != 'function':
+ continue
+
+ loc = find_first(member, 'location')
+ if 'private' in loc.getAttribute('file'):
+ continue
+
+ return_type = get_text(get_child(member, 'type'))
+ if return_type == '':
+ continue
+
+ if return_type.startswith(('ArrayOf', 'DictionaryOf')):
+ parts = return_type.strip('_').split('_')
+ return_type = '%s(%s)' % (parts[0], ', '.join(parts[1:]))
+
+ name = get_text(get_child(member, 'name'))
+
+ vimtag = '*%s()*' % name
+ args = []
+ type_length = 0
+
+ for param in get_children(member, 'param'):
+ arg_type = get_text(get_child(param, 'type')).strip()
+ arg_name = ''
+ declname = get_child(param, 'declname')
+ if declname:
+ arg_name = get_text(declname).strip()
+
+ if arg_name in param_exclude:
+ continue
+
+ if arg_type.endswith('*'):
+ arg_type = arg_type.strip('* ')
+ arg_name = '*' + arg_name
+ type_length = max(type_length, len(arg_type))
+ args.append((arg_type, arg_name))
+
+ c_args = []
+ for arg_type, arg_name in args:
+ c_args.append(' ' + (
+ '%s %s' % (arg_type.ljust(type_length), arg_name)).strip())
+
+ c_decl = textwrap.indent('%s %s(\n%s\n);' % (return_type, name,
+ ',\n'.join(c_args)),
+ ' ')
+
+ prefix = '%s(' % name
+ suffix = '%s)' % ', '.join('{%s}' % a[1] for a in args
+ if a[0] not in ('void', 'Error'))
+
+ # Minimum 8 chars between signature and vimtag
+ lhs = (text_width - 8) - len(prefix)
+
+ if len(prefix) + len(suffix) > lhs:
+ signature = vimtag.rjust(text_width) + '\n'
+ signature += doc_wrap(suffix, width=text_width-8, prefix=prefix,
+ func=True)
+ else:
+ signature = prefix + suffix
+ signature += vimtag.rjust(text_width - len(signature))
+
+ doc = ''
+ desc = find_first(member, 'detaileddescription')
+ if desc:
+ doc = parse_parblock(desc)
+ if 'DEBUG' in os.environ:
+ print(textwrap.indent(
+ re.sub(r'\n\s*\n+', '\n',
+ desc.toprettyxml(indent=' ', newl='\n')), ' ' * 16))
+
+ if not doc:
+ doc = 'TODO: Documentation'
+
+ if 'INCLUDE_C_DECL' in os.environ:
+ doc += '\n\nC Declaration:~\n>\n'
+ doc += c_decl
+ doc += '\n<'
+
+ func_doc = signature + '\n'
+ func_doc += textwrap.indent(clean_lines(doc), ' ' * 16)
+ func_doc = re.sub(r'^\s+([<>])$', r'\1', func_doc, flags=re.M)
+
+ if 'Deprecated' in xrefs:
+ deprecated_functions.append(func_doc)
+ else:
+ functions.append(func_doc)
+
+ xrefs.clear()
+
+ return '\n\n'.join(functions), '\n\n'.join(deprecated_functions)
+
+
+def gen_docs(config):
+ """Generate documentation.
+
+ Doxygen is called and configured through stdin.
+ """
+ p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE)
+ p.communicate(config.format(input=src_dir, output=out_dir,
+ filter=filter_cmd).encode('utf8'))
+ if p.returncode:
+ sys.exit(p.returncode)
+
+ title_length = 0
+ sections = {}
+ sep = '=' * text_width
+
+ base = os.path.join(out_dir, 'xml')
+ dom = minidom.parse(os.path.join(base, 'index.xml'))
+ for compound in dom.getElementsByTagName('compound'):
+ if compound.getAttribute('kind') != 'file':
+ continue
+
+ filename = get_text(find_first(compound, 'name'))
+ if filename.endswith('.c'):
+ functions, deprecated = parse_source_xml(
+ os.path.join(base, '%s.xml' % compound.getAttribute('refid')))
+
+ if not functions and not deprecated:
+ continue
+
+ if functions or deprecated:
+ name = os.path.splitext(os.path.basename(filename))[0]
+ if name == 'ui':
+ name = name.upper()
+ else:
+ name = name.title()
+
+ doc = ''
+ if functions:
+ doc += '\n\n' + functions
+
+ if 'INCLUDE_DEPRECATED' in os.environ and deprecated:
+ doc += '\n\n\nDeprecated %s Functions:~\n\n' % name
+ doc += deprecated
+
+ if doc:
+ filename = os.path.basename(filename)
+ name = section_name.get(filename, name)
+ title = '%s Functions' % name
+ helptag = '*api-%s*' % name.lower()
+ title_length = max(title_length, len(title))
+ sections[filename] = (title, helptag, doc)
+
+ if not sections:
+ return
+
+ title_left = '*%s*' % doc_filename
+ title_center = 'Neovim API Function Reference'
+ title_right = '{Nvim}'
+ margin = max(len(title_left), len(title_right))
+ head = (title_left.ljust(margin) +
+ title_center.center(text_width - margin * 2) +
+ title_right.rjust(margin)) + '\n'
+
+ head += '\n%s\n\n' % doc_wrap(preamble, width=text_width)
+ head += 'Contents:\n\n'
+
+ docs = ''
+
+ title_length += len(str(len(section_order))) + 2
+ i = 0
+ for filename in section_order:
+ if filename not in sections:
+ continue
+ title, helptag, section_doc = sections.pop(filename)
+
+ i += 1
+ docs += sep
+ title = '%d. %s' % (i, title)
+ head += (title.ljust(title_length) + ' ' +
+ helptag.replace('*', '|') + '\n')
+ docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title)))
+ docs += section_doc
+ docs += '\n\n\n'
+
+ if sections:
+ # In case new API sources are added without updating the order dict.
+ for title, helptag, section_doc in sections.values():
+ i += 1
+ docs += sep
+ title = '%d. %s' % (i, title)
+ head += (title.ljust(title_length) + ' ' +
+ helptag.replace('*', '|') + '\n')
+ docs += '\n%s%s' % (title, helptag.rjust(text_width - len(title)))
+ docs += section_doc
+ docs += '\n\n\n'
+
+ docs = '%s\n%s' % (head, docs)
+ docs = docs.rstrip() + '\n\n'
+ docs += ' vim:tw=78:ts=8:ft=help:norl:'
+
+ doc_file = os.path.join(base_dir, 'runtime/doc', doc_filename)
+ with open(doc_file, 'wb') as fp:
+ fp.write(docs.encode('utf8'))
+
+ shutil.rmtree(out_dir)
+
+
+def filter_source(filename):
+ """Filters the source to fix macros that confuse Doxygen."""
+ with open(filename, 'rt') as fp:
+ print(re.sub(r'^(ArrayOf|DictionaryOf)(\(.*?\))',
+ lambda m: m.group(1)+'_'.join(
+ re.split(r'[^\w]+', m.group(2))),
+ fp.read(), flags=re.M))
+
+
+# Doxygen Config {{{
+Doxyfile = '''
+OUTPUT_DIRECTORY = {output}
+INPUT = {input}
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.h *.c
+RECURSIVE = YES
+INPUT_FILTER = "{filter}"
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS = */private/*
+EXCLUDE_SYMBOLS =
+
+GENERATE_HTML = NO
+GENERATE_DOCSET = NO
+GENERATE_HTMLHELP = NO
+GENERATE_QHP = NO
+GENERATE_TREEVIEW = NO
+GENERATE_LATEX = NO
+GENERATE_RTF = NO
+GENERATE_MAN = NO
+GENERATE_DOCBOOK = NO
+GENERATE_AUTOGEN_DEF = NO
+
+GENERATE_XML = YES
+XML_OUTPUT = xml
+XML_PROGRAMLISTING = NO
+
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+'''
+# }}}
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ filter_source(sys.argv[1])
+ else:
+ gen_docs(Doxyfile)
+
+# vim: set ft=python ts=4 sw=4 tw=79 et fdm=marker :
diff --git a/scripts/genoptions.lua b/scripts/genoptions.lua
index 9f7d94969d..9d7f235a3b 100644
--- a/scripts/genoptions.lua
+++ b/scripts/genoptions.lua
@@ -31,6 +31,7 @@ local type_flags={
local redraw_flags={
statuslines='P_RSTAT',
current_window='P_RWIN',
+ current_window_only='P_RWINONLY',
current_buffer='P_RBUF',
all_windows='P_RALL',
everything='P_RCLR',
diff --git a/src/clint.py b/src/clint.py
index efc5f18378..0c9f55c71e 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -2516,6 +2516,10 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line)
match = Search(r'(?<!\bkvec_t)'
+ r'(?<!\bkvec_withinit_t)'
+ r'(?<!\bklist_t)'
+ r'(?<!\bkliter_t)'
+ r'(?<!\bkhash_t)'
r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)'
r' +'
r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line)
@@ -2560,22 +2564,43 @@ def CheckBraces(filename, clean_lines, linenum, error):
line = clean_lines.elided[linenum] # get rid of comments and strings
- if not (filename.endswith('.c') or filename.endswith('.h')):
- if Match(r'\s*{\s*$', line):
- # We allow an open brace to start a line in the case where someone
- # is using braces in a block to explicitly create a new scope, which
- # is commonly used to control the lifetime of stack-allocated
- # variables. Braces are also used for brace initializers inside
- # function calls. We don't detect this perfectly: we just don't
- # complain if the last non-whitespace character on the previous
- # non-blank line is ',', ';', ':', '(', '{', or '}', or if the
- # previous line starts a preprocessor block.
- prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
- if (not Search(r'[,;:}{(]\s*$', prevline) and
- not Match(r'\s*#', prevline)):
- error(filename, linenum, 'whitespace/braces', 4,
- '{ should almost always be at the end'
- ' of the previous line')
+ if Match(r'\s+{\s*$', line):
+ # We allow an open brace to start a line in the case where someone
+ # is using braces in a block to explicitly create a new scope, which
+ # is commonly used to control the lifetime of stack-allocated
+ # variables. Braces are also used for brace initializers inside
+ # function calls. We don't detect this perfectly: we just don't
+ # complain if the last non-whitespace character on the previous
+ # non-blank line is ',', ';', ':', '(', '{', or '}', or if the
+ # previous line starts a preprocessor block.
+ prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
+ if (not Search(r'[,;:}{(]\s*$', prevline) and
+ not Match(r'\s*#', prevline)):
+ error(filename, linenum, 'whitespace/braces', 4,
+ '{ should almost always be at the end'
+ ' of the previous line')
+
+ # Brace must appear after function signature, but on the *next* line
+ if Match(r'^(?:\w+(?: ?\*+)? )+\w+\(', line):
+ pos = line.find('(')
+ (endline, end_linenum, endpos) = CloseExpression(
+ clean_lines, linenum, pos)
+ if endline.endswith('{'):
+ error(filename, end_linenum, 'readability/braces', 5,
+ 'Brace starting function body must be placed on its own line')
+ else:
+ func_start_linenum = end_linenum + 1
+ while not clean_lines.lines[func_start_linenum] == '{':
+ if not Match(r'^(?:\s*\b(?:FUNC_ATTR|REAL_FATTR)_\w+\b(?:\(\d+(, \d+)*\))?)+$',
+ clean_lines.lines[func_start_linenum]):
+ if clean_lines.lines[func_start_linenum].endswith('{'):
+ error(filename, func_start_linenum,
+ 'readability/braces', 5,
+ 'Brace starting function body must be placed '
+ 'after the function signature')
+ break
+ else:
+ func_start_linenum += 1
# An else clause should be on the same line as the preceding closing brace.
# If there is no preceding closing brace, there should be one.
@@ -3001,9 +3026,10 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
include = match.group(2)
is_system = (match.group(1) == '<')
if include in include_state:
- error(filename, linenum, 'build/include', 4,
- '"%s" already included at %s:%s' %
- (include, filename, include_state[include]))
+ if is_system or not include.endswith('.c.h'):
+ error(filename, linenum, 'build/include', 4,
+ '"%s" already included at %s:%s' %
+ (include, filename, include_state[include]))
else:
include_state[include] = linenum
@@ -3408,8 +3434,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
# When reading from stdin, the extension is unknown, so no cpplint tests
# should rely on the extension.
if filename != '-' and file_extension not in _valid_extensions:
- sys.stderr.write('Ignoring %s; not a valid file name '
- '(%s)\n' % (filename, ', '.join(_valid_extensions)))
+ sys.stderr.write('Ignoring {}; only linting {} files\n'.format(
+ filename,
+ ', '.join('.{}'.format(ext) for ext in _valid_extensions)))
else:
ProcessFileData(filename, file_extension, lines, Error,
extra_check_functions)
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index b004cfc7a1..7daa4d7207 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -19,7 +19,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/version.h"
-#include "nvim/eval/typval_encode.h"
#include "nvim/lib/kvec.h"
/// Helper structure for vim_to_object
@@ -327,21 +326,21 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
kv_push(edata->stack, NIL)
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
kv_push(edata->stack, BOOLEAN_OBJ((Boolean)(num)))
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
kv_push(edata->stack, INTEGER_OBJ((Integer)(num)))
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
kv_push(edata->stack, FLOATING_OBJ((Float)(flt)))
-#define TYPVAL_ENCODE_CONV_STRING(str, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
do { \
const size_t len_ = (size_t)(len); \
const char *const str_ = (const char *)(str); \
@@ -354,19 +353,23 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
-#define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
+ TYPVAL_ENCODE_CONV_NIL(tv)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+ do { \
+ TYPVAL_ENCODE_CONV_NIL(tv); \
+ goto typval_encode_stop_converting_one_item; \
+ } while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
kv_push(edata->stack, \
DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 })))
@@ -381,9 +384,11 @@ static inline void typval_encode_list_start(EncodedData *const edata,
})));
}
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
typval_encode_list_start(edata, (size_t)(len))
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
static inline void typval_encode_between_list_items(EncodedData *const edata)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
{
@@ -394,7 +399,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata)
list->data.array.items[list->data.array.size++] = item;
}
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
typval_encode_between_list_items(edata)
static inline void typval_encode_list_end(EncodedData *const edata)
@@ -407,7 +412,7 @@ static inline void typval_encode_list_end(EncodedData *const edata)
#endif
}
-#define TYPVAL_ENCODE_CONV_LIST_END() \
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
typval_encode_list_end(edata)
static inline void typval_encode_dict_start(EncodedData *const edata,
@@ -421,10 +426,12 @@ static inline void typval_encode_dict_start(EncodedData *const edata,
})));
}
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
typval_encode_dict_start(edata, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
+
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
static inline void typval_encode_after_key(EncodedData *const edata)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
@@ -443,7 +450,7 @@ static inline void typval_encode_after_key(EncodedData *const edata)
}
}
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
typval_encode_after_key(edata)
static inline void typval_encode_between_dict_items(EncodedData *const edata)
@@ -456,7 +463,7 @@ static inline void typval_encode_between_dict_items(EncodedData *const edata)
dict->data.dictionary.items[dict->data.dictionary.size++].value = val;
}
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
typval_encode_between_dict_items(edata)
static inline void typval_encode_dict_end(EncodedData *const edata)
@@ -469,34 +476,44 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
#endif
}
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
typval_encode_dict_end(edata)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
TYPVAL_ENCODE_CONV_NIL()
-// object_convert_one_value()
-// encode_vim_to_object()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME object
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE EncodedData *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME edata
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
@@ -510,7 +527,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
Object vim_to_object(typval_T *obj)
{
EncodedData edata = { .stack = KV_INITIAL_VALUE };
- encode_vim_to_object(&edata, obj, "vim_to_object argument");
+ const int evo_ret = encode_vim_to_object(&edata, obj,
+ "vim_to_object argument");
+ (void)evo_ret;
+ assert(evo_ret == OK);
Object ret = kv_A(edata.stack, 0);
assert(kv_size(edata.stack) == 1);
kv_destroy(edata.stack);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index b17b59f7a6..f37e996f06 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -315,7 +315,7 @@ void nvim_set_current_dir(String dir, Error *err)
try_start();
- if (vim_chdir((char_u *)string)) {
+ if (vim_chdir((char_u *)string, kCdScopeGlobal)) {
if (!try_end(err)) {
api_set_error(err, Exception, _("Failed to change directory"));
}
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 8d891effae..68a47c244f 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -28,6 +28,7 @@ return {
'CursorHoldI', -- idem, in Insert mode
'CursorMoved', -- cursor was moved
'CursorMovedI', -- cursor was moved in Insert mode
+ 'DirChanged', -- directory changed
'EncodingChanged', -- after changing the 'encoding' option
'FileAppendCmd', -- append to a file using command
'FileAppendPost', -- after appending to a file
@@ -89,6 +90,7 @@ return {
'VimLeave', -- before exiting Vim
'VimLeavePre', -- before exiting Vim and writing ShaDa file
'VimResized', -- after Vim window was resized
+ 'WinNew', -- when entering a new window
'WinEnter', -- after entering a window
'WinLeave', -- before leaving a window
},
@@ -101,6 +103,7 @@ return {
-- List of neovim-specific events or aliases for the purpose of generating
-- syntax file
neovim_specific = {
+ DirChanged=true,
TabClosed=true,
TabNew=true,
TabNewEntered=true,
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index ad6fdc32c0..26dbbe8bb5 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -189,14 +189,18 @@ open_buffer (
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
- /* Set or reset 'modified' before executing autocommands, so that
- * it can be changed there. */
- if (!readonlymode && !bufempty())
+ // Set or reset 'modified' before executing autocommands, so that
+ // it can be changed there.
+ if (!readonlymode && !bufempty()) {
changed();
- else if (retval != FAIL)
- unchanged(curbuf, FALSE);
- apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,
- curbuf, &retval);
+ } else if (retval == OK) {
+ unchanged(curbuf, false);
+ }
+
+ if (retval == OK) {
+ apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
+ curbuf, &retval);
+ }
}
}
@@ -206,22 +210,21 @@ open_buffer (
parse_cino(curbuf);
}
- /*
- * Set/reset the Changed flag first, autocmds may change the buffer.
- * Apply the automatic commands, before processing the modelines.
- * So the modelines have priority over auto commands.
- */
- /* When reading stdin, the buffer contents always needs writing, so set
- * the changed flag. Unless in readonly mode: "ls | nvim -R -".
- * When interrupted and 'cpoptions' contains 'i' set changed flag. */
+ // Set/reset the Changed flag first, autocmds may change the buffer.
+ // Apply the automatic commands, before processing the modelines.
+ // So the modelines have priority over auto commands.
+
+ // When reading stdin, the buffer contents always needs writing, so set
+ // the changed flag. Unless in readonly mode: "ls | nvim -R -".
+ // When interrupted and 'cpoptions' contains 'i' set changed flag.
if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
- || modified_was_set /* ":set modified" used in autocmd */
- || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
- )
+ || modified_was_set // ":set modified" used in autocmd
+ || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
changed();
- else if (retval != FAIL && !read_stdin)
- unchanged(curbuf, FALSE);
- save_file_ff(curbuf); /* keep this fileformat */
+ } else if (retval == OK && !read_stdin) {
+ unchanged(curbuf, false);
+ }
+ save_file_ff(curbuf); // keep this fileformat
/* require "!" to overwrite the file, because it wasn't read completely */
if (aborting())
@@ -427,9 +430,6 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
buf->b_nwindows = nwindows;
buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
- if (win_valid_any_tab(win) && win->w_buffer == buf) {
- win->w_buffer = NULL; // make sure we don't use the buffer now
- }
/* Autocommands may have deleted the buffer. */
if (!buf_valid(buf))
@@ -437,11 +437,6 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
if (aborting()) /* autocmds may abort script processing */
return;
- /* Autocommands may have opened or closed windows for this buffer.
- * Decrement the count for the close we do here. */
- if (buf->b_nwindows > 0)
- --buf->b_nwindows;
-
/*
* It's possible that autocommands change curbuf to the one being deleted.
* This might cause the previous curbuf to be deleted unexpectedly. But
@@ -452,6 +447,16 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
if (buf == curbuf && !is_curbuf)
return;
+ if (win_valid_any_tab(win) && win->w_buffer == buf) {
+ win->w_buffer = NULL; // make sure we don't use the buffer now
+ }
+
+ // Autocommands may have opened or closed windows for this buffer.
+ // Decrement the count for the close we do here.
+ if (buf->b_nwindows > 0) {
+ buf->b_nwindows--;
+ }
+
/* Change directories when the 'acd' option is set. */
do_autochdir();
@@ -3462,7 +3467,7 @@ int build_stl_str_hl(
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, tmp, TMPLEN))
+ if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN))
str = tmp;
break;
case STL_PAGENUM:
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ea42a58cdd..6b14d21da7 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5162,12 +5162,18 @@ dict_equal (
dictitem_T *item2;
int todo;
- if (d1 == NULL || d2 == NULL)
- return FALSE;
- if (d1 == d2)
- return TRUE;
- if (dict_len(d1) != dict_len(d2))
- return FALSE;
+ if (d1 == NULL && d2 == NULL) {
+ return true;
+ }
+ if (d1 == NULL || d2 == NULL) {
+ return false;
+ }
+ if (d1 == d2) {
+ return true;
+ }
+ if (dict_len(d1) != dict_len(d2)) {
+ return false;
+ }
todo = (int)d1->dv_hashtab.ht_used;
for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) {
@@ -6404,8 +6410,8 @@ static void dict_free_contents(dict_T *d) {
while (!QUEUE_EMPTY(&d->watchers)) {
QUEUE *w = QUEUE_HEAD(&d->watchers);
DictWatcher *watcher = dictwatcher_node_data(w);
- dictwatcher_free(watcher);
QUEUE_REMOVE(w);
+ dictwatcher_free(watcher);
}
hash_clear(&d->dv_hashtab);
@@ -6439,7 +6445,7 @@ void dict_free(dict_T *d) {
*/
dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET
{
- dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(key));
+ dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1);
#ifndef __clang_analyzer__
STRCPY(di->di_key, key);
#endif
@@ -6669,9 +6675,12 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len)
char_u *tofree = NULL;
hashitem_T *hi;
- if (len < 0)
+ if (d == NULL) {
+ return NULL;
+ }
+ if (len < 0) {
akey = key;
- else if (len >= AKEYLEN) {
+ } else if (len >= AKEYLEN) {
tofree = akey = vim_strnsave(key, len);
} else {
/* Avoid a malloc/free by using buf[]. */
@@ -6717,6 +6726,7 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result)
/// Get a string item from a dictionary.
///
/// @param save whether memory should be allocated for the return value
+/// when false a shared buffer is used, can only be used once!
///
/// @return the entry or NULL if the entry doesn't exist.
char_u *get_dict_string(dict_T *d, char *key, bool save)
@@ -8819,34 +8829,73 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| (gettail_dir(name) != name && os_can_exe(name, NULL, false));
}
+static char_u * get_list_line(int c, void *cookie, int indent)
+{
+ listitem_T **p = (listitem_T **)cookie;
+ listitem_T *item = *p;
+ char_u buf[NUMBUFLEN];
+ char_u *s;
+
+ if (item == NULL) {
+ return NULL;
+ }
+ s = get_tv_string_buf_chk(&item->li_tv, buf);
+ *p = item->li_next;
+ return s == NULL ? NULL : vim_strsave(s);
+}
+
// "execute(command)" function
static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int save_msg_silent = msg_silent;
+ int save_emsg_silent = emsg_silent;
+ bool save_emsg_noredir = emsg_noredir;
garray_T *save_capture_ga = capture_ga;
if (check_secure()) {
return;
}
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ char_u buf[NUMBUFLEN];
+ char_u *s = get_tv_string_buf_chk(&argvars[1], buf);
+
+ if (s == NULL) {
+ return;
+ }
+ if (STRNCMP(s, "silent", 6) == 0) {
+ msg_silent++;
+ }
+ if (STRCMP(s, "silent!") == 0) {
+ emsg_silent = true;
+ emsg_noredir = true;
+ }
+ } else {
+ msg_silent++;
+ }
+
garray_T capture_local;
+ ga_init(&capture_local, (int)sizeof(char), 80);
capture_ga = &capture_local;
- ga_init(capture_ga, (int)sizeof(char), 80);
- msg_silent++;
if (argvars[0].v_type != VAR_LIST) {
do_cmdline_cmd((char *)get_tv_string(&argvars[0]));
} else if (argvars[0].vval.v_list != NULL) {
- for (listitem_T *li = argvars[0].vval.v_list->lv_first;
- li != NULL; li = li->li_next) {
- do_cmdline_cmd((char *)get_tv_string(&li->li_tv));
- }
+ list_T *list = argvars[0].vval.v_list;
+ list->lv_refcount++;
+ listitem_T *item = list->lv_first;
+ do_cmdline(NULL, get_list_line, (void *)&item,
+ DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED);
+ list->lv_refcount--;
}
msg_silent = save_msg_silent;
+ emsg_silent = save_emsg_silent;
+ emsg_noredir = save_emsg_noredir;
ga_append(capture_ga, NUL);
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = capture_ga->ga_data;
+ rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
+ ga_clear(capture_ga);
capture_ga = save_capture_ga;
}
@@ -9242,7 +9291,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
dict_T *d = NULL;
typval_T save_val;
typval_T save_key;
- int rem;
+ int rem = false;
int todo;
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
@@ -9525,24 +9574,29 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
- linenr_T lnum;
+ linenr_T foldstart;
+ linenr_T foldend;
+ char_u *dashes;
+ linenr_T lnum;
char_u *s;
char_u *r;
- int len;
+ int len;
char *txt;
+ long count;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
- if ((linenr_T)vimvars[VV_FOLDSTART].vv_nr > 0
- && (linenr_T)vimvars[VV_FOLDEND].vv_nr
- <= curbuf->b_ml.ml_line_count
- && vimvars[VV_FOLDDASHES].vv_str != NULL) {
+
+ foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
+ foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
+ dashes = get_vim_var_str(VV_FOLDDASHES);
+ if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count
+ && dashes != NULL) {
/* Find first non-empty line in the fold. */
- lnum = (linenr_T)vimvars[VV_FOLDSTART].vv_nr;
- while (lnum < (linenr_T)vimvars[VV_FOLDEND].vv_nr) {
- if (!linewhite(lnum))
+ for (lnum = foldstart; lnum < foldend; ++lnum) {
+ if (!linewhite(lnum)) {
break;
- ++lnum;
+ }
}
/* Find interesting text in this line. */
@@ -9550,21 +9604,19 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/* skip C comment-start */
if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) {
s = skipwhite(s + 2);
- if (*skipwhite(s) == NUL
- && lnum + 1 < (linenr_T)vimvars[VV_FOLDEND].vv_nr) {
+ if (*skipwhite(s) == NUL && lnum + 1 < foldend) {
s = skipwhite(ml_get(lnum + 1));
if (*s == '*')
s = skipwhite(s + 1);
}
}
+ count = (long)(foldend - foldstart + 1);
txt = _("+-%s%3ld lines: ");
r = xmalloc(STRLEN(txt)
- + STRLEN(vimvars[VV_FOLDDASHES].vv_str) // for %s
- + 20 // for %3ld
- + STRLEN(s)); // concatenated
- sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].vv_str,
- (long)((linenr_T)vimvars[VV_FOLDEND].vv_nr
- - (linenr_T)vimvars[VV_FOLDSTART].vv_nr + 1));
+ + STRLEN(dashes) // for %s
+ + 20 // for %3ld
+ + STRLEN(s)); // concatenated
+ sprintf((char *)r, txt, dashes, count);
len = (int)STRLEN(r);
STRCAT(r, s);
/* remove 'foldmarker' and 'commentstring' */
@@ -10067,7 +10119,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (STRCMP(varname, "changedtick") == 0) {
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = curbuf->b_changedtick;
- done = TRUE;
+ done = true;
} else {
/* Look up the variable. */
/* Let getbufvar({nr}, "") return the "b:" dictionary. */
@@ -15064,7 +15116,6 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
buf_T *buf;
- aco_save_T aco;
char_u *varname, *bufvarname;
typval_T *varp;
char_u nbuf[NUMBUFLEN];
@@ -15077,29 +15128,34 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
varp = &argvars[2];
if (buf != NULL && varname != NULL && varp != NULL) {
- /* set curbuf to be our buf, temporarily */
- aucmd_prepbuf(&aco, buf);
-
if (*varname == '&') {
long numval;
char_u *strval;
- int error = FALSE;
+ int error = false;
+ aco_save_T aco;
+
+ // set curbuf to be our buf, temporarily
+ aucmd_prepbuf(&aco, buf);
++varname;
numval = get_tv_number_chk(varp, &error);
strval = get_tv_string_buf_chk(varp, nbuf);
if (!error && strval != NULL)
set_option_value(varname, numval, strval, OPT_LOCAL);
+
+ // reset notion of buffer
+ aucmd_restbuf(&aco);
} else {
+ buf_T *save_curbuf = curbuf;
+
bufvarname = xmalloc(STRLEN(varname) + 3);
+ curbuf = buf;
STRCPY(bufvarname, "b:");
STRCPY(bufvarname + 2, varname);
set_var(bufvarname, varp, TRUE);
xfree(bufvarname);
+ curbuf = save_curbuf;
}
-
- /* reset notion of buffer */
- aucmd_restbuf(&aco);
}
}
@@ -15393,12 +15449,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- char_u *group = get_dict_string(d, "group", false);
+ char_u *group = get_dict_string(d, "group", true);
int priority = get_dict_number(d, "priority");
int id = get_dict_number(d, "id");
char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
- ? get_dict_string(d, "conceal",
- false)
+ ? get_dict_string(d, "conceal", true)
: NULL;
if (i == 0) {
match_add(curwin, group,
@@ -15409,6 +15464,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_unref(s);
s = NULL;
}
+ xfree(group);
+ xfree(conceal);
li = li->li_next;
}
rettv->vval.v_number = 0;
@@ -15516,7 +15573,10 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (argvars[1].v_type == VAR_LIST) {
- int len = argvars[1].vval.v_list->lv_len;
+ list_T *ll = argvars[1].vval.v_list;
+ // If the list is NULL handle like an empty list.
+ int len = ll == NULL ? 0 : ll->lv_len;
+
// First half: use for pointers to result lines; second half: use for
// pointers to allocated copies.
char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2));
@@ -15525,7 +15585,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u **curallocval = allocval;
char_u buf[NUMBUFLEN];
- for (listitem_T *li = argvars[1].vval.v_list->lv_first;
+ for (listitem_T *li = ll == NULL ? NULL : ll->lv_first;
li != NULL;
li = li->li_next) {
char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf);
@@ -16939,8 +16999,12 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
}
// get shell command to execute
- char **argv = tv_to_argv(&argvars[0], NULL, NULL);
+ bool executable = true;
+ char **argv = tv_to_argv(&argvars[0], NULL, &executable);
if (!argv) {
+ if (!executable) {
+ set_vim_var_nr(VV_SHELL_ERROR, (long)-1);
+ }
xfree(input);
return; // Already did emsg.
}
@@ -17249,14 +17313,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
-/*
- * "test(list)" function: Just checking the walls...
- */
-static void f_test(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- /* Used for unit testing. Change the code below to your liking. */
-}
-
static bool callback_from_typval(Callback *callback, typval_T *arg)
{
if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) {
@@ -19034,124 +19090,205 @@ void free_tv(typval_T *varp)
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
tv->vval.v_special = kSpecialVarFalse; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_BOOL(ignored) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
+ TYPVAL_ENCODE_CONV_NIL(tv)
-#define TYPVAL_ENCODE_CONV_NUMBER(ignored) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
- (void)ignored; \
+ (void)num; \
tv->vval.v_number = 0; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(ignored) \
- assert(false)
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-#define TYPVAL_ENCODE_CONV_FLOAT(ignored) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
tv->vval.v_float = 0; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_STRING(str, ignored) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- xfree(str); \
+ xfree(buf); \
tv->vval.v_string = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(ignored1, ignored2)
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3)
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+static inline int _nothing_conv_func_start(typval_T *const tv,
+ char_u *const fun)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
+{
+ tv->v_lock = VAR_UNLOCKED;
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt_ = tv->vval.v_partial;
+ if (pt_ != NULL && pt_->pt_refcount > 1) {
+ pt_->pt_refcount--;
+ tv->vval.v_partial = NULL;
+ return OK;
+ }
+ } else {
+ func_unref(fun);
+ if (fun != empty_string) {
+ xfree(fun);
+ }
+ tv->vval.v_string = NULL;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- func_unref(fun); \
- if (fun != empty_string) { \
- xfree(fun); \
+ if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \
+ return OK; \
} \
- tv->vval.v_string = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
- do { \
- partial_unref(pt); \
- pt = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+
+static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
+{
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt = tv->vval.v_partial;
+ if (pt == NULL) {
+ return;
+ }
+ // Dictionary should already be freed by the time.
+ // If it was not freed then it is a part of the reference cycle.
+ assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID);
+ pt->pt_dict = NULL;
+ // As well as all arguments.
+ pt->pt_argc = 0;
+ assert(pt->pt_refcount <= 1);
+ partial_unref(pt);
+ tv->vval.v_partial = NULL;
+ assert(tv->v_lock == VAR_UNLOCKED);
+ }
+}
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
do { \
list_unref(tv->vval.v_list); \
tv->vval.v_list = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
- do { \
- dict_unref(tv->vval.v_dict); \
- tv->vval.v_dict = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_LIST_START(ignored) \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
do { \
- if (tv->vval.v_list->lv_refcount > 1) { \
- tv->vval.v_list->lv_refcount--; \
- tv->vval.v_list = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- return OK; \
+ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
+ dict_unref((dict_T *)dict); \
+ *((dict_T **)&dict) = NULL; \
+ if (tv != NULL) { \
+ ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
-
-#define TYPVAL_ENCODE_CONV_LIST_END() \
- do { \
- typval_T *const cur_tv = cur_mpsv->tv; \
- assert(cur_tv->v_type == VAR_LIST); \
- list_unref(cur_tv->vval.v_list); \
- cur_tv->vval.v_list = NULL; \
- cur_tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+static inline int _nothing_conv_real_list_after_start(
+ typval_T *const tv, MPConvStackVal *const mpsv)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ assert(tv != NULL);
+ tv->v_lock = VAR_UNLOCKED;
+ if (tv->vval.v_list->lv_refcount > 1) {
+ tv->vval.v_list->lv_refcount--;
+ tv->vval.v_list = NULL;
+ mpsv->data.l.li = NULL;
+ return OK;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len)
-#define TYPVAL_ENCODE_CONV_DICT_START(ignored) \
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \
do { \
- if (tv->vval.v_dict->dv_refcount > 1) { \
- tv->vval.v_dict->dv_refcount--; \
- tv->vval.v_dict = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- return OK; \
+ if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \
+ goto typval_encode_stop_converting_one_item; \
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(ignored1, ignored2)
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+static inline void _nothing_conv_list_end(typval_T *const tv)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (tv == NULL) {
+ return;
+ }
+ assert(tv->v_type == VAR_LIST);
+ list_T *const list = tv->vval.v_list;
+ list_unref(list);
+ tv->vval.v_list = NULL;
+}
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+static inline int _nothing_conv_real_dict_after_start(
+ typval_T *const tv, dict_T **const dictp, const void *const nodictvar,
+ MPConvStackVal *const mpsv)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (tv != NULL) {
+ tv->v_lock = VAR_UNLOCKED;
+ }
+ if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) {
+ (*dictp)->dv_refcount--;
+ *dictp = NULL;
+ mpsv->data.d.todo = 0;
+ return OK;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len)
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \
do { \
- typval_T *const cur_tv = cur_mpsv->tv; \
- assert(cur_tv->v_type == VAR_DICT); \
- dict_unref(cur_tv->vval.v_dict); \
- cur_tv->vval.v_dict = NULL; \
- cur_tv->v_lock = VAR_UNLOCKED; \
+ if (_nothing_conv_real_dict_after_start( \
+ tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \
+ &mpsv) != NOTDONE) { \
+ goto typval_encode_stop_converting_one_item; \
+ } \
} while (0)
-#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict)
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
+
+static inline void _nothing_conv_dict_end(typval_T *const tv,
+ dict_T **const dictp,
+ const void *const nodictvar)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if ((const void *)dictp != nodictvar) {
+ dict_unref(*dictp);
+ *dictp = NULL;
+ }
+}
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
+ _nothing_conv_dict_end(tv, (dict_T **)&dict, \
+ (void *)&TYPVAL_ENCODE_NODICT_VAR)
+
+#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type)
-// nothing_convert_one_value()
-// encode_vim_to_nothing()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME nothing
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
#undef TYPVAL_ENCODE_CONV_NIL
@@ -19162,15 +19299,19 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_DICT_START
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_DICT_END
@@ -19182,7 +19323,9 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
void clear_tv(typval_T *varp)
{
if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
- encode_vim_to_nothing(varp, varp, "clear_tv argument");
+ const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument");
+ (void)evn_ret;
+ assert(evn_ret == OK);
}
}
@@ -21570,9 +21713,12 @@ void func_unref(char_u *name)
fp = find_func(name);
if (fp == NULL) {
#ifdef EXITFREE
- if (!entered_free_all_mem) // NOLINT(readability/braces)
-#endif
+ if (!entered_free_all_mem) {
EMSG2(_(e_intern2), "func_unref()");
+ }
+#else
+ EMSG2(_(e_intern2), "func_unref()");
+#endif
} else {
user_func_unref(fp);
}
@@ -23042,11 +23188,10 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf,
terminal_receive(data->term, ptr, count);
}
+ rbuffer_consumed(buf, count);
if (callback->type != kCallbackNone) {
process_job_event(data, callback, type, ptr, count, 0);
}
-
- rbuffer_consumed(buf, count);
}
static void eval_job_process_exit_cb(Process *proc, int status, void *d)
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 980a8d2326..e0b72feb19 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -79,7 +79,7 @@ return {
eval={args=1},
eventhandler={},
executable={args=1},
- execute={args=1},
+ execute={args={1, 2}},
exepath={args=1},
exists={args=1},
exp={args=1, func="float_op_wrapper", data="&exp"},
@@ -302,7 +302,6 @@ return {
tanh={args=1, func="float_op_wrapper", data="&tanh"},
tempname={},
termopen={args={1, 2}},
- test={args=1},
timer_start={args={2,3}},
timer_stop={args=1},
tolower={args=1},
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 5af4893975..ee66b7cf09 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -108,9 +108,12 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
{
garray_T msg_ga;
ga_init(&msg_ga, (int)sizeof(char), 80);
- char *const key_msg = _("key %s");
- char *const key_pair_msg = _("key %s at index %i from special map");
- char *const idx_msg = _("index %i");
+ const char *const key_msg = _("key %s");
+ const char *const key_pair_msg = _("key %s at index %i from special map");
+ const char *const idx_msg = _("index %i");
+ const char *const partial_arg_msg = _("partial");
+ const char *const partial_arg_i_msg = _("argument %i");
+ const char *const partial_self_msg = _("partial self dictionary");
for (size_t i = 0; i < kv_size(*mpstack); i++) {
if (i != 0) {
ga_concat(&msg_ga, ", ");
@@ -154,6 +157,29 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
}
break;
}
+ case kMPConvPartial: {
+ switch (v.data.p.stage) {
+ case kMPConvPartialArgs: {
+ assert(false);
+ break;
+ }
+ case kMPConvPartialSelf: {
+ ga_concat(&msg_ga, partial_arg_msg);
+ break;
+ }
+ case kMPConvPartialEnd: {
+ ga_concat(&msg_ga, partial_self_msg);
+ break;
+ }
+ }
+ break;
+ }
+ case kMPConvPartialList: {
+ const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
+ vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx);
+ ga_concat(&msg_ga, IObuff);
+ break;
+ }
}
}
EMSG3(msg, objname, (kv_size(*mpstack) == 0
@@ -254,7 +280,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
: OK);
}
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
const char *const buf_ = (const char *) buf; \
if (buf == NULL) { \
@@ -273,19 +299,19 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
- TYPVAL_ENCODE_CONV_STRING(buf, len)
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
+ TYPVAL_ENCODE_CONV_STRING(tv, buf, len)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type)
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
ga_concat(gap, numbuf); \
} while (0)
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
switch (fpclassify(flt_)) { \
@@ -308,103 +334,75 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- ga_concat(gap, "function("); \
- TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
- ga_append(gap, ')'); \
+ const char *const fun_ = (const char *)(fun); \
+ if (fun_ == NULL) { \
+ EMSG2(_(e_intern2), "string(): NULL function name"); \
+ ga_concat(gap, "function(NULL"); \
+ } else { \
+ ga_concat(gap, "function("); \
+ TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
+ }\
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
do { \
- int i; \
- ga_concat(gap, "function("); \
- if (pt->pt_name != NULL) { \
- size_t len; \
- char_u *p; \
- len = 3; \
- len += STRLEN(pt->pt_name); \
- for (p = pt->pt_name; *p != NUL; mb_ptr_adv(p)) { \
- if (*p == '\'') { \
- len++; \
- } \
- } \
- char_u *r, *s; \
- s = r = xmalloc(len); \
- if (r != NULL) { \
- *r++ = '\''; \
- for (p = pt->pt_name; *p != NUL; ) { \
- if (*p == '\'') { \
- *r++ = '\''; \
- } \
- MB_COPY_CHAR(p, r); \
- } \
- *r++ = '\''; \
- *r++ = NUL; \
- } \
- ga_concat(gap, s); \
- xfree(s); \
- } \
- if (pt->pt_argc > 0) { \
- ga_concat(gap, ", ["); \
- for (i = 0; i < pt->pt_argc; i++) { \
- if (i > 0) { \
- ga_concat(gap, ", "); \
- } \
- char *tofree = encode_tv2string(&pt->pt_argv[i], NULL); \
- ga_concat(gap, tofree); \
- xfree(tofree); \
- } \
- ga_append(gap, ']'); \
- } \
- if (pt->pt_dict != NULL) { \
- typval_T dtv; \
- ga_concat(gap, ", "); \
- dtv.v_type = VAR_DICT; \
- dtv.vval.v_dict = pt->pt_dict; \
- char *tofree = encode_tv2string(&dtv, NULL); \
- ga_concat(gap, tofree); \
- xfree(tofree); \
- } \
- ga_append(gap, ')'); \
+ if (len != 0) { \
+ ga_concat(gap, ", "); \
+ } \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
+ do { \
+ if ((ptrdiff_t)len != -1) { \
+ ga_concat(gap, ", "); \
+ } \
} while (0)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv) \
+ ga_append(gap, ')')
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
ga_concat(gap, "[]")
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
ga_append(gap, '[')
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
ga_concat(gap, "{}")
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "v:null")
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
ga_concat(gap, ((num)? "v:true": "v:false"))
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num)
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
ga_append(gap, '{')
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
+
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
ga_append(gap, '}')
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
ga_concat(gap, ": ")
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
ga_concat(gap, ", ")
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
-#define TYPVAL_ENCODE_CONV_LIST_END() \
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
ga_append(gap, ']')
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, NULL)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
do { \
@@ -437,9 +435,15 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-// string_convert_one_value()
-// encode_vim_to_string()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME string
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_RECURSE
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
@@ -469,9 +473,15 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
return OK; \
} while (0)
-// echo_convert_one_value()
-// encode_vim_to_echo()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE
+#define TYPVAL_ENCODE_NAME echo
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_RECURSE
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
@@ -489,15 +499,15 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
#undef TYPVAL_ENCODE_CONV_NIL
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "null")
#undef TYPVAL_ENCODE_CONV_BOOL
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
ga_concat(gap, ((num)? "true": "false"))
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
do { \
char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
@@ -505,7 +515,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
} while (0)
#undef TYPVAL_ENCODE_CONV_FLOAT
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
switch (fpclassify(flt_)) { \
@@ -694,7 +704,7 @@ static inline int convert_to_json_string(garray_T *const gap,
}
#undef TYPVAL_ENCODE_CONV_STRING
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
return FAIL; \
@@ -702,25 +712,19 @@ static inline int convert_to_json_string(garray_T *const gap,
} while (0)
#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
xfree(buf); \
EMSG(_("E474: Unable to convert EXT string to JSON")); \
return FAIL; \
} while (0)
-#undef TYPVAL_ENCODE_CONV_FUNC
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
return conv_error(_("E474: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
mpstack, objname)
-#undef TYPVAL_ENCODE_CONV_PARTIAL
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
- return conv_error(_("E474: Error while dumping %s, %s: " \
- "attempt to dump partial"), \
- mpstack, objname)
-
/// Check whether given key can be used in json_encode()
///
/// @param[in] tv Key to check.
@@ -759,8 +763,8 @@ bool encode_check_json_key(const typval_T *const tv)
return true;
}
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) \
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
do { \
if (!encode_check_json_key(&key)) { \
EMSG(_("E474: Invalid key in special dictionary")); \
@@ -768,28 +772,38 @@ bool encode_check_json_key(const typval_T *const tv)
} \
} while (0)
-// json_convert_one_value()
-// encode_vim_to_json()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME json
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
@@ -807,7 +821,10 @@ char *encode_tv2string(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- encode_vim_to_string(&ga, tv, "encode_tv2string() argument");
+ const int evs_ret = encode_vim_to_string(&ga, tv,
+ "encode_tv2string() argument");
+ (void)evs_ret;
+ assert(evs_ret == OK);
did_echo_string_emsg = false;
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -833,7 +850,9 @@ char *encode_tv2echo(typval_T *tv, size_t *len)
ga_concat(&ga, tv->vval.v_string);
}
} else {
- encode_vim_to_echo(&ga, tv, ":echo argument");
+ const int eve_ret = encode_vim_to_echo(&ga, tv, ":echo argument");
+ (void)eve_ret;
+ assert(eve_ret == OK);
}
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -854,16 +873,19 @@ char *encode_tv2json(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ const int evj_ret = encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ if (!evj_ret) {
+ ga_clear(&ga);
+ }
did_echo_string_emsg = false;
if (len != NULL) {
- *len = (size_t) ga.ga_len;
+ *len = (size_t)ga.ga_len;
}
ga_append(&ga, '\0');
- return (char *) ga.ga_data;
+ return (char *)ga.ga_data;
}
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if (buf == NULL) { \
msgpack_pack_bin(packer, 0); \
@@ -874,7 +896,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
do { \
if (buf == NULL) { \
msgpack_pack_str(packer, 0); \
@@ -885,7 +907,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
if (buf == NULL) { \
msgpack_pack_ext(packer, 0, (int8_t) type); \
@@ -896,35 +918,36 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
- msgpack_pack_int64(packer, (int64_t) (num))
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
+ msgpack_pack_int64(packer, (int64_t)(num))
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
- msgpack_pack_double(packer, (double) (flt))
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
+ msgpack_pack_double(packer, (double)(flt))
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
return conv_error(_("E5004: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
mpstack, objname)
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- return conv_error(_("E5004: Error while dumping %s, %s: " \
- "attempt to dump partial"), \
- mpstack, objname)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
msgpack_pack_array(packer, 0)
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
- msgpack_pack_array(packer, (size_t) (len))
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
+ msgpack_pack_array(packer, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
msgpack_pack_map(packer, 0)
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
msgpack_pack_nil(packer)
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \
if ((num)) { \
msgpack_pack_true(packer); \
@@ -933,23 +956,25 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
msgpack_pack_uint64(packer, (num))
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
- msgpack_pack_map(packer, (size_t) (len))
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
+ msgpack_pack_map(packer, (size_t)(len))
+
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
-#define TYPVAL_ENCODE_CONV_DICT_END()
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
-#define TYPVAL_ENCODE_CONV_LIST_END()
+#define TYPVAL_ENCODE_CONV_LIST_END(tv)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
return conv_error(_("E5005: Unable to dump %s: " \
@@ -958,28 +983,38 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
-// msgpack_convert_one_value()
-// encode_vim_to_msgpack()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
+#define TYPVAL_ENCODE_SCOPE
+#define TYPVAL_ENCODE_NAME msgpack
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE msgpack_packer *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME packer
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
new file mode 100644
index 0000000000..365eb2dd77
--- /dev/null
+++ b/src/nvim/eval/typval_encode.c.h
@@ -0,0 +1,802 @@
+/// @file eval/typval_encode.c.h
+///
+/// Contains set of macros used to convert (possibly recursive) typval_T into
+/// something else. For these macros to work the following macros must be
+/// defined:
+
+/// @def TYPVAL_ENCODE_CONV_NIL
+/// @brief Macros used to convert NIL value
+///
+/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
+/// is false) and `v:null`.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to special dictionary.
+
+/// @def TYPVAL_ENCODE_CONV_BOOL
+/// @brief Macros used to convert boolean value
+///
+/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
+/// is false) and `v:true`/`v:false`.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param num Boolean value to convert. Value is an expression which
+/// evaluates to some integer.
+
+/// @def TYPVAL_ENCODE_CONV_NUMBER
+/// @brief Macros used to convert integer
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param num Integer to convert, must accept both varnumber_T and int64_t.
+
+/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
+/// @brief Macros used to convert unsigned integer
+///
+/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
+/// defined.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. Points
+/// to a special dictionary.
+/// @param num Integer to convert, must accept uint64_t.
+
+/// @def TYPVAL_ENCODE_CONV_FLOAT
+/// @brief Macros used to convert floating-point number
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param flt Number to convert, must accept float_T.
+
+/// @def TYPVAL_ENCODE_CONV_STRING
+/// @brief Macros used to convert plain string
+///
+/// Is used to convert VAR_STRING objects as well as BIN strings represented as
+/// special dictionary.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+
+/// @def TYPVAL_ENCODE_CONV_STR_STRING
+/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
+///
+/// Is used to convert dictionary keys and STR strings represented as special
+/// dictionaries.
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+
+/// @def TYPVAL_ENCODE_CONV_EXT_STRING
+/// @brief Macros used to convert EXT string
+///
+/// Is used to convert EXT strings represented as special dictionaries. Never
+/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
+/// defined.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. Points
+/// to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+/// @param type EXT type.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_START
+/// @brief Macros used when starting to convert a funcref or a partial
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param fun Function name. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+/// @brief Macros used before starting to convert partial arguments
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param len Number of arguments. Zero for absent arguments or when
+/// converting a funcref.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+/// @brief Macros used before starting to convert self dictionary
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param len Number of arguments. May be zero for empty dictionary or -1 for
+/// missing self dictionary, also when converting function
+/// reference.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_END
+/// @brief Macros used after converting a funcref or a partial
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
+/// @brief Macros used to convert an empty list
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
+/// @brief Macros used to convert an empty dictionary
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_LIST_START
+/// @brief Macros used before starting to convert non-empty list
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param len List length. Is an expression which evaluates to an integer.
+
+/// @def TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
+/// @brief Macros used after pushing list onto the stack
+///
+/// Only used for real list_T* lists, not for special dictionaries or partial
+/// arguments.
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param mpsv Pushed MPConvStackVal value.
+
+/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
+/// @brief Macros used after finishing converting non-last list item
+///
+/// @param tv Pointer to typval where list is stored. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_LIST_END
+/// @brief Macros used after converting non-empty list
+///
+/// @param tv Pointer to typval where list is stored. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_DICT_START
+/// @brief Macros used before starting to convert non-empty dictionary
+///
+/// Only used for real dict_T* dictionaries, not for special dictionaries. Also
+/// used for partial self dictionary.
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+/// @param len Dictionary length. Is an expression which evaluates to an
+/// integer.
+
+/// @def TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
+/// @brief Macros used after pushing dictionary onto the stack
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL.
+/// May not point to a special dictionary.
+/// @param dict Converted dictionary, lvalue.
+/// @param mpsv Pushed MPConvStackVal value.
+
+/// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+/// @brief Macros used to check special dictionary key
+///
+/// @param label Label for goto in case check was not successfull.
+/// @param key typval_T key to check.
+
+/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
+/// @brief Macros used after finishing converting dictionary key
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
+/// @brief Macros used after finishing converting non-last dictionary value
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_DICT_END
+/// @brief Macros used after converting non-empty dictionary
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_RECURSE
+/// @brief Macros used when self-containing container is detected
+///
+/// @param val Container for which this situation was detected.
+/// @param conv_type Type of the stack entry, @see MPConvStackValType.
+
+/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
+/// @brief Macros that specifies whether special dictionaries are special
+///
+/// Must be something that evaluates to boolean, most likely `true` or `false`.
+/// If it is false then special dictionaries are not treated specially.
+
+/// @def TYPVAL_ENCODE_SCOPE
+/// @brief Scope of the main function: either nothing or `static`
+
+/// @def TYPVAL_ENCODE_NAME
+/// @brief Name of the target converter
+///
+/// After including this file it will define function
+/// `encode_vim_to_{TYPVAL_ENCODE_NAME}` with scope #TYPVAL_ENCODE_SCOPE and
+/// static functions `_typval_encode_{TYPVAL_ENCODE_NAME}_convert_one_value` and
+/// `_typval_encode_{TYPVAL_ENCODE_NAME}_check_self_reference`.
+
+/// @def TYPVAL_ENCODE_FIRST_ARG_TYPE
+/// @brief Type of the first argument, which will be used to return the results
+///
+/// Is expected to be a pointer type.
+
+/// @def TYPVAL_ENCODE_FIRST_ARG_NAME
+/// @brief Name of the first argument
+///
+/// This name will only be used by one of the above macros which are defined by
+/// the caller. Functions defined here do not use first argument directly.
+#ifndef NVIM_EVAL_TYPVAL_ENCODE_C_H
+#define NVIM_EVAL_TYPVAL_ENCODE_C_H
+#undef NVIM_EVAL_TYPVAL_ENCODE_C_H
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "nvim/lib/kvec.h"
+#include "nvim/eval_defs.h"
+#include "nvim/eval/encode.h"
+#include "nvim/func_attr.h"
+#include "nvim/eval/typval_encode.h"
+
+/// Dummy variable used because some macros need lvalue
+///
+/// Must not be written to, if needed one must check that address of the
+/// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`.
+const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL;
+
+static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ void *const val, int *const val_copyID,
+ const MPConvStack *const mpstack, const int copyID,
+ const MPConvStackValType conv_type,
+ const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_ALWAYS_INLINE;
+
+/// Function for checking whether container references itself
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument.
+/// @param[in,out] val Container to check.
+/// @param val_copyID Pointer to the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+/// @param[in] mpstack Stack with values to convert. Read-only, used for error
+/// reporting.
+/// @param[in] copyID CopyID used by the caller.
+/// @param[in] conv_type Type of the conversion, @see MPConvStackValType.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return NOTDONE in case of success, what to return in case of failure.
+static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ void *const val, int *const val_copyID,
+ const MPConvStack *const mpstack, const int copyID,
+ const MPConvStackValType conv_type,
+ const char *const objname)
+{
+ if (*val_copyID == copyID) {
+ TYPVAL_ENCODE_CONV_RECURSE(val, conv_type);
+ return OK;
+ }
+ *val_copyID = copyID;
+ return NOTDONE;
+}
+
+static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
+ typval_T *const tv, const int copyID,
+ const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Convert single value
+///
+/// Only scalar values are converted immediately, everything else is pushed onto
+/// the stack.
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
+/// includer. Only meaningful to macros
+/// defined by the includer.
+/// @param mpstack Stack with values to convert. Values which are not
+/// converted completely by this function (i.e.
+/// non-scalars) are pushed here.
+/// @param cur_mpsv Currently converted value from stack.
+/// @param tv Converted value.
+/// @param[in] copyID CopyID.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return OK in case of success, FAIL in case of failure.
+static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
+ typval_T *const tv, const int copyID,
+ const char *const objname)
+{
+ switch (tv->v_type) {
+ case VAR_STRING: {
+ TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv));
+ break;
+ }
+ case VAR_NUMBER: {
+ TYPVAL_ENCODE_CONV_NUMBER(tv, tv->vval.v_number);
+ break;
+ }
+ case VAR_FLOAT: {
+ TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float);
+ break;
+ }
+ case VAR_FUNC: {
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
+ TYPVAL_ENCODE_CONV_FUNC_END(tv);
+ break;
+ }
+ case VAR_PARTIAL: {
+ partial_T *const pt = tv->vval.v_partial;
+ (void)pt;
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : pt->pt_name));
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .type = kMPConvPartial,
+ .tv = tv,
+ .data = {
+ .p = {
+ .stage = kMPConvPartialArgs,
+ .pt = tv->vval.v_partial,
+ },
+ },
+ }));
+ break;
+ }
+ case VAR_LIST: {
+ if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
+ break;
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
+ kMPConvList);
+ TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .type = kMPConvList,
+ .tv = tv,
+ .data = {
+ .l = {
+ .list = tv->vval.v_list,
+ .li = tv->vval.v_list->lv_first,
+ },
+ },
+ }));
+ TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
+ break;
+ }
+ case VAR_SPECIAL: {
+ switch (tv->vval.v_special) {
+ case kSpecialVarNull: {
+ TYPVAL_ENCODE_CONV_NIL(tv);
+ break;
+ }
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue);
+ break;
+ }
+ }
+ break;
+ }
+ case VAR_DICT: {
+ if (tv->vval.v_dict == NULL
+ || tv->vval.v_dict->dv_hashtab.ht_used == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, tv->vval.v_dict);
+ break;
+ }
+ const dictitem_T *type_di;
+ const dictitem_T *val_di;
+ if (TYPVAL_ENCODE_ALLOW_SPECIALS
+ && tv->vval.v_dict->dv_hashtab.ht_used == 2
+ && (type_di = dict_find((dict_T *)tv->vval.v_dict,
+ (char_u *)"_TYPE", -1)) != NULL
+ && type_di->di_tv.v_type == VAR_LIST
+ && (val_di = dict_find((dict_T *)tv->vval.v_dict,
+ (char_u *)"_VAL", -1)) != NULL) {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) {
+ if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
+ goto _convert_one_value_regular_dict;
+ }
+ switch ((MessagePackType)i) {
+ case kMPNil: {
+ TYPVAL_ENCODE_CONV_NIL(tv);
+ break;
+ }
+ case kMPBoolean: {
+ if (val_di->di_tv.v_type != VAR_NUMBER) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_BOOL(tv, val_di->di_tv.vval.v_number);
+ break;
+ }
+ case kMPInteger: {
+ const list_T *val_list;
+ varnumber_T sign;
+ varnumber_T highest_bits;
+ varnumber_T high_bits;
+ varnumber_T low_bits;
+ // List of 4 integers; first is signed (should be 1 or -1, but
+ // this is not checked), second is unsigned and have at most
+ // one (sign is -1) or two (sign is 1) non-zero bits (number of
+ // bits is not checked), other unsigned and have at most 31
+ // non-zero bits (number of bits is not checked).
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 4
+ || val_list->lv_first->li_tv.v_type != VAR_NUMBER
+ || (sign = val_list->lv_first->li_tv.vval.v_number) == 0
+ || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
+ || (highest_bits =
+ val_list->lv_first->li_next->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
+ || (high_bits =
+ val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_tv.v_type != VAR_NUMBER
+ || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
+ goto _convert_one_value_regular_dict;
+ }
+ uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
+ | (uint64_t)(((uint64_t)high_bits) << 31)
+ | (uint64_t)low_bits);
+ if (sign > 0) {
+ TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
+ } else {
+ TYPVAL_ENCODE_CONV_NUMBER(tv, -number);
+ }
+ break;
+ }
+ case kMPFloat: {
+ if (val_di->di_tv.v_type != VAR_FLOAT) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_FLOAT(tv, val_di->di_tv.vval.v_float);
+ break;
+ }
+ case kMPString:
+ case kMPBinary: {
+ const bool is_string = ((MessagePackType)i == kMPString);
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len,
+ &buf)) {
+ goto _convert_one_value_regular_dict;
+ }
+ if (is_string) {
+ TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len);
+ } else {
+ TYPVAL_ENCODE_CONV_STRING(tv, buf, len);
+ }
+ xfree(buf);
+ break;
+ }
+ case kMPArray: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
+ lv_copyID, copyID,
+ kMPConvList);
+ TYPVAL_ENCODE_CONV_LIST_START(tv,
+ val_di->di_tv.vval.v_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvList,
+ .data = {
+ .l = {
+ .list = val_di->di_tv.vval.v_list,
+ .li = val_di->di_tv.vval.v_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPMap: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ list_T *const val_list = val_di->di_tv.vval.v_list;
+ if (val_list == NULL || val_list->lv_len == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
+ break;
+ }
+ for (const listitem_T *li = val_list->lv_first; li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_LIST
+ || li->li_tv.vval.v_list->lv_len != 2) {
+ goto _convert_one_value_regular_dict;
+ }
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
+ kMPConvPairs);
+ TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
+ val_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvPairs,
+ .data = {
+ .l = {
+ .list = val_list,
+ .li = val_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPExt: {
+ const list_T *val_list;
+ varnumber_T type;
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 2
+ || (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
+ || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
+ || type < INT8_MIN
+ || (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
+ goto _convert_one_value_regular_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
+ &len, &buf)) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
+ xfree(buf);
+ break;
+ }
+ }
+ break;
+ }
+_convert_one_value_regular_dict:
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
+ kMPConvDict);
+ TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
+ tv->vval.v_dict->dv_hashtab.ht_used);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvDict,
+ .data = {
+ .d = {
+ .dict = tv->vval.v_dict,
+ .dictp = &tv->vval.v_dict,
+ .hi = tv->vval.v_dict->dv_hashtab.ht_array,
+ .todo = tv->vval.v_dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict,
+ _mp_last(*mpstack));
+ break;
+ }
+ case VAR_UNKNOWN: {
+ EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
+ return FAIL;
+ }
+ }
+typval_encode_stop_converting_one_item:
+ return OK;
+ // Prevent “unused label” warnings.
+ goto typval_encode_stop_converting_one_item;
+}
+
+TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ typval_T *const tv, const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Convert the whole typval
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
+/// includer. Only meaningful to macros
+/// defined by the includer.
+/// @param top_tv Converted value.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return OK in case of success, FAIL in case of failure.
+TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ typval_T *const top_tv, const char *const objname)
+{
+ const int copyID = get_copyID();
+ MPConvStack mpstack;
+ _mp_init(mpstack);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ NULL,
+ top_tv, copyID, objname)
+ == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+/// Label common for this and convert_one_value functions, used for escaping
+/// from macros like TYPVAL_ENCODE_CONV_DICT_START.
+typval_encode_stop_converting_one_item:
+ while (_mp_size(mpstack)) {
+ MPConvStackVal *cur_mpsv = &_mp_last(mpstack);
+ typval_T *tv = NULL;
+ switch (cur_mpsv->type) {
+ case kMPConvDict: {
+ if (!cur_mpsv->data.d.todo) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.d.dict->dv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);
+ continue;
+ } else if (cur_mpsv->data.d.todo
+ != cur_mpsv->data.d.dict->dv_hashtab.ht_used) {
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv,
+ *cur_mpsv->data.d.dictp);
+ }
+ while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
+ cur_mpsv->data.d.hi++;
+ }
+ dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi);
+ cur_mpsv->data.d.todo--;
+ cur_mpsv->data.d.hi++;
+ TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0],
+ strlen((char *)&di->di_key[0]));
+ TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
+ *cur_mpsv->data.d.dictp);
+ tv = &di->di_tv;
+ break;
+ }
+ case kMPConvList: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
+ continue;
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
+ }
+ tv = &cur_mpsv->data.l.li->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ case kMPConvPairs: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
+ continue;
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(
+ cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
+ }
+ const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
+ TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(
+ encode_vim_to__error_ret, kv_pair->lv_first->li_tv);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME,
+ &mpstack, cur_mpsv,
+ &kv_pair->lv_first->li_tv,
+ copyID,
+ objname) == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+ TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
+ TYPVAL_ENCODE_NODICT_VAR);
+ tv = &kv_pair->lv_last->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ case kMPConvPartial: {
+ partial_T *const pt = cur_mpsv->data.p.pt;
+ tv = cur_mpsv->tv;
+ switch (cur_mpsv->data.p.stage) {
+ case kMPConvPartialArgs: {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv,
+ pt == NULL ? 0 : pt->pt_argc);
+ cur_mpsv->data.p.stage = kMPConvPartialSelf;
+ if (pt != NULL && pt->pt_argc > 0) {
+ TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc);
+ _mp_push(mpstack, ((MPConvStackVal) {
+ .type = kMPConvPartialList,
+ .tv = NULL,
+ .data = {
+ .a = {
+ .arg = pt->pt_argv,
+ .argv = pt->pt_argv,
+ .todo = (size_t)pt->pt_argc,
+ },
+ },
+ }));
+ }
+ break;
+ }
+ case kMPConvPartialSelf: {
+ cur_mpsv->data.p.stage = kMPConvPartialEnd;
+ dict_T *const dict = pt == NULL ? NULL : pt->pt_dict;
+ if (dict != NULL) {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, dict->dv_hashtab.ht_used);
+ if (dict->dv_hashtab.ht_used == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict);
+ continue;
+ }
+ const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_NAME,
+ dict, &dict->dv_copyID, &mpstack, copyID, kMPConvDict,
+ objname);
+ if (te_csr_ret != NOTDONE) {
+ if (te_csr_ret == FAIL) {
+ goto encode_vim_to__error_ret;
+ } else {
+ continue;
+ }
+ }
+ TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,
+ dict->dv_hashtab.ht_used);
+ _mp_push(mpstack, ((MPConvStackVal) {
+ .type = kMPConvDict,
+ .tv = NULL,
+ .data = {
+ .d = {
+ .dict = dict,
+ .dictp = &pt->pt_dict,
+ .hi = dict->dv_hashtab.ht_array,
+ .todo = dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict,
+ _mp_last(mpstack));
+ } else {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
+ }
+ break;
+ }
+ case kMPConvPartialEnd: {
+ TYPVAL_ENCODE_CONV_FUNC_END(tv);
+ (void)_mp_pop(mpstack);
+ break;
+ }
+ }
+ continue;
+ }
+ case kMPConvPartialList: {
+ if (!cur_mpsv->data.a.todo) {
+ (void)_mp_pop(mpstack);
+ TYPVAL_ENCODE_CONV_LIST_END(NULL);
+ continue;
+ } else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
+ TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(NULL);
+ }
+ tv = cur_mpsv->data.a.arg++;
+ cur_mpsv->data.a.todo--;
+ break;
+ }
+ }
+ assert(tv != NULL);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ cur_mpsv, tv, copyID, objname)
+ == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+ }
+ _mp_destroy(mpstack);
+ return OK;
+encode_vim_to__error_ret:
+ _mp_destroy(mpstack);
+ return FAIL;
+ // Prevent “unused label” warnings.
+ goto typval_encode_stop_converting_one_item;
+}
+#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index b79158b30c..ba325b8f55 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -1,161 +1,35 @@
-/// @file eval/typval_convert.h
+/// @file eval/typval_encode.h
///
-/// Contains set of macros used to convert (possibly recursive) typval_T into
-/// something else. For these macros to work the following macros must be
-/// defined:
-
-/// @def TYPVAL_ENCODE_CONV_NIL
-/// @brief Macros used to convert NIL value
-///
-/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
-/// is false) and `v:null`. Accepts no arguments, but still must be
-/// a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_BOOL
-/// @brief Macros used to convert boolean value
-///
-/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
-/// is false) and `v:true`/`v:false`.
-///
-/// @param num Boolean value to convert. Value is an expression which
-/// evaluates to some integer.
-
-/// @def TYPVAL_ENCODE_CONV_NUMBER
-/// @brief Macros used to convert integer
-///
-/// @param num Integer to convert, must accept both varnumber_T and int64_t.
-
-/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-/// @brief Macros used to convert unsigned integer
-///
-/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
-/// defined.
-///
-/// @param num Integer to convert, must accept uint64_t.
-
-/// @def TYPVAL_ENCODE_CONV_FLOAT
-/// @brief Macros used to convert floating-point number
-///
-/// @param flt Number to convert, must accept float_T.
-
-/// @def TYPVAL_ENCODE_CONV_STRING
-/// @brief Macros used to convert plain string
-///
-/// Is used to convert VAR_STRING objects as well as BIN strings represented as
-/// special dictionary.
-///
-/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
-/// @param len String length.
-
-/// @def TYPVAL_ENCODE_CONV_STR_STRING
-/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
-///
-/// Is used to convert dictionary keys and STR strings represented as special
-/// dictionaries.
-
-/// @def TYPVAL_ENCODE_CONV_EXT_STRING
-/// @brief Macros used to convert EXT string
-///
-/// Is used to convert EXT strings represented as special dictionaries. Never
-/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
-/// defined.
-///
-/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
-/// @param len String length.
-/// @param type EXT type.
-
-/// @def TYPVAL_ENCODE_CONV_FUNC
-/// @brief Macros used to convert a function reference
-///
-/// @param fun Function name.
-
-/// @def TYPVAL_ENCODE_CONV_PARTIAL
-/// @brief Macros used to convert a partial
-///
-/// @param pt Partial name.
-
-/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
-/// @brief Macros used to convert an empty list
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
-/// @brief Macros used to convert an empty dictionary
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_START
-/// @brief Macros used before starting to convert non-empty list
-///
-/// @param len List length. Is an expression which evaluates to an integer.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
-/// @brief Macros used after finishing converting non-last list item
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_END
-/// @brief Macros used after converting non-empty list
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_START
-/// @brief Macros used before starting to convert non-empty dictionary
-///
-/// @param len Dictionary length. Is an expression which evaluates to an
-/// integer.
-
-/// @def TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
-/// @brief Macros used to check special dictionary key
-///
-/// @param label Label for goto in case check was not successfull.
-/// @param key typval_T key to check.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
-/// @brief Macros used after finishing converting dictionary key
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-/// @brief Macros used after finishing converting non-last dictionary value
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_END
-/// @brief Macros used after converting non-empty dictionary
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_RECURSE
-/// @brief Macros used when self-containing container is detected
-///
-/// @param val Container for which this situation was detected.
-/// @param conv_type Type of the stack entry, @see MPConvStackValType.
-
-/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
-/// @brief Macros that specifies whether special dictionaries are special
-///
-/// Must be something that evaluates to boolean, most likely `true` or `false`.
-/// If it is false then special dictionaries are not treated specially.
+/// Contains common definitions for eval/typval_encode.c.h. Most of time should
+/// not be included directly.
#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
#define NVIM_EVAL_TYPVAL_ENCODE_H
#include <stddef.h>
#include <inttypes.h>
+#include <string.h>
#include <assert.h>
#include "nvim/lib/kvec.h"
#include "nvim/eval_defs.h"
-#include "nvim/eval/encode.h"
#include "nvim/func_attr.h"
/// Type of the stack entry
typedef enum {
- kMPConvDict, ///< Convert dict_T *dictionary.
- kMPConvList, ///< Convert list_T *list.
+ kMPConvDict, ///< Convert dict_T *dictionary.
+ kMPConvList, ///< Convert list_T *list.
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
+ kMPConvPartial, ///< Convert partial_T* partial.
+ kMPConvPartialList, ///< Convert argc/argv pair coming from a partial.
} MPConvStackValType;
+/// Stage at which partial is being converted
+typedef enum {
+ kMPConvPartialArgs, ///< About to convert arguments.
+ kMPConvPartialSelf, ///< About to convert self dictionary.
+ kMPConvPartialEnd, ///< Already converted everything.
+} MPConvPartialStage;
+
/// Structure representing current VimL to messagepack conversion state
typedef struct {
MPConvStackValType type; ///< Type of the stack entry.
@@ -163,6 +37,9 @@ typedef struct {
union {
struct {
dict_T *dict; ///< Currently converted dictionary.
+ dict_T **dictp; ///< Location where that dictionary is stored.
+ ///< Normally it is &.tv->vval.v_dict, but not when
+ ///< converting partials.
hashitem_T *hi; ///< Currently converted dictionary item.
size_t todo; ///< Amount of items left to process.
} d; ///< State of dictionary conversion.
@@ -170,6 +47,15 @@ typedef struct {
list_T *list; ///< Currently converted list.
listitem_T *li; ///< Currently converted list item.
} l; ///< State of list or generic mapping conversion.
+ struct {
+ MPConvPartialStage stage; ///< Stage at which partial is being converted.
+ partial_T *pt; ///< Currently converted partial.
+ } p; ///< State of partial conversion.
+ struct {
+ typval_T *arg; ///< Currently converted argument.
+ typval_T *argv; ///< Start of the argument list.
+ size_t todo; ///< Number of items left to process.
+ } a; ///< State of list or generic mapping conversion.
} data; ///< Data to convert.
} MPConvStackVal;
@@ -184,21 +70,9 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
#define _mp_pop kv_pop
#define _mp_last kv_last
-/// Code for checking whether container references itself
-///
-/// @param[in,out] val Container to check.
-/// @param copyID_attr Name of the container attribute that holds copyID.
-/// After checking whether value of this attribute is
-/// copyID (variable) it is set to copyID.
-/// @param conv_type Type of the conversion, @see MPConvStackValType.
-#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
- do { \
- if ((val)->copyID_attr == copyID) { \
- TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \
- return OK; \
- } \
- (val)->copyID_attr = copyID; \
- } while (0)
+static inline size_t tv_strlen(const typval_T *const tv)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_NONNULL_ALL;
/// Length of the string stored in typval_T
///
@@ -208,8 +82,6 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
/// @return Length of the string stored in typval_T, including 0 for NULL
/// string.
static inline size_t tv_strlen(const typval_T *const tv)
- FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
- FUNC_ATTR_NONNULL_ALL
{
assert(tv->v_type == VAR_STRING);
return (tv->vval.v_string == NULL
@@ -217,363 +89,57 @@ static inline size_t tv_strlen(const typval_T *const tv)
: strlen((char *) tv->vval.v_string));
}
-/// Define functions to convert a VimL value:
-/// `{name}_convert_one_value(...)`
-/// `encode_vim_to_{name}(...)`
+/// Code for checking whether container references itself
///
-/// @param scope Scope of the main function: either nothing or `static`.
-/// @param name Name of the target converter.
-/// @param firstargtype Type of the first argument. It will be used to return
-/// the results.
-/// @param firstargname Name of the first argument.
-#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \
- firstargname) \
-/* Returns OK or FAIL */ \
-static int name##_convert_one_value(firstargtype firstargname, \
- MPConvStack *const mpstack, \
- typval_T *const tv, \
- const int copyID, \
- const char *const objname) \
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- switch (tv->v_type) { \
- case VAR_STRING: { \
- TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \
- break; \
- } \
- case VAR_NUMBER: { \
- TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \
- break; \
- } \
- case VAR_FLOAT: { \
- TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \
- break; \
- } \
- case VAR_FUNC: { \
- TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \
- break; \
- } \
- case VAR_PARTIAL: { \
- TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \
- break; \
- } \
- case VAR_LIST: { \
- if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
- break; \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \
- kMPConvList); \
- TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .type = kMPConvList, \
- .tv = tv, \
- .data = { \
- .l = { \
- .list = tv->vval.v_list, \
- .li = tv->vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_SPECIAL: { \
- switch (tv->vval.v_special) { \
- case kSpecialVarNull: { \
- TYPVAL_ENCODE_CONV_NIL(); \
- break; \
- } \
- case kSpecialVarTrue: \
- case kSpecialVarFalse: { \
- TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
- break; \
- } \
- } \
- break; \
- } \
- case VAR_DICT: { \
- if (tv->vval.v_dict == NULL \
- || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
- break; \
- } \
- const dictitem_T *type_di; \
- const dictitem_T *val_di; \
- if (TYPVAL_ENCODE_ALLOW_SPECIALS \
- && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
- && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_TYPE", -1)) != NULL \
- && type_di->di_tv.v_type == VAR_LIST \
- && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_VAL", -1)) != NULL) { \
- size_t i; \
- for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \
- if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \
- break; \
- } \
- } \
- if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- switch ((MessagePackType) i) { \
- case kMPNil: { \
- TYPVAL_ENCODE_CONV_NIL(); \
- break; \
- } \
- case kMPBoolean: { \
- if (val_di->di_tv.v_type != VAR_NUMBER) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number); \
- break; \
- } \
- case kMPInteger: { \
- const list_T *val_list; \
- varnumber_T sign; \
- varnumber_T highest_bits; \
- varnumber_T high_bits; \
- varnumber_T low_bits; \
- /* List of 4 integers; first is signed (should be 1 or -1, but */ \
- /* this is not checked), second is unsigned and have at most */ \
- /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
- /* bits is not checked), other unsigned and have at most 31 */ \
- /* non-zero bits (number of bits is not checked).*/ \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 4 \
- || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
- || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
- || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
- || (highest_bits = \
- val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
- || (high_bits = \
- val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
- || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
- | (uint64_t) (((uint64_t) high_bits) << 31) \
- | (uint64_t) low_bits); \
- if (sign > 0) { \
- TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number); \
- } else { \
- TYPVAL_ENCODE_CONV_NUMBER(-number); \
- } \
- break; \
- } \
- case kMPFloat: { \
- if (val_di->di_tv.v_type != VAR_FLOAT) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float); \
- break; \
- } \
- case kMPString: \
- case kMPBinary: { \
- const bool is_string = ((MessagePackType) i == kMPString); \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \
- &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (is_string) { \
- TYPVAL_ENCODE_CONV_STR_STRING(buf, len); \
- } else { \
- TYPVAL_ENCODE_CONV_STRING(buf, len); \
- } \
- xfree(buf); \
- break; \
- } \
- case kMPArray: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \
- lv_copyID, kMPConvList); \
- TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = val_di->di_tv.vval.v_list, \
- .li = val_di->di_tv.vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPMap: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- list_T *const val_list = val_di->di_tv.vval.v_list; \
- if (val_list == NULL || val_list->lv_len == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
- break; \
- } \
- for (const listitem_T *li = val_list->lv_first; li != NULL; \
- li = li->li_next) { \
- if (li->li_tv.v_type != VAR_LIST \
- || li->li_tv.vval.v_list->lv_len != 2) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \
- kMPConvPairs); \
- TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvPairs, \
- .data = { \
- .l = { \
- .list = val_list, \
- .li = val_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPExt: { \
- const list_T *val_list; \
- varnumber_T type; \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 2 \
- || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
- || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
- || type < INT8_MIN \
- || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
- &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \
- xfree(buf); \
- break; \
- } \
- } \
- break; \
- } \
-name##_convert_one_value_regular_dict: \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \
- kMPConvDict); \
- TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvDict, \
- .data = { \
- .d = { \
- .dict = tv->vval.v_dict, \
- .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
- .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_UNKNOWN: { \
- EMSG2(_(e_intern2), #name "_convert_one_value()"); \
- return FAIL; \
- } \
- } \
- return OK; \
-} \
-\
-scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
- const char *const objname) \
- FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- const int copyID = get_copyID(); \
- MPConvStack mpstack; \
- _mp_init(mpstack); \
- if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
- == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- while (_mp_size(mpstack)) { \
- MPConvStackVal *cur_mpsv = &_mp_last(mpstack); \
- typval_T *cur_tv = NULL; \
- switch (cur_mpsv->type) { \
- case kMPConvDict: { \
- if (!cur_mpsv->data.d.todo) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_DICT_END(); \
- continue; \
- } else if (cur_mpsv->data.d.todo \
- != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \
- } \
- while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
- cur_mpsv->data.d.hi++; \
- } \
- dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
- cur_mpsv->data.d.todo--; \
- cur_mpsv->data.d.hi++; \
- TYPVAL_ENCODE_CONV_STR_STRING(&di->di_key[0], \
- strlen((char *) &di->di_key[0])); \
- TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \
- cur_tv = &di->di_tv; \
- break; \
- } \
- case kMPConvList: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_LIST_END(); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \
- } \
- cur_tv = &cur_mpsv->data.l.li->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- case kMPConvPairs: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_DICT_END(); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \
- } \
- const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
- TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK( \
- encode_vim_to_##name##_error_ret, kv_pair->lv_first->li_tv); \
- if (name##_convert_one_value(firstargname, &mpstack, \
- &kv_pair->lv_first->li_tv, copyID, \
- objname) == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \
- cur_tv = &kv_pair->lv_last->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
+/// @param[in,out] val Container to check.
+/// @param copyID_attr Name of the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+/// @param[in] copyID CopyID used by the caller.
+/// @param conv_type Type of the conversion, @see MPConvStackValType.
+#define _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
+ conv_type) \
+ do { \
+ const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( \
+ TYPVAL_ENCODE_FIRST_ARG_NAME, \
+ (val), &(val)->copyID_attr, mpstack, copyID, conv_type, objname); \
+ if (te_csr_ret != NOTDONE) { \
+ return te_csr_ret; \
} \
- } \
- assert(cur_tv != NULL); \
- if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
- objname) == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- } \
- _mp_destroy(mpstack); \
- return OK; \
-encode_vim_to_##name##_error_ret: \
- _mp_destroy(mpstack); \
- return FAIL; \
-}
+ } while (0)
+
+#define _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) \
+ pref##name##suf
+#define _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, name, suf) \
+ _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf)
+
+/// Construct function name, possibly using macros
+///
+/// Is used to expand macros that may appear in arguments.
+///
+/// @note Expands all arguments, even if only one is needed.
+///
+/// @param[in] pref Prefix.
+/// @param[in] suf Suffix.
+///
+/// @return Concat: pref + #TYPVAL_ENCODE_NAME + suf.
+#define _TYPVAL_ENCODE_FUNC_NAME(pref, suf) \
+ _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, TYPVAL_ENCODE_NAME, suf)
+
+/// Self reference checker function name
+#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \
+ _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _check_self_reference)
+
+/// Entry point function name
+#define _TYPVAL_ENCODE_ENCODE \
+ _TYPVAL_ENCODE_FUNC_NAME(encode_vim_to_, )
+
+/// Name of the …convert_one_value function
+#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \
+ _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _convert_one_value)
+
+/// Name of the dummy const dict_T *const variable
+#define TYPVAL_ENCODE_NODICT_VAR \
+ _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _nodict_var)
#endif // NVIM_EVAL_TYPVAL_ENCODE_H
diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c
index a68badcc8f..907187aa17 100644
--- a/src/nvim/event/libuv_process.c
+++ b/src/nvim/event/libuv_process.c
@@ -19,8 +19,7 @@ bool libuv_process_spawn(LibuvProcess *uvproc)
Process *proc = (Process *)uvproc;
uvproc->uvopts.file = proc->argv[0];
uvproc->uvopts.args = proc->argv;
- uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE
- | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
+ uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE;
if (proc->detach) {
uvproc->uvopts.flags |= UV_PROCESS_DETACHED;
}
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 39dd5fd55a..dc7886469b 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -170,8 +170,9 @@ int process_wait(Process *proc, int ms, MultiQueue *events)
int status = -1;
bool interrupted = false;
if (!proc->refcount) {
+ status = proc->status;
LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0);
- return proc->status;
+ return status;
}
if (!events) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index fcaf17a0fc..69eed33736 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -1193,8 +1193,8 @@ static void do_filter(
if (do_out) {
if (otmp != NULL) {
- if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM,
- eap, READ_FILTER) == FAIL) {
+ if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap,
+ READ_FILTER) != OK) {
if (!aborting()) {
msg_putchar('\n');
EMSG2(_(e_notread), otmp);
@@ -1611,6 +1611,7 @@ int do_write(exarg_T *eap)
int retval = FAIL;
char_u *free_fname = NULL;
buf_T *alt_buf = NULL;
+ int name_was_missing;
if (not_writing()) /* check 'write' option */
return FAIL;
@@ -1734,6 +1735,7 @@ int do_write(exarg_T *eap)
fname = curbuf->b_sfname;
}
+ name_was_missing = curbuf->b_ffname == NULL;
retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
eap, eap->append, eap->forceit, TRUE, FALSE);
@@ -1743,7 +1745,11 @@ int do_write(exarg_T *eap)
curbuf->b_p_ro = FALSE;
redraw_tabline = TRUE;
}
- /* Change directories when the 'acd' option is set. */
+ }
+
+ // Change directories when the 'acd' option is set and the file name
+ // got changed or set.
+ if (eap->cmdidx == CMD_saveas || name_was_missing) {
do_autochdir();
}
}
@@ -6087,7 +6093,6 @@ void ex_substitute(exarg_T *eap)
int save_w_p_cul = curwin->w_p_cul;
int save_w_p_cuc = curwin->w_p_cuc;
- emsg_off++; // No error messages during command preview.
curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes
curwin->w_p_cul = false; // Disable 'cursorline'
curwin->w_p_cuc = false; // Disable 'cursorcolumn'
@@ -6114,6 +6119,5 @@ void ex_substitute(exarg_T *eap)
restore_search_patterns();
win_size_restore(&save_view);
ga_clear(&save_view);
- emsg_off--;
unblock_autocmds();
}
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index b998b81284..88095602ba 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -480,6 +480,12 @@ return {
func='ex_close',
},
{
+ command='clearjumps',
+ flags=bit.bor(TRLBAR, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_clearjumps',
+ },
+ {
command='cmap',
flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN),
addr_type=ADDR_LINES,
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 79a0b5849f..e205901635 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1374,7 +1374,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
continue;
}
- if (!checkforcmd(&ea.cmd, "noswapfile", 6)) {
+ if (!checkforcmd(&ea.cmd, "noswapfile", 3)) {
break;
}
cmdmod.noswapfile = true;
@@ -1711,7 +1711,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
xfree(p);
// If the autocommands did something and didn't cause an error, try
// finding the command again.
- p = (ret && !aborting()) ? find_command(&ea, NULL) : NULL;
+ p = (ret && !aborting()) ? find_command(&ea, NULL) : ea.cmd;
}
if (p == NULL) {
@@ -6910,9 +6910,10 @@ static void ex_read(exarg_T *eap)
eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
}
- if (i == FAIL) {
- if (!aborting())
+ if (i != OK) {
+ if (!aborting()) {
EMSG2(_(e_notopen), eap->arg);
+ }
} else {
if (empty && exmode_active) {
/* Delete the empty line that remains. Historically ex does
@@ -6997,8 +6998,6 @@ void post_chdir(CdScope scope)
shorten_fnames(TRUE);
}
-
-
/// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`.
void ex_cd(exarg_T *eap)
{
@@ -7040,30 +7039,31 @@ void ex_cd(exarg_T *eap)
new_dir = NameBuff;
}
#endif
- if (vim_chdir(new_dir)) {
- EMSG(_(e_failed));
- } else {
- CdScope scope = kCdScopeGlobal; // Depends on command invoked
+ CdScope scope = kCdScopeGlobal; // Depends on command invoked
- switch (eap->cmdidx) {
- case CMD_tcd:
- case CMD_tchdir:
- scope = kCdScopeTab;
- break;
- case CMD_lcd:
- case CMD_lchdir:
- scope = kCdScopeWindow;
- break;
- default:
- break;
- }
+ switch (eap->cmdidx) {
+ case CMD_tcd:
+ case CMD_tchdir:
+ scope = kCdScopeTab;
+ break;
+ case CMD_lcd:
+ case CMD_lchdir:
+ scope = kCdScopeWindow;
+ break;
+ default:
+ break;
+ }
+ if (vim_chdir(new_dir, scope)) {
+ EMSG(_(e_failed));
+ } else {
post_chdir(scope);
-
- /* Echo the new current directory if the command was typed. */
- if (KeyTyped || p_verbose >= 5)
+ // Echo the new current directory if the command was typed.
+ if (KeyTyped || p_verbose >= 5) {
ex_pwd(eap);
+ }
}
+
xfree(tofree);
}
}
@@ -9674,9 +9674,20 @@ bool cmd_can_preview(char_u *cmd)
if (*ea.cmd == '*') {
ea.cmd = skipwhite(ea.cmd + 1);
}
- find_command(&ea, NULL);
+ char_u *end = find_command(&ea, NULL);
+
+ switch (ea.cmdidx) {
+ case CMD_substitute:
+ case CMD_smagic:
+ case CMD_snomagic:
+ // Only preview once the pattern delimiter has been typed
+ if (*end && !ASCII_ISALNUM(*end)) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
- return ea.cmdidx == CMD_substitute
- || ea.cmdidx == CMD_smagic
- || ea.cmdidx == CMD_snomagic;
+ return false;
}
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index fb6aac223f..4def4cbbae 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -19,21 +19,6 @@
#define EXMODE_NORMAL 1
#define EXMODE_VIM 2
-/// The scope of a working-directory command like `:cd`.
-///
-/// Scopes are enumerated from lowest to highest. When adding a scope make sure
-/// to update all functions using scopes as well, such as the implementation of
-/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes
-/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead.
-typedef enum {
- kCdScopeInvalid = -1,
- kCdScopeWindow, ///< Affects one window.
- kCdScopeTab, ///< Affects one tab page.
- kCdScopeGlobal, ///< Affects the entire instance of Neovim.
-} CdScope;
-#define MIN_CD_SCOPE kCdScopeWindow
-#define MAX_CD_SCOPE kCdScopeGlobal
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_docmd.h.generated.h"
#endif
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 7c725179dd..dba7a73814 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1607,7 +1607,9 @@ static int command_line_changed(CommandLineState *s)
// - Update the screen while the effects are in place.
// - Immediately undo the effects.
State |= CMDPREVIEW;
+ emsg_silent++; // Block error reporting as the command may be incomplete
do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT);
+ emsg_silent--; // Unblock error reporting
// Restore the window "view".
curwin->w_cursor = s->old_cursor;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 2ac8e27047..03cb504f17 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -48,6 +48,7 @@
#include <limits.h>
#include "nvim/vim.h"
+#include "nvim/eval.h"
#include "nvim/ascii.h"
#include "nvim/file_search.h"
#include "nvim/charset.h"
@@ -1522,6 +1523,47 @@ theend:
return file_name;
}
+static void do_autocmd_dirchanged(char_u *new_dir, CdScope scope)
+{
+ static bool recursive = false;
+
+ if (recursive || !has_event(EVENT_DIRCHANGED)) {
+ // No autocommand was defined or we changed
+ // the directory from this autocommand.
+ return;
+ }
+
+ recursive = true;
+
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+ char buf[8];
+
+ switch (scope) {
+ case kCdScopeGlobal:
+ snprintf(buf, sizeof(buf), "global");
+ break;
+ case kCdScopeTab:
+ snprintf(buf, sizeof(buf), "tab");
+ break;
+ case kCdScopeWindow:
+ snprintf(buf, sizeof(buf), "window");
+ break;
+ case kCdScopeInvalid:
+ // Should never happen.
+ assert(false);
+ }
+
+ dict_add_nr_str(dict, "scope", 0L, (char_u *)buf);
+ dict_add_nr_str(dict, "cwd", 0L, new_dir);
+ dict_set_keys_readonly(dict);
+
+ apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, new_dir, false, NULL);
+
+ dict_clear(dict);
+
+ recursive = false;
+}
+
/// Change to a file's directory.
/// Caller must call shorten_fnames()!
/// @return OK or FAIL
@@ -1531,18 +1573,28 @@ int vim_chdirfile(char_u *fname)
STRLCPY(dir, fname, MAXPATHL);
*path_tail_with_sep(dir) = NUL;
- return os_chdir((char *)dir) == 0 ? OK : FAIL;
+ if (os_chdir((char *)dir) != 0) {
+ return FAIL;
+ }
+ do_autocmd_dirchanged(dir, kCdScopeWindow);
+
+ return OK;
}
/// Change directory to "new_dir". Search 'cdpath' for relative directory names.
-int vim_chdir(char_u *new_dir)
+int vim_chdir(char_u *new_dir, CdScope scope)
{
char_u *dir_name = find_directory_in_path(new_dir, STRLEN(new_dir),
FNAME_MESS, curbuf->b_ffname);
if (dir_name == NULL) {
return -1;
}
+
int r = os_chdir((char *)dir_name);
+ if (r == 0) {
+ do_autocmd_dirchanged(dir_name, scope);
+ }
+
xfree(dir_name);
return r;
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 529b48adbd..13329d771b 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -51,6 +51,7 @@
#include "nvim/window.h"
#include "nvim/shada.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
@@ -248,7 +249,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
* READ_DUMMY read into a dummy buffer (to check if file contents changed)
* READ_KEEP_UNDO don't clear undo info or read it from a file
*
- * return FAIL for failure, OK otherwise
+ * return FAIL for failure, NOTDONE for directory (failure), or OK
*/
int
readfile (
@@ -257,7 +258,7 @@ readfile (
linenr_T from,
linenr_T lines_to_skip,
linenr_T lines_to_read,
- exarg_T *eap, /* can be NULL! */
+ exarg_T *eap, // can be NULL!
int flags
)
{
@@ -280,7 +281,7 @@ readfile (
int wasempty; /* buffer was empty before reading */
colnr_T len;
long size = 0;
- char_u *p;
+ char_u *p = NULL;
off_t filesize = 0;
int skip_read = FALSE;
context_sha256_T sha_ctx;
@@ -443,13 +444,14 @@ readfile (
// ... or a character special file named /dev/fd/<n>
# endif
) {
- if (S_ISDIR(perm))
+ if (S_ISDIR(perm)) {
filemess(curbuf, fname, (char_u *)_("is a directory"), 0);
- else
+ } else {
filemess(curbuf, fname, (char_u *)_("is not a file"), 0);
+ }
msg_end();
msg_scroll = msg_save;
- return FAIL;
+ return S_ISDIR(perm) ? NOTDONE : FAIL;
}
#endif
}
@@ -1881,16 +1883,20 @@ failed:
xfree(keep_msg);
keep_msg = NULL;
msg_scrolled_ign = TRUE;
- p = msg_trunc_attr(IObuff, FALSE, 0);
+
+ if (!read_stdin && !read_buffer) {
+ p = msg_trunc_attr(IObuff, FALSE, 0);
+ }
+
if (read_stdin || read_buffer || restart_edit != 0
- || (msg_scrolled != 0 && !need_wait_return))
- /* Need to repeat the message after redrawing when:
- * - When reading from stdin (the screen will be cleared next).
- * - When restart_edit is set (otherwise there will be a delay
- * before redrawing).
- * - When the screen was scrolled but there is no wait-return
- * prompt. */
+ || (msg_scrolled != 0 && !need_wait_return)) {
+ // Need to repeat the message after redrawing when:
+ // - When reading from stdin (the screen will be cleared next).
+ // - When restart_edit is set (otherwise there will be a delay before
+ // redrawing).
+ // - When the screen was scrolled but there is no wait-return prompt.
set_keep_msg(p, 0);
+ }
msg_scrolled_ign = FALSE;
}
@@ -5103,13 +5109,13 @@ void buf_reload(buf_T *buf, int orig_mode)
}
if (saved == OK) {
- curbuf->b_flags |= BF_CHECK_RO; /* check for RO again */
- keep_filetype = TRUE; /* don't detect 'filetype' */
- if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0,
- (linenr_T)0,
- (linenr_T)MAXLNUM, &ea, flags) == FAIL) {
- if (!aborting())
+ curbuf->b_flags |= BF_CHECK_RO; // check for RO again
+ keep_filetype = true; // don't detect 'filetype'
+ if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0,
+ (linenr_T)MAXLNUM, &ea, flags) != OK) {
+ if (!aborting()) {
EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname);
+ }
if (savebuf != NULL && buf_valid(savebuf) && buf == curbuf) {
/* Put the text back from the save buffer. First
* delete any lines that readfile() added. */
@@ -5224,6 +5230,10 @@ static void vim_maketempdir(void)
// Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
char_u template[TEMP_FILE_PATH_MAXLEN];
char_u path[TEMP_FILE_PATH_MAXLEN];
+
+ // Make sure the umask doesn't remove the executable bit.
+ // "repl" has been reported to use "0177".
+ mode_t umask_save = umask(0077);
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
// Expand environment variables, leave room for "/nvimXXXXXX/999999999"
expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
@@ -5247,6 +5257,7 @@ static void vim_maketempdir(void)
os_rmdir((char *)path);
}
}
+ (void)umask(umask_save);
}
/// Delete "name" and everything in it, recursively.
@@ -5366,6 +5377,8 @@ static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */
*/
static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
+// use get_deleted_augroup() to get this
+static char_u *deleted_augroup = NULL;
/*
* The ID of the current group. Group 0 is the default one.
@@ -5380,6 +5393,14 @@ static event_T last_event;
static int last_group;
static int autocmd_blocked = 0; /* block all autocmds */
+static char_u *get_deleted_augroup(void)
+{
+ if (deleted_augroup == NULL) {
+ deleted_augroup = (char_u *)_("--Deleted--");
+ }
+ return deleted_augroup;
+}
+
/*
* Show the autocommands for one AutoPat.
*/
@@ -5399,10 +5420,11 @@ static void show_autocmd(AutoPat *ap, event_T event)
return;
if (event != last_event || ap->group != last_group) {
if (ap->group != AUGROUP_DEFAULT) {
- if (AUGROUP_NAME(ap->group) == NULL)
- msg_puts_attr((char_u *)_("--Deleted--"), hl_attr(HLF_E));
- else
+ if (AUGROUP_NAME(ap->group) == NULL) {
+ msg_puts_attr(get_deleted_augroup(), hl_attr(HLF_E));
+ } else {
msg_puts_attr(AUGROUP_NAME(ap->group), hl_attr(HLF_T));
+ }
msg_puts((char_u *)" ");
}
msg_puts_attr(event_nr2name(event), hl_attr(HLF_T));
@@ -5568,11 +5590,33 @@ static void au_del_group(char_u *name)
int i;
i = au_find_group(name);
- if (i == AUGROUP_ERROR) /* the group doesn't exist */
+ if (i == AUGROUP_ERROR) { // the group doesn't exist
EMSG2(_("E367: No such group: \"%s\""), name);
- else {
+ } else if (i == current_augroup) {
+ EMSG(_("E936: Cannot delete the current group"));
+ } else {
+ event_T event;
+ AutoPat *ap;
+ int in_use = false;
+
+ for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
+ event = (event_T)((int)event + 1)) {
+ for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) {
+ if (ap->group == i && ap->pat != NULL) {
+ give_warning((char_u *)
+ _("W19: Deleting augroup that is still in use"), true);
+ in_use = true;
+ event = NUM_EVENTS;
+ break;
+ }
+ }
+ }
xfree(AUGROUP_NAME(i));
- AUGROUP_NAME(i) = NULL;
+ if (in_use) {
+ AUGROUP_NAME(i) = get_deleted_augroup();
+ } else {
+ AUGROUP_NAME(i) = NULL;
+ }
}
}
@@ -5584,8 +5628,9 @@ static void au_del_group(char_u *name)
static int au_find_group(const char_u *name)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (int i = 0; i < augroups.ga_len; ++i) {
- if (AUGROUP_NAME(i) != NULL && STRCMP(AUGROUP_NAME(i), name) == 0) {
+ for (int i = 0; i < augroups.ga_len; i++) {
+ if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup()
+ && STRCMP(AUGROUP_NAME(i), name) == 0) {
return i;
}
}
@@ -5633,10 +5678,21 @@ void do_augroup(char_u *arg, int del_group)
#if defined(EXITFREE)
void free_all_autocmds(void)
{
+ int i;
+ char_u *s;
+
for (current_augroup = -1; current_augroup < augroups.ga_len;
- ++current_augroup)
- do_autocmd((char_u *)"", TRUE);
- ga_clear_strings(&augroups);
+ current_augroup++) {
+ do_autocmd((char_u *)"", true);
+ }
+
+ for (i = 0; i < augroups.ga_len; i++) {
+ s = ((char_u **)(augroups.ga_data))[i];
+ if (s != get_deleted_augroup()) {
+ xfree(s);
+ }
+ }
+ ga_clear(&augroups);
}
#endif
@@ -5652,13 +5708,14 @@ static event_T event_name2nr(char_u *start, char_u **end)
int i;
int len;
- /* the event name ends with end of line, a blank or a comma */
- for (p = start; *p && !ascii_iswhite(*p) && *p != ','; ++p)
- ;
- for (i = 0; event_names[i].name != NULL; ++i) {
- len = (int) event_names[i].len;
- if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
+ // the event name ends with end of line, '|', a blank or a comma */
+ for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {
+ }
+ for (i = 0; event_names[i].name != NULL; i++) {
+ len = (int)event_names[i].len;
+ if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) {
break;
+ }
}
if (*p == ',')
++p;
@@ -5700,7 +5757,7 @@ find_end_event (
}
pat = arg + 1;
} else {
- for (pat = arg; *pat && !ascii_iswhite(*pat); pat = p) {
+ for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) {
if (have_group)
EMSG2(_("E216: No such event: %s"), pat);
@@ -5815,8 +5872,9 @@ void au_event_restore(char_u *old_ei)
*
* Mostly a {group} argument can optionally appear before <event>.
*/
-void do_autocmd(char_u *arg, int forceit)
+void do_autocmd(char_u *arg_in, int forceit)
{
+ char_u *arg = arg_in;
char_u *pat;
char_u *envpat = NULL;
char_u *cmd;
@@ -5825,10 +5883,13 @@ void do_autocmd(char_u *arg, int forceit)
int nested = FALSE;
int group;
- /*
- * Check for a legal group name. If not, use AUGROUP_ALL.
- */
- group = au_get_grouparg(&arg);
+ if (*arg == '|') {
+ arg = (char_u *)"";
+ group = AUGROUP_ALL; // no argument, use all groups
+ } else {
+ // Check for a legal group name. If not, use AUGROUP_ALL.
+ group = au_get_grouparg(&arg);
+ }
/*
* Scan over the events.
@@ -5838,50 +5899,54 @@ void do_autocmd(char_u *arg, int forceit)
if (pat == NULL)
return;
- /*
- * Scan over the pattern. Put a NUL at the end.
- */
pat = skipwhite(pat);
- cmd = pat;
- while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\'))
- cmd++;
- if (*cmd)
- *cmd++ = NUL;
-
- /* Expand environment variables in the pattern. Set 'shellslash', we want
- * forward slashes here. */
- if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
+ if (*pat == '|') {
+ pat = (char_u *)"";
+ cmd = (char_u *)"";
+ } else {
+ // Scan over the pattern. Put a NUL at the end.
+ cmd = pat;
+ while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\')) {
+ cmd++;
+ }
+ if (*cmd) {
+ *cmd++ = NUL;
+ }
+
+ // Expand environment variables in the pattern. Set 'shellslash', we want
+ // forward slashes here.
+ if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
#ifdef BACKSLASH_IN_FILENAME
- int p_ssl_save = p_ssl;
+ int p_ssl_save = p_ssl;
- p_ssl = TRUE;
+ p_ssl = true;
#endif
- envpat = expand_env_save(pat);
+ envpat = expand_env_save(pat);
#ifdef BACKSLASH_IN_FILENAME
- p_ssl = p_ssl_save;
+ p_ssl = p_ssl_save;
#endif
- if (envpat != NULL)
- pat = envpat;
- }
+ if (envpat != NULL) {
+ pat = envpat;
+ }
+ }
- /*
- * Check for "nested" flag.
- */
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
- nested = TRUE;
- cmd = skipwhite(cmd + 6);
- }
+ // Check for "nested" flag.
+ cmd = skipwhite(cmd);
+ if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
+ && ascii_iswhite(cmd[6])) {
+ nested = true;
+ cmd = skipwhite(cmd + 6);
+ }
- /*
- * Find the start of the commands.
- * Expand <sfile> in it.
- */
- if (*cmd != NUL) {
- cmd = expand_sfile(cmd);
- if (cmd == NULL) /* some error */
- return;
- need_free = TRUE;
+ // Find the start of the commands.
+ // Expand <sfile> in it.
+ if (*cmd != NUL) {
+ cmd = expand_sfile(cmd);
+ if (cmd == NULL) { // some error
+ return;
+ }
+ need_free = true;
+ }
}
/*
@@ -5895,16 +5960,17 @@ void do_autocmd(char_u *arg, int forceit)
/*
* Loop over the events.
*/
- last_event = (event_T)-1; /* for listing the event name */
- last_group = AUGROUP_ERROR; /* for listing the group name */
- if (*arg == '*' || *arg == NUL) {
+ last_event = (event_T)-1; // for listing the event name
+ last_group = AUGROUP_ERROR; // for listing the group name
+ if (*arg == '*' || *arg == NUL || *arg == '|') {
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- if (do_autocmd_event(event, pat,
- nested, cmd, forceit, group) == FAIL)
+ event = (event_T)((int)event + 1)) {
+ if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
break;
+ }
+ }
} else {
- while (*arg && !ascii_iswhite(*arg)) {
+ while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
event_T event = event_name2nr(arg, &arg);
assert(event < NUM_EVENTS);
if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
@@ -5931,7 +5997,8 @@ static int au_get_grouparg(char_u **argp)
char_u *arg = *argp;
int group = AUGROUP_ALL;
- p = skiptowhite(arg);
+ for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) {
+ }
if (p > arg) {
group_name = vim_strnsave(arg, (int)(p - arg));
group = au_find_group(group_name);
@@ -6698,8 +6765,9 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
fname = vim_strsave(fname); /* make a copy, so we can change it */
} else {
sfname = vim_strsave(fname);
- // don't try expanding the following events
+ // Don't try expanding the following events.
if (event == EVENT_COLORSCHEME
+ || event == EVENT_DIRCHANGED
|| event == EVENT_FILETYPE
|| event == EVENT_FUNCUNDEFINED
|| event == EVENT_OPTIONSET
@@ -6708,10 +6776,11 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|| event == EVENT_REMOTEREPLY
|| event == EVENT_SPELLFILEMISSING
|| event == EVENT_SYNTAX
- || event == EVENT_TABCLOSED)
+ || event == EVENT_TABCLOSED) {
fname = vim_strsave(fname);
- else
- fname = (char_u *)FullName_save((char *)fname, FALSE);
+ } else {
+ fname = (char_u *)FullName_save((char *)fname, false);
+ }
}
if (fname == NULL) { /* out of memory */
xfree(sfname);
@@ -7087,9 +7156,11 @@ char_u *get_augroup_name(expand_T *xp, int idx)
return (char_u *)"END";
if (idx >= augroups.ga_len) /* end of list */
return NULL;
- if (AUGROUP_NAME(idx) == NULL) /* skip deleted entries */
+ if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup()) {
+ // skip deleted entries
return (char_u *)"";
- return AUGROUP_NAME(idx); /* return a name */
+ }
+ return AUGROUP_NAME(idx); // return a name
}
static int include_groups = FALSE;
@@ -7146,10 +7217,12 @@ set_context_in_autocmd (
*/
char_u *get_event_name(expand_T *xp, int idx)
{
- if (idx < augroups.ga_len) { /* First list group names, if wanted */
- if (!include_groups || AUGROUP_NAME(idx) == NULL)
- return (char_u *)""; /* skip deleted entries */
- return AUGROUP_NAME(idx); /* return a name */
+ if (idx < augroups.ga_len) { // First list group names, if wanted
+ if (!include_groups || AUGROUP_NAME(idx) == NULL
+ || AUGROUP_NAME(idx) == get_deleted_augroup()) {
+ return (char_u *)""; // skip deleted entries
+ }
+ return AUGROUP_NAME(idx); // return a name
}
return (char_u *)event_names[idx - augroups.ga_len].name;
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 872ff8d5b8..e3c84cb852 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -409,10 +409,6 @@ EXTERN struct caller_scope {
} provider_caller_scope;
EXTERN int provider_call_nesting INIT(= 0);
-/* Magic number used for hashitem "hi_key" value indicating a deleted item.
- * Only the address is used. */
-EXTERN char_u hash_removed;
-
EXTERN int t_colors INIT(= 256); // int value of T_CCO
@@ -636,10 +632,6 @@ EXTERN int exiting INIT(= FALSE);
/* TRUE when planning to exit Vim. Might
* still keep on running if there is a changed
* buffer. */
-#if defined(EXITFREE)
-// true when in or after free_all_mem()
-EXTERN bool entered_free_all_mem INIT(= false);
-#endif
// volatile because it is used in signal handler deathtrap().
EXTERN volatile int full_screen INIT(= false);
// TRUE when doing full-screen output
@@ -877,9 +869,10 @@ EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped.
EXTERN cmdmod_T cmdmod; /* Ex command modifiers */
-EXTERN int msg_silent INIT(= 0); /* don't print messages */
-EXTERN int emsg_silent INIT(= 0); /* don't print error messages */
-EXTERN int cmd_silent INIT(= FALSE); /* don't echo the command line */
+EXTERN int msg_silent INIT(= 0); // don't print messages
+EXTERN int emsg_silent INIT(= 0); // don't print error messages
+EXTERN bool emsg_noredir INIT(= false); // don't redirect error messages
+EXTERN int cmd_silent INIT(= false); // don't echo the command line
/* Values for swap_exists_action: what to do when swap file already exists */
#define SEA_NONE 0 /* don't use dialog */
@@ -1256,4 +1249,20 @@ typedef enum {
kBroken
} WorkingStatus;
+/// The scope of a working-directory command like `:cd`.
+///
+/// Scopes are enumerated from lowest to highest. When adding a scope make sure
+/// to update all functions using scopes as well, such as the implementation of
+/// `getcwd()`. When using scopes as limits (e.g. in loops) don't use the scopes
+/// directly, use `MIN_CD_SCOPE` and `MAX_CD_SCOPE` instead.
+typedef enum {
+ kCdScopeInvalid = -1,
+ kCdScopeWindow, ///< Affects one window.
+ kCdScopeTab, ///< Affects one tab page.
+ kCdScopeGlobal, ///< Affects the entire instance of Neovim.
+} CdScope;
+
+#define MIN_CD_SCOPE kCdScopeWindow
+#define MAX_CD_SCOPE kCdScopeGlobal
+
#endif /* NVIM_GLOBALS_H */
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index fa4077f22f..376f33e23e 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -36,6 +36,8 @@
# include "hashtab.c.generated.h"
#endif
+char hash_removed;
+
/// Initialize an empty hash table.
void hash_init(hashtab_T *ht)
{
@@ -380,3 +382,13 @@ hash_T hash_hash(char_u *key)
return hash;
}
+
+/// Function to get HI_KEY_REMOVED value
+///
+/// Used for testing because luajit ffi does not allow getting addresses of
+/// globals.
+const char_u *_hash_key_removed(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return HI_KEY_REMOVED;
+}
diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h
index 7233d8c47c..0da2b13f2e 100644
--- a/src/nvim/hashtab.h
+++ b/src/nvim/hashtab.h
@@ -5,14 +5,19 @@
#include "nvim/types.h"
+/// Magic number used for hashitem "hi_key" value indicating a deleted item
+///
+/// Only the address is used.
+extern char hash_removed;
+
/// Type for hash number (hash calculation result).
typedef size_t hash_T;
/// The address of "hash_removed" is used as a magic number
/// for hi_key to indicate a removed item.
-#define HI_KEY_REMOVED &hash_removed
+#define HI_KEY_REMOVED ((char_u *)&hash_removed)
#define HASHITEM_EMPTY(hi) ((hi)->hi_key == NULL \
- || (hi)->hi_key == &hash_removed)
+ || (hi)->hi_key == (char_u *)&hash_removed)
/// A hastable item.
///
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index 99e94fc60f..94bbaf4239 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -573,8 +573,10 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp,
} else {
l = 1;
}
- if (end - bp > l && bp[l + 1] == '>') {
- bp += l; // anything accepted, like <C-?>
+ if (end - bp > l && bp[l] != '"' && bp[l + 1] == '>') {
+ // Anything accepted, like <C-?>, except <C-">, because the "
+ // ends the string.
+ bp += l;
}
}
}
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 6453c41415..4e05845eb5 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -130,17 +130,23 @@ int setmark_pos(int c, pos_T *pos, int fnum)
return OK;
}
- if (c > 'z') /* some islower() and isupper() cannot handle
- characters above 127 */
+ buf_T *buf = buflist_findnr(fnum);
+ // Can't set a mark in a non-existant buffer.
+ if (buf == NULL) {
return FAIL;
- if (islower(c)) {
+ }
+
+ if (ASCII_ISLOWER(c)) {
i = c - 'a';
- RESET_FMARK(curbuf->b_namedm + i, *pos, curbuf->b_fnum);
+ RESET_FMARK(buf->b_namedm + i, *pos, fnum);
return OK;
}
- if (isupper(c)) {
- assert(c >= 'A' && c <= 'Z');
- i = c - 'A';
+ if (ASCII_ISUPPER(c) || ascii_isdigit(c)) {
+ if (ascii_isdigit(c)) {
+ i = c - '0' + NMARKS;
+ } else {
+ i = c - 'A';
+ }
RESET_XFMARK(namedfm + i, *pos, fnum, NULL);
return OK;
}
@@ -798,6 +804,13 @@ void ex_jumps(exarg_T *eap)
MSG_PUTS("\n>");
}
+void ex_clearjumps(exarg_T *eap)
+{
+ free_jumplist(curwin);
+ curwin->w_jumplistlen = 0;
+ curwin->w_jumplistidx = 0;
+}
+
/*
* print the changelist
*/
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index a9a53ebca7..b8891f6560 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1063,11 +1063,12 @@ void ml_recover(void)
if (!cannot_open) {
line_count = pp->pb_pointer[idx].pe_line_count;
if (readfile(curbuf->b_ffname, NULL, lnum,
- pp->pb_pointer[idx].pe_old_lnum - 1,
- line_count, NULL, 0) == FAIL)
- cannot_open = TRUE;
- else
+ pp->pb_pointer[idx].pe_old_lnum - 1, line_count,
+ NULL, 0) != OK) {
+ cannot_open = true;
+ } else {
lnum += line_count;
+ }
}
if (cannot_open) {
++error;
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 1884d55999..92ead873ae 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -17,16 +17,41 @@
// Force je_ prefix on jemalloc functions.
# define JEMALLOC_NO_DEMANGLE
# include <jemalloc/jemalloc.h>
-# define malloc(size) je_malloc(size)
-# define calloc(count, size) je_calloc(count, size)
-# define realloc(ptr, size) je_realloc(ptr, size)
-# define free(ptr) je_free(ptr)
+#endif
+
+#ifdef UNIT_TESTING
+# define malloc(size) mem_malloc(size)
+# define calloc(count, size) mem_calloc(count, size)
+# define realloc(ptr, size) mem_realloc(ptr, size)
+# define free(ptr) mem_free(ptr)
+# ifdef HAVE_JEMALLOC
+MemMalloc mem_malloc = &je_malloc;
+MemFree mem_free = &je_free;
+MemCalloc mem_calloc = &je_calloc;
+MemRealloc mem_realloc = &je_realloc;
+# else
+MemMalloc mem_malloc = &malloc;
+MemFree mem_free = &free;
+MemCalloc mem_calloc = &calloc;
+MemRealloc mem_realloc = &realloc;
+# endif
+#else
+# ifdef HAVE_JEMALLOC
+# define malloc(size) je_malloc(size)
+# define calloc(count, size) je_calloc(count, size)
+# define realloc(ptr, size) je_realloc(ptr, size)
+# define free(ptr) je_free(ptr)
+# endif
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memory.c.generated.h"
#endif
+#ifdef EXITFREE
+bool entered_free_all_mem = false;
+#endif
+
/// Try to free memory. Used when trying to recover from out of memory errors.
/// @see {xmalloc}
void try_to_free_memory(void)
@@ -353,15 +378,15 @@ char *xstpncpy(char *restrict dst, const char *restrict src, size_t maxlen)
size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size)
FUNC_ATTR_NONNULL_ALL
{
- size_t ret = strlen(src);
+ size_t ret = strlen(src);
- if (size) {
- size_t len = (ret >= size) ? size - 1 : ret;
- memcpy(dst, src, len);
- dst[len] = '\0';
- }
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ }
- return ret;
+ return ret;
}
/// strdup() wrapper
@@ -371,6 +396,7 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size)
/// @return pointer to a copy of the string
char *xstrdup(const char *str)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
+ FUNC_ATTR_NONNULL_ALL
{
return xmemdupz(str, strlen(str));
}
@@ -401,6 +427,7 @@ void *xmemrchr(const void *src, uint8_t c, size_t len)
/// @return pointer to a copy of the string
char *xstrndup(const char *str, size_t len)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
+ FUNC_ATTR_NONNULL_ALL
{
char *p = memchr(str, '\0', len);
return xmemdupz(str, p ? (size_t)(p - str) : len);
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index 62cc78360c..250ac3e08f 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -1,9 +1,41 @@
#ifndef NVIM_MEMORY_H
#define NVIM_MEMORY_H
+#include <stdbool.h> // for bool
#include <stdint.h> // for uint8_t
#include <stddef.h> // for size_t
-#include <time.h> // for time_t
+#include <time.h> // for time_t
+
+/// `malloc()` function signature
+typedef void *(*MemMalloc)(size_t);
+
+/// `free()` function signature
+typedef void (*MemFree)(void *);
+
+/// `calloc()` function signature
+typedef void *(*MemCalloc)(size_t, size_t);
+
+/// `realloc()` function signature
+typedef void *(*MemRealloc)(void *, size_t);
+
+#ifdef UNIT_TESTING
+/// When unit testing: pointer to the `malloc()` function, may be altered
+extern MemMalloc mem_malloc;
+
+/// When unit testing: pointer to the `free()` function, may be altered
+extern MemFree mem_free;
+
+/// When unit testing: pointer to the `calloc()` function, may be altered
+extern MemCalloc mem_calloc;
+
+/// When unit testing: pointer to the `realloc()` function, may be altered
+extern MemRealloc mem_realloc;
+#endif
+
+#ifdef EXITFREE
+/// Indicates that free_all_mem function was or is running
+extern bool entered_free_all_mem;
+#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memory.h.generated.h"
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 637b89ccbe..2f8feda6ec 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -508,20 +508,22 @@ int emsg(char_u *s)
* But do write it to the redirection file.
*/
if (emsg_silent != 0) {
- msg_start();
- p = get_emsg_source();
- if (p != NULL) {
- STRCAT(p, "\n");
- redir_write(p, STRLEN(p));
- xfree(p);
- }
- p = get_emsg_lnum();
- if (p != NULL) {
- STRCAT(p, "\n");
- redir_write(p, STRLEN(p));
- xfree(p);
+ if (!emsg_noredir) {
+ msg_start();
+ p = get_emsg_source();
+ if (p != NULL) {
+ STRCAT(p, "\n");
+ redir_write(p, STRLEN(p));
+ xfree(p);
+ }
+ p = get_emsg_lnum();
+ if (p != NULL) {
+ STRCAT(p, "\n");
+ redir_write(p, STRLEN(p));
+ xfree(p);
+ }
+ redir_write(s, STRLEN(s));
}
- redir_write(s, STRLEN(s));
return true;
}
@@ -2508,8 +2510,7 @@ static void redir_write(char_u *str, int maxlen)
int redirecting(void)
{
return redir_fd != NULL || *p_vfile != NUL
- || redir_reg || redir_vname
- ;
+ || redir_reg || redir_vname || capture_ga != NULL;
}
/*
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 907353c271..10d6be85f8 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1404,7 +1404,7 @@ int op_delete(oparg_T *oap)
if (oap->regname == 0) {
set_clipboard(0, reg);
- yank_do_autocmd(oap, reg);
+ do_autocmd_textyankpost(oap, reg);
}
}
@@ -2315,7 +2315,7 @@ bool op_yank(oparg_T *oap, bool message)
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
set_clipboard(oap->regname, reg);
- yank_do_autocmd(oap, reg);
+ do_autocmd_textyankpost(oap, reg);
return true;
}
@@ -2538,7 +2538,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx)
///
/// @param oap Operator arguments.
/// @param reg The yank register used.
-static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
+static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
FUNC_ATTR_NONNULL_ALL
{
static bool recursive = false;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 52a8b19ca4..a4e7da770e 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -213,12 +213,12 @@ typedef struct vimoption {
#define P_VI_DEF 0x400U /* Use Vi default for Vim */
#define P_VIM 0x800U /* Vim option */
-/* when option changed, what to display: */
-#define P_RSTAT 0x1000U /* redraw status lines */
-#define P_RWIN 0x2000U /* redraw current window */
-#define P_RBUF 0x4000U /* redraw current buffer */
-#define P_RALL 0x6000U /* redraw all windows */
-#define P_RCLR 0x7000U /* clear and redraw all */
+// when option changed, what to display:
+#define P_RSTAT 0x1000U ///< redraw status lines
+#define P_RWIN 0x2000U ///< redraw current window and recompute text
+#define P_RBUF 0x4000U ///< redraw current buffer and recompute text
+#define P_RALL 0x6000U ///< redraw all windows
+#define P_RCLR 0x7000U ///< clear and redraw all
#define P_COMMA 0x8000U ///< comma separated list
#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive
@@ -238,6 +238,8 @@ typedef struct vimoption {
///< when there is a redraw flag
#define P_NO_DEF_EXP 0x8000000U ///< Do not expand default value.
+#define P_RWINONLY 0x10000000U ///< only redraw current window
+
#define HIGHLIGHT_INIT \
"8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \
"d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr," \
@@ -4357,17 +4359,24 @@ static void check_redraw(uint32_t flags)
bool doclear = (flags & P_RCLR) == P_RCLR;
bool all = ((flags & P_RALL) == P_RALL || doclear);
- if ((flags & P_RSTAT) || all) /* mark all status lines dirty */
+ if ((flags & P_RSTAT) || all) { // mark all status lines dirty
status_redraw_all();
+ }
- if ((flags & P_RBUF) || (flags & P_RWIN) || all)
+ if ((flags & P_RBUF) || (flags & P_RWIN) || all) {
changed_window_setting();
- if (flags & P_RBUF)
+ }
+ if (flags & P_RBUF) {
redraw_curbuf_later(NOT_VALID);
- if (doclear)
+ }
+ if (flags & P_RWINONLY) {
+ redraw_later(NOT_VALID);
+ }
+ if (doclear) {
redraw_all_later(CLEAR);
- else if (all)
+ } else if (all) {
redraw_all_later(NOT_VALID);
+ }
}
/// Find index for named option
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 70d1f73cf4..859658e40d 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -17,8 +17,8 @@
-- types: bool, number, string
-- lists: (nil), comma, onecomma, flags, flagscomma
-- scopes: global, buffer, window
--- redraw options: statuslines, current_window, current_buffer, all_windows,
--- everything, curswant
+-- redraw options: statuslines, current_window, curent_window_only,
+-- current_buffer, all_windows, everything, curswant
-- default: {vi=…[, vim=…]}
-- defaults: {condition=#if condition, if_true=default, if_false=default}
-- #if condition:
@@ -539,7 +539,7 @@ return {
full_name='cursorline', abbreviation='cul',
type='bool', scope={'window'},
vi_def=true,
- redraw={'current_window'},
+ redraw={'current_window_only'},
defaults={if_true={vi=false}}
},
{
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 83a0262545..47d541c6a7 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -560,8 +560,8 @@ const void *vim_colon_env_iter_rev(const char *const val,
/// Vim's version of getenv().
/// Special handling of $HOME, $VIM and $VIMRUNTIME, allowing the user to
/// override the vim runtime directory at runtime. Also does ACP to 'enc'
-/// conversion for Win32. Results must be freed by the calling function.
-/// @param name Name of environment variable to expand
+/// conversion for Win32. Result must be freed by the caller.
+/// @param name Environment variable to expand
char *vim_getenv(const char *name)
{
const char *kos_env_path = os_getenv(name);
@@ -598,6 +598,27 @@ char *vim_getenv(const char *name)
if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL) {
vim_path = (char *)p_hf;
}
+
+#ifdef WIN32
+ // Find runtime path relative to the nvim binary i.e. ../share/runtime
+ if (vim_path == NULL) {
+ char exe_name[MAXPATHL];
+ size_t exe_name_len = MAXPATHL;
+ if (os_exepath(exe_name, &exe_name_len) == 0) {
+ char *path_end = (char *)path_tail_with_sep((char_u *)exe_name);
+ *path_end = '\0'; // remove the trailing "nvim.exe"
+ path_end = (char *)path_tail((char_u *)exe_name);
+ *path_end = '\0'; // remove the trailing "bin/"
+ if (append_path(
+ exe_name,
+ "share" _PATHSEPSTR "nvim" _PATHSEPSTR "runtime" _PATHSEPSTR,
+ MAXPATHL) == OK) {
+ vim_path = exe_name;
+ }
+ }
+ }
+#endif
+
if (vim_path != NULL) {
// remove the file name
char *vim_path_end = (char *)path_tail((char_u *)vim_path);
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 3c821936e9..4aa727733e 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -192,6 +192,18 @@ int os_nodetype(const char *name)
return nodetype;
}
+/// Gets the absolute path of the currently running executable.
+///
+/// @param[out] buffer Returns the path string.
+/// @param[in] size Size of `buffer`.
+///
+/// @return `0` on success, or libuv error code on failure.
+int os_exepath(char *buffer, size_t *size)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return uv_exepath(buffer, size);
+}
+
/// Checks if the given path represents an executable file.
///
/// @param[in] name Name of the executable.
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 10b3f4c091..afb9bdec31 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -16,20 +16,29 @@ static const char *xdg_env_vars[] = {
[kXDGDataDirs] = "XDG_DATA_DIRS",
};
+#ifdef WIN32
+static const char *const xdg_defaults_env_vars[] = {
+ [kXDGConfigHome] = "LOCALAPPDATA",
+ [kXDGDataHome] = "LOCALAPPDATA",
+ [kXDGCacheHome] = "TEMP",
+ [kXDGRuntimeDir] = NULL,
+ [kXDGConfigDirs] = NULL,
+ [kXDGDataDirs] = NULL,
+};
+#endif
+
/// Defaults for XDGVarType values
///
/// Used in case environment variables contain nothing. Need to be expanded.
static const char *const xdg_defaults[] = {
#ifdef WIN32
- // Windows
- [kXDGConfigHome] = "$LOCALAPPDATA",
- [kXDGDataHome] = "$LOCALAPPDATA",
- [kXDGCacheHome] = "$TEMP",
+ [kXDGConfigHome] = "~\\AppData\\Local",
+ [kXDGDataHome] = "~\\AppData\\Local",
+ [kXDGCacheHome] = "~\\AppData\\Local\\Temp",
[kXDGRuntimeDir] = NULL,
[kXDGConfigDirs] = NULL,
[kXDGDataDirs] = NULL,
#else
- // Linux, BSD, CYGWIN, Apple
[kXDGConfigHome] = "~/.config",
[kXDGDataHome] = "~/.local/share",
[kXDGCacheHome] = "~/.cache",
@@ -50,7 +59,14 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
const char *const env = xdg_env_vars[idx];
const char *const fallback = xdg_defaults[idx];
- const char *const env_val = os_getenv(env);
+ const char *env_val = os_getenv(env);
+
+#ifdef WIN32
+ if (env_val == NULL) {
+ env_val = os_getenv(xdg_defaults_env_vars[idx]);
+ }
+#endif
+
char *ret = NULL;
if (env_val != NULL) {
ret = xstrdup(env_val);
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index 2205ad0958..8ce2ecf4f4 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -7,6 +7,7 @@
#include <uv.h>
#include "nvim/os/time.h"
+#include "nvim/os/input.h"
#include "nvim/event/loop.h"
#include "nvim/vim.h"
#include "nvim/main.h"
@@ -34,10 +35,10 @@ uint64_t os_hrtime(void)
return uv_hrtime();
}
-/// Sleeps for a certain amount of milliseconds
+/// Sleeps for a certain amount of milliseconds.
///
/// @param milliseconds Number of milliseconds to sleep
-/// @param ignoreinput If true, allow a SIGINT to interrupt us
+/// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt.
void os_delay(uint64_t milliseconds, bool ignoreinput)
{
if (ignoreinput) {
@@ -46,26 +47,42 @@ void os_delay(uint64_t milliseconds, bool ignoreinput)
}
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int);
} else {
- os_microdelay(milliseconds * 1000);
+ os_microdelay(milliseconds * 1000u, ignoreinput);
}
}
-/// Sleeps for a certain amount of microseconds
+/// Sleeps for a certain amount of microseconds.
///
-/// @param microseconds Number of microseconds to sleep
-void os_microdelay(uint64_t microseconds)
+/// @param ms Number of microseconds to sleep.
+/// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C).
+/// If false, waiting is aborted on any input.
+void os_microdelay(uint64_t ms, bool ignoreinput)
{
- uint64_t elapsed = 0;
- uint64_t ns = microseconds * 1000; // convert to nanoseconds
+ uint64_t elapsed = 0u;
uint64_t base = uv_hrtime();
+ // Convert microseconds to nanoseconds, or UINT64_MAX on overflow.
+ const uint64_t ns = (ms < UINT64_MAX / 1000u) ? ms * 1000u : UINT64_MAX;
uv_mutex_lock(&delay_mutex);
while (elapsed < ns) {
- if (uv_cond_timedwait(&delay_cond, &delay_mutex, ns - elapsed)
- == UV_ETIMEDOUT)
+ // If ignoring input, we simply wait the full delay.
+ // Else we check for input in ~100ms intervals.
+ const uint64_t ns_delta = ignoreinput
+ ? ns - elapsed
+ : MIN(ns - elapsed, 100000000u); // 100ms
+
+ const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
+ if (0 != rv && UV_ETIMEDOUT != rv) {
+ assert(false);
+ break;
+ } // Else: Timeout proceeded normally.
+
+ if (!ignoreinput && os_char_avail()) {
break;
- uint64_t now = uv_hrtime();
+ }
+
+ const uint64_t now = uv_hrtime();
elapsed += now - base;
base = now;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 6149e1ab99..3d1def8dd4 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -417,15 +417,11 @@ char *FullName_save(char *fname, bool force)
}
char *buf = xmalloc(MAXPATHL);
- char *new_fname = NULL;
- if (vim_FullName(fname, buf, MAXPATHL, force) != FAIL) {
- new_fname = xstrdup(buf);
- } else {
- new_fname = xstrdup(fname);
+ if (vim_FullName(fname, buf, MAXPATHL, force) == FAIL) {
+ xfree(buf);
+ return xstrdup(fname);
}
- xfree(buf);
-
- return new_fname;
+ return buf;
}
/// Saves the absolute path.
@@ -1649,30 +1645,37 @@ bool vim_isAbsName(char_u *name)
/// Save absolute file name to "buf[len]".
///
-/// @param fname is the filename to evaluate
-/// @param[out] buf is the buffer for returning the absolute path for `fname`
-/// @param len is the length of `buf`
-/// @param force is a flag to force expanding even if the path is absolute
+/// @param fname filename to evaluate
+/// @param[out] buf contains `fname` absolute path, or:
+/// - truncated `fname` if longer than `len`
+/// - unmodified `fname` if absolute path fails or is a URL
+/// @param len length of `buf`
+/// @param force flag to force expanding even if the path is absolute
///
/// @return FAIL for failure, OK otherwise
int vim_FullName(const char *fname, char *buf, size_t len, bool force)
FUNC_ATTR_NONNULL_ARG(2)
{
- int retval = OK;
- int url;
-
*buf = NUL;
- if (fname == NULL)
+ if (fname == NULL) {
return FAIL;
+ }
- url = path_with_url(fname);
- if (!url)
- retval = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
- if (url || retval == FAIL) {
- /* something failed; use the file name (truncate when too long) */
+ if (strlen(fname) > (len - 1)) {
+ xstrlcpy(buf, fname, len); // truncate
+ return FAIL;
+ }
+
+ if (path_with_url(fname)) {
xstrlcpy(buf, fname, len);
+ return OK;
}
- return retval;
+
+ int rv = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
+ if (rv == FAIL) {
+ xstrlcpy(buf, fname, len); // something failed; use the filename
+ }
+ return rv;
}
/// Get the full resolved path for `fname`
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index 3145931bfe..cff140508b 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -4424,8 +4424,8 @@ msgstr ""
"перед записом: %s"
#, c-format
-msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!"
-msgstr "E138: Усі файли %s.tmp.X зайнято, неможливо записати файл ShaDa!"
+msgid "E929: All %s.tmp.X files exist, cannot write ShaDa file!"
+msgstr "E929: Усі файли %s.tmp.X зайнято, неможливо записати файл ShaDa!"
#, c-format
msgid "System error while opening temporary ShaDa file %s for writing: %s"
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 41acc48f97..c0db076eff 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -4912,7 +4912,7 @@ void win_redr_status(win_T *wp)
screen_fill(row, row + 1, len + wp->w_wincol,
this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
- if (get_keymap_str(wp, NameBuff, MAXPATHL)
+ if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)
&& this_ru_col - len > (int)(STRLEN(NameBuff) + 1))
screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff)
- 1 + wp->w_wincol), attr);
@@ -4993,8 +4993,9 @@ int stl_connected(win_T *wp)
int
get_keymap_str (
win_T *wp,
- char_u *buf, /* buffer for the result */
- int len /* length of buffer */
+ char_u *fmt, // format string containing one %s item
+ char_u *buf, // buffer for the result
+ int len // length of buffer
)
{
char_u *p;
@@ -5021,10 +5022,9 @@ get_keymap_str (
else
p = (char_u *)"lang";
}
- if ((int)(STRLEN(p) + 3) < len)
- sprintf((char *)buf, "<%s>", p);
- else
+ if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) {
buf[0] = NUL;
+ }
xfree(s);
}
return buf[0] != NUL;
@@ -6752,10 +6752,12 @@ int showmode(void)
if (p_fkmap)
MSG_PUTS_ATTR(farsi_text_5, attr);
if (State & LANGMAP) {
- if (curwin->w_p_arab)
+ if (curwin->w_p_arab) {
MSG_PUTS_ATTR(_(" Arabic"), attr);
- else
- MSG_PUTS_ATTR(_(" (lang)"), attr);
+ } else if (get_keymap_str(curwin, (char_u *)" (%s)",
+ NameBuff, MAXPATHL)) {
+ MSG_PUTS_ATTR(NameBuff, attr);
+ }
}
if ((State & INSERT) && p_paste)
MSG_PUTS_ATTR(_(" (paste)"), attr);
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 1029190db4..2a087276e7 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -3976,12 +3976,10 @@ current_search (
return OK;
}
-/*
- * Check if the pattern is one character or zero-width.
- * If move is true, check from the beginning of the buffer,
- * else from the current cursor position.
- * Returns TRUE, FALSE or -1 for failure.
- */
+/// Check if the pattern is one character long or zero-width.
+/// If move is true, check from the beginning of the buffer,
+/// else from the current cursor position.
+/// Returns TRUE, FALSE or -1 for failure.
static int is_one_char(char_u *pattern, bool move)
{
regmmatch_T regmatch;
@@ -3991,11 +3989,17 @@ static int is_one_char(char_u *pattern, bool move)
int save_called_emsg = called_emsg;
int flag = 0;
+ if (pattern == NULL) {
+ pattern = spats[last_idx].pat;
+ }
+
if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH,
SEARCH_KEEP, &regmatch) == FAIL)
return -1;
- /* move to match */
+ // init startcol correctly
+ regmatch.startpos[0].col = -1;
+ // move to match
if (move) {
clearpos(&pos);
} else {
@@ -4003,21 +4007,29 @@ static int is_one_char(char_u *pattern, bool move)
/* accept a match at the cursor position */
flag = SEARCH_START;
}
- if (searchit(curwin, curbuf, &pos, FORWARD, spats[last_idx].pat, 1,
- SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) {
- /* Zero-width pattern should match somewhere, then we can check if
- * start and end are in the same position. */
- called_emsg = FALSE;
- nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
- pos.lnum, (colnr_T)0, NULL);
-
- if (!called_emsg)
+ if (searchit(curwin, curbuf, &pos, FORWARD, pattern, 1,
+ SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) {
+ // Zero-width pattern should match somewhere, then we can check if
+ // start and end are in the same position.
+ called_emsg = false;
+ do {
+ regmatch.startpos[0].col++;
+ nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
+ pos.lnum, regmatch.startpos[0].col, NULL);
+ if (!nmatched) {
+ break;
+ }
+ } while (regmatch.startpos[0].col < pos.col);
+
+ if (!called_emsg) {
result = (nmatched != 0
&& regmatch.startpos[0].lnum == regmatch.endpos[0].lnum
&& regmatch.startpos[0].col == regmatch.endpos[0].col);
-
- if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col)
- result = TRUE;
+ // one char width
+ if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) {
+ result = true;
+ }
+ }
}
called_emsg |= save_called_emsg;
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 867c697a9a..8cf5976e8b 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -118,9 +118,10 @@ KHASH_SET_INIT_STR(strset)
// E576: Missing '>'
// E577: Illegal register name
// E886: Can't rename viminfo file to %s!
+// E929: Too many viminfo temp files, like %s!
// Now only six of them are used:
// E137: ShaDa file is not writeable (for pre-open checks)
-// E138: All %s.tmp.X files exist, cannot write ShaDa file!
+// E929: All %s.tmp.X files exist, cannot write ShaDa file!
// RCERR (E576) for critical read errors.
// RNERR (E136) for various errors when renaming.
// RERR (E575) for various errors inside read ShaDa file.
@@ -148,6 +149,9 @@ KHASH_SET_INIT_STR(strset)
/// Common prefix for all ignorable “write” errors
#define WERR "E574: "
+/// Callback function for add_search_pattern
+typedef void (*SearchPatternGetter)(SearchPattern *);
+
/// Flags for shada_read_file and children
typedef enum {
kShaDaWantInfo = 1, ///< Load non-mark information
@@ -2323,8 +2327,9 @@ static inline ShaDaWriteResult shada_read_when_writing(
/// @param[in] removable_bufs Buffers which are ignored
///
/// @return ShadaEntry List of buffers to save, kSDItemBufferList entry.
-static ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+static inline ShadaEntry shada_get_buflist(
+ khash_t(bufset) *const removable_bufs)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
{
int max_bufs = get_shada_parameter('%');
size_t buf_count = 0;
@@ -2368,6 +2373,62 @@ static ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs)
return buflist_entry;
}
+/// Save search pattern to PossiblyFreedShadaEntry
+///
+/// @param[out] ret_pse Location where result will be saved.
+/// @param[in] get_pattern Function used to get pattern.
+/// @param[in] is_substitute_pattern True if pattern in question is substitute
+/// pattern. Also controls whether some
+/// fields should be initialized to default
+/// or values from get_pattern.
+/// @param[in] search_last_used Result of search_was_last_used().
+/// @param[in] search_highlighted True if search pattern was highlighted by
+/// &hlsearch and this information should be
+/// saved.
+static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse,
+ const SearchPatternGetter get_pattern,
+ const bool is_substitute_pattern,
+ const bool search_last_used,
+ const bool search_highlighted)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ const ShadaEntry defaults = sd_default_values[kSDItemSearchPattern];
+ SearchPattern pat;
+ get_pattern(&pat);
+ if (pat.pat != NULL) {
+ *ret_pse = (PossiblyFreedShadaEntry) {
+ .can_free_entry = false,
+ .data = {
+ .type = kSDItemSearchPattern,
+ .timestamp = pat.timestamp,
+ .data = {
+ .search_pattern = {
+ .magic = pat.magic,
+ .smartcase = !pat.no_scs,
+ .has_line_offset = (is_substitute_pattern
+ ? defaults.data.search_pattern.has_line_offset
+ : pat.off.line),
+ .place_cursor_at_end = (
+ is_substitute_pattern
+ ? defaults.data.search_pattern.place_cursor_at_end
+ : pat.off.end),
+ .offset = (is_substitute_pattern
+ ? defaults.data.search_pattern.offset
+ : pat.off.off),
+ .is_last_used = (is_substitute_pattern ^ search_last_used),
+ .is_substitute_pattern = is_substitute_pattern,
+ .highlighted = ((is_substitute_pattern ^ search_last_used)
+ && search_highlighted),
+ .pat = (char *)pat.pat,
+ .additional_data = pat.additional_data,
+ .search_backward = (!is_substitute_pattern && pat.off.dir == '?'),
+ }
+ }
+ }
+ };
+ }
+}
+
/// Write ShaDa file
///
/// @param[in] sd_writer Structure containing file writer definition.
@@ -2529,45 +2590,14 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
const bool search_highlighted = !(no_hlsearch
|| find_shada_parameter('h') != NULL);
const bool search_last_used = search_was_last_used();
-#define ADD_SEARCH_PAT(func, wms_attr, hlo, pcae, o, is_sub) \
- do { \
- SearchPattern pat; \
- func(&pat); \
- if (pat.pat != NULL) { \
- wms->wms_attr = (PossiblyFreedShadaEntry) { \
- .can_free_entry = false, \
- .data = { \
- .type = kSDItemSearchPattern, \
- .timestamp = pat.timestamp, \
- .data = { \
- .search_pattern = { \
- .magic = pat.magic, \
- .smartcase = !pat.no_scs, \
- .has_line_offset = hlo, \
- .place_cursor_at_end = pcae, \
- .offset = o, \
- .is_last_used = (is_sub ^ search_last_used), \
- .is_substitute_pattern = is_sub, \
- .highlighted = ((is_sub ^ search_last_used) \
- && search_highlighted), \
- .pat = (char *) pat.pat, \
- .additional_data = pat.additional_data, \
- .search_backward = (!is_sub && pat.off.dir == '?'), \
- } \
- } \
- } \
- }; \
- } \
- } while (0)
// Initialize search pattern
- ADD_SEARCH_PAT(get_search_pattern, search_pattern, pat.off.line, \
- pat.off.end, pat.off.off, false);
+ add_search_pattern(&wms->search_pattern, &get_search_pattern, false,
+ search_last_used, search_highlighted);
// Initialize substitute search pattern
- ADD_SEARCH_PAT(get_substitute_pattern, sub_search_pattern, false, false, 0,
- true);
-#undef ADD_SEARCH_PAT
+ add_search_pattern(&wms->sub_search_pattern, &get_substitute_pattern, true,
+ search_last_used, search_highlighted);
// Initialize substitute replacement string
{
@@ -2590,10 +2620,12 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
// Initialize jump list
const void *jump_iter = NULL;
+ setpcmark();
+ cleanup_jumplist();
do {
xfmark_T fm;
- cleanup_jumplist();
jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm);
+
const buf_T *const buf = (fm.fmark.fnum == 0
? NULL
: buflist_findnr(fm.fmark.fnum));
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 7119ac6dc1..7e0bbd6ad1 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -292,6 +292,9 @@
#include <stdlib.h>
#include <wctype.h>
+/* for offsetof() */
+#include <stddef.h>
+
#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/spell.h"
@@ -583,9 +586,8 @@ typedef struct wordcount_S {
char_u wc_word[1]; // word, actually longer
} wordcount_T;
-static wordcount_T dumwc;
-#define WC_KEY_OFF (unsigned)(dumwc.wc_word - (char_u *)&dumwc)
-#define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF))
+#define WC_KEY_OFF offsetof(wordcount_T, wc_word)
+#define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF))
#define MAXWORDCOUNT 0xffff
// Information used when looking for suggestions.
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 84a0c0b889..814c0bc3cb 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -34,6 +34,7 @@ NEW_TESTS = \
test_cmdline.res \
test_cscope.res \
test_diffmode.res \
+ test_gn.res \
test_hardcopy.res \
test_help_tagjump.res \
test_history.res \
@@ -43,8 +44,8 @@ NEW_TESTS = \
test_quickfix.res \
test_signs.res \
test_syntax.res \
- test_usercommands.res \
test_timers.res \
+ test_usercommands.res \
test_viml.res \
test_visual.res \
test_window_id.res \
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 20863bbaf3..eb5912086b 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -39,6 +39,9 @@ if &lines < 24 || &columns < 80
cquit
endif
+" Common with all tests on all systems.
+source setup.vim
+
" This also enables use of line continuation.
set viminfo+=nviminfo
@@ -59,9 +62,6 @@ lang mess C
" Always use forward slashes.
set shellslash
-" Make sure $HOME does not get read or written.
-let $HOME = '/does/not/exist'
-
" Align with vim defaults.
set directory^=.
set nohidden
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
new file mode 100644
index 0000000000..52876d1e6c
--- /dev/null
+++ b/src/nvim/testdir/setup.vim
@@ -0,0 +1,7 @@
+" Common preparations for running tests.
+
+" Make sure 'runtimepath' does not include $HOME.
+set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
+
+" Make sure $HOME does not get read or written.
+let $HOME = '/does/not/exist'
diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in
index 83abe17770..1a4ac6b6d1 100644
--- a/src/nvim/testdir/test17.in
+++ b/src/nvim/testdir/test17.in
@@ -4,13 +4,7 @@ Tests for:
STARTTEST
:set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,}
-:function! DeleteDirectory(dir)
-: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32")
-: exec "silent !rmdir /Q /S " . a:dir
-: else
-: exec "silent !rm -rf " . a:dir
-: endif
-:endfun
+:"
:if has("unix")
:let $CDIR = "."
/CDIR
@@ -36,7 +30,7 @@ STARTTEST
:" check for 'include' without \zs or \ze
:lang C
:call delete("./Xbase.a")
-:call DeleteDirectory("Xdir1")
+:call delete("Xdir1", "rf")
:!mkdir Xdir1
:!mkdir "Xdir1/dir2"
:e! Xdir1/dir2/foo.a
@@ -61,7 +55,7 @@ ENDTEST
STARTTEST
:" check for 'include' with \zs and \ze
:call delete("./Xbase.b")
-:call DeleteDirectory("Xdir1")
+:call delete("Xdir1", "rf")
:!mkdir Xdir1
:!mkdir "Xdir1/dir2"
:let &include='^\s*%inc\s*/\zs[^/]\+\ze'
@@ -91,7 +85,7 @@ ENDTEST
STARTTEST
:" check for 'include' with \zs and no \ze
:call delete("./Xbase.c")
-:call DeleteDirectory("Xdir1")
+:call delete("Xdir1", "rf")
:!mkdir Xdir1
:!mkdir "Xdir1/dir2"
:let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze'
diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in
index f3778c5192..20b5d019af 100644
--- a/src/nvim/testdir/test53.in
+++ b/src/nvim/testdir/test53.in
@@ -4,8 +4,6 @@ Note that the end-of-line moves the cursor to the next test line.
Also test match() and matchstr()
-Also test the gn command and repeating it.
-
STARTTEST
/^start:/
da"
@@ -52,35 +50,6 @@ dit
:put =match('abc', '\zs', 2, 1) " 2
:put =match('abc', '\zs', 3, 1) " 3
:put =match('abc', '\zs', 4, 1) " -1
-/^foobar
-gncsearchmatch/one\_s*two\_s
-:1
-gnd
-/[a]bcdx
-:1
-2gnd/join
-/$
-0gnd
-/\>\zs
-0gnd/^
-gnd$h/\zs
-gnd/[u]niquepattern/s
-vlgnd
-/mother
-:set selection=exclusive
-$cgNmongoose/i
-cgnj
-:" Make sure there is no other match y uppercase.
-/x59
-gggnd
-:" test repeating dgn
-/^Johnny
-ggdgn.
-:" test repeating gUgn
-/^Depp
-gggUgn.
-gg/a:0\@!\zs\d\+
-nygnop
:/^start:/,/^end:/wq! test.out
ENDTEST
@@ -102,32 +71,4 @@ innertext object
</b>
</begin>
SEARCH:
-foobar
-one
-two
-abcdx | abcdx | abcdx
-join
-lines
-zero width pattern
-delete first and last chars
-uniquepattern uniquepattern
-my very excellent mother just served us nachos
-for (i=0; i<=10; i++)
-a:10
-
-a:1
-
-a:20
-Y
-text
-Y
---1
-Johnny
---2
-Johnny
---3
-Depp
---4
-Depp
---5
end:
diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok
index 05206972a4..d57d86bbb0 100644
--- a/src/nvim/testdir/test53.ok
+++ b/src/nvim/testdir/test53.ok
@@ -42,30 +42,4 @@ a
3
-1
SEARCH:
-searchmatch
-abcdx | | abcdx
-join lines
-zerowidth pattern
-elete first and last char
- uniquepattern
-my very excellent mongoose just served us nachos
-for (j=0; i<=10; i++)
-a:10
-
-a:1
-1
-
-a:20
-
-text
-Y
---1
-
---2
-
---3
-DEPP
---4
-DEPP
---5
end:
diff --git a/src/nvim/testdir/test73.in b/src/nvim/testdir/test73.in
index 7d6c7287a5..9d50f7a789 100644
--- a/src/nvim/testdir/test73.in
+++ b/src/nvim/testdir/test73.in
@@ -8,16 +8,9 @@ STARTTEST
:" This will cause a few errors, do it silently.
:set visualbell
:"
-:function! DeleteDirectory(dir)
-: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32")
-: exec "silent !rmdir /Q /S " . a:dir
-: else
-: exec "silent !rm -rf " . a:dir
-: endif
-:endfun
:" On windows a stale "Xfind" directory may exist, remove it so that
:" we start from a clean state.
-:call DeleteDirectory("Xfind")
+:call delete("Xfind", "rf")
:new
:let cwd=getcwd()
:let test_out = cwd . '/test.out'
@@ -169,7 +162,7 @@ SVoyager 2:w
:exec "w >>" . test_out
:q
:exec "cd " . cwd
-:call DeleteDirectory("Xfind")
+:call delete("Xfind", "rf")
:qa!
ENDTEST
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 818ff7cf54..60248bf430 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -4,6 +4,7 @@
source test_assign.vim
source test_autocmd.vim
source test_cursor_func.vim
+source test_execute_func.vim
source test_ex_undo.vim
source test_expr.vim
source test_expr_utf8.vim
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1dceb70cd4..f05a55f1aa 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -19,6 +19,7 @@ if has('timers')
call timer_start(100, 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
+ au! CursorHoldI
endfunc
func Test_cursorhold_insert_ctrl_x()
@@ -29,6 +30,7 @@ if has('timers')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
+ au! CursorHoldI
endfunc
endif
@@ -58,5 +60,139 @@ function Test_bufunload()
bwipeout
call assert_equal(["bufunload", "bufdelete", "bufwipeout"], s:li)
+ au! test_bufunload_group
augroup! test_bufunload_group
endfunc
+
+" SEGV occurs in older versions. (At least 7.4.2005 or older)
+function Test_autocmd_bufunload_with_tabnext()
+ tabedit
+ tabfirst
+
+ augroup test_autocmd_bufunload_with_tabnext_group
+ autocmd!
+ autocmd BufUnload <buffer> tabnext
+ augroup END
+
+ quit
+ call assert_equal(2, tabpagenr('$'))
+
+ augroup! test_autocmd_bufunload_with_tabnext_group
+ tablast
+ quit
+endfunc
+
+func Test_win_tab_autocmd()
+ let g:record = []
+
+ augroup testing
+ au WinNew * call add(g:record, 'WinNew')
+ au WinEnter * call add(g:record, 'WinEnter')
+ au WinLeave * call add(g:record, 'WinLeave')
+ au TabNew * call add(g:record, 'TabNew')
+ au TabClosed * call add(g:record, 'TabClosed')
+ au TabEnter * call add(g:record, 'TabEnter')
+ au TabLeave * call add(g:record, 'TabLeave')
+ augroup END
+
+ split
+ tabnew
+ close
+ close
+
+ call assert_equal([
+ \ 'WinLeave', 'WinNew', 'WinEnter',
+ \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
+ \ 'WinLeave', 'TabLeave', 'TabClosed', 'WinEnter', 'TabEnter',
+ \ 'WinLeave', 'WinEnter'
+ \ ], g:record)
+
+ let g:record = []
+ tabnew somefile
+ tabnext
+ bwipe somefile
+
+ call assert_equal([
+ \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
+ \ 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter',
+ \ 'TabClosed'
+ \ ], g:record)
+
+ augroup testing
+ au!
+ augroup END
+ unlet g:record
+endfunc
+
+func s:AddAnAutocmd()
+ augroup vimBarTest
+ au BufReadCmd * echo 'hello'
+ augroup END
+ call assert_equal(3, len(split(execute('au vimBarTest'), "\n")))
+endfunc
+
+func Test_early_bar()
+ " test that a bar is recognized before the {event}
+ call s:AddAnAutocmd()
+ augroup vimBarTest | au! | augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ call s:AddAnAutocmd()
+ augroup vimBarTest| au!| augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ " test that a bar is recognized after the {event}
+ call s:AddAnAutocmd()
+ augroup vimBarTest| au!BufReadCmd| augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ " test that a bar is recognized after the {group}
+ call s:AddAnAutocmd()
+ au! vimBarTest|echo 'hello'
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+endfunc
+
+func RemoveGroup()
+ autocmd! StartOK
+ augroup! StartOK
+endfunc
+
+func Test_augroup_warning()
+ augroup TheWarning
+ au VimEnter * echo 'entering'
+ augroup END
+ call assert_true(match(execute('au VimEnter'), "TheWarning.*VimEnter") >= 0)
+ redir => res
+ augroup! TheWarning
+ redir END
+ call assert_true(match(res, "W19:") >= 0)
+ call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0)
+
+ " check "Another" does not take the pace of the deleted entry
+ augroup Another
+ augroup END
+ call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0)
+
+ " no warning for postpone aucmd delete
+ augroup StartOK
+ au VimEnter * call RemoveGroup()
+ augroup END
+ call assert_true(match(execute('au VimEnter'), "StartOK.*VimEnter") >= 0)
+ redir => res
+ doautocmd VimEnter
+ redir END
+ call assert_true(match(res, "W19:") < 0)
+ au! VimEnter
+endfunc
+
+func Test_augroup_deleted()
+ " This caused a crash before E936 was introduced
+ augroup x
+ call assert_fails('augroup! x', 'E936:')
+ au VimEnter * echo
+ augroup end
+ augroup! x
+ call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0)
+ au! VimEnter
+endfunc
+
diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim
new file mode 100644
index 0000000000..6f61bede93
--- /dev/null
+++ b/src/nvim/testdir/test_execute_func.vim
@@ -0,0 +1,55 @@
+" test execute()
+
+func NestedEval()
+ let nested = execute('echo "nested\nlines"')
+ echo 'got: "' . nested . '"'
+endfunc
+
+func NestedRedir()
+ redir => var
+ echo 'broken'
+ redir END
+endfunc
+
+func Test_execute_string()
+ call assert_equal("\nnocompatible", execute('set compatible?'))
+ call assert_equal("\nsomething\nnice", execute('echo "something\nnice"'))
+ call assert_equal("noendofline", execute('echon "noendofline"'))
+ call assert_equal("", execute(123))
+
+ call assert_equal("\ngot: \"\nnested\nlines\"", execute('call NestedEval()'))
+ redir => redired
+ echo 'this'
+ let evaled = execute('echo "that"')
+ echo 'theend'
+ redir END
+" Nvim supports execute('... :redir ...'), so this test is intentionally
+" disabled.
+" call assert_equal("\nthis\ntheend", redired)
+ call assert_equal("\nthat", evaled)
+
+ call assert_fails('call execute("doesnotexist")', 'E492:')
+ call assert_fails('call execute(3.4)', 'E806:')
+" Nvim supports execute('... :redir ...'), so this test is intentionally
+" disabled.
+" call assert_fails('call execute("call NestedRedir()")', 'E930:')
+
+ call assert_equal("\nsomething", execute('echo "something"', ''))
+ call assert_equal("\nsomething", execute('echo "something"', 'silent'))
+ call assert_equal("\nsomething", execute('echo "something"', 'silent!'))
+ call assert_equal("", execute('burp', 'silent!'))
+ call assert_fails('call execute("echo \"x\"", 3.4)', 'E806:')
+
+ call assert_equal("", execute(""))
+endfunc
+
+func Test_execute_list()
+ call assert_equal("\nsomething\nnice", execute(['echo "something"', 'echo "nice"']))
+ let l = ['for n in range(0, 3)',
+ \ 'echo n',
+ \ 'endfor']
+ call assert_equal("\n0\n1\n2\n3", execute(l))
+
+ call assert_equal("", execute([]))
+ call assert_equal("", execute(v:_null_list))
+endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 571a37c62c..39dcacb55f 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -1,20 +1,5 @@
" Tests for expressions.
-func Test_version()
- call assert_true(has('patch-7.4.001'))
- call assert_true(has('patch-7.4.01'))
- call assert_true(has('patch-7.4.1'))
- call assert_true(has('patch-6.9.999'))
- call assert_true(has('patch-7.1.999'))
- call assert_true(has('patch-7.4.123'))
-
- call assert_false(has('patch-7'))
- call assert_false(has('patch-7.4'))
- call assert_false(has('patch-7.4.'))
- call assert_false(has('patch-9.1.0'))
- call assert_false(has('patch-9.9.1'))
-endfunc
-
func Test_equal()
let base = {}
func base.method()
@@ -37,6 +22,35 @@ func Test_equal()
call assert_fails('echo base.method > instance.method')
endfunc
+func Test_version()
+ call assert_true(has('patch-7.4.001'))
+ call assert_true(has('patch-7.4.01'))
+ call assert_true(has('patch-7.4.1'))
+ call assert_true(has('patch-6.9.999'))
+ call assert_true(has('patch-7.1.999'))
+ call assert_true(has('patch-7.4.123'))
+
+ call assert_false(has('patch-7'))
+ call assert_false(has('patch-7.4'))
+ call assert_false(has('patch-7.4.'))
+ call assert_false(has('patch-9.1.0'))
+ call assert_false(has('patch-9.9.1'))
+endfunc
+
+func Test_dict()
+ let d = {'': 'empty', 'a': 'a', 0: 'zero'}
+ call assert_equal('empty', d[''])
+ call assert_equal('a', d['a'])
+ call assert_equal('zero', d[0])
+ call assert_true(has_key(d, ''))
+ call assert_true(has_key(d, 'a'))
+
+ let d[''] = 'none'
+ let d['a'] = 'aaa'
+ call assert_equal('none', d[''])
+ call assert_equal('aaa', d['a'])
+endfunc
+
func Test_strgetchar()
call assert_equal(char2nr('a'), strgetchar('axb', 0))
call assert_equal(char2nr('x'), strgetchar('axb', 1))
@@ -61,23 +75,34 @@ func Test_strcharpart()
call assert_equal('a', strcharpart('axb', -1, 2))
endfunc
-func Test_dict()
- let d = {'': 'empty', 'a': 'a', 0: 'zero'}
- call assert_equal('empty', d[''])
- call assert_equal('a', d['a'])
- call assert_equal('zero', d[0])
- call assert_true(has_key(d, ''))
- call assert_true(has_key(d, 'a'))
-
- let d[''] = 'none'
- let d['a'] = 'aaa'
- call assert_equal('none', d[''])
- call assert_equal('aaa', d['a'])
-endfunc
-
func Test_loop_over_null_list()
let null_list = submatch(1, 1)
for i in null_list
call assert_true(0, 'should not get here')
endfor
endfunc
+
+func Test_compare_null_dict()
+ call assert_fails('let x = v:_null_dict[10]')
+ call assert_equal({}, {})
+ call assert_equal(v:_null_dict, v:_null_dict)
+ call assert_notequal({}, v:_null_dict)
+endfunc
+
+func Test_set_reg_null_list()
+ call setreg('x', v:_null_list)
+endfunc
+
+func Test_special_char()
+ " The failure is only visible using valgrind.
+ call assert_fails('echo "\<C-">')
+endfunc
+
+func Test_setmatches()
+ hi def link 1 Comment
+ hi def link 2 PreProc
+ let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4, "conceal": 5}]
+ let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4, "conceal": '5'}]
+ call setmatches(set)
+ call assert_equal(exp, getmatches())
+endfunc
diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim
new file mode 100644
index 0000000000..3eca99bd99
--- /dev/null
+++ b/src/nvim/testdir/test_gn.vim
@@ -0,0 +1,93 @@
+" Test for gn command
+
+func Test_gn_command()
+ noa new
+ " replace a single char by itsself quoted:
+ call setline('.', 'abc x def x ghi x jkl')
+ let @/='x'
+ exe "norm! cgn'x'\<esc>.."
+ call assert_equal("abc 'x' def 'x' ghi 'x' jkl", getline('.'))
+ sil! %d_
+ " simple search match
+ call setline('.', 'foobar')
+ let @/='foobar'
+ exe "norm! gncsearchmatch"
+ call assert_equal('searchmatch', getline('.'))
+ sil! %d _
+ " replace a multi-line match
+ call setline('.', ['', 'one', 'two'])
+ let @/='one\_s*two\_s'
+ exe "norm! gnceins\<CR>zwei"
+ call assert_equal(['','eins','zwei'], getline(1,'$'))
+ sil! %d _
+ " test count argument
+ call setline('.', ['', 'abcdx | abcdx | abcdx'])
+ let @/='[a]bcdx'
+ exe "norm! 2gnd"
+ call assert_equal(['','abcdx | | abcdx'], getline(1,'$'))
+ sil! %d _
+ " join lines
+ call setline('.', ['join ', 'lines'])
+ let @/='$'
+ exe "norm! 0gnd"
+ call assert_equal(['join lines'], getline(1,'$'))
+ sil! %d _
+ " zero-width match
+ call setline('.', ['', 'zero width pattern'])
+ let @/='\>\zs'
+ exe "norm! 0gnd"
+ call assert_equal(['', 'zerowidth pattern'], getline(1,'$'))
+ sil! %d _
+ " delete first and last chars
+ call setline('.', ['delete first and last chars'])
+ let @/='^'
+ exe "norm! 0gnd$"
+ let @/='\zs'
+ exe "norm! gnd"
+ call assert_equal(['elete first and last char'], getline(1,'$'))
+ sil! %d _
+ " using visual mode
+ call setline('.', ['', 'uniquepattern uniquepattern'])
+ exe "norm! /[u]niquepattern/s\<cr>vlgnd"
+ call assert_equal(['', ' uniquepattern'], getline(1,'$'))
+ sil! %d _
+ " backwards search
+ call setline('.', ['my very excellent mother just served us nachos'])
+ let @/='mother'
+ exe "norm! $cgNmongoose"
+ call assert_equal(['my very excellent mongoose just served us nachos'], getline(1,'$'))
+ sil! %d _
+ " search for single char
+ call setline('.', ['','for (i=0; i<=10; i++)'])
+ let @/='i'
+ exe "norm! cgnj"
+ call assert_equal(['','for (j=0; i<=10; i++)'], getline(1,'$'))
+ sil! %d _
+ " search hex char
+ call setline('.', ['','Y'])
+ set noignorecase
+ let @/='\%x59'
+ exe "norm! gnd"
+ call assert_equal(['',''], getline(1,'$'))
+ sil! %d _
+ " test repeating gdn
+ call setline('.', ['', '1', 'Johnny', '2', 'Johnny', '3'])
+ let @/='Johnny'
+ exe "norm! dgn."
+ call assert_equal(['','1', '', '2', '', '3'], getline(1,'$'))
+ sil! %d _
+ " test repeating gUgn
+ call setline('.', ['', '1', 'Depp', '2', 'Depp', '3'])
+ let @/='Depp'
+ exe "norm! gUgn."
+ call assert_equal(['', '1', 'DEPP', '2', 'DEPP', '3'], getline(1,'$'))
+ sil! %d _
+ " test using look-ahead assertions
+ call setline('.', ['a:10', '', 'a:1', '', 'a:20'])
+ let @/='a:0\@!\zs\d\+'
+ exe "norm! 2nygno\<esc>p"
+ call assert_equal(['a:10', '', 'a:1', '1', '', 'a:20'], getline(1,'$'))
+ sil! %d _
+endfu
+
+" vim: tabstop=2 shiftwidth=0 expandtab
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index 2afd96b296..b6ac5720c3 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -18,3 +18,18 @@ func Test_gee_dee()
call assert_equal(14, col('.'))
quit!
endfunc
+
+" Check that setting 'cursorline' does not change curswant
+func Test_cursorline_keep_col()
+ new
+ call setline(1, ['long long long line', 'short line'])
+ normal ggfi
+ let pos = getcurpos()
+ normal j
+ set cursorline
+ normal k
+ call assert_equal(pos, getcurpos())
+ bwipe!
+ set nocursorline
+endfunc
+
diff --git a/src/nvim/testdir/test_syn_attr.vim b/src/nvim/testdir/test_syn_attr.vim
index 94d6b8735f..64aab3411d 100644
--- a/src/nvim/testdir/test_syn_attr.vim
+++ b/src/nvim/testdir/test_syn_attr.vim
@@ -20,7 +20,7 @@ func Test_missing_attr()
if fontname == ''
let fontname = 'something'
endif
- exe 'hi Mine guifg=blue guibg=red font=' . escape(fontname, ' \')
+ exe "hi Mine guifg=blue guibg=red font='" . fontname . "'"
call assert_equal('blue', synIDattr(hlID("Mine"), "fg", 'gui'))
call assert_equal('red', synIDattr(hlID("Mine"), "bg", 'gui'))
call assert_equal(fontname, synIDattr(hlID("Mine"), "font", 'gui'))
diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim
index f593d16dbf..d0864ec64c 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -8,31 +8,57 @@ function Test_cmdmods()
MyCmd
aboveleft MyCmd
+ abo MyCmd
belowright MyCmd
+ bel MyCmd
botright MyCmd
+ bo MyCmd
browse MyCmd
+ bro MyCmd
confirm MyCmd
+ conf MyCmd
hide MyCmd
+ hid MyCmd
keepalt MyCmd
+ keepa MyCmd
keepjumps MyCmd
+ keepj MyCmd
keepmarks MyCmd
+ kee MyCmd
keeppatterns MyCmd
+ keepp MyCmd
+ leftabove MyCmd " results in :aboveleft
+ lefta MyCmd
lockmarks MyCmd
+ loc MyCmd
+ " noautocmd MyCmd
noswapfile MyCmd
+ nos MyCmd
+ rightbelow MyCmd " results in :belowright
+ rightb MyCmd
+ " sandbox MyCmd
silent MyCmd
+ sil MyCmd
tab MyCmd
topleft MyCmd
+ to MyCmd
+ " unsilent MyCmd
verbose MyCmd
+ verb MyCmd
vertical MyCmd
+ vert MyCmd
aboveleft belowright botright browse confirm hide keepalt keepjumps
\ keepmarks keeppatterns lockmarks noswapfile silent tab
\ topleft verbose vertical MyCmd
- call assert_equal(' aboveleft belowright botright browse confirm ' .
- \ 'hide keepalt keepjumps keepmarks keeppatterns lockmarks ' .
- \ 'noswapfile silent tab topleft verbose vertical aboveleft ' .
- \ 'belowright botright browse confirm hide keepalt keepjumps ' .
+ call assert_equal(' aboveleft aboveleft belowright belowright botright ' .
+ \ 'botright browse browse confirm confirm hide hide ' .
+ \ 'keepalt keepalt keepjumps keepjumps keepmarks keepmarks ' .
+ \ 'keeppatterns keeppatterns aboveleft aboveleft lockmarks lockmarks noswapfile ' .
+ \ 'noswapfile belowright belowright silent silent tab topleft topleft verbose verbose ' .
+ \ 'vertical vertical ' .
+ \ 'aboveleft belowright botright browse confirm hide keepalt keepjumps ' .
\ 'keepmarks keeppatterns lockmarks noswapfile silent tab topleft ' .
\ 'verbose vertical ', g:mods)
@@ -46,3 +72,33 @@ function Test_cmdmods()
delcommand MyQCmd
unlet g:mods
endfunction
+
+func Test_Ambiguous()
+ command Doit let g:didit = 'yes'
+ command Dothat let g:didthat = 'also'
+ call assert_fails('Do', 'E464:')
+ Doit
+ call assert_equal('yes', g:didit)
+ Dothat
+ call assert_equal('also', g:didthat)
+ unlet g:didit
+ unlet g:didthat
+
+ delcommand Doit
+ Do
+ call assert_equal('also', g:didthat)
+ delcommand Dothat
+endfunc
+
+func Test_CmdUndefined()
+ call assert_fails('Doit', 'E492:')
+ au CmdUndefined Doit :command Doit let g:didit = 'yes'
+ Doit
+ call assert_equal('yes', g:didit)
+ delcommand Doit
+
+ call assert_fails('Dothat', 'E492:')
+ au CmdUndefined * let g:didnot = 'yes'
+ call assert_fails('Dothat', 'E492:')
+ call assert_equal('yes', g:didnot)
+endfunc
diff --git a/src/nvim/types.h b/src/nvim/types.h
index 35a5d1e2bd..317bead3bb 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -14,4 +14,5 @@ typedef unsigned char char_u;
typedef uint32_t u8char_T;
typedef struct expand expand_T;
+
#endif // NVIM_TYPES_H
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 05267028ee..6f519383ec 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -126,8 +126,8 @@ static int included_patches[] = {
// 2317,
// 2316 NA
// 2315,
- // 2314,
- // 2313,
+ 2314,
+ 2313,
2312,
// 2311 NA
// 2310 NA
@@ -140,7 +140,7 @@ static int included_patches[] = {
// 2303,
// 2302 NA
// 2301 NA
- // 2300,
+ 2300,
// 2299,
// 2298 NA
// 2297 NA
@@ -280,7 +280,7 @@ static int included_patches[] = {
2163,
2162,
// 2161,
- // 2160,
+ 2160,
// 2159,
2158,
// 2157 NA
@@ -313,7 +313,7 @@ static int included_patches[] = {
// 2130 NA
// 2129 NA
2128,
- // 2127,
+ 2127,
2126,
// 2125 NA
2124,
@@ -323,25 +323,25 @@ static int included_patches[] = {
// 2120,
// 2119,
// 2118 NA
- // 2117,
+ 2117,
// 2116 NA
// 2115 NA
// 2114 NA
// 2113,
2112,
// 2111,
- // 2110,
+ 2110,
2109,
// 2108 NA
// 2107,
// 2106,
// 2105 NA
// 2104,
- // 2103,
+ 2103,
// 2102 NA
// 2101,
- // 2100,
- // 2099,
+ 2100,
+ 2099,
// 2098,
// 2097,
// 2096,
@@ -363,9 +363,9 @@ static int included_patches[] = {
// 2080,
// 2079 NA
// 2078 NA
- // 2077,
+ 2077,
// 2076,
- // 2075,
+ 2075,
// 2074,
// 2073 NA
// 2072,
@@ -375,10 +375,10 @@ static int included_patches[] = {
// 2068,
// 2067,
2066,
- // 2065,
+ 2065,
// 2064,
// 2063 NA
- // 2062,
+ 2062,
// 2061,
// 2060 NA
// 2059 NA
@@ -397,7 +397,7 @@ static int included_patches[] = {
// 2046,
// 2045 NA
// 2044,
- // 2043,
+ 2043,
// 2042 NA
// 2041 NA
// 2040 NA
@@ -409,7 +409,7 @@ static int included_patches[] = {
// 2034 NA
2033,
// 2032 NA
- // 2031,
+ 2031,
// 2030 NA
// 2029,
2028,
@@ -425,16 +425,16 @@ static int included_patches[] = {
// 2018,
// 2017,
// 2016 NA
- // 2015,
+ 2015,
2014,
2013,
2012,
2011,
2010,
- // 2009,
- // 2008,
+ 2009,
+ 2008,
2007,
- // 2006,
+ 2006,
2005,
// 2004 NA
// 2003 NA
@@ -472,7 +472,7 @@ static int included_patches[] = {
1971,
1970,
// 1969 NA
- // 1968,
+ 1968,
1967,
1966,
// 1965 NA
@@ -515,7 +515,7 @@ static int included_patches[] = {
1928,
// 1927 NA
// 1926 NA
- // 1925 NA
+ 1925,
// 1924 NA
1923,
// 1922 NA
@@ -530,7 +530,7 @@ static int included_patches[] = {
1913,
1912,
// 1911 NA
- // 1910,
+ 1910,
1909,
// 1908 NA
// 1907 NA
@@ -551,7 +551,7 @@ static int included_patches[] = {
1892,
// 1891 NA
// 1890 NA
- // 1889,
+ 1889,
// 1888 NA
// 1887 NA
// 1886 NA
@@ -589,11 +589,11 @@ static int included_patches[] = {
// 1854 NA
// 1853 NA
// 1852 NA
- // 1851,
+ 1851,
// 1850 NA
// 1849 NA
// 1848 NA
- // 1847,
+ 1847,
// 1846 NA
// 1845 NA
// 1844 NA
@@ -602,7 +602,7 @@ static int included_patches[] = {
1841,
1840,
1839,
- // 1838 NA
+ 1838,
1837,
1836,
1835,
@@ -689,7 +689,6 @@ static int included_patches[] = {
1755,
1754,
1753,
- // 1753,
1752,
1751,
// 1750 NA
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 00229ccca9..89228e1b0f 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -973,11 +973,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
/*
* make the new window the current window
*/
- win_enter(wp, false);
- if (flags & WSP_VERT)
+ win_enter_ext(wp, false, false, true, true, true);
+ if (flags & WSP_VERT) {
p_wiw = i;
- else
+ } else {
p_wh = i;
+ }
return OK;
}
@@ -1718,6 +1719,7 @@ close_windows (
{
tabpage_T *tp, *nexttp;
int h = tabline_height();
+ int count = tabpage_index(NULL);
++RedrawingDisabled;
@@ -1754,9 +1756,14 @@ close_windows (
--RedrawingDisabled;
- redraw_tabline = TRUE;
- if (h != tabline_height())
+ if (count != tabpage_index(NULL)) {
+ apply_autocmds(EVENT_TABCLOSED, NULL, NULL, false, curbuf);
+ }
+
+ redraw_tabline = true;
+ if (h != tabline_height()) {
shell_new_rows();
+ }
}
/// Check that current window is the last one.
@@ -2014,10 +2021,11 @@ int win_close(win_T *win, int free_buf)
}
if (close_curwin) {
- win_enter_ext(wp, false, TRUE, TRUE, TRUE);
- if (other_buffer)
- /* careful: after this wp and win may be invalid! */
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
+ win_enter_ext(wp, false, true, false, true, true);
+ if (other_buffer) {
+ // careful: after this wp and win may be invalid!
+ apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
+ }
}
/*
@@ -3045,8 +3053,9 @@ int win_new_tabpage(int after, char_u *filename)
redraw_all_later(CLEAR);
- apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
+ apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
return OK;
@@ -3204,8 +3213,8 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
/* We would like doing the TabEnter event first, but we don't have a
* valid current window yet, which may break some commands.
* This triggers autocommands, thus may make "tp" invalid. */
- win_enter_ext(tp->tp_curwin, false, TRUE,
- trigger_enter_autocmds, trigger_leave_autocmds);
+ win_enter_ext(tp->tp_curwin, false, true, false,
+ trigger_enter_autocmds, trigger_leave_autocmds);
prevwin = next_prevwin;
last_status(FALSE); /* status line may appear or disappear */
@@ -3546,7 +3555,7 @@ end:
*/
void win_enter(win_T *wp, bool undo_sync)
{
- win_enter_ext(wp, undo_sync, FALSE, TRUE, TRUE);
+ win_enter_ext(wp, undo_sync, false, false, true, true);
}
/*
@@ -3554,7 +3563,9 @@ void win_enter(win_T *wp, bool undo_sync)
* Can be called with "curwin_invalid" TRUE, which means that curwin has just
* been closed and isn't valid.
*/
-static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int trigger_enter_autocmds, int trigger_leave_autocmds)
+static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid,
+ int trigger_new_autocmds, int trigger_enter_autocmds,
+ int trigger_leave_autocmds)
{
int other_buffer = FALSE;
@@ -3630,6 +3641,9 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri
shorten_fnames(TRUE);
}
+ if (trigger_new_autocmds) {
+ apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
+ }
if (trigger_enter_autocmds) {
apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
if (other_buffer)
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 3245e1b52d..aa91cd1396 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -8,8 +8,6 @@ local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.fu
local source, next_message = helpers.source, helpers.next_message
local meths = helpers.meths
-if helpers.pending_win32(pending) then return end
-
describe('server -> client', function()
local cid
@@ -212,6 +210,8 @@ describe('server -> client', function()
funcs.jobstop(jobid)
end)
+ if helpers.pending_win32(pending) then return end
+
it('rpc and text stderr can be combined', function()
eq("ok",funcs.rpcrequest(jobid, "poll"))
funcs.rpcnotify(jobid, "ping")
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index 162e112047..c38bd75c69 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -12,8 +12,8 @@ describe('autocmds:', function()
local expected = {
'WinLeave',
'TabLeave',
- 'TabNew',
'WinEnter',
+ 'TabNew',
'TabEnter',
'BufLeave',
'BufEnter'
diff --git a/test/functional/autocmd/bufenter_spec.lua b/test/functional/autocmd/bufenter_spec.lua
new file mode 100644
index 0000000000..ccbcdf5c5e
--- /dev/null
+++ b/test/functional/autocmd/bufenter_spec.lua
@@ -0,0 +1,35 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local eval = helpers.eval
+local execute = helpers.execute
+local request = helpers.request
+local source = helpers.source
+
+describe('autocmd BufEnter', function()
+ before_each(clear)
+
+ it("triggered by nvim_command('edit <dir>')", function()
+ command("autocmd BufEnter * if isdirectory(expand('<afile>')) | let g:dir_bufenter = 1 | endif")
+ request("nvim_command", "split .")
+ eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory.
+ eq(2, eval("bufnr('%')")) -- Switched to the dir buffer.
+ end)
+
+ it('triggered by "try|:split <dir>|endtry" in a function', function()
+ command("autocmd BufEnter * if isdirectory(expand('<afile>')) | let g:dir_bufenter = 1 | endif")
+ source([[
+ function! Test()
+ try
+ exe 'split .'
+ catch
+ endtry
+ endfunction
+ ]])
+ execute("call Test()")
+ eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory.
+ eq(2, eval("bufnr('%')")) -- Switched to the dir buffer.
+ end)
+end)
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
new file mode 100644
index 0000000000..15196dbd44
--- /dev/null
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -0,0 +1,113 @@
+local lfs = require('lfs')
+local h = require('test.functional.helpers')(after_each)
+
+local clear = h.clear
+local command = h.command
+local eq = h.eq
+local eval = h.eval
+local request = h.request
+
+describe('autocmd DirChanged', function()
+ local curdir = string.gsub(lfs.currentdir(), '\\', '/')
+ local dirs = {
+ curdir .. '/Xtest-functional-autocmd-dirchanged.dir1',
+ curdir .. '/Xtest-functional-autocmd-dirchanged.dir2',
+ curdir .. '/Xtest-functional-autocmd-dirchanged.dir3',
+ }
+
+ setup(function() for _, dir in pairs(dirs) do h.mkdir(dir) end end)
+ teardown(function() for _, dir in pairs(dirs) do h.rmdir(dir) end end)
+
+ before_each(function()
+ clear()
+ command('autocmd DirChanged * let [g:event, g:scope, g:cdcount] = [copy(v:event), expand("<amatch>"), 1 + get(g:, "cdcount", 0)]')
+ end)
+
+ it('sets v:event', function()
+ command('lcd '..dirs[1])
+ eq({cwd=dirs[1], scope='window'}, eval('g:event'))
+ eq(1, eval('g:cdcount'))
+
+ command('tcd '..dirs[2])
+ eq({cwd=dirs[2], scope='tab'}, eval('g:event'))
+ eq(2, eval('g:cdcount'))
+
+ command('cd '..dirs[3])
+ eq({cwd=dirs[3], scope='global'}, eval('g:event'))
+ eq(3, eval('g:cdcount'))
+ end)
+
+ it('disallows recursion', function()
+ command('set shellslash')
+ -- Set up a _nested_ handler.
+ command('autocmd DirChanged * nested lcd '..dirs[3])
+ command('lcd '..dirs[1])
+ eq({cwd=dirs[1], scope='window'}, eval('g:event'))
+ eq(1, eval('g:cdcount'))
+ -- autocmd changed to dirs[3], but did NOT trigger another DirChanged.
+ eq(dirs[3], eval('getcwd()'))
+ end)
+
+ it('sets <amatch> to CWD "scope"', function()
+ command('lcd '..dirs[1])
+ eq('window', eval('g:scope'))
+
+ command('tcd '..dirs[2])
+ eq('tab', eval('g:scope'))
+
+ command('cd '..dirs[3])
+ eq('global', eval('g:scope'))
+ end)
+
+ it('does not trigger if :cd fails', function()
+ command('let g:event = {}')
+
+ local status1, err1 = pcall(function()
+ command('lcd '..dirs[1] .. '/doesnotexist')
+ end)
+ eq({}, eval('g:event'))
+
+ local status2, err2 = pcall(function()
+ command('lcd '..dirs[2] .. '/doesnotexist')
+ end)
+ eq({}, eval('g:event'))
+
+ local status3, err3 = pcall(function()
+ command('lcd '..dirs[3] .. '/doesnotexist')
+ end)
+ eq({}, eval('g:event'))
+
+ eq(false, status1)
+ eq(false, status2)
+ eq(false, status3)
+
+ eq('E344:', string.match(err1, "E%d*:"))
+ eq('E344:', string.match(err2, "E%d*:"))
+ eq('E344:', string.match(err3, "E%d*:"))
+ end)
+
+ it("is triggered by 'autochdir'", function()
+ command('set autochdir')
+
+ command('split '..dirs[1]..'/foo')
+ eq({cwd=dirs[1], scope='window'}, eval('g:event'))
+
+ command('split '..dirs[2]..'/bar')
+ eq({cwd=dirs[2], scope='window'}, eval('g:event'))
+ end)
+
+ it('is triggered by nvim_set_current_dir()', function()
+ request('nvim_set_current_dir', dirs[1])
+ eq({cwd=dirs[1], scope='global'}, eval('g:event'))
+
+ request('nvim_set_current_dir', dirs[2])
+ eq({cwd=dirs[2], scope='global'}, eval('g:event'))
+
+ local status, err = pcall(function()
+ request('nvim_set_current_dir', '/doesnotexist')
+ end)
+ eq(false, status)
+ eq('Failed to change directory', string.match(err, ': (.*)'))
+ eq({cwd=dirs[2], scope='global'}, eval('g:event'))
+ end)
+end)
diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua
index 2148b21832..ad40954f76 100644
--- a/test/functional/autocmd/tabnew_spec.lua
+++ b/test/functional/autocmd/tabnew_spec.lua
@@ -5,8 +5,6 @@ local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
-if helpers.pending_win32(pending) then return end
-
describe('autocmd TabNew', function()
before_each(clear)
@@ -19,12 +17,11 @@ describe('autocmd TabNew', function()
end)
it('matches when opening a new tab for FILE', function()
- local tmp_path = helpers.funcs.tempname()
command('let g:test = "foo"')
- command('autocmd! TabNew ' .. tmp_path .. ' let g:test = "bar"')
- command('tabnew ' .. tmp_path ..'X')
+ command('autocmd! TabNew Xtest-tabnew let g:test = "bar"')
+ command('tabnew Xtest-tabnewX')
eq('foo', eval('g:test'))
- command('tabnew ' .. tmp_path)
+ command('tabnew Xtest-tabnew')
eq('bar', eval('g:test'))
end)
end)
diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua
index f033bd5fe4..bdbe677132 100644
--- a/test/functional/autocmd/tabnewentered_spec.lua
+++ b/test/functional/autocmd/tabnewentered_spec.lua
@@ -1,8 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq
-if helpers.pending_win32(pending) then return end
-
describe('TabNewEntered', function()
describe('au TabNewEntered', function()
describe('with * as <afile>', function()
@@ -15,9 +13,9 @@ describe('TabNewEntered', function()
end)
describe('with FILE as <afile>', function()
it('matches when opening a new tab for FILE', function()
- local tmp_path = nvim('eval', 'tempname()')
- nvim('command', 'au! TabNewEntered '..tmp_path..' echom "tabnewentered:match"')
- eq("\n\""..tmp_path.."\" [New File]\ntabnewentered:4:4\ntabnewentered:match", nvim('command_output', 'tabnew '..tmp_path))
+ nvim('command', 'au! TabNewEntered Xtest-tabnewentered echom "tabnewentered:match"')
+ eq('\n"Xtest-tabnewentered" [New File]\ntabnewentered:4:4\ntabnewentered:match',
+ nvim('command_output', 'tabnew Xtest-tabnewentered'))
end)
end)
describe('with CTRL-W T', function()
diff --git a/test/functional/core/job_partial_spec.lua b/test/functional/core/job_partial_spec.lua
index b60f239db9..7643b283c4 100644
--- a/test/functional/core/job_partial_spec.lua
+++ b/test/functional/core/job_partial_spec.lua
@@ -2,13 +2,14 @@ local helpers = require('test.functional.helpers')(after_each)
local clear, eq, next_msg, nvim, source = helpers.clear, helpers.eq,
helpers.next_message, helpers.nvim, helpers.source
-if helpers.pending_win32(pending) then return end
-
describe('jobs with partials', function()
local channel
before_each(function()
clear()
+ if helpers.os_name() == 'windows' then
+ helpers.set_shell_powershell()
+ end
channel = nvim('get_api_info')[1]
nvim('set_var', 'channel', channel)
end)
@@ -16,12 +17,14 @@ describe('jobs with partials', function()
it('works correctly', function()
source([[
function PrintArgs(a1, a2, id, data, event)
- call rpcnotify(g:channel, '1', a:a1, a:a2, a:data, a:event)
+ " Windows: Remove ^M char.
+ let normalized = map(a:data, 'substitute(v:val, "\r", "", "g")')
+ call rpcnotify(g:channel, '1', a:a1, a:a2, normalized, a:event)
endfunction
let Callback = function('PrintArgs', ["foo", "bar"])
let g:job_opts = {'on_stdout': Callback}
- call jobstart(['echo'], g:job_opts)
+ call jobstart('echo "some text"', g:job_opts)
]])
- eq({'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, next_msg())
+ eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg())
end)
end)
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index 75b50aad0a..48a4689545 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -312,6 +312,24 @@ describe('jobs', function()
end)
end)
+ it('does not repeat output with slow output handlers', function()
+ source([[
+ let d = {'data': []}
+ function! d.on_stdout(job, data, event) dict
+ call add(self.data, a:data)
+ sleep 200m
+ endfunction
+ if has('win32')
+ let cmd = '1,2,3,4,5 | foreach-object -process {echo $_; sleep 0.1}'
+ else
+ let cmd = ['sh', '-c', 'for i in $(seq 1 5); do echo $i; sleep 0.1; done']
+ endif
+ call jobwait([jobstart(cmd, d)])
+ call rpcnotify(g:channel, 'data', d.data)
+ ]])
+ eq({'notification', 'data', {{{'1', ''}, {'2', ''}, {'3', ''}, {'4', ''}, {'5', ''}}}}, next_msg())
+ end)
+
describe('jobwait', function()
it('returns a list of status codes', function()
source([[
diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua
index fc13c0a72b..cc9b61b842 100644
--- a/test/functional/eval/execute_spec.lua
+++ b/test/functional/eval/execute_spec.lua
@@ -7,7 +7,7 @@ local redir_exec = helpers.redir_exec
local exc_exec = helpers.exc_exec
local funcs = helpers.funcs
local Screen = require('test.functional.ui.screen')
-local feed = helpers.feed
+local command = helpers.command
describe('execute()', function()
before_each(clear)
@@ -62,11 +62,11 @@ describe('execute()', function()
ret = exc_exec('call execute(function("tr"))')
eq('Vim(call):E729: using Funcref as a String', ret)
ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])')
- eq('Vim(call):E806: using Float as a String', ret)
+ eq('Vim:E806: using Float as a String', ret)
ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])')
- eq('Vim(call):E731: using Dictionary as a String', ret)
+ eq('Vim:E731: using Dictionary as a String', ret)
ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])')
- eq('Vim(call):E729: using Funcref as a String', ret)
+ eq('Vim:E729: using Funcref as a String', ret)
end)
-- This matches Vim behavior.
@@ -74,18 +74,75 @@ describe('execute()', function()
eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"'))
end)
- it('silences command run inside', function()
- local screen = Screen.new(40, 5)
- screen:attach()
- screen:set_default_attr_ids( {[0] = {bold=true, foreground=255}} )
- feed(':let g:mes = execute("echon 42")<CR>')
- screen:expect([[
- ^ |
- {0:~ }|
- {0:~ }|
- {0:~ }|
- :let g:mes = execute("echon 42") |
- ]])
- eq('42', eval('g:mes'))
+ describe('{silent} argument', function()
+ it('captures & displays output for ""', function()
+ local screen = Screen.new(40, 5)
+ screen:attach()
+ command('let g:mes = execute("echon 42", "")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ 42 |
+ ]])
+ eq('42', eval('g:mes'))
+ end)
+
+ it('captures but does not display output for "silent"', function()
+ local screen = Screen.new(40, 5)
+ screen:attach()
+ command('let g:mes = execute("echon 42")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ eq('42', eval('g:mes'))
+
+ command('let g:mes = execute("echon 13", "silent")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ eq('13', eval('g:mes'))
+ end)
+
+ it('suppresses errors for "silent!"', function()
+ eq(0, exc_exec('let g:mes = execute(0.0, "silent!")'))
+ eq('', eval('g:mes'))
+
+ eq(0, exc_exec('let g:mes = execute("echon add(1, 1)", "silent!")'))
+ eq('1', eval('g:mes'))
+
+ eq(0, exc_exec('let g:mes = execute(["echon 42", "echon add(1, 1)"], "silent!")'))
+ eq('421', eval('g:mes'))
+ end)
+
+ it('propagates errors for "" and "silent"', function()
+ local ret
+ ret = exc_exec('call execute(0.0, "")')
+ eq('Vim(call):E806: using Float as a String', ret)
+
+ ret = exc_exec('call execute(v:_null_dict, "silent")')
+ eq('Vim(call):E731: using Dictionary as a String', ret)
+
+ ret = exc_exec('call execute("echo add(1, 1)", "")')
+ eq('Vim(echo):E714: List required', ret)
+
+ ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")')
+ eq('Vim(echo):E714: List required', ret)
+
+ ret = exc_exec('call execute("echo add(1, 1)", "silent")')
+ eq('Vim(echo):E714: List required', ret)
+
+ ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")')
+ eq('Vim(echo):E714: List required', ret)
+ end)
end)
end)
diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua
index 4a6758019b..fc0a19bdfa 100644
--- a/test/functional/eval/json_functions_spec.lua
+++ b/test/functional/eval/json_functions_spec.lua
@@ -672,6 +672,12 @@ describe('json_encode() function', function()
exc_exec('call json_encode(function("tr"))'))
end)
+ it('fails to dump a partial', function()
+ execute('function T() dict\nendfunction')
+ eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
+ exc_exec('call json_encode(function("T", [1, 2], {}))'))
+ end)
+
it('fails to dump a function reference in a list', function()
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
exc_exec('call json_encode([function("tr")])'))
diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua
index 3fb54c2ee7..44c01d2226 100644
--- a/test/functional/eval/msgpack_functions_spec.lua
+++ b/test/functional/eval/msgpack_functions_spec.lua
@@ -493,6 +493,12 @@ describe('msgpackparse() function', function()
exc_exec('call msgpackparse(function("tr"))'))
end)
+ it('fails to parse a partial', function()
+ execute('function T() dict\nendfunction')
+ eq('Vim(call):E686: Argument of msgpackparse() must be a List',
+ exc_exec('call msgpackparse(function("T", [1, 2], {}))'))
+ end)
+
it('fails to parse a float', function()
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
exc_exec('call msgpackparse(0.0)'))
@@ -570,6 +576,13 @@ describe('msgpackdump() function', function()
exc_exec('call msgpackdump([Todump])'))
end)
+ it('fails to dump a partial', function()
+ execute('function T() dict\nendfunction')
+ execute('let Todump = function("T", [1, 2], {})')
+ eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
+ exc_exec('call msgpackdump([Todump])'))
+ end)
+
it('fails to dump a function reference in a list', function()
execute('let todump = [function("tr")]')
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
@@ -675,6 +688,12 @@ describe('msgpackdump() function', function()
exc_exec('call msgpackdump(function("tr"))'))
end)
+ it('fails to dump a partial', function()
+ execute('function T() dict\nendfunction')
+ eq('Vim(call):E686: Argument of msgpackdump() must be a List',
+ exc_exec('call msgpackdump(function("T", [1, 2], {}))'))
+ end)
+
it('fails to dump a float', function()
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(0.0)'))
diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua
index d2c985e894..420aea04aa 100644
--- a/test/functional/eval/server_spec.lua
+++ b/test/functional/eval/server_spec.lua
@@ -4,8 +4,6 @@ local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval
local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
local os_name = helpers.os_name
-if helpers.pending_win32(pending) then return end
-
describe('serverstart(), serverstop()', function()
before_each(clear)
@@ -42,8 +40,8 @@ describe('serverstart(), serverstop()', function()
-- v:servername will take the next available server.
local servername = (os_name() == 'windows'
- and [[\\.\pipe\Xtest-functional-server-server-pipe]]
- or 'Xtest-functional-server-server-socket')
+ and [[\\.\pipe\Xtest-functional-server-pipe]]
+ or 'Xtest-functional-server-socket')
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
end)
@@ -63,9 +61,11 @@ describe('serverlist()', function()
local n = eval('len(serverlist())')
-- Add a few
- local servs = {'should-not-exist', 'another-one-that-shouldnt'}
+ local servs = (os_name() == 'windows'
+ and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
+ or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] })
for _, s in ipairs(servs) do
- eq(s, eval('serverstart("'..s..'")'))
+ eq(s, eval("serverstart('"..s.."')"))
end
local new_servs = eval('serverlist()')
@@ -75,10 +75,9 @@ describe('serverlist()', function()
-- The new servers should be at the end of the list.
for i = 1, #servs do
eq(servs[i], new_servs[i + n])
- nvim('command', 'call serverstop("'..servs[i]..'")')
+ nvim('command', "call serverstop('"..servs[i].."')")
end
- -- After calling serverstop() on the new servers, they should no longer be
- -- in the list.
+ -- After serverstop() the servers should NOT be in the list.
eq(n, eval('len(serverlist())'))
end)
end)
diff --git a/test/functional/eval/setpos_spec.lua b/test/functional/eval/setpos_spec.lua
new file mode 100644
index 0000000000..2e27cd8ac0
--- /dev/null
+++ b/test/functional/eval/setpos_spec.lua
@@ -0,0 +1,64 @@
+local helpers = require('test.functional.helpers')(after_each)
+local setpos = helpers.funcs.setpos
+local getpos = helpers.funcs.getpos
+local insert = helpers.insert
+local clear = helpers.clear
+local execute = helpers.execute
+local eval = helpers.eval
+local eq = helpers.eq
+local exc_exec = helpers.exc_exec
+
+
+describe('setpos() function', function()
+ before_each(function()
+ clear()
+ insert([[
+ First line of text
+ Second line of text
+ Third line of text]])
+ execute('new')
+ insert([[
+ Line of text 1
+ Line of text 2
+ Line of text 3]])
+ end)
+ it('can set the current cursor position', function()
+ setpos(".", {0, 2, 1, 0})
+ eq(getpos("."), {0, 2, 1, 0})
+ setpos(".", {2, 1, 1, 0})
+ eq(getpos("."), {0, 1, 1, 0})
+ -- Ensure get an error attempting to set position to another buffer
+ local ret = exc_exec('call setpos(".", [1, 1, 1, 0])')
+ eq('Vim(call):E474: Invalid argument', ret)
+ end)
+ it('can set lowercase marks in the current buffer', function()
+ setpos("'d", {0, 2, 1, 0})
+ eq(getpos("'d"), {0, 2, 1, 0})
+ execute('undo', 'call setpos("\'d", [2, 3, 1, 0])')
+ eq(getpos("'d"), {0, 3, 1, 0})
+ end)
+ it('can set lowercase marks in other buffers', function()
+ local retval = setpos("'d", {1, 2, 1, 0})
+ eq(0, retval)
+ setpos("'d", {1, 2, 1, 0})
+ eq(getpos("'d"), {0, 0, 0, 0})
+ execute('wincmd w')
+ eq(eval('bufnr("%")'), 1)
+ eq(getpos("'d"), {0, 2, 1, 0})
+ end)
+ it("fails when setting a mark in a buffer that doesn't exist", function()
+ local retval = setpos("'d", {3, 2, 1, 0})
+ eq(-1, retval)
+ eq(getpos("'d"), {0, 0, 0, 0})
+ retval = setpos("'D", {3, 2, 1, 0})
+ eq(-1, retval)
+ eq(getpos("'D"), {0, 0, 0, 0})
+ end)
+ it('can set uppercase marks', function()
+ setpos("'D", {2, 2, 3, 0})
+ eq(getpos("'D"), {2, 2, 3, 0})
+ -- Can set a mark in another buffer
+ setpos("'D", {1, 2, 2, 0})
+ eq(getpos("'D"), {1, 2, 2, 0})
+ end)
+end)
diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua
index 9e2dc4e111..f6279e85e8 100644
--- a/test/functional/eval/string_spec.lua
+++ b/test/functional/eval/string_spec.lua
@@ -9,6 +9,8 @@ local redir_exec = helpers.redir_exec
local funcs = helpers.funcs
local write_file = helpers.write_file
local NIL = helpers.NIL
+local source = helpers.source
+local dedent = helpers.dedent
describe('string() function', function()
before_each(clear)
@@ -110,10 +112,10 @@ describe('string() function', function()
function Test1()
endfunction
- function s:Test2()
+ function s:Test2() dict
endfunction
- function g:Test3()
+ function g:Test3() dict
endfunction
let g:Test2_f = function('s:Test2')
@@ -137,6 +139,85 @@ describe('string() function', function()
it('dumps references to script functions', function()
eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)'))
end)
+
+ it('dumps partials with self referencing a partial', function()
+ source([[
+ function TestDict() dict
+ endfunction
+ let d = {}
+ let TestDictRef = function('TestDict', d)
+ let d.tdr = TestDictRef
+ ]])
+ eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})",
+ redir_exec('echo string(d.tdr)'))
+ end)
+
+ it('dumps automatically created partials', function()
+ eq('function(\'<SNR>1_Test2\', {\'f\': function(\'<SNR>1_Test2\')})',
+ eval('string({"f": Test2_f}.f)'))
+ eq('function(\'<SNR>1_Test2\', [1], {\'f\': function(\'<SNR>1_Test2\', [1])})',
+ eval('string({"f": function(Test2_f, [1])}.f)'))
+ end)
+
+ it('dumps manually created partials', function()
+ eq('function(\'Test3\', [1, 2], {})',
+ eval('string(function("Test3", [1, 2], {}))'))
+ eq('function(\'Test3\', {})',
+ eval('string(function("Test3", {}))'))
+ eq('function(\'Test3\', [1, 2])',
+ eval('string(function("Test3", [1, 2]))'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in self',
+ function()
+ meths.set_var('d', {v=true})
+ eq(dedent([[
+
+ E724: unable to correctly dump variable with self-referencing container
+ {'p': function('<SNR>1_Test2', {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]),
+ redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
+ end)
+
+ it('does not show errors when dumping partials referencing the same dictionary',
+ function()
+ command('let d = {}')
+ -- Regression for “eval/typval_encode: Dump empty dictionary before
+ -- checking for refcycle”, results in error.
+ eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('string([function("tr", d), function("tr", d)])'))
+ -- Regression for “eval: Work with reference cycles in partials (self)
+ -- properly”, results in crash.
+ eval('extend(d, {"a": 1})')
+ eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('string([function("tr", d), function("tr", d)])'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in arguments',
+ function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ -- Regression: the below line used to crash (add returns original list and
+ -- there was error in dumping partials). Tested explicitly in
+ -- test/unit/api/private_helpers_spec.lua.
+ eval('add(l, function("Test1", l))')
+ eq(dedent([=[
+
+ E724: unable to correctly dump variable with self-referencing container
+ function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]),
+ redir_exec('echo string(function("Test1", l))'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in self and arguments',
+ function()
+ meths.set_var('d', {v=true})
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eval('add(l, function("Test1", l))')
+ eval('add(l, function("Test1", d))')
+ eq(dedent([=[
+
+ E724: unable to correctly dump variable with self-referencing container
+ {'p': function('<SNR>1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]),
+ redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
+ end)
end)
describe('used to represent lists', function()
@@ -174,6 +255,13 @@ describe('string() function', function()
eq('{}', eval('string({})'))
end)
+ it('dumps list with two same empty dictionaries, also in partials', function()
+ command('let d = {}')
+ eq('[{}, {}]', eval('string([d, d])'))
+ eq('[function(\'tr\', {}), {}]', eval('string([function("tr", d), d])'))
+ eq('[{}, function(\'tr\', {})]', eval('string([d, function("tr", d)])'))
+ end)
+
it('dumps non-empty dictionary', function()
eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1}))
end)
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
index 6393477260..d5845132dd 100644
--- a/test/functional/eval/system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -1,12 +1,10 @@
local helpers = require('test.functional.helpers')(after_each)
-local eq, clear, eval, execute, feed, nvim =
- helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.feed,
- helpers.nvim
+local eq, call, clear, eval, execute, feed, nvim =
+ helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.execute,
+ helpers.feed, helpers.nvim
local Screen = require('test.functional.ui.screen')
-if helpers.pending_win32(pending) then return end
-
local function create_file_with_nuls(name)
return function()
feed('ipart1<C-V>000part2<C-V>000part3<ESC>:w '..name..'<CR>')
@@ -31,7 +29,70 @@ end
describe('system()', function()
before_each(clear)
- it('sets the v:shell_error variable', function()
+ describe('command passed as a List', function()
+ local printargs_path = helpers.nvim_dir..'/printargs-test'
+ .. (helpers.os_name() == 'windows' and '.exe' or '')
+
+ it('sets v:shell_error if cmd[0] is not executable', function()
+ call('system', { 'this-should-not-exist' })
+ eq(-1, eval('v:shell_error'))
+ end)
+
+ it('parameter validation does NOT modify v:shell_error', function()
+ -- 1. Call system() with invalid parameters.
+ -- 2. Assert that v:shell_error was NOT set.
+ execute('call system({})')
+ eq('E475: Invalid argument: expected String or List', eval('v:errmsg'))
+ eq(0, eval('v:shell_error'))
+ execute('call system([])')
+ eq('E474: Invalid argument', eval('v:errmsg'))
+ eq(0, eval('v:shell_error'))
+
+ -- Provoke a non-zero v:shell_error.
+ call('system', { 'this-should-not-exist' })
+ local old_val = eval('v:shell_error')
+ eq(-1, old_val)
+
+ -- 1. Call system() with invalid parameters.
+ -- 2. Assert that v:shell_error was NOT modified.
+ execute('call system({})')
+ eq(old_val, eval('v:shell_error'))
+ execute('call system([])')
+ eq(old_val, eval('v:shell_error'))
+ end)
+
+ it('quotes arguments correctly #5280', function()
+ local out = call('system',
+ { printargs_path, [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] })
+
+ eq(0, eval('v:shell_error'))
+ eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out)
+
+ out = call('system', { printargs_path, [['1]], [[2 "3]] })
+ eq(0, eval('v:shell_error'))
+ eq([[arg1='1;arg2=2 "3;]], out)
+
+ out = call('system', { printargs_path, "A\nB" })
+ eq(0, eval('v:shell_error'))
+ eq("arg1=A\nB;", out)
+ end)
+
+ it('calls executable in $PATH', function()
+ if 0 == eval("executable('python')") then pending("missing `python`") end
+ eq("foo\n", eval([[system(['python', '-c', 'print("foo")'])]]))
+ eq(0, eval('v:shell_error'))
+ end)
+
+ it('does NOT run in shell', function()
+ if helpers.os_name() ~= 'windows' then
+ eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])"))
+ end
+ end)
+ end)
+
+ if helpers.pending_win32(pending) then return end
+
+ it('sets v:shell_error', function()
eval([[system("sh -c 'exit'")]])
eq(0, eval('v:shell_error'))
eval([[system("sh -c 'exit 1'")]])
@@ -158,7 +219,7 @@ describe('system()', function()
end)
end)
- describe('passing number as input', function()
+ describe('input passed as Number', function()
it('stringifies the input', function()
eq('1', eval('system("cat", 1)'))
end)
@@ -175,8 +236,8 @@ describe('system()', function()
end)
end)
- describe('passing list as input', function()
- it('joins list items with linefeed characters', function()
+ describe('input passed as List', function()
+ it('joins List items with linefeed characters', function()
eq('line1\nline2\nline3',
eval("system('cat -', ['line1', 'line2', 'line3'])"))
end)
@@ -185,7 +246,7 @@ describe('system()', function()
-- is inconsistent and is a good reason for the existence of the
-- `systemlist()` function, where input and output map to the same
-- characters(see the following tests with `systemlist()` below)
- describe('with linefeed characters inside list items', function()
+ describe('with linefeed characters inside List items', function()
it('converts linefeed characters to NULs', function()
eq('l1\001p2\nline2\001a\001b\nl3',
eval([[system('cat -', ["l1\np2", "line2\na\nb", 'l3'])]]))
@@ -202,7 +263,7 @@ describe('system()', function()
describe("with a program that doesn't close stdout", function()
if not xclip then
- pending('skipped (missing xclip)', function() end)
+ pending('missing `xclip`', function() end)
else
it('will exit properly after passing input', function()
eq('', eval([[system('xclip -i -selection clipboard', 'clip-data')]]))
@@ -210,18 +271,12 @@ describe('system()', function()
end)
end
end)
-
- describe('command passed as a list', function()
- it('does not execute &shell', function()
- eq('* $NOTHING ~/file',
- eval("system(['echo', '-n', '*', '$NOTHING', '~/file'])"))
- end)
- end)
end)
+if helpers.pending_win32(pending) then return end
+
describe('systemlist()', function()
- -- behavior is similar to `system()` but it returns a list instead of a
- -- string.
+ -- Similar to `system()`, but returns List instead of String.
before_each(clear)
it('sets the v:shell_error variable', function()
@@ -334,14 +389,14 @@ describe('systemlist()', function()
end)
end)
- describe('passing list as input', function()
+ describe('input passed as List', function()
it('joins list items with linefeed characters', function()
eq({'line1', 'line2', 'line3'},
eval("systemlist('cat -', ['line1', 'line2', 'line3'])"))
end)
-- Unlike `system()` which uses SOH to represent NULs, with `systemlist()`
- -- input and ouput are the same
+ -- input and ouput are the same.
describe('with linefeed characters inside list items', function()
it('converts linefeed characters to NULs', function()
eq({'l1\np2', 'line2\na\nb', 'l3'},
@@ -381,7 +436,7 @@ describe('systemlist()', function()
describe("with a program that doesn't close stdout", function()
if not xclip then
- pending('skipped (missing xclip)', function() end)
+ pending('missing `xclip`', function() end)
else
it('will exit properly after passing input', function()
eq({}, eval(
diff --git a/test/functional/ex_cmds/ctrl_c_spec.lua b/test/functional/ex_cmds/ctrl_c_spec.lua
new file mode 100644
index 0000000000..b0acb02000
--- /dev/null
+++ b/test/functional/ex_cmds/ctrl_c_spec.lua
@@ -0,0 +1,62 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed, source = helpers.clear, helpers.feed, helpers.source
+local execute = helpers.execute
+
+if helpers.pending_win32(pending) then return end
+
+describe("CTRL-C (mapped)", function()
+ before_each(function()
+ clear()
+ end)
+
+ it("interrupts :global", function()
+ if helpers.skip_fragile(pending,
+ (os.getenv("TRAVIS") and os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN"))
+ then
+ return
+ end
+
+ source([[
+ set nomore nohlsearch undolevels=-1
+ nnoremap <C-C> <NOP>
+ ]])
+
+ execute("silent edit! test/functional/fixtures/bigfile.txt")
+ local screen = Screen.new(52, 6)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [0] = {foreground = Screen.colors.White,
+ background = Screen.colors.Red},
+ [1] = {bold = true,
+ foreground = Screen.colors.SeaGreen}
+ })
+
+ screen:expect([[
+ ^0000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
+ 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
+ 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
+ 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
+ 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;|
+ |
+ ]])
+
+ local function test_ctrl_c(ms)
+ feed(":global/^/p<CR>")
+ helpers.sleep(ms)
+ feed("<C-C>")
+ screen:expect([[Interrupt]], nil, nil, nil, true)
+ end
+
+ -- The test is time-sensitive. Try different sleep values.
+ local ms_values = {1, 10, 100}
+ for i, ms in ipairs(ms_values) do
+ if i < #ms_values then
+ local status, _ = pcall(test_ctrl_c, ms)
+ if status then break end
+ else -- Call the last attempt directly.
+ test_ctrl_c(ms)
+ end
+ end
+ end)
+end)
diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index e6f7609016..5e89986c0f 100644
--- a/test/functional/ex_cmds/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
@@ -3,6 +3,7 @@ local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
local eq, next_msg = helpers.eq, helpers.next_message
local exc_exec = helpers.exc_exec
local command = helpers.command
+local eval = helpers.eval
describe('dictionary change notifications', function()
@@ -255,5 +256,21 @@ describe('dictionary change notifications', function()
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
end)
+
+ it('does not crash when freeing a watched dictionary', function()
+ source([[
+ function! Watcher(dict, key, value)
+ echo a:key string(a:value)
+ endfunction
+
+ function! MakeWatch()
+ let d = {'foo': 'bar'}
+ call dictwatcheradd(d, 'foo', function('Watcher'))
+ endfunction
+ ]])
+
+ command('call MakeWatch()')
+ eq(2, eval('1+1')) -- Still alive?
+ end)
end)
end)
diff --git a/test/functional/ex_cmds/global_spec.lua b/test/functional/ex_cmds/global_spec.lua
deleted file mode 100644
index 81a0ef3248..0000000000
--- a/test/functional/ex_cmds/global_spec.lua
+++ /dev/null
@@ -1,74 +0,0 @@
-local helpers = require('test.functional.helpers')(after_each)
-local Screen = require('test.functional.ui.screen')
-local clear, feed, source = helpers.clear, helpers.feed, helpers.source
-
-if helpers.pending_win32(pending) then return end
-
-describe(':global', function()
- before_each(function()
- clear()
- end)
-
- it('is interrupted by mapped CTRL-C', function()
- if os.getenv("TRAVIS") and os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN" then
- -- XXX: ASAN_UBSAN is too slow to react to the CTRL-C.
- pending("", function() end)
- return
- end
-
- source([[
- set nomore
- set undolevels=-1
- nnoremap <C-C> <NOP>
- for i in range(0, 99999)
- put ='XXX'
- endfor
- put ='ZZZ'
- 1
- .delete
- ]])
-
- local screen = Screen.new(52, 6)
- screen:attach()
- screen:set_default_attr_ids({
- [0] = {foreground = Screen.colors.White,
- background = Screen.colors.Red},
- [1] = {bold = true,
- foreground = Screen.colors.SeaGreen}
- })
-
- screen:expect([[
- ^XXX |
- XXX |
- XXX |
- XXX |
- XXX |
- |
- ]])
-
- local function test_ctrl_c(ms)
- feed(":global/^/p<CR>")
- helpers.sleep(ms)
- feed("<C-C>")
- screen:expect([[
- XXX |
- XXX |
- XXX |
- XXX |
- {0:Interrupted} |
- Interrupt: {1:Press ENTER or type command to continue}^ |
- ]])
- end
-
- -- The test is time-sensitive. Try with different sleep values.
- local ms_values = {10, 50, 100}
- for i, ms in ipairs(ms_values) do
- if i < #ms_values then
- local status, _ = pcall(test_ctrl_c, ms)
- if status then break end
- else -- Call the last attempt directly.
- test_ctrl_c(ms)
- end
- end
- end)
-end)
diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt
index 70aee6efa9..8537ea390f 100644
--- a/test/functional/fixtures/CMakeLists.txt
+++ b/test/functional/fixtures/CMakeLists.txt
@@ -2,3 +2,4 @@ add_executable(tty-test tty-test.c)
target_link_libraries(tty-test ${LIBUV_LIBRARIES})
add_executable(shell-test shell-test.c)
+add_executable(printargs-test printargs-test.c)
diff --git a/test/functional/fixtures/printargs-test.c b/test/functional/fixtures/printargs-test.c
new file mode 100644
index 0000000000..2c25cf8447
--- /dev/null
+++ b/test/functional/fixtures/printargs-test.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ for (int i=1; i<argc; i++) {
+ printf("arg%d=%s;", i, argv[i]);
+ }
+ return 0;
+}
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index f3332cff4f..2939184d2c 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -14,6 +14,7 @@ local neq = global_helpers.neq
local eq = global_helpers.eq
local ok = global_helpers.ok
+local start_dir = lfs.currentdir()
local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim'
local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
'--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.',
@@ -21,6 +22,9 @@ local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
local mpack = require('mpack')
+local tmpname = global_helpers.tmpname
+local uname = global_helpers.uname
+
-- Formulate a path to the directory containing nvim. We use this to
-- help run test executables. It helps to keep the tests working, even
-- when the build is not in the default location.
@@ -334,44 +338,6 @@ local function write_file(name, text, dont_dedent)
file:close()
end
--- Tries to get platform name from $SYSTEM_NAME, uname; fallback is "Windows".
-local uname = (function()
- local platform = nil
- return (function()
- if platform then
- return platform
- end
-
- platform = os.getenv("SYSTEM_NAME")
- if platform then
- return platform
- end
-
- local status, f = pcall(io.popen, "uname -s")
- if status then
- platform = f:read("*l")
- else
- platform = 'Windows'
- end
- return platform
- end)
-end)()
-
-local function tmpname()
- local fname = os.tmpname()
- if uname() == 'Windows' and fname:sub(1, 2) == '\\s' then
- -- In Windows tmpname() returns a filename starting with
- -- special sequence \s, prepend $TEMP path
- local tmpdir = os.getenv('TEMP')
- return tmpdir..fname
- elseif fname:match('^/tmp') and uname() == 'Darwin' then
- -- In OS X /tmp links to /private/tmp
- return '/private'..fname
- else
- return fname
- end
-end
-
local function source(code)
local fname = tmpname()
write_file(fname, code)
@@ -380,6 +346,14 @@ local function source(code)
return fname
end
+local function set_shell_powershell()
+ source([[
+ set shell=powershell shellquote=\" shellpipe=\| shellredir=>
+ set shellcmdflag=\ -ExecutionPolicy\ RemoteSigned\ -Command
+ let &shellxquote=' '
+ ]])
+end
+
local function nvim(method, ...)
return request('nvim_'..method, ...)
end
@@ -475,6 +449,12 @@ end
local function rmdir(path)
local ret, _ = pcall(do_rmdir, path)
+ if not ret and os_name() == "windows" then
+ -- Maybe "Permission denied"; try again after changing the nvim
+ -- process to the top-level directory.
+ nvim_command([[exe 'cd '.fnameescape(']]..start_dir.."')")
+ ret, _ = pcall(do_rmdir, path)
+ end
-- During teardown, the nvim process may not exit quickly enough, then rmdir()
-- will fail (on Windows).
if not ret then -- Try again.
@@ -520,12 +500,12 @@ local function create_callindex(func)
end
-- Helper to skip tests. Returns true in Windows systems.
--- pending_func is pending() from busted
-local function pending_win32(pending_func)
+-- pending_fn is pending() from busted
+local function pending_win32(pending_fn)
clear()
if uname() == 'Windows' then
- if pending_func ~= nil then
- pending_func('FIXME: Windows', function() end)
+ if pending_fn ~= nil then
+ pending_fn('FIXME: Windows', function() end)
end
return true
else
@@ -533,6 +513,22 @@ local function pending_win32(pending_func)
end
end
+-- Calls pending() and returns `true` if the system is too slow to
+-- run fragile or expensive tests. Else returns `false`.
+local function skip_fragile(pending_fn, cond)
+ if pending_fn == nil or type(pending_fn) ~= type(function()end) then
+ error("invalid pending_fn")
+ end
+ if cond then
+ pending_fn("skipped (test is fragile on this system)", function() end)
+ return true
+ elseif os.getenv("TEST_SKIP_FRAGILE") then
+ pending_fn("skipped (TEST_SKIP_FRAGILE)", function() end)
+ return true
+ end
+ return false
+end
+
local funcs = create_callindex(nvim_call)
local meths = create_callindex(nvim)
local uimeths = create_callindex(ui)
@@ -601,6 +597,8 @@ return function(after_each)
curwinmeths = curwinmeths,
curtabmeths = curtabmeths,
pending_win32 = pending_win32,
+ skip_fragile = skip_fragile,
+ set_shell_powershell = set_shell_powershell,
tmpname = tmpname,
NIL = mpack.NIL,
}
diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua
new file mode 100644
index 0000000000..06f7c1dd11
--- /dev/null
+++ b/test/functional/legacy/autochdir_spec.lua
@@ -0,0 +1,26 @@
+local lfs = require('lfs')
+local helpers = require('test.functional.helpers')(after_each)
+local clear, eq = helpers.clear, helpers.eq
+local eval, execute = helpers.eval, helpers.execute
+
+describe('autochdir behavior', function()
+ local dir = 'Xtest-functional-legacy-autochdir'
+
+ before_each(function()
+ lfs.mkdir(dir)
+ clear()
+ end)
+
+ after_each(function()
+ helpers.rmdir(dir)
+ end)
+
+ -- Tests vim/vim/777 without test_autochdir().
+ it('sets filename', function()
+ execute('set acd')
+ execute('new')
+ execute('w '..dir..'/Xtest')
+ eq('Xtest', eval("expand('%')"))
+ eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]]))
+ end)
+end)
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index 1ae855f26c..caeca5e4e2 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -9,8 +9,6 @@ local eval = helpers.eval
local eq = helpers.eq
local neq = helpers.neq
-if helpers.pending_win32(pending) then return end
-
local function init_session(...)
local args = { helpers.nvim_prog, '-i', 'NONE', '--embed',
'--cmd', 'set shortmess+=I background=light noswapfile noautoindent',
@@ -24,6 +22,8 @@ end
describe('startup defaults', function()
describe(':filetype', function()
+ if helpers.pending_win32(pending) then return end
+
local function expect_filetype(expected)
local screen = Screen.new(48, 4)
screen:attach()
@@ -99,8 +99,37 @@ describe('startup defaults', function()
end)
describe('XDG-based defaults', function()
- -- Need to be in separate describe() block to not run clear() twice.
+ -- Need separate describe() blocks to not run clear() twice.
-- Do not put before_each() here for the same reasons.
+
+ describe('with empty/broken environment', function()
+ it('sets correct defaults', function()
+ clear({env={
+ XDG_CONFIG_HOME=nil,
+ XDG_DATA_HOME=nil,
+ XDG_CACHE_HOME=nil,
+ XDG_RUNTIME_DIR=nil,
+ XDG_CONFIG_DIRS=nil,
+ XDG_DATA_DIRS=nil,
+ LOCALAPPDATA=nil,
+ HOMEPATH=nil,
+ HOMEDRIVE=nil,
+ HOME=nil,
+ TEMP=nil,
+ VIMRUNTIME=nil,
+ USER=nil,
+ }})
+
+ eq('.', meths.get_option('backupdir'))
+ eq('.', meths.get_option('viewdir'))
+ eq('.', meths.get_option('directory'))
+ eq('.', meths.get_option('undodir'))
+ end)
+ end)
+
+ -- TODO(jkeyes): tests below fail on win32 because of path separator.
+ if helpers.pending_win32(pending) then return end
+
describe('with too long XDG variables', function()
before_each(function()
clear({env={
diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua
index c8da8e8f6c..5ba19708cf 100644
--- a/test/functional/plugin/msgpack_spec.lua
+++ b/test/functional/plugin/msgpack_spec.lua
@@ -652,6 +652,8 @@ describe('In autoload/msgpack.vim', function()
eval_eq('integer', ('a'):byte(), '\'a\'')
eval_eq('integer', 0xAB, '\'«\'')
+ eval_eq('integer', 0, '\'\\0\'')
+ eval_eq('integer', 10246567, '\'\\10246567\'')
end)
it('correctly loads constants', function()
diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua
index b1209a22e9..b543037ae2 100644
--- a/test/functional/plugin/shada_spec.lua
+++ b/test/functional/plugin/shada_spec.lua
@@ -609,6 +609,18 @@ describe('In autoload/shada.vim', function()
'abc',
-1,
]}] ]]):gsub('\n', ''))
+ -- Regression: NUL separator must be properly supported
+ sd2strings_eq({
+ 'History entry with timestamp ' .. epoch .. ':',
+ ' @ Description_ Value',
+ ' - history type SEARCH',
+ ' - contents ""',
+ ' - separator \'\\0\'',
+ }, ([[ [{'type': 4, 'timestamp': 0, 'data': [
+ 1,
+ '',
+ 0x0
+ ]}] ]]):gsub('\n', ''))
end)
it('works with register items', function()
@@ -837,7 +849,7 @@ describe('In autoload/shada.vim', function()
sd2strings_eq({
'Global mark with timestamp ' .. epoch .. ':',
' % Key Description Value',
- ' + n name 20',
+ ' + n name \'\\20\'',
' + f file name "foo"',
' # Value is negative',
' + l line number -10',
@@ -852,7 +864,18 @@ describe('In autoload/shada.vim', function()
sd2strings_eq({
'Global mark with timestamp ' .. epoch .. ':',
' % Key Description Value',
- ' + n name 20',
+ ' + n name 128',
+ ' + f file name "foo"',
+ ' + l line number 1',
+ ' + c column 0',
+ }, ([[ [{'type': 7, 'timestamp': 0, 'data': {
+ 'n': 128,
+ 'f': 'foo',
+ }}] ]]):gsub('\n', ''))
+ sd2strings_eq({
+ 'Global mark with timestamp ' .. epoch .. ':',
+ ' % Key Description Value',
+ ' + n name \'\\20\'',
' + f file name "foo"',
' # Expected integer',
' + l line number "FOO"',
@@ -1123,7 +1146,7 @@ describe('In autoload/shada.vim', function()
'Local mark with timestamp ' .. epoch .. ':',
' % Key Description Value',
' + f file name "foo"',
- ' + n name 20',
+ ' + n name \'\\20\'',
' # Value is negative',
' + l line number -10',
' # Value is negative',
@@ -1138,7 +1161,7 @@ describe('In autoload/shada.vim', function()
'Local mark with timestamp ' .. epoch .. ':',
' % Key Description Value',
' + f file name "foo"',
- ' + n name 20',
+ ' + n name \'\\20\'',
' # Expected integer',
' + l line number "FOO"',
' # Expected integer',
@@ -1932,13 +1955,13 @@ describe('In autoload/shada.vim', function()
'Buffer list with timestamp ' .. epoch .. ':',
' % Key Description Value',
' # Expected binary string',
- ' + f file name 10',
+ ' + f file name \'\\10\'',
' + l line number 1',
' + c column 0',
'',
' % Key Description Value',
' # Expected binary string',
- ' + f file name 20',
+ ' + f file name \'\\20\'',
' + l line number 1',
' + c column 0',
})
@@ -1948,7 +1971,7 @@ describe('In autoload/shada.vim', function()
'Buffer list with timestamp ' .. epoch .. ':',
' % Key Description Value',
' # Expected binary string',
- ' + f file name 10',
+ ' + f file name \'\\10\'',
' + l line number 1',
' + c column 0',
'',
diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua
index 7e6338897a..a4746c2205 100644
--- a/test/functional/shada/buffers_spec.lua
+++ b/test/functional/shada/buffers_spec.lua
@@ -8,8 +8,6 @@ local reset, set_additional_cmd, clear =
shada_helpers.reset, shada_helpers.set_additional_cmd,
shada_helpers.clear
-if helpers.pending_win32(pending) then return end
-
describe('ShaDa support code', function()
local testfilename = 'Xtestfile-functional-shada-buffers'
local testfilename_2 = 'Xtestfile-functional-shada-buffers-2'
diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua
index b7c0f61f57..fa760ceb5b 100644
--- a/test/functional/shada/marks_spec.lua
+++ b/test/functional/shada/marks_spec.lua
@@ -14,8 +14,6 @@ local nvim_current_line = function()
return curwinmeths.get_cursor()[1]
end
-if helpers.pending_win32(pending) then return end
-
describe('ShaDa support code', function()
local testfilename = 'Xtestfile-functional-shada-marks'
local testfilename_2 = 'Xtestfile-functional-shada-marks-2'
@@ -153,6 +151,19 @@ describe('ShaDa support code', function()
eq(saved, redir_exec('jumps'))
end)
+ it('when dumping jump list also dumps current position', function()
+ nvim_command('edit ' .. testfilename)
+ nvim_command('normal! G')
+ nvim_command('split ' .. testfilename_2)
+ nvim_command('normal! G')
+ nvim_command('wshada')
+ nvim_command('quit')
+ nvim_command('rshada')
+ nvim_command('normal! \15') -- <C-o>
+ eq(testfilename_2, funcs.bufname('%'))
+ eq({2, 0}, curwinmeths.get_cursor())
+ end)
+
it('is able to dump and restore jump list with different times (slow!)',
function()
nvim_command('edit ' .. testfilename_2)
diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua
index f845f6f93b..32598fc399 100644
--- a/test/functional/shada/shada_spec.lua
+++ b/test/functional/shada/shada_spec.lua
@@ -23,8 +23,6 @@ local wshada, _, shada_fname, clean =
local dirname = 'Xtest-functional-shada-shada.d'
local dirshada = dirname .. '/main.shada'
-if helpers.pending_win32(pending) then return end
-
describe('ShaDa support code', function()
before_each(reset)
after_each(function()
@@ -173,6 +171,7 @@ describe('ShaDa support code', function()
end
it('correctly uses shada-r option', function()
+ nvim_command('set shellslash')
meths.set_var('__home', paths.test_source_path)
nvim_command('let $HOME = __home')
nvim_command('unlet __home')
@@ -196,6 +195,7 @@ describe('ShaDa support code', function()
end)
it('correctly ignores case with shada-r option', function()
+ nvim_command('set shellslash')
local pwd = funcs.getcwd()
local relfname = 'абв/test'
local fname = pwd .. '/' .. relfname
@@ -240,6 +240,8 @@ describe('ShaDa support code', function()
end)
it('does not crash when ShaDa file directory is not writable', function()
+ if helpers.pending_win32(pending) then return end
+
funcs.mkdir(dirname, '', 0)
eq(0, funcs.filewritable(dirname))
set_additional_cmd('set shada=')
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 35aeb6e67c..41ebfd2334 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -42,6 +42,7 @@ local function common_setup(screen, inccommand, text)
[14] = {foreground = Screen.colors.White, background = Screen.colors.Red},
[15] = {bold=true, foreground=Screen.colors.Blue},
[16] = {background=Screen.colors.Grey90}, -- cursorline
+ vis = {background=Screen.colors.LightGrey}
})
end
@@ -207,6 +208,42 @@ describe(":substitute, 'inccommand' preserves", function()
end)
end
+ for _, case in pairs{"", "split", "nosplit"} do
+ it("visual selection for non-previewable command (inccommand="..case..") #5888", function()
+ local screen = Screen.new(30,10)
+ common_setup(screen, case, default_text)
+ feed('1G2V')
+
+ feed(':s')
+ screen:expect([[
+ {vis:Inc substitution on} |
+ t{vis:wo lines} |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :'<,'>s^ |
+ ]])
+
+ feed('o')
+ screen:expect([[
+ {vis:Inc substitution on} |
+ t{vis:wo lines} |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :'<,'>so^ |
+ ]])
+ end)
+ end
+
end)
describe(":substitute, 'inccommand' preserves undo", function()
@@ -1201,6 +1238,40 @@ describe(":substitute, 'inccommand' with a failing expression", function()
end
end)
+ it('in the range does not error #5912', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed(':100s/')
+
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :100s/^ |
+ ]])
+
+ feed('<enter>')
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ ^ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {14:E16: Invalid range} |
+ ]])
+ end
+ end)
+
end)
describe("'inccommand' and :cnoremap", function()
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index b729b0db08..17d949825a 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -153,9 +153,10 @@ describe('Mouse input', function()
end)
it('in tabline to the left moves tab left', function()
- if os.getenv("TRAVIS") and (helpers.os_name() == "osx"
- or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN") then
- pending("[Fails on Travis macOS, ASAN_UBSAN. #4874]", function() end)
+ if helpers.skip_fragile(pending,
+ os.getenv("TRAVIS") and (helpers.os_name() == "osx"
+ or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN")) -- #4874
+ then
return
end
@@ -257,9 +258,10 @@ describe('Mouse input', function()
end)
it('out of tabline to the left moves tab left', function()
- if os.getenv("TRAVIS") and (helpers.os_name() == "osx"
- or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN") then
- pending("[Fails on Travis macOS, ASAN_UBSAN. #4874]", function() end)
+ if helpers.skip_fragile(pending,
+ os.getenv("TRAVIS") and (helpers.os_name() == "osx"
+ or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN")) -- #4874
+ then
return
end
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 2581b36711..ef1f0783e7 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -1,31 +1,17 @@
--- This module contains the Screen class, a complete Nvim screen implementation
--- designed for functional testing. The goal is to provide a simple and
--- intuitive API for verifying screen state after a set of actions.
+-- This module contains the Screen class, a complete Nvim UI implementation
+-- designed for functional testing (verifying screen state, in particular).
--
--- The screen class exposes a single assertion method, "Screen:expect". This
--- method takes a string representing the expected screen state and an optional
--- set of attribute identifiers for checking highlighted characters(more on
--- this later).
---
--- The string passed to "expect" will be processed according to these rules:
---
--- - Each line of the string represents and is matched individually against
--- a screen row.
--- - The entire string is stripped of common indentation
--- - Expected screen rows are stripped of the last character. The last
--- character should be used to write pipes(|) that make clear where the
--- screen ends
--- - The last line is stripped, so the string must have (row count + 1)
--- lines.
+-- Screen:expect() takes a string representing the expected screen state and an
+-- optional set of attribute identifiers for checking highlighted characters.
--
-- Example usage:
--
-- local screen = Screen.new(25, 10)
--- -- attach the screen to the current Nvim instance
+-- -- Attach the screen to the current Nvim instance.
-- screen:attach()
--- --enter insert mode and type some text
+-- -- Enter insert-mode and type some text.
-- feed('ihello screen')
--- -- declare an expectation for the eventual screen state
+-- -- Assert the expected screen state.
-- screen:expect([[
-- hello screen |
-- ~ |
@@ -39,31 +25,19 @@
-- -- INSERT -- |
-- ]]) -- <- Last line is stripped
--
--- Since screen updates are received asynchronously, "expect" is actually
--- specifying the eventual screen state. This is how "expect" works: It will
--- start the event loop with a timeout of 5 seconds. Each time it receives an
--- update the expected state will be checked against the updated state.
---
--- If the expected state matches the current state, the event loop will be
--- stopped and "expect" will return. If the timeout expires, the last match
--- error will be reported and the test will fail.
+-- Since screen updates are received asynchronously, expect() actually specifies
+-- the _eventual_ screen state.
--
--- If the second argument is passed to "expect", the screen rows will be
--- transformed before being matched against the string lines. The
--- transformation rule is simple: Each substring "S" composed with characters
--- having the exact same set of attributes will be substituted by "{K:S}",
--- where K is a key associated the attribute set via the second argument of
--- "expect".
--- If a transformation table is present, unexpected attribute sets in the final
--- state is considered an error. To make testing simpler, a list of attribute
--- sets that should be ignored can be passed as a third argument. Alternatively,
--- this third argument can be "true" to indicate that all unexpected attribute
--- sets should be ignored.
+-- This is how expect() works:
+-- * It starts the event loop with a timeout.
+-- * Each time it receives an update it checks that against the expected state.
+-- * If the expected state matches the current state, the event loop will be
+-- stopped and expect() will return.
+-- * If the timeout expires, the last match error will be reported and the
+-- test will fail.
--
--- To illustrate how this works, let's say that in the above example we wanted
--- to assert that the "-- INSERT --" string is highlighted with the bold
--- attribute(which normally is), here's how the call to "expect" should look
--- like:
+-- Continuing the above example, say we want to assert that "-- INSERT --" is
+-- highlighted with the bold attribute. The expect() call should look like this:
--
-- NonText = Screen.colors.Blue
-- screen:expect([[
@@ -81,29 +55,20 @@
--
-- In this case "b" is a string associated with the set composed of one
-- attribute: bold. Note that since the {b:} markup is not a real part of the
--- screen, the delimiter(|) had to be moved right. Also, the highlighting of the
--- NonText markers (~) is ignored in this test.
+-- screen, the delimiter "|" moved to the right. Also, the highlighting of the
+-- NonText markers "~" is ignored in this test.
+--
+-- Tests will often share a group of attribute sets to expect(). Those can be
+-- defined at the beginning of a test:
--
--- Multiple expect:s will likely share a group of attribute sets to test.
--- Therefore these could be specified at the beginning of a test like this:
-- NonText = Screen.colors.Blue
-- screen:set_default_attr_ids( {
-- [1] = {reverse = true, bold = true},
-- [2] = {reverse = true}
-- })
-- screen:set_default_attr_ignore( {{}, {bold=true, foreground=NonText}} )
--- These can be overridden for a specific expect expression, by passing
--- different sets as parameters.
--
--- To help writing screen tests, there is a utility function
--- "screen:snapshot_util()", that can be placed in a test file at any point an
--- "expect(...)" should be. It will wait a short amount of time and then dump
--- the current state of the screen, in the form of an "expect(..)" expression
--- that would match it exactly. "snapshot_util" optionally also take the
--- transformation and ignore set as parameters, like expect, or uses the default
--- set. It will generate a larger attribute transformation set, if needed.
--- To generate a text-only test without highlight checks,
--- use `screen:snapshot_util({},true)`
+-- To help write screen tests, see screen:snapshot_util().
local helpers = require('test.functional.helpers')(nil)
local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths
@@ -205,13 +170,22 @@ end
function Screen:try_resize(columns, rows)
uimeths.try_resize(columns, rows)
+ -- Give ourselves a chance to _handle_resize, which requires using
+ -- self.sleep() (for the resize notification) rather than run()
+ self:sleep(0.1)
end
-- Asserts that `expected` eventually matches the screen state.
--
--- expected: Expected screen state (string).
--- attr_ids: Text attribute definitions.
--- attr_ignore: Ignored text attributes.
+-- expected: Expected screen state (string). Each line represents a screen
+-- row. Last character of each row (typically "|") is stripped.
+-- Common indentation is stripped.
+-- attr_ids: Expected text attributes. Screen rows are transformed according
+-- to this table, as follows: each substring S composed of
+-- characters having the same attributes will be substituted by
+-- "{K:S}", where K is a key in `attr_ids`. Any unexpected
+-- attributes in the final state are an error.
+-- attr_ignore: Ignored text attributes, or `true` to ignore all.
-- condition: Function asserting some arbitrary condition.
-- any: true: Succeed if `expected` matches ANY screen line(s).
-- false (default): `expected` must match screen exactly.
@@ -224,6 +198,11 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any)
row = row:sub(1, #row - 1)
table.insert(expected_rows, row)
end
+ if not any then
+ assert(self._height == #expected_rows,
+ "Expected screen state's row count(" .. #expected_rows
+ .. ') differs from configured height(' .. self._height .. ') of Screen.')
+ end
local ids = attr_ids or self._default_attr_ids
local ignore = attr_ignore or self._default_attr_ignore
self:wait(function()
@@ -541,8 +520,10 @@ function Screen:_current_screen()
return table.concat(rv, '\n')
end
+-- Utility to generate/debug tests. Call it where screen:expect() would be.
+-- Waits briefly, then dumps the current screen state in the form of
+-- screen:expect(). Use snapshot_util({},true) to generate a text-only test.
function Screen:snapshot_util(attrs, ignore)
- -- util to generate screen test
self:sleep(250)
self:print_snapshot(attrs, ignore)
end
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index d03f98c26f..0824585717 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -4,8 +4,7 @@ local spawn, set_session, clear = helpers.spawn, helpers.set_session, helpers.cl
local feed, execute = helpers.feed, helpers.execute
local insert = helpers.insert
local eq = helpers.eq
-
-if helpers.pending_win32(pending) then return end
+local eval = helpers.eval
describe('Initial screen', function()
local screen
@@ -563,11 +562,10 @@ describe('Screen', function()
]])
end)
- -- FIXME this has some race conditions that cause it to fail periodically
- pending('has minimum width/height values', function()
+ it('has minimum width/height values', function()
screen:try_resize(1, 1)
screen:expect([[
- -- INS^ERT --|
+ {2:-- INS^ERT --}|
|
]])
feed('<esc>:ls')
@@ -690,4 +688,12 @@ describe('Screen', function()
end)
end)
end)
+
+ it('nvim_ui_attach() handles very large width/height #2180', function()
+ screen:detach()
+ screen = Screen.new(999, 999)
+ screen:attach()
+ eq(999, eval('&lines'))
+ eq(999, eval('&columns'))
+ end)
end)
diff --git a/test/helpers.lua b/test/helpers.lua
index 4c50c7644f..0bc62da5d7 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -52,9 +52,49 @@ local function check_logs()
assert(0 == runtime_errors)
end
+-- Tries to get platform name from $SYSTEM_NAME, uname; fallback is "Windows".
+local uname = (function()
+ local platform = nil
+ return (function()
+ if platform then
+ return platform
+ end
+
+ platform = os.getenv("SYSTEM_NAME")
+ if platform then
+ return platform
+ end
+
+ local status, f = pcall(io.popen, "uname -s")
+ if status then
+ platform = f:read("*l")
+ else
+ platform = 'Windows'
+ end
+ return platform
+ end)
+end)()
+
+local function tmpname()
+ local fname = os.tmpname()
+ if uname() == 'Windows' and fname:sub(1, 2) == '\\s' then
+ -- In Windows tmpname() returns a filename starting with
+ -- special sequence \s, prepend $TEMP path
+ local tmpdir = os.getenv('TEMP')
+ return tmpdir..fname
+ elseif fname:match('^/tmp') and uname() == 'Darwin' then
+ -- In OS X /tmp links to /private/tmp
+ return '/private'..fname
+ else
+ return fname
+ end
+end
+
return {
eq = eq,
neq = neq,
ok = ok,
check_logs = check_logs,
+ uname = uname,
+ tmpname = tmpname,
}
diff --git a/test/unit/api/private_helpers_spec.lua b/test/unit/api/private_helpers_spec.lua
index 1d7c03787b..8c54ea6a2a 100644
--- a/test/unit/api/private_helpers_spec.lua
+++ b/test/unit/api/private_helpers_spec.lua
@@ -7,6 +7,7 @@ local NULL = helpers.NULL
local eq = helpers.eq
local lua2typvalt = eval_helpers.lua2typvalt
+local typvalt2lua = eval_helpers.typvalt2lua
local typvalt = eval_helpers.typvalt
local nil_value = api_helpers.nil_value
@@ -14,6 +15,7 @@ local list_type = api_helpers.list_type
local int_type = api_helpers.int_type
local type_key = api_helpers.type_key
local obj2lua = api_helpers.obj2lua
+local func_type = api_helpers.func_type
local api = cimport('./src/nvim/api/private/helpers.h')
@@ -85,4 +87,19 @@ describe('vim_to_object', function()
eq(nil, tt.vval.v_dict)
eq({}, obj2lua(api.vim_to_object(tt)))
end)
+
+ it('regression: partials in a list', function()
+ local llist = {
+ {
+ [type_key]=func_type,
+ value='printf',
+ args={'%s'},
+ dict={v=1},
+ },
+ {},
+ }
+ local list = lua2typvalt(llist)
+ eq(llist, typvalt2lua(list))
+ eq({nil_value, {}}, obj2lua(api.vim_to_object(list)))
+ end)
end)
diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua
index 45fbf8da5c..c3c27e4fed 100644
--- a/test/unit/eval/helpers.lua
+++ b/test/unit/eval/helpers.lua
@@ -5,10 +5,12 @@ local to_cstr = helpers.to_cstr
local ffi = helpers.ffi
local eq = helpers.eq
-local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h')
+local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h',
+ './src/nvim/hashtab.h')
local null_string = {[true]='NULL string'}
local null_list = {[true]='NULL list'}
+local null_dict = {[true]='NULL dict'}
local type_key = {[true]='type key'}
local list_type = {[true]='list type'}
local dict_type = {[true]='dict type'}
@@ -18,27 +20,28 @@ local flt_type = {[true]='flt type'}
local nil_value = {[true]='nil'}
+local lua2typvalt
+
+local function li_alloc(nogc)
+ local gcfunc = eval.listitem_free
+ if nogc then gcfunc = nil end
+ local li = ffi.gc(eval.listitem_alloc(), gcfunc)
+ li.li_next = nil
+ li.li_prev = nil
+ li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED}
+ return li
+end
+
local function list(...)
local ret = ffi.gc(eval.list_alloc(), eval.list_unref)
eq(0, ret.lv_refcount)
ret.lv_refcount = 1
for i = 1, select('#', ...) do
local val = select(i, ...)
- local typ = type(val)
- if typ == 'string' then
- eval.list_append_string(ret, to_cstr(val))
- elseif typ == 'table' and val == null_string then
- eval.list_append_string(ret, nil)
- elseif typ == 'table' and val == null_list then
- eval.list_append_list(ret, nil)
- elseif typ == 'table' and val[type_key] == list_type then
- local itemlist = ffi.gc(list(table.unpack(val)), nil)
- eq(1, itemlist.lv_refcount)
- itemlist.lv_refcount = 0
- eval.list_append_list(ret, itemlist)
- else
- assert(false, 'Not implemented yet')
- end
+ local li_tv = ffi.gc(lua2typvalt(val), nil)
+ local li = li_alloc(true)
+ li.li_tv = li_tv
+ eval.tv_list_append(ret, li)
end
return ret
end
@@ -49,9 +52,14 @@ local special_tab = {
[eval.kSpecialVarTrue] = true,
}
+local ptr2key = function(ptr)
+ return tostring(ptr)
+end
+
local lst2tbl
local dct2tbl
+local typvalt2lua
local typvalt2lua_tab
typvalt2lua_tab = {
@@ -72,33 +80,88 @@ typvalt2lua_tab = {
return ffi.string(str)
end
end,
- [tonumber(eval.VAR_LIST)] = function(t)
- return lst2tbl(t.vval.v_list)
+ [tonumber(eval.VAR_LIST)] = function(t, processed)
+ return lst2tbl(t.vval.v_list, processed)
end,
- [tonumber(eval.VAR_DICT)] = function(t)
- return dct2tbl(t.vval.v_dict)
+ [tonumber(eval.VAR_DICT)] = function(t, processed)
+ return dct2tbl(t.vval.v_dict, processed)
end,
- [tonumber(eval.VAR_FUNC)] = function(t)
- return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t)}
+ [tonumber(eval.VAR_FUNC)] = function(t, processed)
+ return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t, processed or {})}
+ end,
+ [tonumber(eval.VAR_PARTIAL)] = function(t, processed)
+ local p_key = ptr2key(t)
+ if processed[p_key] then
+ return processed[p_key]
+ end
+ local pt = t.vval.v_partial
+ local value, auto, dict, argv = nil, nil, nil, nil
+ if pt ~= nil then
+ value = ffi.string(pt.pt_name)
+ auto = pt.pt_auto and true or nil
+ argv = {}
+ for i = 1, pt.pt_argc do
+ argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed)
+ end
+ if pt.pt_dict ~= nil then
+ dict = dct2tbl(pt.pt_dict)
+ end
+ end
+ return {
+ [type_key]=func_type,
+ value=value,
+ auto=auto,
+ args=argv,
+ dict=dict,
+ }
end,
}
-local typvalt2lua = function(t)
+typvalt2lua = function(t, processed)
return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner)
assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet')
- end)(t))
+ end)(t, processed or {}))
end
-lst2tbl = function(l)
- local ret = {[type_key]=list_type}
+local function list_iter(l)
+ local init_s = {
+ idx=0,
+ li=l.lv_first,
+ }
+ local function f(s, _)
+ -- (listitem_T *) NULL is equal to nil, but yet it is not false.
+ if s.li == nil then
+ return nil
+ end
+ local ret_li = s.li
+ s.li = s.li.li_next
+ s.idx = s.idx + 1
+ return s.idx, ret_li
+ end
+ return f, init_s, nil
+end
+
+local function list_items(l)
+ local ret = {}
+ for i, li in list_iter(l) do
+ ret[i] = li
+ end
+ return ret
+end
+
+lst2tbl = function(l, processed)
if l == nil then
- return ret
+ return null_list
+ end
+ processed = processed or {}
+ local p_key = ptr2key(l)
+ if processed[p_key] then
+ return processed[p_key]
end
- local li = l.lv_first
- -- (listitem_T *) NULL is equal to nil, but yet it is not false.
- while li ~= nil do
- ret[#ret + 1] = typvalt2lua(li.li_tv)
- li = li.li_next
+ local ret = {[type_key]=list_type}
+ processed[p_key] = ret
+ for i, li in list_iter(l) do
+ ret[i] = typvalt2lua(li.li_tv, processed)
end
if ret[1] then
ret[type_key] = nil
@@ -106,16 +169,71 @@ lst2tbl = function(l)
return ret
end
-dct2tbl = function(d)
- local ret = {d=d}
- assert(false, 'Converting dictionaries is not implemented yet')
+local hi_key_removed = eval._hash_key_removed()
+
+local function dict_iter(d, return_hi)
+ local init_s = {
+ todo=d.dv_hashtab.ht_used,
+ hi=d.dv_hashtab.ht_array,
+ }
+ local function f(s, _)
+ if s.todo == 0 then return nil end
+ while s.todo > 0 do
+ if s.hi.hi_key ~= nil and s.hi.hi_key ~= hi_key_removed then
+ local key = ffi.string(s.hi.hi_key)
+ local ret
+ if return_hi then
+ ret = s.hi
+ else
+ ret = ffi.cast('dictitem_T*',
+ s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key'))
+ end
+ s.todo = s.todo - 1
+ s.hi = s.hi + 1
+ return key, ret
+ end
+ s.hi = s.hi + 1
+ end
+ end
+ return f, init_s, nil
+end
+
+local function first_di(d)
+ local f, init_s, v = dict_iter(d)
+ return select(2, f(init_s, v))
+end
+
+local function dict_items(d)
+ local ret = {[0]=0}
+ for k, hi in dict_iter(d) do
+ ret[k] = hi
+ ret[0] = ret[0] + 1
+ ret[ret[0]] = hi
+ end
return ret
end
-local lua2typvalt
+dct2tbl = function(d, processed)
+ if d == nil then
+ return null_dict
+ end
+ processed = processed or {}
+ local p_key = ptr2key(d)
+ if processed[p_key] then
+ return processed[p_key]
+ end
+ local ret = {}
+ processed[p_key] = ret
+ for k, di in dict_iter(d) do
+ ret[k] = typvalt2lua(di.di_tv, processed)
+ end
+ return ret
+end
local typvalt = function(typ, vval)
- if type(typ) == 'string' then
+ if typ == nil then
+ typ = eval.VAR_UNKNOWN
+ elseif type(typ) == 'string' then
typ = eval[typ]
end
return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv)
@@ -164,12 +282,68 @@ local lua2typvalt_type_tab = {
end
return ret
end,
+ [func_type] = function(l, processed)
+ if processed[l] then
+ processed[l].pt_refcount = processed[l].pt_refcount + 1
+ return typvalt(eval.VAR_PARTIAL, {v_partial=processed[l]})
+ end
+ if l.args or l.dict then
+ local pt = ffi.gc(ffi.cast('partial_T*', eval.xmalloc(ffi.sizeof('partial_T'))), nil)
+ processed[l] = pt
+ local argv = nil
+ if l.args and #l.args > 0 then
+ argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil)
+ for i, arg in ipairs(l.args) do
+ local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil)
+ eval.copy_tv(arg_tv, argv[i - 1])
+ eval.clear_tv(arg_tv)
+ end
+ end
+ local dict = nil
+ if l.dict then
+ local dict_tv = ffi.gc(lua2typvalt(l.dict, processed), nil)
+ assert(dict_tv.v_type == eval.VAR_DICT)
+ dict = dict_tv.vval.v_dict
+ end
+ pt.pt_refcount = 1
+ pt.pt_name = eval.xmemdupz(to_cstr(l.value), #l.value)
+ pt.pt_auto = not not l.auto
+ pt.pt_argc = l.args and #l.args or 0
+ pt.pt_argv = argv
+ pt.pt_dict = dict
+ return typvalt(eval.VAR_PARTIAL, {v_partial=pt})
+ else
+ return typvalt(eval.VAR_FUNC, {
+ v_string=eval.xmemdupz(to_cstr(l.value), #l.value)
+ })
+ end
+ end,
}
+local special_vals = {
+ [null_string] = {eval.VAR_STRING, {v_string=ffi.cast('char_u*', nil)}},
+ [null_list] = {eval.VAR_LIST, {v_list=ffi.cast('list_T*', nil)}},
+ [null_dict] = {eval.VAR_DICT, {v_dict=ffi.cast('dict_T*', nil)}},
+ [nil_value] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull}},
+ [true] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarTrue}},
+ [false] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarFalse}},
+}
+
+for k, v in pairs(special_vals) do
+ local tmp = function(typ, vval)
+ special_vals[k] = function()
+ return typvalt(typ, vval)
+ end
+ end
+ tmp(v[1], v[2])
+end
+
lua2typvalt = function(l, processed)
processed = processed or {}
if l == nil or l == nil_value then
- return typvalt(eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull})
+ return special_vals[nil_value]()
+ elseif special_vals[l] then
+ return special_vals[l]()
elseif type(l) == 'table' then
if l[type_key] then
return lua2typvalt_type_tab[l[type_key]](l, processed)
@@ -182,18 +356,35 @@ lua2typvalt = function(l, processed)
end
elseif type(l) == 'number' then
return typvalt(eval.VAR_FLOAT, {v_float=l})
- elseif type(l) == 'boolean' then
- return typvalt(eval.VAR_SPECIAL, {
- v_special=(l and eval.kSpecialVarTrue or eval.kSpecialVarFalse)
- })
elseif type(l) == 'string' then
return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)})
+ elseif type(l) == 'cdata' then
+ local tv = typvalt(eval.VAR_UNKNOWN)
+ eval.tv_copy(l, tv)
+ return tv
end
end
+local function void(ptr)
+ return ffi.cast('void*', ptr)
+end
+
+local alloc_logging_helpers = {
+ list = function(l) return {func='calloc', args={1, ffi.sizeof('list_T')}, ret=void(l)} end,
+ li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end,
+ dict = function(d) return {func='malloc', args={ffi.sizeof('dict_T')}, ret=void(d)} end,
+ di = function(di, size)
+ return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)}
+ end,
+ str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end,
+
+ freed = function(p) return {func='free', args={p and void(p)}} end,
+}
+
return {
null_string=null_string,
null_list=null_list,
+ null_dict=null_dict,
list_type=list_type,
dict_type=dict_type,
func_type=func_type,
@@ -212,4 +403,15 @@ return {
typvalt2lua=typvalt2lua,
typvalt=typvalt,
+
+ li_alloc=li_alloc,
+
+ dict_iter=dict_iter,
+ list_iter=list_iter,
+ first_di=first_di,
+
+ alloc_logging_helpers=alloc_logging_helpers,
+
+ list_items=list_items,
+ dict_items=dict_items,
}
diff --git a/test/unit/eval/tv_clear_spec.lua b/test/unit/eval/tv_clear_spec.lua
new file mode 100644
index 0000000000..96eccdbd71
--- /dev/null
+++ b/test/unit/eval/tv_clear_spec.lua
@@ -0,0 +1,127 @@
+local helpers = require('test.unit.helpers')
+local eval_helpers = require('test.unit.eval.helpers')
+
+local alloc_log_new = helpers.alloc_log_new
+local cimport = helpers.cimport
+local ffi = helpers.ffi
+local eq = helpers.eq
+
+local a = eval_helpers.alloc_logging_helpers
+local type_key = eval_helpers.type_key
+local list_type = eval_helpers.list_type
+local list_items = eval_helpers.list_items
+local dict_items = eval_helpers.dict_items
+local lua2typvalt = eval_helpers.lua2typvalt
+
+local lib = cimport('./src/nvim/eval_defs.h', './src/nvim/eval.h')
+
+local alloc_log = alloc_log_new()
+
+before_each(function()
+ alloc_log:before_each()
+end)
+
+after_each(function()
+ alloc_log:after_each()
+end)
+
+describe('clear_tv()', function()
+ it('successfully frees all lists in [&l [1], *l, *l]', function()
+ local l_inner = {1}
+ local list = {l_inner, l_inner, l_inner}
+ local list_tv = ffi.gc(lua2typvalt(list), nil)
+ local list_p = list_tv.vval.v_list
+ local lis = list_items(list_p)
+ local list_inner_p = lis[1].li_tv.vval.v_list
+ local lis_inner = list_items(list_inner_p)
+ alloc_log:check({
+ a.list(list_p),
+ a.list(list_inner_p),
+ a.li(lis_inner[1]),
+ a.li(lis[1]),
+ a.li(lis[2]),
+ a.li(lis[3]),
+ })
+ eq(3, list_inner_p.lv_refcount)
+ lib.clear_tv(list_tv)
+ alloc_log:check({
+ a.freed(lis_inner[1]),
+ a.freed(list_inner_p),
+ a.freed(lis[1]),
+ a.freed(lis[2]),
+ a.freed(lis[3]),
+ a.freed(list_p),
+ })
+ end)
+ it('successfully frees all lists in [&l [], *l, *l]', function()
+ local l_inner = {[type_key]=list_type}
+ local list = {l_inner, l_inner, l_inner}
+ local list_tv = ffi.gc(lua2typvalt(list), nil)
+ local list_p = list_tv.vval.v_list
+ local lis = list_items(list_p)
+ local list_inner_p = lis[1].li_tv.vval.v_list
+ alloc_log:check({
+ a.list(list_p),
+ a.list(list_inner_p),
+ a.li(lis[1]),
+ a.li(lis[2]),
+ a.li(lis[3]),
+ })
+ eq(3, list_inner_p.lv_refcount)
+ lib.clear_tv(list_tv)
+ alloc_log:check({
+ a.freed(list_inner_p),
+ a.freed(lis[1]),
+ a.freed(lis[2]),
+ a.freed(lis[3]),
+ a.freed(list_p),
+ })
+ end)
+ it('successfully frees all dictionaries in [&d {}, *d]', function()
+ local d_inner = {}
+ local list = {d_inner, d_inner}
+ local list_tv = ffi.gc(lua2typvalt(list), nil)
+ local list_p = list_tv.vval.v_list
+ local lis = list_items(list_p)
+ local dict_inner_p = lis[1].li_tv.vval.v_dict
+ alloc_log:check({
+ a.list(list_p),
+ a.dict(dict_inner_p),
+ a.li(lis[1]),
+ a.li(lis[2]),
+ })
+ eq(2, dict_inner_p.dv_refcount)
+ lib.clear_tv(list_tv)
+ alloc_log:check({
+ a.freed(dict_inner_p),
+ a.freed(lis[1]),
+ a.freed(lis[2]),
+ a.freed(list_p),
+ })
+ end)
+ it('successfully frees all dictionaries in [&d {a: 1}, *d]', function()
+ local d_inner = {a=1}
+ local list = {d_inner, d_inner}
+ local list_tv = ffi.gc(lua2typvalt(list), nil)
+ local list_p = list_tv.vval.v_list
+ local lis = list_items(list_p)
+ local dict_inner_p = lis[1].li_tv.vval.v_dict
+ local dis = dict_items(dict_inner_p)
+ alloc_log:check({
+ a.list(list_p),
+ a.dict(dict_inner_p),
+ a.di(dis.a, 1),
+ a.li(lis[1]),
+ a.li(lis[2]),
+ })
+ eq(2, dict_inner_p.dv_refcount)
+ lib.clear_tv(list_tv)
+ alloc_log:check({
+ a.freed(dis.a),
+ a.freed(dict_inner_p),
+ a.freed(lis[1]),
+ a.freed(lis[2]),
+ a.freed(list_p),
+ })
+ end)
+end)
diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua
index 00637e0b8d..e288081960 100644
--- a/test/unit/formatc.lua
+++ b/test/unit/formatc.lua
@@ -219,13 +219,7 @@ local function standalone(...) -- luacheck: ignore
Preprocess.add_to_include_path('./../../build/include')
Preprocess.add_to_include_path('./../../.deps/usr/include')
- local input = Preprocess.preprocess_stream(arg[1])
- local raw = input:read('*all')
- input:close()
-
- if raw == nil then
- print("ERROR: Preprocess.preprocess_stream():read() returned empty")
- end
+ local raw = Preprocess.preprocess('', arg[1])
local formatted
if #arg == 2 and arg[2] == 'no' then
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 3564f76442..1bfdd32739 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -9,6 +9,12 @@ local neq = global_helpers.neq
local eq = global_helpers.eq
local ok = global_helpers.ok
+-- C constants.
+local NULL = ffi.cast('void*', 0)
+
+local OK = 1
+local FAIL = 0
+
-- add some standard header locations
for _, p in ipairs(Paths.include_paths) do
Preprocess.add_to_include_path(p)
@@ -45,6 +51,8 @@ local function filter_complex_blocks(body)
return table.concat(result, "\n")
end
+local previous_defines = ''
+
-- use this helper to import C files, you can pass multiple paths at once,
-- this helper will return the C namespace of the nvim library.
local function cimport(...)
@@ -66,17 +74,8 @@ local function cimport(...)
return libnvim
end
- local body = nil
- for _ = 1, 10 do
- local stream = Preprocess.preprocess_stream(unpack(paths))
- body = stream:read("*a")
- stream:close()
- if body ~= nil then break end
- end
-
- if body == nil then
- print("ERROR: helpers.lua: Preprocess.preprocess_stream():read() returned empty")
- end
+ local body
+ body, previous_defines = Preprocess.preprocess(previous_defines, unpack(paths))
-- format it (so that the lines are "unique" statements), also filter out
-- Objective-C blocks
@@ -125,6 +124,67 @@ local function cppimport(path)
return cimport(Paths.test_include_path .. '/' .. path)
end
+local function alloc_log_new()
+ local log = {
+ log={},
+ lib=cimport('./src/nvim/memory.h'),
+ original_functions={},
+ null={['\0:is_null']=true},
+ }
+ local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
+ function log:save_original_functions()
+ for _, funcname in ipairs(allocator_functions) do
+ self.original_functions[funcname] = self.lib['mem_' .. funcname]
+ end
+ end
+ function log:set_mocks()
+ for _, k in ipairs(allocator_functions) do
+ do
+ local kk = k
+ self.lib['mem_' .. k] = function(...)
+ local log_entry = {func=kk, args={...}}
+ self.log[#self.log + 1] = log_entry
+ if kk == 'free' then
+ self.original_functions[kk](...)
+ else
+ log_entry.ret = self.original_functions[kk](...)
+ end
+ for i, v in ipairs(log_entry.args) do
+ if v == nil then
+ -- XXX This thing thinks that {NULL} ~= {NULL}.
+ log_entry.args[i] = self.null
+ end
+ end
+ if self.hook then self:hook(log_entry) end
+ if log_entry.ret then
+ return log_entry.ret
+ end
+ end
+ end
+ end
+ end
+ function log:clear()
+ self.log = {}
+ end
+ function log:check(exp)
+ eq(exp, self.log)
+ self:clear()
+ end
+ function log:restore_original_functions()
+ for k, v in pairs(self.original_functions) do
+ self.lib['mem_' .. k] = v
+ end
+ end
+ function log:before_each()
+ log:save_original_functions()
+ log:set_mocks()
+ end
+ function log:after_each()
+ log:restore_original_functions()
+ end
+ return log
+end
+
cimport('./src/nvim/types.h')
-- take a pointer to a C-allocated string and return an interned
@@ -136,7 +196,7 @@ end
local cstr = ffi.typeof('char[?]')
local function to_cstr(string)
- return cstr((string.len(string)) + 1, string)
+ return cstr(#string + 1, string)
end
-- initialize some global variables, this is still necessary to unit test
@@ -149,12 +209,6 @@ do
main.event_init()
end
--- C constants.
-local NULL = ffi.cast('void*', 0)
-
-local OK = 1
-local FAIL = 0
-
return {
cimport = cimport,
cppimport = cppimport,
@@ -168,5 +222,6 @@ return {
to_cstr = to_cstr,
NULL = NULL,
OK = OK,
- FAIL = FAIL
+ FAIL = FAIL,
+ alloc_log_new = alloc_log_new,
}
diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua
index 9b76834383..ccaf0228ab 100644
--- a/test/unit/path_spec.lua
+++ b/test/unit/path_spec.lua
@@ -336,6 +336,17 @@ describe('more path function', function()
eq(FAIL, result)
end)
+ it('fails safely if given length is wrong #5737', function()
+ local force_expansion = 1
+ local filename = 'foo/bar/bazzzzzzz/buz/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a'
+ local too_short_len = 8
+ local buf = cstr(too_short_len, '')
+ local result = path.vim_FullName(filename, buf, too_short_len, force_expansion)
+ local expected = string.sub(filename, 1, (too_short_len - 1))
+ eq(expected, (ffi.string(buf)))
+ eq(FAIL, result)
+ end)
+
it('uses the filename if the filename is a URL', function()
local force_expansion = 1
local filename = 'http://www.neovim.org'
diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua
index 10ba997758..1c9b290462 100644
--- a/test/unit/preprocess.lua
+++ b/test/unit/preprocess.lua
@@ -7,22 +7,22 @@ local ccs = {}
local env_cc = os.getenv("CC")
if env_cc then
- table.insert(ccs, {path = "/usr/bin/env " .. tostring(env_cc), type = "gcc"})
+ table.insert(ccs, {path = {"/usr/bin/env", env_cc}, type = "gcc"})
end
if ffi.os == "Windows" then
- table.insert(ccs, {path = "cl", type = "msvc"})
+ table.insert(ccs, {path = {"cl"}, type = "msvc"})
end
-table.insert(ccs, {path = "/usr/bin/env cc", type = "gcc"})
-table.insert(ccs, {path = "/usr/bin/env gcc", type = "gcc"})
-table.insert(ccs, {path = "/usr/bin/env gcc-4.9", type = "gcc"})
-table.insert(ccs, {path = "/usr/bin/env gcc-4.8", type = "gcc"})
-table.insert(ccs, {path = "/usr/bin/env gcc-4.7", type = "gcc"})
-table.insert(ccs, {path = "/usr/bin/env clang", type = "clang"})
-table.insert(ccs, {path = "/usr/bin/env icc", type = "gcc"})
+table.insert(ccs, {path = {"/usr/bin/env", "cc"}, type = "gcc"})
+table.insert(ccs, {path = {"/usr/bin/env", "gcc"}, type = "gcc"})
+table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.9"}, type = "gcc"})
+table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.8"}, type = "gcc"})
+table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.7"}, type = "gcc"})
+table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"})
+table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"})
-local quote_me = '[^%w%+%-%=%@%_%/]' -- complement (needn't quote)
+local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote)
local function shell_quote(str)
if string.find(str, quote_me) or str == '' then
return "'" .. string.gsub(str, "'", [['"'"']]) .. "'"
@@ -61,12 +61,12 @@ end
-- will produce a string that represents a meta C header file that includes
-- all the passed in headers. I.e.:
--
--- headerize({"stdio.h", "math.h", true}
+-- headerize({"stdio.h", "math.h"}, true)
-- produces:
-- #include <stdio.h>
-- #include <math.h>
--
--- headerize({"vim.h", "memory.h", false}
+-- headerize({"vim.h", "memory.h"}, false)
-- produces:
-- #include "vim.h"
-- #include "memory.h"
@@ -79,8 +79,7 @@ local function headerize(headers, global)
end
local formatted = {}
- for i = 1, #headers do
- local hdr = headers[i]
+ for _, hdr in ipairs(headers) do
formatted[#formatted + 1] = "#include " ..
tostring(pre) ..
tostring(hdr) ..
@@ -91,49 +90,78 @@ local function headerize(headers, global)
end
local Gcc = {
+ preprocessor_extra_flags = {},
+ get_defines_extra_flags = {'-std=c99', '-dM', '-E'},
+ get_declarations_extra_flags = {'-std=c99', '-P', '-E'},
+}
+
+function Gcc:define(name, args, val)
+ local define = '-D' .. name
+ if args ~= nil then
+ define = define .. '(' .. table.concat(args, ',') .. ')'
+ end
+ if val ~= nil then
+ define = define .. '=' .. val
+ end
+ self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = define
+end
+
+function Gcc:undefine(name)
+ self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = (
+ '-U' .. name)
+end
+
+function Gcc:init_defines()
-- preprocessor flags that will hopefully make the compiler produce C
-- declarations that the LuaJIT ffi understands.
- preprocessor_extra_flags = {
- '-D "aligned(ARGS)="',
- '-D "__attribute__(ARGS)="',
- '-D "__asm(ARGS)="',
- '-D "__asm__(ARGS)="',
- '-D "__inline__="',
- '-D "EXTERN=extern"',
- '-D "INIT(...)="',
- '-D_GNU_SOURCE',
- '-DINCLUDE_GENERATED_DECLARATIONS',
-
- -- Needed for FreeBSD
- '-D "_Thread_local="',
-
- -- Needed for macOS Sierra
- '-D "_Nullable="',
- '-D "_Nonnull="',
- '-U__BLOCKS__',
- }
-}
+ self:define('aligned', {'ARGS'}, '')
+ self:define('__attribute__', {'ARGS'}, '')
+ self:define('__asm', {'ARGS'}, '')
+ self:define('__asm__', {'ARGS'}, '')
+ self:define('__inline__', nil, '')
+ self:define('EXTERN', nil, 'extern')
+ self:define('INIT', {'...'}, '')
+ self:define('_GNU_SOURCE')
+ self:define('INCLUDE_GENERATED_DECLARATIONS')
+ self:define('UNIT_TESTING')
+ -- Needed for FreeBSD
+ self:define('_Thread_local', nil, '')
+ -- Needed for macOS Sierra
+ self:define('_Nullable', nil, '')
+ self:define('_Nonnull', nil, '')
+ self:undefine('__BLOCKS__')
+end
function Gcc:new(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
+ self:init_defines()
return obj
end
function Gcc:add_to_include_path(...)
- local paths = {...}
- for i = 1, #paths do
- local path = paths[i]
- local directive = '-I ' .. '"' .. path .. '"'
+ for i = 1, select('#', ...) do
+ local path = select(i, ...)
local ef = self.preprocessor_extra_flags
- ef[#ef + 1] = directive
+ ef[#ef + 1] = '-I' .. path
+ end
+end
+
+local function argss_to_cmd(...)
+ local cmd = ''
+ for i = 1, select('#', ...) do
+ for _, arg in ipairs(select(i, ...)) do
+ cmd = cmd .. ' ' .. shell_quote(arg)
+ end
end
+ return cmd
end
-- returns a list of the headers files upon which this file relies
function Gcc:dependencies(hdr)
- local out = io.popen(tostring(self.path) .. " -M " .. tostring(hdr) .. " 2>&1")
+ local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1'
+ local out = io.popen(cmd)
local deps = out:read("*a")
out:close()
if deps then
@@ -143,23 +171,51 @@ function Gcc:dependencies(hdr)
end
end
+local function repeated_call(...)
+ local cmd = argss_to_cmd(...)
+ for _ = 1, 10 do
+ local stream = io.popen(cmd)
+ local ret = stream:read('*a')
+ stream:close()
+ if ret then
+ return ret
+ end
+ end
+ print('ERROR: preprocess.lua: Failed to execute ' .. cmd .. ': nil return after 10 attempts')
+ return nil
+end
+
-- returns a stream representing a preprocessed form of the passed-in headers.
-- Don't forget to close the stream by calling the close() method on it.
-function Gcc:preprocess_stream(...)
+function Gcc:preprocess(previous_defines, ...)
-- create pseudo-header
local pseudoheader = headerize({...}, false)
- local defines = table.concat(self.preprocessor_extra_flags, ' ')
- local cmd = ("echo $hdr | " ..
- tostring(self.path) ..
- " " ..
- tostring(defines) ..
- " -std=c99 -P -E -"):gsub('$hdr', shell_quote(pseudoheader))
+ local pseudoheader_fname = 'tmp_pseudoheader.h'
+ local pseudoheader_file = io.open(pseudoheader_fname, 'w')
+ pseudoheader_file:write(previous_defines)
+ pseudoheader_file:write("\n")
+ pseudoheader_file:write(pseudoheader)
+ pseudoheader_file:flush()
+ pseudoheader_file:close()
+
+ local defines = repeated_call(self.path, self.preprocessor_extra_flags,
+ self.get_defines_extra_flags,
+ {pseudoheader_fname})
+
-- lfs = require("lfs")
-- print("CWD: #{lfs.currentdir!}")
-- print("CMD: #{cmd}")
-- io.stderr\write("CWD: #{lfs.currentdir!}\n")
-- io.stderr\write("CMD: #{cmd}\n")
- return io.popen(cmd)
+
+ local declarations = repeated_call(self.path, self.preprocessor_extra_flags,
+ self.get_declarations_extra_flags,
+ {pseudoheader_fname})
+
+ os.remove(pseudoheader_fname)
+
+ assert(declarations and defines)
+ return declarations, defines
end
local Clang = Gcc:new()
@@ -197,8 +253,8 @@ return {
includes = function(hdr)
return cc:dependencies(hdr)
end,
- preprocess_stream = function(...)
- return cc:preprocess_stream(...)
+ preprocess = function(...)
+ return cc:preprocess(...)
end,
add_to_include_path = function(...)
return cc:add_to_include_path(...)