aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--CMakeLists.txt19
-rw-r--r--CONTRIBUTING.md6
-rw-r--r--busted/outputHandlers/TAP.lua92
-rw-r--r--cmake/RunTests.cmake3
-rw-r--r--config/config.h.in2
-rw-r--r--runtime/CMakeLists.txt24
-rw-r--r--runtime/autoload/provider.vim18
-rw-r--r--runtime/autoload/provider/clipboard.vim22
-rw-r--r--runtime/autoload/provider/pythonx.vim16
-rw-r--r--runtime/autoload/tutor.vim197
-rw-r--r--runtime/doc/change.txt3
-rw-r--r--runtime/doc/digraph.txt3
-rw-r--r--runtime/doc/eval.txt85
-rw-r--r--runtime/doc/intro.txt3
-rw-r--r--runtime/doc/map.txt1
-rw-r--r--runtime/doc/nvim.txt2
-rw-r--r--runtime/doc/options.txt2
-rw-r--r--runtime/doc/starting.txt3
-rw-r--r--runtime/doc/term.txt154
-rw-r--r--runtime/doc/vim_diff.txt8
-rw-r--r--runtime/ftplugin/tutor.vim19
-rw-r--r--runtime/syntax/tutor.vim22
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor92
-rw-r--r--runtime/tutor/en/vim-01-beginner.tutor.json45
-rw-r--r--runtime/tutor/tutor.tutor118
-rw-r--r--runtime/tutor/tutor.tutor.json35
-rw-r--r--src/nvim/README.md39
-rw-r--r--src/nvim/buffer.c24
-rw-r--r--src/nvim/digraph.c1
-rw-r--r--src/nvim/eval.c36
-rw-r--r--src/nvim/eval.lua9
-rw-r--r--src/nvim/ex_cmds.c14
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_docmd.c32
-rw-r--r--src/nvim/ex_eval.c19
-rw-r--r--src/nvim/ex_eval.h27
-rw-r--r--src/nvim/ex_getln.c1
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua3
-rw-r--r--src/nvim/getchar.h15
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/log.c50
-rw-r--r--src/nvim/log.h4
-rw-r--r--src/nvim/mbyte.c12
-rw-r--r--src/nvim/memory.c2
-rw-r--r--src/nvim/menu.c294
-rw-r--r--src/nvim/menu.h47
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/msgpack_rpc/channel.c43
-rw-r--r--src/nvim/normal.c68
-rw-r--r--src/nvim/option.c39
-rw-r--r--src/nvim/os/shell.c12
-rw-r--r--src/nvim/quickfix.c96
-rw-r--r--src/nvim/regexp.c994
-rw-r--r--src/nvim/regexp_nfa.c221
-rw-r--r--src/nvim/screen.c4
-rw-r--r--src/nvim/testdir/runtest.vim12
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_bufwintabinfo.vim13
-rw-r--r--src/nvim/testdir/test_cmdline.vim28
-rw-r--r--src/nvim/testdir/test_expr.vim8
-rw-r--r--src/nvim/testdir/test_fileformat.vim17
-rw-r--r--src/nvim/testdir/test_goto.vim281
-rw-r--r--src/nvim/testdir/test_matchadd_conceal.vim1
-rw-r--r--src/nvim/testdir/test_nested_function.vim36
-rw-r--r--src/nvim/testdir/test_normal.vim26
-rw-r--r--src/nvim/testdir/test_quickfix.vim299
-rw-r--r--src/nvim/tui/tui.c23
-rw-r--r--src/nvim/ui.c25
-rw-r--r--src/nvim/ui_bridge.c28
-rw-r--r--src/nvim/version.c54
-rw-r--r--src/nvim/vim.h3
-rw-r--r--src/nvim/window.c22
-rw-r--r--test/functional/ex_cmds/menu_spec.lua325
-rw-r--r--test/functional/legacy/assert_spec.lua5
-rw-r--r--test/functional/ui/mouse_spec.lua4
76 files changed, 2810 insertions, 1511 deletions
diff --git a/.gitignore b/.gitignore
index fb506305b6..3a8994a5f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,9 @@ tags
/src/nvim/po/vim.pot
/src/nvim/po/*.ck
+# generated by tests with $NVIM_LOG_FILE set.
+/.nvimlog
+
# Files generated by scripts/vim-patch.sh
/.vim-src/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 141fa81e16..594f631ba0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -192,6 +192,16 @@ if(CMAKE_EXE_LINKER_FLAGS MATCHES "--sort-common" OR
string(REGEX REPLACE "-Wl($| )" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}")
endif()
+check_c_source_compiles("
+#include <execinfo.h>
+int main(void)
+{
+ void *trace[1];
+ int trace_size = backtrace(trace, 1);
+ return 0;
+}
+" HAVE_EXECINFO_BACKTRACE)
+
if(MSVC)
# XXX: /W4 gives too many warnings. #3241
add_definitions(/W3 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
@@ -240,6 +250,14 @@ if(HAS_DIAG_COLOR_FLAG)
add_definitions(-fdiagnostics-color=auto)
endif()
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.5")
+ # Array-bounds testing is broken in some GCC versions before 4.8.5.
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56273
+ add_definitions(-Wno-array-bounds)
+ endif()
+endif()
+
option(TRAVIS_CI_BUILD "Travis/QuickBuild CI. Extra flags will be set." OFF)
if(TRAVIS_CI_BUILD)
@@ -316,7 +334,6 @@ main(void)
return MSGPACK_OBJECT_FLOAT32;
}
" MSGPACK_HAS_FLOAT32)
-
if(MSGPACK_HAS_FLOAT32)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_MSGPACK_HAS_FLOAT32")
endif()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8580e8ef39..20bce8ca62 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,6 +27,7 @@ Reporting problems
- When reporting a crash, include a stacktrace.
- [Bisect][git-bisect] to the cause of a regression, if you are able. This is _extremely_ helpful.
- Check `$NVIM_LOG_FILE`, if it exists.
+- Include `cmake --system-information` for **build** issues.
Pull requests ("PRs")
---------------------
@@ -94,8 +95,9 @@ and [AppVeyor].
See [Building Neovim#running-tests][wiki-run-tests] to run tests locally.
Passing locally doesn't guarantee passing the CI build, because of the
different compilers and platforms tested against.
-- CI runs [ASan] and other analyzers. To run valgrind locally:
- `VALGRIND=1 make test`
+- CI runs [ASan] and other analyzers.
+ - To run valgrind locally: `VALGRIND=1 make test`
+ - To run Clang ASan/UBSan locally: `CC=clang make CMAKE_FLAGS="-DCLANG_ASAN_UBSAN=ON"`
- The `lint` build ([#3174][3174]) checks modified lines _and their immediate
neighbors_. This is to encourage incrementally updating the legacy style to
meet our style guidelines.
diff --git a/busted/outputHandlers/TAP.lua b/busted/outputHandlers/TAP.lua
new file mode 100644
index 0000000000..ff93a7cc75
--- /dev/null
+++ b/busted/outputHandlers/TAP.lua
@@ -0,0 +1,92 @@
+-- TODO(jkeyes): remove this and use the upstream version as soon as it is
+-- available in a release of busted.
+
+local pretty = require 'pl.pretty'
+
+return function(options)
+ local busted = require 'busted'
+ local handler = require 'busted.outputHandlers.base'()
+
+ local success = 'ok %u - %s'
+ local failure = 'not ' .. success
+ local skip = 'ok %u - # SKIP %s'
+ local counter = 0
+
+ handler.suiteReset = function()
+ counter = 0
+ return nil, true
+ end
+
+ handler.suiteEnd = function()
+ print('1..' .. counter)
+ io.flush()
+ return nil, true
+ end
+
+ local function showFailure(t)
+ local message = t.message
+ local trace = t.trace or {}
+
+ if message == nil then
+ message = 'Nil error'
+ elseif type(message) ~= 'string' then
+ message = pretty.write(message)
+ end
+
+ print(failure:format(counter, t.name))
+ print('# ' .. t.element.trace.short_src .. ' @ ' .. t.element.trace.currentline)
+ if t.randomseed then print('# Random seed: ' .. t.randomseed) end
+ print('# Failure message: ' .. message:gsub('\n', '\n# '))
+ if options.verbose and trace.traceback then
+ print('# ' .. trace.traceback:gsub('^\n', '', 1):gsub('\n', '\n# '))
+ end
+ end
+
+ handler.testStart = function(element, parent)
+ local trace = element.trace
+ if options.verbose and trace and trace.short_src then
+ local fileline = trace.short_src .. ' @ ' .. trace.currentline .. ': '
+ local testName = fileline .. handler.getFullName(element)
+ print('# ' .. testName)
+ end
+ io.flush()
+
+ return nil, true
+ end
+
+ handler.testEnd = function(element, parent, status, trace)
+ counter = counter + 1
+ if status == 'success' then
+ local t = handler.successes[#handler.successes]
+ print(success:format(counter, t.name))
+ elseif status == 'pending' then
+ local t = handler.pendings[#handler.pendings]
+ print(skip:format(counter, (t.message or t.name)))
+ elseif status == 'failure' then
+ showFailure(handler.failures[#handler.failures])
+ elseif status == 'error' then
+ showFailure(handler.errors[#handler.errors])
+ end
+ io.flush()
+
+ return nil, true
+ end
+
+ handler.error = function(element, parent, message, debug)
+ if element.descriptor ~= 'it' then
+ counter = counter + 1
+ showFailure(handler.errors[#handler.errors])
+ end
+ io.flush()
+
+ return nil, true
+ end
+
+ busted.subscribe({ 'suite', 'reset' }, handler.suiteReset)
+ busted.subscribe({ 'suite', 'end' }, handler.suiteEnd)
+ busted.subscribe({ 'test', 'start' }, handler.testStart, { predicate = handler.cancelOnPending })
+ busted.subscribe({ 'test', 'end' }, handler.testEnd, { predicate = handler.cancelOnPending })
+ busted.subscribe({ 'error' }, handler.error)
+
+ return handler
+end
diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake
index 5f818cc9ad..95c06aeb94 100644
--- a/cmake/RunTests.cmake
+++ b/cmake/RunTests.cmake
@@ -1,3 +1,6 @@
+# Set LC_ALL to meet expectations of some locale-sensitive tests.
+set(ENV{LC_ALL} "en_US.UTF-8")
+
set(ENV{VIMRUNTIME} ${WORKING_DIR}/runtime)
set(ENV{NVIM_RPLUGIN_MANIFEST} ${WORKING_DIR}/Xtest_rplugin_manifest)
set(ENV{XDG_CONFIG_HOME} ${WORKING_DIR}/Xtest_xdg/config)
diff --git a/config/config.h.in b/config/config.h.in
index f26b5a50a8..962eefd7a7 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -68,4 +68,6 @@
#cmakedefine ORDER_BIG_ENDIAN
#define ENDIAN_INCLUDE_FILE <@ENDIAN_INCLUDE_FILE@>
+#cmakedefine HAVE_EXECINFO_BACKTRACE
+
#endif // AUTO_CONFIG_H
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index 69498dc1a1..a9efc90b87 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -100,20 +100,6 @@ add_custom_target(
${GENERATED_PACKAGE_TAGS}
)
-# Optional targets for nvim.desktop file and icon.
-find_program(XDG_MENU_PRG xdg-desktop-menu)
-find_program(XDG_ICON_PRG xdg-icon-resource)
-if(XDG_MENU_PRG)
- add_custom_target(desktop-file
- COMMAND xdg-desktop-menu install --novendor ${PROJECT_SOURCE_DIR}/runtime/nvim.desktop)
- # add_dependencies(runtime desktop-file)
-endif()
-if(XDG_ICON_PRG)
- add_custom_target(desktop-icon
- COMMAND xdg-icon-resource install --novendor --size 128 ${PROJECT_SOURCE_DIR}/runtime/nvim.png)
- # add_dependencies(runtime desktop-icon)
-endif()
-
# CMake is painful here. It will create the destination using the user's
# current umask, and we don't want that. And we don't just want to install
# the target directory, as it will mess with existing permissions. So this
@@ -128,6 +114,16 @@ install_helper(
FILES ${GENERATED_SYN_VIM}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/syntax/vim)
+if(NOT APPLE)
+ install_helper(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.desktop
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
+
+ install_helper(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/nvim.png
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps)
+endif()
+
file(GLOB_RECURSE RUNTIME_PROGRAMS
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.awk *.sh *.bat)
diff --git a/runtime/autoload/provider.vim b/runtime/autoload/provider.vim
new file mode 100644
index 0000000000..a4d5241b57
--- /dev/null
+++ b/runtime/autoload/provider.vim
@@ -0,0 +1,18 @@
+" Common functionality for providers
+
+let s:stderr = {}
+
+function! provider#stderr_collector(chan_id, data, event) dict
+ let stderr = get(s:stderr, a:chan_id, [''])
+ let stderr[-1] .= a:data[0]
+ call extend(stderr, a:data[1:])
+ let s:stderr[a:chan_id] = stderr
+endfunction
+
+function! provider#clear_stderr(chan_id)
+ silent! call delete(s:stderr, a:chan_id)
+endfunction
+
+function! provider#get_stderr(chan_id)
+ return get(s:stderr, a:chan_id, [])
+endfunction
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index a67681d28e..8eb694e9fa 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -6,7 +6,7 @@ let s:paste = {}
" When caching is enabled, store the jobid of the xclip/xsel process keeping
" ownership of the selection, so we know how long the cache is valid.
-let s:selection = { 'owner': 0, 'data': [] }
+let s:selection = { 'owner': 0, 'data': [], 'on_stderr': function('provider#stderr_collector') }
function! s:selection.on_exit(jobid, data, event) abort
" At this point this nvim instance might already have launched
@@ -14,6 +14,13 @@ function! s:selection.on_exit(jobid, data, event) abort
if self.owner == a:jobid
let self.owner = 0
endif
+ if a:data != 0
+ let stderr = provider#get_stderr(a:jobid)
+ echohl WarningMsg
+ echomsg 'clipboard: error invoking '.get(self.argv, 0, '?').': '.join(stderr)
+ echohl None
+ endif
+ call provider#clear_stderr(a:jobid)
endfunction
let s:selections = { '*': s:selection, '+': copy(s:selection)}
@@ -135,18 +142,19 @@ function! s:clipboard.set(lines, regtype, reg) abort
end
let selection.data = [a:lines, a:regtype]
let argv = split(s:copy[a:reg], " ")
+ let selection.argv = argv
let selection.detach = s:cache_enabled
let selection.cwd = "/"
let jobid = jobstart(argv, selection)
- if jobid <= 0
+ if jobid > 0
+ call jobsend(jobid, a:lines)
+ call jobclose(jobid, 'stdin')
+ let selection.owner = jobid
+ else
echohl WarningMsg
- echo "clipboard: error when invoking provider"
+ echomsg 'clipboard: failed to execute: '.(s:copy[a:reg])
echohl None
- return 0
endif
- call jobsend(jobid, a:lines)
- call jobclose(jobid, 'stdin')
- let selection.owner = jobid
endfunction
function! provider#clipboard#Call(method, args) abort
diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim
index 2f64c22c71..7285ed43ea 100644
--- a/runtime/autoload/provider/pythonx.vim
+++ b/runtime/autoload/provider/pythonx.vim
@@ -5,17 +5,7 @@ endif
let s:loaded_pythonx_provider = 1
-let s:stderr = {}
-let s:job_opts = {'rpc': v:true}
-
-" TODO(bfredl): this logic is common and should be builtin
-function! s:job_opts.on_stderr(chan_id, data, event)
- let stderr = get(s:stderr, a:chan_id, [''])
- let last = remove(stderr, -1)
- let a:data[0] = last.a:data[0]
- call extend(stderr, a:data)
- let s:stderr[a:chan_id] = stderr
-endfunction
+let s:job_opts = {'rpc': v:true, 'on_stderr': function('provider#stderr_collector')}
function! provider#pythonx#Require(host) abort
let ver = (a:host.orig_name ==# 'python') ? 2 : 3
@@ -38,9 +28,11 @@ function! provider#pythonx#Require(host) abort
catch
echomsg v:throwpoint
echomsg v:exception
- for row in get(s:stderr, channel_id, [])
+ for row in provider#get_stderr(channel_id)
echomsg row
endfor
+ finally
+ call provider#clear_stderr(channel_id)
endtry
throw remote#host#LoadErrorForHost(a:host.orig_name,
\ '$NVIM_PYTHON_LOG_FILE')
diff --git a/runtime/autoload/tutor.vim b/runtime/autoload/tutor.vim
index 43d8a87886..56e2283465 100644
--- a/runtime/autoload/tutor.vim
+++ b/runtime/autoload/tutor.vim
@@ -15,30 +15,17 @@ function! tutor#SetupVim()
endif
endfunction
-" Mappings: {{{1
-
-function! s:CheckMaps()
- nmap
+" Loads metadata file, if available
+function! tutor#LoadMetadata()
+ let b:tutor_metadata = json_decode(join(readfile(expand('%').'.json'), "\n"))
endfunction
-function! s:MapKeyWithRedirect(key, cmd)
- if maparg(a:key) !=# ''
- redir => l:keys
- silent call s:CheckMaps()
- redir END
- let l:key_list = split(l:keys, '\n')
-
- let l:raw_map = filter(copy(l:key_list), "v:val =~# '\\* ".a:key."'")
- if len(l:raw_map) == 0
- exe "nnoremap <buffer> <expr> ".a:key." ".a:cmd
- return
- endif
- let l:map_data = split(l:raw_map[0], '\s*')
+" Mappings: {{{1
- exe "nnoremap <buffer> <expr> ".l:map_data[0]." ".a:cmd
- else
- exe "nnoremap <buffer> <expr> ".a:key." ".a:cmd
- endif
+function! tutor#SetNormalMappings()
+ nnoremap <silent> <buffer> <CR> :call tutor#FollowLink(0)<cr>
+ nnoremap <silent> <buffer> <2-LeftMouse> :call tutor#MouseDoubleClick()<cr>
+ nnoremap <buffer> >> :call tutor#InjectCommand()<cr>
endfunction
function! tutor#MouseDoubleClick()
@@ -46,7 +33,7 @@ function! tutor#MouseDoubleClick()
normal! zo
else
if match(getline('.'), '^#\{1,} ') > -1
- normal! zc
+ silent normal! zc
else
call tutor#FollowLink(0)
endif
@@ -59,114 +46,6 @@ function! tutor#InjectCommand()
redraw | echohl WarningMsg | echon "tutor: ran" | echohl None | echon " " | echohl Statement | echon l:cmd
endfunction
-function! tutor#SetNormalMappings()
- call s:MapKeyWithRedirect('l', 'tutor#ForwardSkipConceal(v:count1)')
- call s:MapKeyWithRedirect('h', 'tutor#BackwardSkipConceal(v:count1)')
- call s:MapKeyWithRedirect('<right>', 'tutor#ForwardSkipConceal(v:count1)')
- call s:MapKeyWithRedirect('<left>', 'tutor#BackwardSkipConceal(v:count1)')
-
- nnoremap <silent> <buffer> <CR> :call tutor#FollowLink(0)<cr>
- nnoremap <silent> <buffer> <2-LeftMouse> :call tutor#MouseDoubleClick()<cr>
- nnoremap <buffer> >> :call tutor#InjectCommand()<cr>
-endfunction
-
-function! tutor#SetSampleTextMappings()
- noremap <silent> <buffer> A :if match(getline('.'), '^--->') > -1 \| call search('\s{\@=', 'Wc') \| startinsert \| else \| startinsert! \| endif<cr>
- noremap <silent> <buffer> $ :if match(getline('.'), '^--->') > -1 \| call search('.\s{\@=', 'Wc') \| else \| call search('$', 'Wc') \| endif<cr>
- onoremap <silent> <buffer> $ :if match(getline('.'), '^--->') > -1 \| call search('.\s{\@=', 'Wc') \| else \| call search('$', 'Wc') \| endif<cr>
- noremap <silent> <buffer> ^ :if match(getline('.'), '^--->') > -1 \| call search('\(--->\s\)\@<=.', 'bcW') \| else \| call search('^', 'bcW') \|endif<cr>
- onoremap <silent> <buffer> ^ :if match(getline('.'), '^--->') > -1 \| call search('\(--->\s\)\@<=.', 'bcW') \| else \| call search('^', 'bcW') \|endif<cr>
- nmap <silent> <buffer> 0 ^<esc>
- nmap <silent> <buffer> <Home> ^<esc>
- nmap <silent> <buffer> <End> $
- imap <silent> <buffer> <Home> <esc>^<esc>:startinsert<cr>
- imap <silent> <buffer> <End> <esc>$:startinsert<cr>
- noremap <silent> <buffer> I :exe "normal! 0" \| startinsert<cr>
-endfunction
-
-" Navigation: {{{1
-
-" taken from http://stackoverflow.com/a/24224578
-
-function! tutor#ForwardSkipConceal(count)
- let cnt=a:count
- let mvcnt=0
- let c=col('.')
- let l=line('.')
- let lc=col('$')
- let line=getline('.')
- while cnt
- if c>=lc
- let mvcnt+=cnt
- break
- endif
- if stridx(&concealcursor, 'n')==-1
- let isconcealed=0
- else
- let [isconcealed, cchar, group] = synconcealed(l, c)
- endif
- if isconcealed
- let cnt-=strchars(cchar)
- let oldc=c
- let c+=1
- while c < lc
- let [isconcealed2, cchar2, group2] = synconcealed(l, c)
- if !isconcealed2 || cchar2 != cchar
- break
- endif
- let c+= 1
- endwhile
- let mvcnt+=strchars(line[oldc-1:c-2])
- else
- let cnt-=1
- let mvcnt+=1
- let c+=len(matchstr(line[c-1:], '.'))
- endif
- endwhile
- return mvcnt.'l'
-endfunction
-
-function! tutor#BackwardSkipConceal(count)
- let cnt=a:count
- let mvcnt=0
- let c=col('.')
- let l=line('.')
- let lc=0
- let line=getline('.')
- while cnt
- if c<=1
- let mvcnt+=cnt
- break
- endif
- if stridx(&concealcursor, 'n')==-1 || c == 0
- let isconcealed=0
- else
- let [isconcealed, cchar, group]=synconcealed(l, c-1)
- endif
- if isconcealed
- let cnt-=strchars(cchar)
- let oldc=c
- let c-=1
- while c>1
- let [isconcealed2, cchar2, group2] = synconcealed(l, c-1)
- if !isconcealed2 || cchar2 != cchar
- break
- endif
- let c-=1
- endwhile
- let c = max([c, 1])
- let mvcnt+=strchars(line[c-1:oldc-2])
- else
- let cnt-=1
- let mvcnt+=1
- let c-=len(matchstr(line[:c-2], '.$'))
- endif
- endwhile
- return mvcnt.'h'
-endfunction
-
-" Hypertext: {{{1
-
function! tutor#FollowLink(force)
let l:stack_s = join(map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")'), '')
if l:stack_s =~# 'tutorLink'
@@ -209,42 +88,40 @@ function! tutor#InfoText()
return join(l:info_parts, " ")
endfunction
-" Marks {{{1
-function! tutor#PlaceXMarks()
- call cursor(1, 1)
- let b:tutor_sign_id = 1
- while search('^--->', 'W') > 0
- call tutor#CheckText(getline('.'))
- let b:tutor_sign_id+=1
- endwhile
- call cursor(1, 1)
+
+" Marks: {{{1
+
+function! tutor#ApplyMarks()
+ hi! link tutorExpect Special
+ if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
+ let b:tutor_sign_id = 1
+ for expct in keys(b:tutor_metadata['expect'])
+ let lnum = eval(expct)
+ call matchaddpos('tutorExpect', [lnum])
+ call tutor#CheckLine(lnum)
+ endfor
+ endif
endfunction
-function! tutor#CheckText(text)
- if match(a:text, '{expect:ANYTHING}\s*$') == -1
- if match(getline('.'), '^--->\s*$') > -1
- exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorbad buffer=".bufnr('%')
- else
- if match(getline('.'), '|expect:.\+|') == -1
- let l:cur_text = matchstr(a:text, '---> \zs.\{-}\ze {expect:')
- let l:expected_text = matchstr(a:text, '{expect:\zs.*\ze}\s*$')
- else
- let l:cur_text = matchstr(a:text, '---> \zs.\{-}\ze |expect:')
- let l:expected_text = matchstr(a:text, '|expect:\zs.*\ze|\s*$')
- endif
- if l:cur_text ==# l:expected_text
- exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorok buffer=".bufnr('%')
- else
- exe "sign place ".b:tutor_sign_id." line=".line('.')." name=tutorbad buffer=".bufnr('%')
- endif
+function! tutor#ApplyMarksOnChanged()
+ if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
+ let lnum = line('.')
+ if index(keys(b:tutor_metadata['expect']), string(lnum)) > -1
+ call tutor#CheckLine(lnum)
endif
endif
endfunction
-function! tutor#OnTextChanged()
- let l:text = getline('.')
- if match(l:text, '^--->') > -1
- call tutor#CheckText(l:text)
+function! tutor#CheckLine(line)
+ if exists('b:tutor_metadata') && has_key(b:tutor_metadata, 'expect')
+ let bufn = bufnr('%')
+ let ctext = getline(a:line)
+ if b:tutor_metadata['expect'][string(a:line)] == -1 || ctext ==# b:tutor_metadata['expect'][string(a:line)]
+ exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorok buffer=".bufn
+ else
+ exe "sign place ".b:tutor_sign_id." line=".a:line." name=tutorbad buffer=".bufn
+ endif
+ let b:tutor_sign_id+=1
endif
endfunction
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 6af3c4d5d7..f82d61370c 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -861,8 +861,7 @@ Exceptions:
Substitute with an expression *sub-replace-expression*
*sub-replace-\=* *s/\=*
When the substitute string starts with "\=" the remainder is interpreted as an
-expression. This does not work recursively: a |substitute()| function inside
-the expression cannot use "\=" for the substitute string.
+expression.
The special meaning for characters as mentioned at |sub-replace-special| does
not apply except for "<CR>". A <NL> character is used as a line break, you
diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt
index 89351c5b4f..43f8ccab06 100644
--- a/runtime/doc/digraph.txt
+++ b/runtime/doc/digraph.txt
@@ -143,7 +143,7 @@ a standard meaning:
Two 2 Hook
Nine 9 Horn
- Equals = Cyrillic (= used as second char)
+ Equals = Cyrillic (= used as second char)
Asterisk * Greek
Percent sign % Greek/Cyrillic special
Plus + smalls: Arabic, capitals: Hebrew
@@ -922,6 +922,7 @@ char digraph hex dec official name ~
† /- 2020 8224 DAGGER
‡ /= 2021 8225 DOUBLE DAGGER
‥ .. 2025 8229 TWO DOT LEADER
+… ,. 2026 8230 HORIZONTAL ELLIPSIS
‰ %0 2030 8240 PER MILLE SIGN
′ 1' 2032 8242 PRIME
″ 2' 2033 8243 DOUBLE PRIME
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 3a928c97ec..e1d156e91f 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -4046,7 +4046,9 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
locale locale names (as output of locale -a)
mapping mapping name
menu menus
+ messages |:messages| suboptions
option options
+ packadd optional package |pack-add| names
shellcmd Shell command
sign |:sign| suboptions
syntax syntax file names |'syntax'|
@@ -4273,7 +4275,8 @@ getqflist([{what}]) *getqflist()*
If the optional {what} dictionary argument is supplied, then
returns only the items listed in {what} as a dictionary. The
following string items are supported in {what}:
- nr get information for this quickfix list
+ nr get information for this quickfix list; zero
+ means the current quickfix list
title get the list title
winid get the |window-ID| (if opened)
all all of the above quickfix properties
@@ -5526,6 +5529,51 @@ max({expr}) Return the maximum value of all items in {expr}.
items in {expr} cannot be used as a Number this results in
an error. An empty |List| or |Dictionary| results in zero.
+menu_get({path}, {modes}) *menu_get()*
+ Returns a |List| of |Dictionaries| describing |menus| (defined
+ by |:menu|, |:amenu|, etc.).
+ {path} limits the result to a subtree of the menu hierarchy
+ (empty string matches all menus). E.g. to get items in the
+ "File" menu subtree: >
+ :echo menu_get('File','')
+<
+ {modes} is a string of zero or more modes (see |maparg()| or
+ |creating-menus| for the list of modes). "a" means "all".
+
+ For example: >
+ nnoremenu &Test.Test inormal
+ inoremenu Test.Test insert
+ vnoremenu Test.Test x
+ echo menu_get("")
+<
+ returns something like this:
+>
+ [ {
+ "hidden": 0,
+ "name": "Test",
+ "priority": 500,
+ "shortcut": 84,
+ "submenus": [ {
+ "hidden": 0,
+ "mappings": {
+ i": {
+ "enabled": 1,
+ "noremap": 1,
+ "rhs": "insert",
+ "sid": 1,
+ "silent": 0
+ },
+ n": { ... },
+ s": { ... },
+ v": { ... }
+ },
+ "name": "Test",
+ "priority": 500,
+ "shortcut": 0
+ } ]
+ } ]
+<
+
*min()*
min({expr}) Return the minimum value of all items in {expr}.
{expr} can be a list or a dictionary. For a dictionary,
@@ -5984,9 +6032,9 @@ range({expr} [, {max} [, {stride}]]) *range()*
*readfile()*
readfile({fname} [, {binary} [, {max}]])
Read file {fname} and return a |List|, each line of the file
- as an item. Lines broken at NL characters. Macintosh files
- separated with CR will result in a single long line (unless a
- NL appears somewhere).
+ as an item. Lines are broken at NL characters. Macintosh
+ files separated with CR will result in a single long line
+ (unless a NL appears somewhere).
All NUL characters are replaced with a NL character.
When {binary} contains "b" binary mode is used:
- When the last line ends in a NL an extra empty list item is
@@ -6504,15 +6552,27 @@ serverlist() *serverlist()*
nvim --cmd "echo serverlist()" --cmd "q"
<
serverstart([{address}]) *serverstart()*
- Opens a TCP socket (IPv4/IPv6), Unix domain socket (Unix),
- or named pipe (Windows) at {address} for clients to connect
- to and returns {address}.
+ Opens a socket or named pipe at {address} and listens for
+ |RPC| messages. Clients can send |API| commands to the address
+ to control Nvim. Returns the address string.
- If {address} contains `:`, a TCP socket is used. Everything in
- front of the last occurrence of `:` is the IP or hostname,
- everything after it the port. If the port is empty or `0`,
- a random port will be assigned.
+ If {address} does not contain a colon ":" it is interpreted as
+ a named pipe or Unix domain socket path.
+ Example: >
+ if has('win32')
+ call serverstart('\\.\pipe\nvim-pipe-1234')
+ else
+ call serverstart('nvim.sock')
+ endif
+<
+ If {address} contains a colon ":" it is interpreted as a TCP
+ address where the last ":" separates the host and port.
+ Assigns a random port if it is empty or 0. Supports IPv4/IPv6.
+
+ Example: >
+ :call serverstart('::1:12345')
+<
If no address is given, it is equivalent to: >
:call serverstart(tempname())
@@ -7291,6 +7351,9 @@ submatch({nr}[, {list}]) *submatch()* *E935*
|substitute()| this list will always contain one or zero
items, since there are no real line breaks.
+ When substitute() is used recursively only the submatches in
+ the current (deepest) call can be obtained.
+
Example: >
:s/\d\+/\=submatch(0) + 1/
< This finds the first number in the line and adds one to it.
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index 88d04aa76b..b9cc94ce5f 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -249,8 +249,7 @@ Vi "the original". Without further remarks this is the version
of Vi that appeared in Sun OS 4.x. ":version" returns
"Version 3.7, 6/7/85". Sometimes other versions are referred
to. Only runs under Unix. Source code only available with a
- license. More information on Vi can be found through:
- http://vi-editor.org [doesn't currently work...]
+ license.
*Nvi*
Nvi The "New" Vi. The version of Vi that comes with BSD 4.4 and FreeBSD.
Very good compatibility with the original Vi, with a few extensions.
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index bfcf621cb8..944f7474be 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -1202,6 +1202,7 @@ completion can be enabled:
-complete=locale locale names (as output of locale -a)
-complete=mapping mapping name
-complete=menu menus
+ -complete=messages |:messages| suboptions
-complete=option options
-complete=packadd optional package |pack-add| names
-complete=shellcmd Shell command
diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt
index 29059f83d9..f3f4305ad5 100644
--- a/runtime/doc/nvim.txt
+++ b/runtime/doc/nvim.txt
@@ -6,6 +6,8 @@
Nvim *nvim* *nvim-intro*
+Nvim is based on Vim by Bram Moolenaar.
+
If you are new to Vim see |help.txt|, or type ":Tutor".
If you already use Vim see |nvim-from-vim| for a quickstart.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index dc968cd666..bc9705d116 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2736,7 +2736,7 @@ A jump table for the options with a short description can be found at |Q_op|.
'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20")
global
Configures the cursor style for each mode. Works in the GUI and many
- terminals. See |cursor-shape| for details.
+ terminals. See |tui-cursor-shape|.
To disable cursor-styling, reset the option: >
:set guicursor=
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index d869516bff..0b72ec9960 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -349,6 +349,9 @@ argument.
*--api-info*
--api-info Print msgpack-encoded |api-metadata| and exit.
+ *--headless*
+--headless Do not start the built-in UI.
+
==============================================================================
2. Initialization *initialization* *startup*
diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt
index bce944eab5..73c8956085 100644
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -1,76 +1,62 @@
*term.txt* Nvim
- VIM REFERENCE MANUAL by Bram Moolenaar
+ NVIM REFERENCE MANUAL
Terminal information
-Vim uses information about the terminal you are using to fill the screen and
-recognize what keys you hit. If this information is not correct, the screen
-may be messed up or keys may not be recognized. The actions which have to be
-performed on the screen are accomplished by outputting a string of
-characters.
+Nvim (except in |--headless| mode) uses information about the terminal you are
+using to present a built-in UI. If that information is not correct, the
+screen may be messed up or keys may not be recognized.
Type <M-]> to see the table of contents.
==============================================================================
Startup *startup-terminal*
-When Vim is started a default terminal type is assumed. for MS-DOS this is
-the pc terminal, for Unix an ansi terminal.
+Nvim (except in |--headless| mode) guesses a terminal type when it starts.
+|$TERM| is the primary hint that determines the terminal type.
*terminfo* *E557* *E558* *E559*
-On Unix the terminfo database is used. There is no access to the terminfo
-settings with |:set|.
+The terminfo database is used if available.
The Unibilium library (used by Nvim to read terminfo) allows you to override
-an out-of-date system terminfo database with one in your $HOME/.terminfo/
-directory, in part or in whole.
+the system terminfo with one in $HOME/.terminfo/ directory, in part or in
+whole.
-Building your own up-to-date terminfo database is usually as simple as running
-this as a non-superuser:
+Building your own terminfo is usually as simple as running this as
+a non-superuser:
>
- wget http://invisible-island.net/datafiles/current/terminfo.src.gz
+ curl -LO http://invisible-island.net/datafiles/current/terminfo.src.gz
gunzip terminfo.src.gz
tic terminfo.src
<
- *TERM*
-If you experience terminal difficulties, first ensure that you have set the
-correct terminal type in your $TERM environment variable so that Nvim is
-pulling the correct entry from the terminfo database in the first place.
+ *$TERM*
+The $TERM environment variable must match the terminal you are using!
+Otherwise Nvim cannot know what sequences your terminal expects, and weird
+or sub-optimal behavior will result (scrolling quirks, wrong colors, etc.).
-Per the terminfo source file from ncurses:
+$TERM is also important because it is mirrored by SSH to the remote session,
+unlike other common client-end environment variables ($COLORTERM,
+$XTERM_VERSION, $VTE_VERSION, $KONSOLE_PROFILE_NAME, $TERM_PROGRAM, ...).
- For these terminals Set $TERM to |builtin-terms|?
+ For this terminal Set $TERM to |builtin-terms|?
iTerm.app "iterm" or "iTerm.app" Y
anything libvte based "vte" or "vte-256color" Y
- (e.g. GNOME Terminal) ("gnome" and "gnome-256color" are
- available as aliases for these)
+ (e.g. GNOME Terminal) (aliases: "gnome", "gnome-256color")
tmux "tmux" or "tmux-256color" Y
screen "screen" or "screen-256color" Y
PuTTY "putty" or "putty-256color" Y
Terminal.app "nsterm" N
Linux virtual terminal "linux" or "linux-256color" Y
-Describing any of these as "xterm" or "xterm-256colour" will not describe the
-terminal correctly to Nvim, and will cause various kinds of problematic
-behaviours.
-
-Setting your $TERM environment variable to the correct value also avoids the
-problem that SSH does not mirror arbitrary client-end environment variables
-such as $COLORTERM, $XTERM_VERSION, $VTE_VERSION, $KONSOLE_PROFILE_NAME, and
-$TERM_PROGRAM to the server end, whereas it does send the $TERM environment
-variable.
-
-See |terminfo| for dealing with out of date terminfo databases.
-
*builtin-terms* *builtin_terms*
If a |terminfo| database is not available, or no entry for the terminal type is
-found in that database, Nvim will look up the terminal type in a compiled-in
-mini-database of terminfo entries for "xterm", "putty", "screen", "tmux",
-"rxvt", "iterm", "interix", "linux", "st", "vte", "gnome", and "ansi".
+found in that database, Nvim will use a compiled-in mini-database of terminfo
+entries for "xterm", "putty", "screen", "tmux", "rxvt", "iterm", "interix",
+"linux", "st", "vte", "gnome", and "ansi".
The lookup matches the initial portion of the terminal type, so (for example)
"putty-256color" and "putty" will both be mapped to the built-in "putty"
@@ -87,8 +73,8 @@ supplying an external one with entries for the terminal type.
Settings depending on terminal *term-dependent-settings*
-If you want to set options or mappings, depending on the terminal name, you
-can do this best in your init.vim. Example: >
+If you want to set terminal-dependent options or mappings, you can do this in
+your init.vim. Example: >
if $TERM =~ '^\(rxvt\|screen\|interix\|putty\)\(-.*\)\?$'
set notermguicolors
@@ -127,62 +113,49 @@ variable, set by genuine Xterm, that it looks for is not automatically
replicated over an SSH login session.
*256-color* *terminfo-colors* *termcap-colors*
-Nvim can make use of 256-colour terminals and tries to do so whereever it can.
+Nvim uses 256 colours by default, ignoring |terminfo| for most terminal types,
+including "linux" (whose virtual terminals have had 256-colour support since
+4.8) and anything claiming to be "xterm". Also when $COLORTERM or $TERM
+contain the string "256".
-If the |terminfo| description of the terminal says that it supports fewer
-colours, Nvim will override this for many terminal types, including "linux"
-(whose virtual terminals have had 256-colour support since version 4.8) and
-anything (even if falsely) claiming to be "xterm". It will also set 256
-colours when the COLORTERM or TERM environment variables contain the string
-"256" somewhere.
-
-Nvim similarly assumes that any terminal emulator that sets the COLORTERM
-environment variable at all, to anything, is capable of at least 16-colour
-operation; and it will override |terminfo| saying that it has fewer colours
-available.
+Nvim similarly assumes that any terminal emulator that sets $COLORTERM to any
+value, is capable of at least 16-colour operation.
*true-color* *xterm-true-color*
-Nvim supports using true (24-bit) colours in the terminal, on terminals that
-support it. It uses the same |terminfo| extensions that were proposed by
-Rüdiger Sonderfeld in 2013 for this: "setrgbf" and "setrgbb". If your
-terminfo definition specifies these, then nothing more is required.
-
-If your terminfo definition is missing them, then Nvim will decide whether to
-add them to your terminfo definition, using the ISO 8613-6:1994/ITU T.416:1993
-control sequences for setting RGB colours, but modified to use semicolons
-instead of colons unless the terminal is known to follow the standard.
-(Semicolons cause ambiguities that the standard avoided by specifying colons
-as a sub-parameter delimiter. A historical misunderstanding meant that many
-terminal emulators ended up using semicolons for many years, though.)
-
-A new convention, pioneered in 2016 by tmux, is the "Tc" terminfo extension.
-If your terminal's terminfo definition has this flag, Nvim will add
-constructed "setrgbf" and "setrgbb" capabilities as if they had been in the
-terminfo definition.
-
-If your terminal's terminfo definition does not (yet) have this flag, Nvim
-will fall back to looking at the TERM and other environment variables. It
-will add constructed "setrgbf" and "setrgbb" capabilities in the case of the
-the "rxvt", "linux", "st", "tmux", and "iterm" terminal types, or when
-Konsole, genuine Xterm, a libvte terminal emulator version 0.36 or later, or a
-terminal emulator that sets the COLORTERM environment variable to "truecolor"
-is detected.
+Nvim emits true (24-bit) colours in the terminal, if 'termguicolors' is set.
+
+It uses the "setrgbf" and "setrgbb" |terminfo| extensions (proposed by Rüdiger
+Sonderfeld in 2013). If your terminfo definition is missing them, then Nvim
+will decide whether to add them to your terminfo definition, using the ISO
+8613-6:1994/ITU T.416:1993 control sequences for setting RGB colours (but
+modified to use semicolons instead of colons unless the terminal is known to
+follow the standard).
+
+Another convention, pioneered in 2016 by tmux, is the "Tc" terminfo extension.
+If terminfo has this flag, Nvim will add constructed "setrgbf" and "setrgbb"
+capabilities as if they had been in the terminfo definition.
+
+If terminfo does not (yet) have this flag, Nvim will fall back to $TERM and
+other environment variables. It will add constructed "setrgbf" and "setrgbb"
+capabilities in the case of the the "rxvt", "linux", "st", "tmux", and "iterm"
+terminal types, or when Konsole, genuine Xterm, a libvte terminal emulator
+version 0.36 or later, or a terminal emulator that sets the COLORTERM
+environment variable to "truecolor" is detected.
*xterm-resize*
Nvim can resize the terminal display on some terminals that implement an
-extension pioneered by the dtterm program. |terminfo| does not have a flag
-for this extension. So Nvim simply assumes that (all) "dtterm", "xterm",
-"teraterm", "rxvt" terminal types, and Konsole, are capable of this.
+extension pioneered by dtterm. |terminfo| does not have a flag for this
+extension. So Nvim simply assumes that (all) "dtterm", "xterm", "teraterm",
+"rxvt" terminal types, and Konsole, are capable of this.
- *cursor-shape* *terminfo-cursor-shape* *termcap-cursor-shape*
+ *tui-cursor-shape*
Nvim will adjust the shape of the cursor from a block to a line when in insert
mode (or as specified by the 'guicursor' option), on terminals that support
it. It uses the same |terminfo| extensions that were pioneered by tmux for
-this: "Ss" and "Se". If your terminfo definition specifies these, as some
-(such as those based upon "xterm+tmux") do, then nothing more is required.
+this: "Ss" and "Se".
If your terminfo definition is missing them, then Nvim will decide whether to
-add them to your terminfo definition, by looking at the TERM and other
+add them to your terminfo definition, by looking at $TERM and other
environment variables. For the "rxvt", "putty", "linux", "screen",
"teraterm", and "iterm" terminal types, or when Konsole, a libvte-based
terminal emulator, or genuine Xterm are detected, it will add constructed
@@ -195,19 +168,14 @@ receives from Nvim into whatever control sequence is appropriate for the
terminal that it is outputting to. It shares a common mechanism with Nvim, of
using the "Ss" and "Se" capabilities from terminfo (for the output terminal)
if they are present. Unlike Nvim, if they are not present in terminfo you
-will have to add them by setting the tmux "terminal-overrides" setting in
-$HOME/.tmux.conf .
+must add them by setting "terminal-overrides" in ~/.tmux.conf .
See the tmux(1) manual page for the details of how and what to do in the tmux
configuration file. It will look something like: >
-
- set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
+ set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
<or (alas!) for Konsole specifically, something more complex like: >
- set -ga terminal-overrides \
- 'xterm*:\E]50;CursorShape=%?%p1%{3}%<%t%{0}%e%{1}%;%d\007'
-<but these are only rough examples that do not include all of the other stuff
-that occurs in that setting.
-
+ set -ga terminal-overrides 'xterm*:\E]50;CursorShape=%?%p1%{3}%<%t%{0}%e%{1}%;%d\007'
+<
*cs7-problem*
Note: If the terminal settings are changed after running Vim, you might have
an illegal combination of settings. This has been reported on Solaris 2.5
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 42f273588a..9e063b1c04 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -79,8 +79,8 @@ Working intuitively and consistently is a major goal of Nvim. Examples:
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).
+- Vim's internal test functions (test_autochdir(), test_settime(), etc.) are
+ not exposed (nor implemented); instead Nvim has a robust API.
ARCHITECTURE ~
@@ -89,6 +89,9 @@ stability and allows those plugins to work without blocking the editor. Even
"legacy" Python and Ruby plugins which use the old Vim interfaces (|if_py| and
|if_ruby|) run out-of-process.
+Platform and I/O facilities are built upon libuv. Nvim benefits from libuv
+features and bug fixes, and other projects benefit from improvements to libuv
+by Nvim developers.
FEATURES ~
@@ -126,6 +129,7 @@ Commands:
Functions:
|dictwatcheradd()| notifies a callback whenever a |Dict| is modified
|dictwatcherdel()|
+ |menu_get()|
|msgpackdump()|, |msgpackparse()| provide msgpack de/serialization
Events:
diff --git a/runtime/ftplugin/tutor.vim b/runtime/ftplugin/tutor.vim
index 1579753170..ec55472b78 100644
--- a/runtime/ftplugin/tutor.vim
+++ b/runtime/ftplugin/tutor.vim
@@ -19,27 +19,30 @@ setlocal noundofile
setlocal keywordprg=:help
setlocal iskeyword=@,-,_
-setlocal foldmethod=expr
+" The user will have to enable the folds himself, but we provide the foldexpr
+" function.
+setlocal foldmethod=manual
setlocal foldexpr=tutor#TutorFolds()
-setlocal foldcolumn=1
setlocal foldlevel=4
-setlocal nowrap
setlocal statusline=%{toupper(expand('%:t:r'))}\ tutorial%=
setlocal statusline+=%{tutor#InfoText()}
+" Load metadata if it exists: {{{1
+if filereadable(expand('%').'.json')
+ call tutor#LoadMetadata()
+endif
+
" Mappings: {{{1
call tutor#SetNormalMappings()
-call tutor#SetSampleTextMappings()
" Checks: {{{1
sign define tutorok text=✓ texthl=tutorOK
sign define tutorbad text=✗ texthl=tutorX
-if !exists('g:tutor_debug') || g:tutor_debug == 0
- call tutor#PlaceXMarks()
- autocmd! TextChanged <buffer> call tutor#OnTextChanged()
- autocmd! TextChangedI <buffer> call tutor#OnTextChanged()
+if !exists('g:tutor_debug') || g:tutor_debug == 0
+ call tutor#ApplyMarks()
+ autocmd! TextChanged,TextChangedI <buffer> call tutor#ApplyMarksOnChanged()
endif
diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim
index bce9189660..fbf159582a 100644
--- a/runtime/syntax/tutor.vim
+++ b/runtime/syntax/tutor.vim
@@ -31,26 +31,20 @@ syn keyword tutorMarks TODO NOTE IMPORTANT TIP ATTENTION EXERCISE
syn keyword tutorMarks todo note tip attention exercise
syn keyword tutorMarks Todo Note Tip Excersise
-syn match tutorTextMark /\\\@<!--->/ conceal cchar=→
-syn region tutorSampleText start=/^\(--->\)\@=/ end=/$/ keepend contains=@SPELL
-syn match tutorSampleTextMark /^--->/ contained containedin=tutorSampleText conceal cchar=→
-syn match tutorSampleTextExpect /\}\@<! {expect:.\+}\s*$/ contained containedin=tutorSampleText conceal
-syn match tutorSampleTextExpect /|\@<! |expect:.\+|\s*$/ contained containedin=tutorSampleText conceal
-
syn region tutorCodeblock matchgroup=Delimiter start=/^\~\{3}.*$/ end=/^\~\{3}/
-syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL concealends
+syn region tutorShell matchgroup=Delimiter start=/^\~\{3} sh\s*$/ end=/^\~\{3}/ keepend contains=@TUTORSHELL
syn match tutorShellPrompt /\(^\s*\)\@<=[$#]/ contained containedin=tutorShell
-syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/ concealends
+syn region tutorInlineCode matchgroup=Delimiter start=/\\\@<!`/ end=/\\\@<!\(`{\@!\|`\s\)/
-syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM concealends
-syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM concealends
+syn region tutorCommand matchgroup=Delimiter start=/^\~\{3} cmd\( :\)\?\s*$/ end=/^\~\{3}/ keepend contains=@VIM
+syn region tutorInlineCommand matchgroup=Delimiter start=/\\\@<!`\(.*{vim}\)\@=/ end=/\\\@<!`\({vim}\)\@=/ nextgroup=tutorInlineType contains=@VIM
-syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL concealends
-syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL concealends
+syn region tutorNormal matchgroup=Delimiter start=/^\~\{3} norm\(al\?\)\?\s*$/ end=/^\~\{3}/ contains=@VIMNORMAL
+syn region tutorInlineNormal matchgroup=Delimiter start=/\\\@<!`\(\S*{normal}\)\@=/ end=/\\\@<!`\({normal}\)\@=/ nextgroup=tutorInlineType contains=@VIMNORMAL
-syn match tutorInlineType /{\(normal\|vim\)}/ contained conceal
+syn match tutorInlineType /{\(normal\|vim\)}/ contained
syn match tutorInlineOK /✓/
syn match tutorInlineX /✗/
@@ -72,7 +66,7 @@ hi! tutorMarks cterm=bold gui=bold
hi! tutorEmphasis gui=italic cterm=italic
hi! tutorBold gui=bold cterm=bold
-hi! link tutorSampleText Special
+hi! link tutorExpect Special
hi! tutorOK ctermfg=green guifg=#00ff88 cterm=bold gui=bold
hi! tutorX ctermfg=red guifg=#ff2000 cterm=bold gui=bold
hi! link tutorInlineOK tutorOK
diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor
index 47d4ed06a1..fee7de21d2 100644
--- a/runtime/tutor/en/vim-01-beginner.tutor
+++ b/runtime/tutor/en/vim-01-beginner.tutor
@@ -18,10 +18,10 @@ be saved. Don't worry about messing things up; just remember that pressing
[<Esc>](<Esc>) and then [u](u) will undo the latest change.
This tutorial is interactive, and there are a few things you should know.
-Pressing [<Enter>](<Enter>) over text highlighted [like this](holy-grail) will take you to some relevant
-help (hopefully), and pressing K over any word will try to do so too. Sometimes
-you will be required to modify text like
----> this here {expect:this here}
+Pressing [<Enter>](<Enter>) over text highlighted [like this](holy-grail) will take you to some
+relevant help (hopefully), and pressing K over any word will try to do so too.
+Sometimes you will be required to modify text like
+this here
Once you have done the changes correctly, the ✗ sign at the left will change
to ✓. I imagine you can already see how neat Vim can be ;)
Other times, you'll be prompted to run a command (I'll explain this later):
@@ -99,7 +99,7 @@ NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you
4. Repeat steps 2 through 4 until the sentence is correct.
----> The ccow jumpedd ovverr thhe mooon. {expect:The cow jumped over the moon.}
+The ccow jumpedd ovverr thhe mooon.
5. Now that the line is correct, go on to Lesson 1.4.
@@ -119,8 +119,8 @@ NOTE: As you go through this tutor, do not try to memorize, learn by usage.
4. As each error is fixed press <Esc> to return to Normal mode.
Repeat steps 2 through 4 to correct the sentence.
----> There is text misng this . {expect:There is some text missing from this line.}
----> There is some text missing from this line. {expect:There is some text missing from this line.}
+There is text misng this .
+There is some text missing from this line.
5. When you are comfortable inserting text move to lesson 1.5.
@@ -138,10 +138,10 @@ NOTE: As you go through this tutor, do not try to memorize, learn by usage.
4. Move the cursor to the second line marked ---> and repeat
steps 2 and 3 to correct this sentence.
----> There is some text missing from th {expect:There is some text missing from this line.}
----> There is some text missing from this line. {expect:There is some text missing from this line.}
----> There is also some text miss {expect:There is also some text missing here.}
----> There is also some text missing here. {expect:There is also some text missing here.}
+There is some text missing from th
+There is some text missing from this line.
+There is also some text miss
+There is also some text missing here.
5. When you are comfortable appending text move to lesson 1.6.
@@ -212,7 +212,7 @@ Now continue with Lesson 2.
4. Type [d](d)[w](w) to make the word disappear.
----> There are a some words fun that don't belong paper in this sentence. {expect:There are some words that don't belong in this sentence.}
+There are a some words fun that don't belong paper in this sentence.
5. Repeat steps 3 and 4 until the sentence is correct and go to Lesson 2.2.
@@ -228,7 +228,7 @@ Now continue with Lesson 2.
4. Type `d$`{normal} to delete to the end of the line.
----> Somebody typed the end of this line twice. end of this line twice. {expect:ANYTHING}
+Somebody typed the end of this line twice. end of this line twice.
5. Move on to Lesson 2.3 to understand what is happening.
@@ -268,7 +268,7 @@ NOTE: Pressing just the motion while in Normal mode without an operator will
5. Repeat steps 2 and 3 with different numbers.
----> This is just a line with words you can move around in. {expect:ANYTHING}
+This is just a line with words you can move around in.
6. Move on to Lesson 2.5.
@@ -287,7 +287,7 @@ insert a count before the motion to delete more:
3. Repeat steps 1 and 2 with a different count to delete the consecutive
UPPER CASE words with one command
----> this ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up. {expect:this line of words is cleaned up.}
+this ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up.
# Lesson 2.6: OPERATING ON LINES
@@ -301,13 +301,13 @@ insert a count before the motion to delete more:
3. Now move to the fourth line.
4. Type `2dd`{normal} to delete two lines.
----> 1) Roses are red, {expect:ANYTHING}
----> 2) Mud is fun, {expect:ANYTHING}
----> 3) Violets are blue, {expect:ANYTHING}
----> 4) I have a car, {expect:ANYTHING}
----> 5) Clocks tell time, {expect:ANYTHING}
----> 6) Sugar is sweet {expect:ANYTHING}
----> 7) And so are you. {expect:ANYTHING}
+1) Roses are red,
+2) Mud is fun,
+3) Violets are blue,
+4) I have a car,
+5) Clocks tell time,
+6) Sugar is sweet
+7) And so are you.
# Lesson 2.7: THE UNDO COMMAND
@@ -322,7 +322,7 @@ insert a count before the motion to delete more:
6. Now type `u`{normal} a few times to undo the U and preceding commands.
7. Now type `<Ctrl-r>`{normal} a few times to redo the commands (undo the undo's).
----> Fiix the errors oon thhis line and reeplace them witth undo. {expect:Fix the errors on this line and replace them with undo.}
+Fiix the errors oon thhis line and reeplace them witth undo.
8. These are very useful commands. Now move on to the Lesson 2 Summary.
@@ -362,10 +362,10 @@ insert a count before the motion to delete more:
5. Repeat steps 2 through 4 to put all the lines in correct order.
----> d) Can you learn too? {expect:ANYTHING}
----> b) Violets are blue, {expect:ANYTHING}
----> c) Intelligence is learned, {expect:ANYTHING}
----> a) Roses are red, {expect:ANYTHING}
+d) Can you learn too?
+b) Violets are blue,
+c) Intelligence is learned,
+a) Roses are red,
# Lesson 3.2: THE REPLACE COMMAND
@@ -379,8 +379,8 @@ insert a count before the motion to delete more:
4. Repeat steps 2 and 3 until the first line is equal to the second one.
----> Whan this lime was tuoed in, someone presswd some wrojg keys! {expect:When this line was typed in, someone pressed some wrong keys!}
----> When this line was typed in, someone pressed some wrong keys! {expect:When this line was typed in, someone pressed some wrong keys!}
+Whan this lime was tuoed in, someone presswd some wrojg keys!
+When this line was typed in, someone pressed some wrong keys!
5. Now move on to Lesson 3.3.
@@ -400,8 +400,8 @@ NOTE: Remember that you should be learning by doing, not memorization.
5. Repeat steps 3 and 4 until the first sentence is the same as the second.
----> This lubw has a few wptfd that mrrf changing usf the change operator. {expect:This line has a few words that need changing using the change operator.}
----> This line has a few words that need changing using the change operator. {expect:This line has a few words that need changing using the change operator.}
+This lubw has a few wptfd that mrrf changing usf the change operator.
+This line has a few words that need changing using the change operator.
Notice that [c](c)e deletes the word and places you in Insert mode.
@@ -421,8 +421,8 @@ Notice that [c](c)e deletes the word and places you in Insert mode.
5. Type `c$`{normal} and type the rest of the line like the second and press `<Esc>`{normal}.
----> The end of this line needs some help to make it like the second. {expect:The end of this line needs to be corrected using the c$ command.}
----> The end of this line needs to be corrected using the c$ command. {expect:The end of this line needs to be corrected using the c$ command.}
+The end of this line needs some help to make it like the second.
+The end of this line needs to be corrected using the c$ command.
NOTE: You can use the Backspace key to correct mistakes while typing.
@@ -484,7 +484,7 @@ NOTE: You may see the cursor position in the lower right corner of the screen
5. To go back to where you came from press `<Ctrl-o>`{normal} (Keep Ctrl down while
pressing the letter o). Repeat to go back further. `<Ctrl-i>`{normal} goes forward.
----> "errroor" is not the way to spell error; errroor is an error. {expect:ANYTHING}
+"errroor" is not the way to spell error; errroor is an error.
NOTE: When the search reaches the end of the file it will continue at the
start, unless the ['wrapscan']('wrapscan') option has been reset.
@@ -503,7 +503,7 @@ NOTE: When the search reaches the end of the file it will continue at the
5. Move the cursor to another (,),[,],{ or } and see what `%`{normal} does.
----> This ( is a test line with ('s, ['s ] and {'s } in it. )) {expect:ANYTHING}
+This ( is a test line with ('s, ['s ] and {'s } in it. ))
NOTE: This is very useful in debugging a program with unmatched parentheses!
@@ -528,7 +528,7 @@ NOTE: This is very useful in debugging a program with unmatched parentheses!
Adding the g [flag](:s_flags) means to substitute globally in the line, change
all occurrences of "thee" in the line.
----> thee best time to see thee flowers is in thee spring. {expect:the best time to see the flowers is in the spring.}
+thee best time to see thee flowers is in thee spring.
4. To change every occurrence of a character string between two lines, type
~~~ cmd
@@ -719,12 +719,12 @@ NOTE: You can also read the output of an external command. For example,
3. Now type some text and press `<Esc>`{normal} to exit Insert mode.
----> After typing o the cursor is placed on the open line in Insert mode. {expect:ANYTHING}
+After typing o the cursor is placed on the open line in Insert mode.
4. To open up a line ABOVE the cursor, simply type a [capital O](O), rather
than a lowercase `o`{normal}. Try this on the line below.
----> Open up a line above this by typing O while the cursor is on this line. {expect:ANYTHING}
+Open up a line above this by typing O while the cursor is on this line.
# Lesson 6.2: THE APPEND COMMAND
@@ -741,8 +741,8 @@ NOTE: You can also read the output of an external command. For example,
5. Use `e`{normal} to move to the next incomplete word and repeat steps 3 and 4.
----> This li will allow you to pract appendi text to a line. {expect:This line will allow you to practice appending text to a line.}
----> This line will allow you to practice appending text to a line. {expect:This line will allow you to practice appending text to a line.}
+This li will allow you to pract appendi text to a line.
+This line will allow you to practice appending text to a line.
NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only difference is where
the characters are inserted.
@@ -762,8 +762,8 @@ NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only differ
4. Repeat the steps to replace the remaining "xxx".
----> Adding 123 to xxx gives you xxx. {expect:Adding 123 to 456 gives you 579.}
----> Adding 123 to 456 gives you 579. {expect:Adding 123 to 456 gives you 579.}
+Adding 123 to xxx gives you xxx.
+Adding 123 to 456 gives you 579.
NOTE: Replace mode is like Insert mode, but every typed character deletes an
existing character.
@@ -785,8 +785,8 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an
6. Use Visual mode to select " item.", yank it with `y`{normal}, move to the end of
the next line with `j$`{normal} and put the text there with `p`{normal}.
----> a) this is the first item.
----> b) {expect: b) this is the second item}
+a) this is the first item.
+ b)
NOTE: you can also use `y`{normal} as an operator; `yw`{normal} yanks one word.
@@ -947,8 +947,10 @@ There are many resources online to learn more about vim. Here's a bunch of them:
- Vim Video-Tutorials by Derek Wyatt: http://derekwyatt.org/vim/tutorials/
- *Learn Vimscript the Hard Way*: http://learnvimscriptthehardway.stevelosh.com/
- *7 Habits of Effective Text Editing*: http://www.moolenaar.net/habits.html
+- *vim-galore*: https://github.com/mhinz/vim-galore
-If you prefer a book, *Practival Vim* by Drew Neil is recommended often.
+If you prefer a book, *Practical Vim* by Drew Neil is recommended often (the sequel, *Modern
+Vim*, includes material specific to nvim!).
This tutorial was written by Michael C. Pierce and Robert K. Ware, Colorado
School of Mines using ideas supplied by Charles Smith, Colorado State
diff --git a/runtime/tutor/en/vim-01-beginner.tutor.json b/runtime/tutor/en/vim-01-beginner.tutor.json
new file mode 100644
index 0000000000..3f55971a09
--- /dev/null
+++ b/runtime/tutor/en/vim-01-beginner.tutor.json
@@ -0,0 +1,45 @@
+{
+ "expect": {
+ "24": -1,
+ "102": "The cow jumped over the moon.",
+ "122": "There is some text missing from this line.",
+ "123": "There is some text missing from this line.",
+ "141": "There is some text missing from this line.",
+ "142": "There is some text missing from this line.",
+ "143": "There is also some text missing here.",
+ "144": "There is also some text missing here.",
+ "215": "There are some words that don't belong in this sentence.",
+ "231": "Somebody typed the end of this line twice.",
+ "271": -1,
+ "290": "this line of words is cleaned up.",
+ "304": -1,
+ "305": -1,
+ "306": -1,
+ "307": -1,
+ "308": -1,
+ "309": -1,
+ "310": -1,
+ "325": "Fix the errors on this line and replace them with undo.",
+ "365": -1,
+ "366": -1,
+ "367": -1,
+ "368": -1,
+ "382": "When this line was typed in, someone pressed some wrong keys!",
+ "383": "When this line was typed in, someone pressed some wrong keys!",
+ "403": "This line has a few words that need changing using the change operator.",
+ "404": "This line has a few words that need changing using the change operator.",
+ "424": "The end of this line needs to be corrected using the c$ command.",
+ "425": "The end of this line needs to be corrected using the c$ command.",
+ "487": -1,
+ "506": -1,
+ "531": "the best time to see the flowers is in the spring.",
+ "722": -1,
+ "727": -1,
+ "744": "This line will allow you to practice appending text to a line.",
+ "745": "This line will allow you to practice appending text to a line.",
+ "765": "Adding 123 to 456 gives you 579.",
+ "766": "Adding 123 to 456 gives you 579.",
+ "788": "a) this is the first item.",
+ "789": " b) this is the second item."
+ }
+}
diff --git a/runtime/tutor/tutor.tutor b/runtime/tutor/tutor.tutor
index 1ad64a18ff..c937bd686a 100644
--- a/runtime/tutor/tutor.tutor
+++ b/runtime/tutor/tutor.tutor
@@ -60,27 +60,27 @@ is displayed like
1. Format the line below so it becomes a lesson description:
----> This is text with important information {expect:This is text with **important information**}
----> This is text with **important information** {expect:This is text with **important information**}
+This is text with important information
+This is text with **important information**
Note: Some words (e.g., NOTE, IMPORTANT, tip, ATTENTION, etc.) will also be
highlighted. You don't need to mark them specially.
2. Turn the line below into a TODO item:
----> Document '&variable' {expect:TODO: Document '&variable'}
----> TODO: Document '&variable' {expect:TODO: Document '&variable'}
+Document '&variable'
+TODO: Document '&variable'
### Headers *headers*
3. Practice fixing the lines below:
----> This is a level 1 header {expect:# This is a level 1 header}
----> # This is a level 1 header {expect:# This is a level 1 header}
----> This is a level 3 header {expect:### This is a level 3 header}
----> ### This is a level 3 header {expect:### This is a level 3 header}
----> This is a header with a label {expect:# This is a header with a label {*label*}}
----> # This is a header with a label {*label*} {expect:# This is a header with a label {*label*}}
+This is a level 1 header
+# This is a level 1 header
+This is a level 3 header
+### This is a level 3 header
+This is a header with a label
+# This is a header with a label {*label*}
4. Now, create a 4th level section here, and add a label like in the previous
exercise:
@@ -105,8 +105,8 @@ If the target of a link matches a help topic, opening it will open it.
5. Fix the following line:
----> A link to help for the 'breakindent' option {expect:A link to help for the ['breakindent']('breakindent') option}
----> A link to help for the ['breakindent']('breakindent') option {expect:A link to help for the ['breakindent']('breakindent') option}
+A link to help for the 'breakindent' option
+A link to help for the ['breakindent']('breakindent') option
#### Anchor links
@@ -120,8 +120,8 @@ and are hidden by default. Links to them look like
6. Add the appropiate link:
----> A link to the Links section {expect:A link to the [Links](*links*) section}
----> A link to the [Links](*links*) section {expect:A link to the [Links](*links*) section}
+A link to the Links section
+A link to the [Links](*links*) section
7. Now, create a link to the section you created on exercise 4
above.
@@ -136,8 +136,8 @@ You can also have links to other tutorials. For this, you'll write the anchor in
7. Create a link to this tutorial:
----> A link to the vim-tutor-mode tutorial {expect:A link to [the vim-tutor-mode tutorial](@tutor:tutor)}
----> A link to [the vim-tutor-mode tutorial](@tutor:tutor) {expect:A link to [the vim-tutor-mode tutorial](@tutor:tutor)}
+A link to the vim-tutor-mode tutorial
+A link to [the vim-tutor-mode tutorial](@tutor:tutor)
### Codeblocks *codeblocks*
@@ -154,13 +154,13 @@ echom "hello"
8. Copy the viml section below
----> {expect:~~~ viml}
----> {expect:echom "the value of &number is".string(&number)}
----> {expect:~~~}
----> ~~~ viml {expect:~~~ viml}
----> echom "the value of &number is".string(&number) {expect:echom "the value of &number is".string(&number)}
----> ~~~ {expect:~~~}
+
+
+
+~~~ viml
+echom 'the value of &number is'.string(&number)
+~~~
You can inline viml code using "\`" and "\`{vim}":
@@ -185,13 +185,13 @@ Note: you can also write `norm` or `normal`.
9. Copy the normal section below
----> {expect:~~~ normal}
----> {expect:d2w}
----> {expect:~~~}
----> ~~~ normal {expect:~~~ normal}
----> d2w {expect:d2w}
----> ~~~ {expect:~~~}
+
+
+
+~~~ normal
+d2w
+~~~
You can also inline normal commands by using "\`" and "\`{normal}":
@@ -203,10 +203,11 @@ is displayed:
10. Complete the line as shown
----> d {expect:«d2w»}
----> «d2w» {expect:«d2w»}
+d
+`d2w`{normal}
-Commands to run in the system shell can be highlighted by indenting a line starting with "$".
+Commands to run in the system shell can be highlighted by indenting a line
+starting with "$".
~~~ sh
$ vim --version
@@ -215,45 +216,32 @@ Commands to run in the system shell can be highlighted by indenting a line start
## INTERACTIVE ELEMENTS *interactive*
As visible in this very document, vim-tutor-mode includes some interactive
-elements, to provide feedback to the user about his progress. These elements
-all have the syntax
-
- \---> TEXT {CLAUSE}
-
-where \---> must start at the beginning of the line. If TEXT satisfies CLAUSE,
-a ✓ sign will appear to the left. A ✗ sign is displayed otherwise. The CLAUSE
-itself is hidden unless debug mode is set or ['conceallevel']('conceallevel')
-is 2.
+elements to provide feedback to the user about his progress. If the text in
+these elements satisfies some set condition, a ✓ sign will appear in the gutter
+to the left. Otherwise, a ✗ sign is displayed.
### expect *expect*
-The basic clause is "expect", which is satisfied if TEXT is the same as the
-content of the clause. For example
-
- \---> TEXT {expect:TEXT}
-
-is satisfied, but
-
- \---> OTHER TEXT {expect:TEXT}
-
-is not.
+"expect" lines check that the contents of the line are identical to some preset text
+(like in the exercises above).
-13. Make both lines the same:
+These elements are specified in separate JSON files like this
----> this is not right {expect:---> this is right} |expect:---> this is right {expect:---> this is right}|
----> ---> this is right {expect:---> this is right} |expect:---> this is right {expect:---> this is right}|
-
-
-If the content of a expect clause is ANYTHING, no checks will be performed. This is
-useful to create a line that is highlighted you want the user to play with.
-
- \---> TEXT {expect:ANYTHING}
-
-is displayed
+~~~ json
+{
+ "expect": {
+ "1": "This is how this line should look.",
+ "2": "This is how this line should look.",
+ "3": -1
+ }
+}
+~~~
----> this is free text {expect:ANYTHING}
+These files contain an "expect" dictionary, for which the keys are line numbers and
+the values are the expected text. A value of -1 means that the condition for the line
+will always be satisfied, no matter what (this is useful for letting the user play a bit).
-14. Turn the line below into free text:
+This is an "expect" line that is always satisfied. Try changing it.
----> this is some text |expect:---> this is some text {expect:ANYTHING}|
----> ---> this is some text {expect:ANYTHING} |expect:---> this is some text {expect:ANYTHING}|
+These files conventionally have the same name as the tutorial document with the `.json`
+extension appended (for a full example, see the file that corresponds to this tutorial).
diff --git a/runtime/tutor/tutor.tutor.json b/runtime/tutor/tutor.tutor.json
new file mode 100644
index 0000000000..bf3eae8586
--- /dev/null
+++ b/runtime/tutor/tutor.tutor.json
@@ -0,0 +1,35 @@
+{
+ "expect": {
+ "63": "This is text with **important information**",
+ "64": "This is text with **important information**",
+ "71": "Document '&variable'",
+ "72": "Document '&variable'",
+ "78": "# This is a level 1 header",
+ "79": "# This is a level 1 header",
+ "80": "### This is a level 3 header",
+ "81": "### This is a level 3 header",
+ "82": "# This is a header with a label {*label*}",
+ "83": "# This is a header with a label {*label*}",
+ "108": "A link to help for the ['breakindent']('breakindent') option",
+ "109": "A link to help for the ['breakindent']('breakindent') option",
+ "123": "A link to the [Links](*links*) section",
+ "124": "A link to the [Links](*links*) section",
+ "139": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
+ "140": "A link to [the vim-tutor-mode tutorial](@tutor:tutor)",
+ "157": "~~~ viml",
+ "158": "echom 'the value of &number is'.string(&number)",
+ "159": "~~~",
+ "161": "~~~ viml",
+ "162": "echom 'the value of &number is'.string(&number)",
+ "163": "~~~",
+ "188": "~~~ normal",
+ "189": "d2w",
+ "190": "~~~",
+ "192": "~~~ normal",
+ "193": "d2w",
+ "194": "~~~",
+ "206": "`d2w`{normal}",
+ "207": "`d2w`{normal}",
+ "244": -1
+ }
+}
diff --git a/src/nvim/README.md b/src/nvim/README.md
index 3032913500..1c1c3c364e 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -1,14 +1,26 @@
-## Source code overview
+Nvim core source
+================
-This document is an overview of how Nvim works internally, focusing on parts
-that are different from Vim. Since Nvim inherited from Vim, some information in
-[its README](https://raw.githubusercontent.com/vim/vim/master/src/README.txt)
-still applies.
+Module-specific details are documented at the top of each module (`terminal.c`,
+`screen.c`, ...).
-For module-specific details, read the source code. Some files are extensively
-commented at the top (e.g. terminal.c, screen.c).
+See `:help development` for more guidelines.
-### Source file name conventions
+Logs
+----
+
+Low-level log messages sink to `$NVIM_LOG_FILE`.
+
+You can use `LOG_CALLSTACK()` anywhere in the source to log the current
+stacktrace. (Currently Linux-only.)
+
+UI events are logged at level 0 (`DEBUG_LOG_LEVEL`).
+
+ rm -rf build/
+ make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
+
+Filename conventions
+--------------------
The source files use extensions to hint about their purpose.
@@ -19,10 +31,12 @@ The source files use extensions to hint about their purpose.
- `*.h.generated.h` - exported functions’ declarations.
- `*.c.generated.h` - static functions’ declarations.
-### Top-level program loops
+Nvim lifecycle
+--------------
+
+Following describes how Nvim processes input.
-Let's understand what a Vim-like program does by analyzing the workflow of
-a typical editing session:
+Consider a typical Vim-like editing session:
01. Vim dispays the welcome screen
02. User types: `:`
@@ -154,7 +168,8 @@ modes managed by the `state_enter` loop:
- insert mode: `insert_{enter,check,execute}()`(`edit.c`)
- terminal mode: `terminal_{enter,execute}()`(`terminal.c`)
-### Async event support
+Async event support
+-------------------
One of the features Nvim added is the support for handling arbitrary
asynchronous events, which can include:
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 90a564bb6a..724a8578ac 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1203,8 +1203,8 @@ do_buffer (
*/
while (buf == curbuf
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
- && (firstwin != lastwin || first_tabpage->tp_next != NULL)) {
- if (win_close(curwin, FALSE) == FAIL)
+ && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) {
+ if (win_close(curwin, false) == FAIL)
break;
}
@@ -4428,15 +4428,17 @@ do_arg_all (
continue;
}
}
- /* don't close last window */
- if (firstwin == lastwin
- && (first_tabpage->tp_next == NULL || !had_tab))
- use_firstwin = TRUE;
- else {
+ // don't close last window
+ if (ONE_WINDOW
+ && (first_tabpage->tp_next == NULL || !had_tab)) {
+ use_firstwin = true;
+ } else {
win_close(wp, !P_HID(buf) && !bufIsChanged(buf));
- /* check if autocommands removed the next window */
- if (!win_valid(wpnext))
- wpnext = firstwin; /* start all over... */
+ // check if autocommands removed the next window
+ if (!win_valid(wpnext)) {
+ // start all over...
+ wpnext = firstwin;
+ }
}
}
}
@@ -4593,7 +4595,7 @@ void ex_buffer_all(exarg_T *eap)
- tabline_height()
: wp->w_width != Columns)
|| (had_tab > 0 && wp != firstwin))
- && firstwin != lastwin
+ && !ONE_WINDOW
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
) {
win_close(wp, FALSE);
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 32e71f61f7..bfb1b94738 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -793,6 +793,7 @@ static digr_T digraphdefault[] =
{ '/', '-', 0x2020 },
{ '/', '=', 0x2021 },
{ '.', '.', 0x2025 },
+ { ',', '.', 0x2026 },
{ '%', '0', 0x2030 },
{ '1', '\'', 0x2032 },
{ '2', '\'', 0x2033 },
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index afa6711645..40847fb806 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -47,6 +47,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
#include "nvim/keymap.h"
@@ -4239,11 +4240,17 @@ static int eval7(
// use its contents.
s = deref_func_name((const char *)s, &len, &partial, !evaluate);
+ // Need to make a copy, in case evaluating the arguments makes
+ // the name invalid.
+ s = xmemdupz(s, len);
+
// Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&len, evaluate, partial, NULL);
+ xfree(s);
+
// If evaluate is false rettv->v_type was not set in
// get_func_tv, but it's needed in handle_subscript() to parse
// what follows. So set it here.
@@ -8167,6 +8174,19 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+
+/// "menu_get(path [, modes])" function
+static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ tv_list_alloc_ret(rettv);
+ int modes = MENU_ALL_MODES;
+ if (argvars[1].v_type == VAR_STRING) {
+ const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]);
+ modes = get_menu_cmd_modes(strmodes, false, NULL, NULL);
+ }
+ menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list);
+}
+
/*
* "extend(list, list [, idx])" function
* "extend(dict, dict [, action])" function
@@ -15140,7 +15160,8 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
const char *error = NULL;
- uint64_t id = channel_connect(tcp, address, 50, &error);
+ eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
+ uint64_t id = channel_connect(tcp, address, 50, (char *)IObuff, &error);
if (error) {
EMSG2(_("connection failed: %s"), error);
@@ -22448,8 +22469,9 @@ static inline bool common_job_start(TerminalJobData *data, typval_T *rettv)
if (data->rpc) {
- // the rpc channel takes over the in and out streams
- channel_from_process(proc, data->id);
+ eval_format_source_name_line((char *)IObuff, sizeof(IObuff));
+ // RPC channel takes over the in/out streams.
+ channel_from_process(proc, data->id, (char *)IObuff);
} else {
wstream_init(proc->in, 0);
if (proc->out) {
@@ -22774,3 +22796,11 @@ bool eval_has_provider(const char *name)
return false;
}
+
+/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
+void eval_format_source_name_line(char *buf, size_t bufsize)
+{
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR,
+ (sourcing_name ? sourcing_name : (char_u *)"?"),
+ (sourcing_name ? sourcing_lnum : 0));
+}
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 30766a0734..0e359fb61c 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -2,10 +2,10 @@
--
-- Keys:
--
--- args Number of arguments, list with maximum and minimum number of arguments
--- or list with a minimum number of arguments only. Defaults to zero
+-- args Number of arguments, list with maximum and minimum number of arguments
+-- or list with a minimum number of arguments only. Defaults to zero
-- arguments.
--- func Name of the C function which implements the VimL function. Defaults to
+-- func Name of the C function which implements the VimL function. Defaults to
-- `f_{funcname}`.
local varargs = function(nr)
@@ -29,7 +29,7 @@ return {
assert_exception={args={1, 2}},
assert_fails={args={1, 2}},
assert_false={args={1, 2}},
- assert_inrange={args={2, 3}},
+ assert_inrange={args={3, 4}},
assert_match={args={2, 3}},
assert_notequal={args={2, 3}},
assert_notmatch={args={2, 3}},
@@ -208,6 +208,7 @@ return {
matchstr={args={2, 4}},
matchstrpos={args={2,4}},
max={args=1},
+ menu_get={args={1, 2}},
min={args=1},
mkdir={args={1, 3}},
mode={args={0, 1}},
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 0987cb3915..a555fb77e8 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2802,16 +2802,18 @@ void ex_z(exarg_T *eap)
int j;
linenr_T lnum = eap->line2;
- /* Vi compatible: ":z!" uses display height, without a count uses
- * 'scroll' */
- if (eap->forceit)
+ // Vi compatible: ":z!" uses display height, without a count uses
+ // 'scroll'
+ if (eap->forceit) {
bigness = curwin->w_height;
- else if (firstwin == lastwin)
+ } else if (ONE_WINDOW) {
bigness = curwin->w_p_scr * 2;
- else
+ } else {
bigness = curwin->w_height - 3;
- if (bigness < 1)
+ }
+ if (bigness < 1) {
bigness = 1;
+ }
x = eap->arg;
kind = x;
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 7203fbd97d..46f7a7bc40 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -1,4 +1,4 @@
-bit = require 'bit'
+local bit = require 'bit'
-- Description of the values below is contained in ex_cmds_defs.h file.
local RANGE = 0x001
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 5d7246581c..d7821fc636 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -844,8 +844,6 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
break;
case ET_INTERRUPT:
break;
- default:
- p = vim_strsave((char_u *)_(e_internal));
}
saved_sourcing_name = sourcing_name;
@@ -3440,6 +3438,11 @@ const char * set_one_cmd_context(
xp->xp_pattern = (char_u *)arg;
break;
+ case CMD_messages:
+ xp->xp_context = EXPAND_MESSAGES;
+ xp->xp_pattern = (char_u *)arg;
+ break;
+
case CMD_history:
xp->xp_context = EXPAND_HISTORY;
xp->xp_pattern = (char_u *)arg;
@@ -4874,6 +4877,7 @@ static struct {
#endif
{ EXPAND_MAPPINGS, "mapping" },
{ EXPAND_MENUS, "menu" },
+ { EXPAND_MESSAGES, "messages" },
{ EXPAND_OWNSYNTAX, "syntax" },
{ EXPAND_SYNTIME, "syntime" },
{ EXPAND_SETTINGS, "option" },
@@ -5976,7 +5980,7 @@ static void ex_quit(exarg_T *eap)
// specified. Example:
// :h|wincmd w|1q - don't quit
// :h|wincmd w|q - quit
- if (only_one_window() && (firstwin == lastwin || eap->addr_count == 0)) {
+ if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0)) {
getout(0);
}
/* close window; may free buffer */
@@ -6174,12 +6178,14 @@ static void ex_tabonly(exarg_T *eap)
*/
void tabpage_close(int forceit)
{
- /* First close all the windows but the current one. If that worked then
- * close the last window in this tab, that will close it. */
- if (lastwin != firstwin)
- close_others(TRUE, forceit);
- if (lastwin == firstwin)
+ // First close all the windows but the current one. If that worked then
+ // close the last window in this tab, that will close it.
+ if (!ONE_WINDOW) {
+ close_others(true, forceit);
+ }
+ if (ONE_WINDOW) {
ex_win_close(forceit, curwin, NULL);
+ }
}
/*
@@ -9593,6 +9599,16 @@ char_u *get_behave_arg(expand_T *xp, int idx)
return NULL;
}
+// Function given to ExpandGeneric() to obtain the possible arguments of the
+// ":messages {clear}" command.
+char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx == 0) {
+ return (char_u *)"clear";
+ }
+ return NULL;
+}
+
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 4434dbe1a5..9037b3c151 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -374,10 +374,9 @@ int do_intthrow(struct condstack *cstack)
return TRUE;
}
-/*
- * Get an exception message that is to be stored in current_exception->value.
- */
-char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should_free)
+// Get an exception message that is to be stored in current_exception->value.
+char_u *get_exception_string(void *value, except_type_T type, char_u *cmdname,
+ int *should_free)
{
char_u *ret, *mesg;
char_u *p, *val;
@@ -435,13 +434,11 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should
}
-/*
- * Throw a new exception. Return FAIL when out of memory or it was tried to
- * throw an illegal user exception. "value" is the exception string for a
- * user or interrupt exception, or points to a message list in case of an
- * error exception.
- */
-static int throw_exception(void *value, int type, char_u *cmdname)
+// Throw a new exception. Return FAIL when out of memory or it was tried to
+// throw an illegal user exception. "value" is the exception string for a
+// user or interrupt exception, or points to a message list in case of an
+// error exception.
+static int throw_exception(void *value, except_type_T type, char_u *cmdname)
{
except_T *excp;
int should_free;
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index f61e01d25b..d5f8737bf3 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -89,28 +89,29 @@ struct msglist {
struct msglist *next; /* next of several messages in a row */
};
+// The exception types.
+typedef enum
+{
+ ET_USER, // exception caused by ":throw" command
+ ET_ERROR, // error exception
+ ET_INTERRUPT // interrupt exception triggered by Ctrl-C
+} except_type_T;
+
/*
* Structure describing an exception.
* (don't use "struct exception", it's used by the math library).
*/
typedef struct vim_exception except_T;
struct vim_exception {
- int type; /* exception type */
- char_u *value; /* exception value */
- struct msglist *messages; /* message(s) causing error exception */
- char_u *throw_name; /* name of the throw point */
- linenr_T throw_lnum; /* line number of the throw point */
- except_T *caught; /* next exception on the caught stack */
+ except_type_T type; // exception type
+ char_u *value; // exception value
+ struct msglist *messages; // message(s) causing error exception
+ char_u *throw_name; // name of the throw point
+ linenr_T throw_lnum; // line number of the throw point
+ except_T *caught; // next exception on the caught stack
};
/*
- * The exception types.
- */
-#define ET_USER 0 /* exception caused by ":throw" command */
-#define ET_ERROR 1 /* error exception */
-#define ET_INTERRUPT 2 /* interrupt exception triggered by Ctrl-C */
-
-/*
* Structure to save the error/interrupt/exception state between calls to
* enter_cleanup() and leave_cleanup(). Must be allocated as an automatic
* variable by the (common) caller of these functions.
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 6eb975fea3..4bbcf186ed 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -4377,6 +4377,7 @@ ExpandFromContext (
} tab[] = {
{ EXPAND_COMMANDS, get_command_name, false, true },
{ EXPAND_BEHAVE, get_behave_arg, true, true },
+ { EXPAND_MESSAGES, get_messages_arg, true, true },
{ EXPAND_HISTORY, get_history_arg, true, true },
{ EXPAND_USER_COMMANDS, get_user_commands, false, true },
{ EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index acdb25ca67..d2b90db707 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -119,7 +119,7 @@ for i = 1, #events do
write_signature(bridge_output, ev, 'UI *ui')
bridge_output:write('\n{\n')
bridge_output:write(send)
- bridge_output:write(' UI_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
+ bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n')
end
end
@@ -128,6 +128,7 @@ for i = 1, #events do
call_output:write('\n{\n')
if ev.remote_only then
write_arglist(call_output, ev, false)
+ call_output:write(' UI_LOG('..ev.name..', 0);\n')
call_output:write(' ui_event("'..ev.name..'", args);\n')
else
call_output:write(' UI_CALL')
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 28584e0534..e634273e0d 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -5,12 +5,15 @@
#include "nvim/buffer_defs.h"
#include "nvim/ex_cmds_defs.h"
-/* Values for "noremap" argument of ins_typebuf(). Also used for
- * map->m_noremap and menu->noremap[]. */
-#define REMAP_YES 0 /* allow remapping */
-#define REMAP_NONE -1 /* no remapping */
-#define REMAP_SCRIPT -2 /* remap script-local mappings only */
-#define REMAP_SKIP -3 /* no remapping for first char */
+/// Values for "noremap" argument of ins_typebuf(). Also used for
+/// map->m_noremap and menu->noremap[].
+/// @addtogroup REMAP_VALUES
+/// @{
+#define REMAP_YES 0 ///< allow remapping
+#define REMAP_NONE -1 ///< no remapping
+#define REMAP_SCRIPT -2 ///< remap script-local mappings only
+#define REMAP_SKIP -3 ///< no remapping for first char
+/// @}
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 6d1bd1de12..f08812600f 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -494,6 +494,7 @@ EXTERN int updating_screen INIT(= FALSE);
EXTERN win_T *firstwin; /* first window */
EXTERN win_T *lastwin; /* last window */
EXTERN win_T *prevwin INIT(= NULL); /* previous window */
+# define ONE_WINDOW (firstwin == lastwin)
/*
* When using this macro "break" only breaks out of the inner loop. Use "goto"
* to break out of the tabpage loop.
diff --git a/src/nvim/log.c b/src/nvim/log.c
index f1dbe61dda..b64aef3cac 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -25,6 +25,10 @@ static uv_mutex_t mutex;
# include "log.c.generated.h"
#endif
+#ifdef HAVE_EXECINFO_BACKTRACE
+# include <execinfo.h>
+#endif
+
static bool log_try_create(char *fname)
{
if (fname == NULL || fname[0] == '\0') {
@@ -173,6 +177,52 @@ FILE *open_log_file(void)
return stderr;
}
+#ifdef HAVE_EXECINFO_BACKTRACE
+void log_callstack(const char *const func_name, const int line_num)
+{
+ void *trace[100];
+ int trace_size = backtrace(trace, ARRAY_SIZE(trace));
+
+ char exepath[MAXPATHL] = { 0 };
+ size_t exepathlen = MAXPATHL;
+ if (os_exepath(exepath, &exepathlen) != 0) {
+ abort();
+ }
+ assert(24 + exepathlen < IOSIZE); // Must fit in `cmdbuf` below.
+
+ do_log(DEBUG_LOG_LEVEL, func_name, line_num, true, "trace:");
+
+ char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace))];
+ snprintf(cmdbuf, sizeof(cmdbuf), "addr2line -e %s -f -p", exepath);
+ for (int i = 1; i < trace_size; i++) {
+ char buf[20]; // 64-bit pointer 0xNNNNNNNNNNNNNNNN with leading space.
+ snprintf(buf, sizeof(buf), " %p", trace[i]);
+ xstrlcat(cmdbuf, buf, sizeof(cmdbuf));
+ }
+ // Now we have a command string like:
+ // addr2line -e /path/to/exe -f -p 0x123 0x456 ...
+
+ log_lock();
+ FILE *log_file = open_log_file();
+ if (log_file == NULL) {
+ goto end;
+ }
+
+ FILE *fp = popen(cmdbuf, "r");
+ char linebuf[IOSIZE];
+ while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) {
+ fprintf(log_file, " %s", linebuf);
+ }
+ pclose(fp);
+
+ if (log_file != stderr && log_file != stdout) {
+ fclose(log_file);
+ }
+end:
+ log_unlock();
+}
+#endif
+
static bool do_log_to_file(FILE *log_file, int log_level,
const char *func_name, int line_num, bool eol,
const char* fmt, ...)
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 221f0bbaf6..743a8d17aa 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -61,6 +61,10 @@
__VA_ARGS__)
#endif
+#ifdef HAVE_EXECINFO_BACKTRACE
+# define LOG_CALLSTACK() log_callstack(__func__, __LINE__)
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "log.h.generated.h"
#endif
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 4aee1c8e68..b24770a409 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -932,12 +932,12 @@ int utf_char2len(int c)
return 6;
}
-/*
- * Convert Unicode character "c" to UTF-8 string in "buf[]".
- * Returns the number of bytes.
- * This does not include composing characters.
- */
-int utf_char2bytes(int c, char_u *buf)
+/// Convert Unicode character to UTF-8 string
+///
+/// @param c character to convert to \p buf
+/// @param[out] buf UTF-8 string generated from \p c, does not add \0
+/// @return Number of bytes (1-6). Does not include composing characters.
+int utf_char2bytes(int c, char_u *const buf)
{
if (c < 0x80) { /* 7 bits */
buf[0] = c;
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 74c58fb203..328b96fd5c 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -585,7 +585,7 @@ void free_all_mem(void)
p_ea = false;
if (first_tabpage->tp_next != NULL)
do_cmdline_cmd("tabonly!");
- if (firstwin != lastwin)
+ if (!ONE_WINDOW)
do_cmdline_cmd("only!");
/* Free all spell info. */
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index c8e6012e5c..a498916e5e 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -26,7 +26,7 @@
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
-
+#include "nvim/eval/typval.h"
#define MENUDEPTH 10 /* maximum depth of menus */
@@ -38,8 +38,8 @@
-/* The character for each menu mode */
-static char_u menu_mode_chars[] = {'n', 'v', 's', 'o', 'i', 'c', 't'};
+/// The character for each menu mode
+static char_u menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' };
static char_u e_notsubmenu[] = N_(
"E327: Part of menu-item path is not sub-menu");
@@ -47,17 +47,14 @@ static char_u e_othermode[] = N_("E328: Menu only exists in another mode");
static char_u e_nomenu[] = N_("E329: No menu \"%s\"");
-/*
- * Do the :menu command and relatives.
- */
-void
-ex_menu (
- exarg_T *eap /* Ex command arguments */
-)
+/// Do the :menu command and relatives.
+/// @param eap Ex command arguments
+void
+ex_menu(exarg_T *eap)
{
char_u *menu_path;
int modes;
- char_u *map_to;
+ char_u *map_to; // command mapped to the menu entry
int noremap;
bool silent = false;
int unmenu;
@@ -93,7 +90,8 @@ ex_menu (
}
- /* Locate an optional "icon=filename" argument. */
+ // Locate an optional "icon=filename" argument
+ // TODO(nvim): Currently this is only parsed. Should expose it to UIs.
if (STRNCMP(arg, "icon=", 5) == 0) {
arg += 5;
while (*arg != NUL && *arg != ' ') {
@@ -107,12 +105,12 @@ ex_menu (
}
}
- /*
- * Fill in the priority table.
- */
- for (p = arg; *p; ++p)
- if (!ascii_isdigit(*p) && *p != '.')
+ // Fill in the priority table.
+ for (p = arg; *p; p++) {
+ if (!ascii_isdigit(*p) && *p != '.') {
break;
+ }
+ }
if (ascii_iswhite(*p)) {
for (i = 0; i < MENUDEPTH && !ascii_iswhite(*arg); ++i) {
pri_tab[i] = getdigits_long(&arg);
@@ -226,8 +224,7 @@ ex_menu (
menuarg.modes = modes;
menuarg.noremap[0] = noremap;
menuarg.silent[0] = silent;
- add_menu_path(menu_path, &menuarg, pri_tab, map_to
- );
+ add_menu_path(menu_path, &menuarg, pri_tab, map_to);
/*
* For the PopUp menu, add a menu for each mode separately.
@@ -252,16 +249,18 @@ theend:
;
}
-/*
- * Add the menu with the given name to the menu hierarchy
- */
-static int
-add_menu_path (
- char_u *menu_path,
- vimmenu_T *menuarg, /* passes modes, iconfile, iconidx,
- icon_builtin, silent[0], noremap[0] */
- long *pri_tab,
- char_u *call_data
+
+/// Add the menu with the given name to the menu hierarchy
+///
+/// @param[out] menuarg menu entry
+/// @param[] pri_tab priority table
+/// @param[in] call_data Right hand side command
+static int
+add_menu_path(
+ const char_u *const menu_path,
+ vimmenu_T *menuarg,
+ const long *const pri_tab,
+ const char_u *const call_data
)
{
char_u *path_name;
@@ -296,8 +295,9 @@ add_menu_path (
if (map_to != NULL) {
en_name = name;
name = map_to;
- } else
+ } else {
en_name = NULL;
+ }
dname = menu_text(name, NULL, NULL);
if (*dname == NUL) {
/* Only a mnemonic or accelerator is not valid. */
@@ -311,14 +311,15 @@ add_menu_path (
while (menu != NULL) {
if (menu_name_equal(name, menu) || menu_name_equal(dname, menu)) {
if (*next_name == NUL && menu->children != NULL) {
- if (!sys_menu)
+ if (!sys_menu) {
EMSG(_("E330: Menu path must not lead to a sub-menu"));
+ }
goto erret;
}
- if (*next_name != NUL && menu->children == NULL
- ) {
- if (!sys_menu)
+ if (*next_name != NUL && menu->children == NULL) {
+ if (!sys_menu) {
EMSG(_(e_notsubmenu));
+ }
goto erret;
}
break;
@@ -352,7 +353,7 @@ add_menu_path (
menu->modes = modes;
menu->enabled = MENU_ALL_MODES;
menu->name = vim_strsave(name);
- /* separate mnemonic and accelerator text from actual menu name */
+ // separate mnemonic and accelerator text from actual menu name
menu->dname = menu_text(name, &menu->mnemonic, &menu->actext);
if (en_name != NULL) {
menu->en_name = vim_strsave(en_name);
@@ -364,9 +365,7 @@ add_menu_path (
menu->priority = pri_tab[pri_idx];
menu->parent = parent;
- /*
- * Add after menu that has lower priority.
- */
+ // Add after menu that has lower priority.
menu->next = *lower_pri;
*lower_pri = menu;
@@ -392,8 +391,9 @@ add_menu_path (
name = next_name;
xfree(dname);
dname = NULL;
- if (pri_tab[pri_idx + 1] != -1)
- ++pri_idx;
+ if (pri_tab[pri_idx + 1] != -1) {
+ pri_idx++;
+ }
}
xfree(path_name);
@@ -419,8 +419,7 @@ add_menu_path (
// Don't do this for "<Nop>".
c = 0;
d = 0;
- if (amenu && call_data != NULL && *call_data != NUL
- ) {
+ if (amenu && call_data != NULL && *call_data != NUL) {
switch (1 << i) {
case MENU_VISUAL_MODE:
case MENU_SELECT_MODE:
@@ -438,9 +437,9 @@ add_menu_path (
if (c != 0) {
menu->strings[i] = xmalloc(STRLEN(call_data) + 5 );
menu->strings[i][0] = c;
- if (d == 0)
+ if (d == 0) {
STRCPY(menu->strings[i] + 1, call_data);
- else {
+ } else {
menu->strings[i][1] = d;
STRCPY(menu->strings[i] + 2, call_data);
}
@@ -452,8 +451,9 @@ add_menu_path (
menu->strings[i][len + 1] = Ctrl_G;
menu->strings[i][len + 2] = NUL;
}
- } else
+ } else {
menu->strings[i] = p;
+ }
menu->noremap[i] = menuarg->noremap[0];
menu->silent[i] = menuarg->silent[0];
}
@@ -657,20 +657,109 @@ static void free_menu_string(vimmenu_T *menu, int idx)
menu->strings[idx] = NULL;
}
-/*
- * Show the mapping associated with a menu item or hierarchy in a sub-menu.
- */
-static int show_menus(char_u *path_name, int modes)
+/// Export menus
+///
+/// @param[in] menu if null, starts from root_menu
+/// @param modes, a choice of \ref MENU_MODES
+/// @return a dict with name/commands
+/// @see menu_get
+static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
+{
+ dict_T *dict;
+ char buf[sizeof(menu->mnemonic)];
+ int mnemonic_len;
+
+ if (!menu || (menu->modes & modes) == 0x0) {
+ return NULL;
+ }
+
+ dict = tv_dict_alloc();
+ tv_dict_add_str(dict, S_LEN("name"), (char *)menu->dname);
+ tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority);
+ tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname));
+
+ if (menu->mnemonic) {
+ mnemonic_len = utf_char2bytes(menu->mnemonic, (u_char *)buf);
+ buf[mnemonic_len] = '\0';
+ tv_dict_add_str(dict, S_LEN("shortcut"), buf);
+ }
+
+ if (menu->modes & MENU_TIP_MODE && menu->strings[MENU_INDEX_TIP]) {
+ tv_dict_add_str(dict, S_LEN("tooltip"),
+ (char *)menu->strings[MENU_INDEX_TIP]);
+ }
+
+ if (!menu->children) {
+ // leaf menu
+ dict_T *commands = tv_dict_alloc();
+ tv_dict_add_dict(dict, S_LEN("mappings"), commands);
+
+ for (int bit = 0; bit < MENU_MODES; bit++) {
+ if ((menu->modes & modes & (1 << bit)) != 0) {
+ dict_T *impl = tv_dict_alloc();
+ if (*menu->strings[bit] == NUL) {
+ tv_dict_add_str(impl, S_LEN("rhs"), (char *)"<Nop>");
+ } else {
+ tv_dict_add_str(impl, S_LEN("rhs"), (char *)menu->strings[bit]);
+ }
+ tv_dict_add_nr(impl, S_LEN("silent"), menu->silent[bit]);
+ tv_dict_add_nr(impl, S_LEN("enabled"),
+ (menu->enabled & (1 << bit)) ? 1 : 0);
+ tv_dict_add_nr(impl, S_LEN("noremap"),
+ (menu->noremap[bit] & REMAP_NONE) ? 1 : 0);
+ tv_dict_add_nr(impl, S_LEN("sid"),
+ (menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0);
+ tv_dict_add_dict(commands, (char *)&menu_mode_chars[bit], 1, impl);
+ }
+ }
+ } else {
+ // visit recursively all children
+ list_T *children_list = tv_list_alloc();
+ for (menu = menu->children; menu != NULL; menu = menu->next) {
+ dict_T *dic = menu_get_recursive(menu, modes);
+ if (dict && tv_dict_len(dict) > 0) {
+ tv_list_append_dict(children_list, dic);
+ }
+ }
+ tv_dict_add_list(dict, S_LEN("submenus"), children_list);
+ }
+ return dict;
+}
+
+
+/// Export menus matching path \p path_name
+///
+/// @param path_name
+/// @param modes supported modes, see \ref MENU_MODES
+/// @param[in,out] list must be allocated
+/// @return false if could not find path_name
+bool menu_get(char_u *const path_name, int modes, list_T *list)
{
- char_u *p;
- char_u *name;
vimmenu_T *menu;
- vimmenu_T *parent = NULL;
+ menu = find_menu(root_menu, path_name, modes);
+ if (!menu) {
+ return false;
+ }
+ for (; menu != NULL; menu = menu->next) {
+ dict_T *dict = menu_get_recursive(menu, modes);
+ if (dict && tv_dict_len(dict) > 0) {
+ tv_list_append_dict(list, dict);
+ }
+ }
+ return true;
+}
- menu = root_menu;
- name = path_name = vim_strsave(path_name);
- /* First, find the (sub)menu with the given name */
+/// Find menu matching required name and modes
+///
+/// @param menu top menu to start looking from
+/// @param name path towards the menu
+/// @return menu if \p name is null, found menu or NULL
+vimmenu_T *
+find_menu(vimmenu_T *menu, char_u * name, int modes)
+{
+ char_u *p;
+
while (*name) {
p = menu_name_skip(name);
while (menu != NULL) {
@@ -678,39 +767,46 @@ static int show_menus(char_u *path_name, int modes)
/* Found menu */
if (*p != NUL && menu->children == NULL) {
EMSG(_(e_notsubmenu));
- xfree(path_name);
- return FAIL;
+ return NULL;
} else if ((menu->modes & modes) == 0x0) {
EMSG(_(e_othermode));
- xfree(path_name);
- return FAIL;
+ return NULL;
}
break;
}
menu = menu->next;
}
+
if (menu == NULL) {
EMSG2(_(e_nomenu), name);
- xfree(path_name);
- return FAIL;
+ return NULL;
}
name = p;
- parent = menu;
menu = menu->children;
}
- xfree(path_name);
+ return menu;
+}
+
+/// Show the mapping associated with a menu item or hierarchy in a sub-menu.
+static int show_menus(char_u *const path_name, int modes)
+{
+ vimmenu_T *menu;
+
+ // First, find the (sub)menu with the given name
+ menu = find_menu(root_menu, path_name, modes);
+ if (!menu) {
+ return FAIL;
+ }
/* Now we have found the matching menu, and we list the mappings */
/* Highlight title */
MSG_PUTS_TITLE(_("\n--- Menus ---"));
- show_menus_recursive(parent, modes, 0);
+ show_menus_recursive(menu->parent, modes, 0);
return OK;
}
-/*
- * Recursively show the mappings associated with the menus under the given one
- */
+/// Recursively show the mappings associated with the menus under the given one
static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
{
int i;
@@ -993,12 +1089,13 @@ char_u *get_menu_names(expand_T *xp, int idx)
return str;
}
-/*
- * Skip over this element of the menu path and return the start of the next
- * element. Any \ and ^Vs are removed from the current element.
- * "name" may be modified.
- */
-char_u *menu_name_skip(char_u *name)
+
+/// Skip over this element of the menu path and return the start of the next
+/// element. Any \ and ^Vs are removed from the current element.
+///
+/// @param name may be modified.
+/// @return start of the next element
+char_u *menu_name_skip(char_u *const name)
{
char_u *p;
@@ -1018,16 +1115,16 @@ char_u *menu_name_skip(char_u *name)
* Return TRUE when "name" matches with menu "menu". The name is compared in
* two ways: raw menu name and menu name without '&'. ignore part after a TAB.
*/
-static int menu_name_equal(char_u *name, vimmenu_T *menu)
+static bool menu_name_equal(const char_u *const name, vimmenu_T *const menu)
{
if (menu->en_name != NULL
&& (menu_namecmp(name, menu->en_name)
|| menu_namecmp(name, menu->en_dname)))
- return TRUE;
+ return true;
return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname);
}
-static int menu_namecmp(char_u *name, char_u *mname)
+static bool menu_namecmp(const char_u *const name, const char_u *const mname)
{
int i;
@@ -1038,18 +1135,20 @@ static int menu_namecmp(char_u *name, char_u *mname)
&& (mname[i] == NUL || mname[i] == TAB);
}
-/*
- * Return the modes specified by the given menu command (eg :menu! returns
- * MENU_CMDLINE_MODE | MENU_INSERT_MODE).
- * If "noremap" is not NULL, then the flag it points to is set according to
- * whether the command is a "nore" command.
- * If "unmenu" is not NULL, then the flag it points to is set according to
- * whether the command is an "unmenu" command.
- */
-static int
-get_menu_cmd_modes (
- char_u *cmd,
- int forceit, /* Was there a "!" after the command? */
+
+/// Returns the \ref MENU_MODES specified by menu command `cmd`.
+/// (eg :menu! returns MENU_CMDLINE_MODE | MENU_INSERT_MODE)
+///
+/// @param[in] cmd string like "nmenu", "vmenu", etc.
+/// @param[in] forceit bang (!) was given after the command
+/// @param[out] noremap If not NULL, the flag it points to is set according
+/// to whether the command is a "nore" command.
+/// @param[out] unmenu If not NULL, the flag it points to is set according
+/// to whether the command is an "unmenu" command.
+int
+get_menu_cmd_modes(
+ const char_u * cmd,
+ bool forceit,
int *noremap,
int *unmenu
)
@@ -1090,12 +1189,15 @@ get_menu_cmd_modes (
}
/* FALLTHROUGH */
default:
- --cmd;
- if (forceit) /* menu!! */
+ cmd--;
+ if (forceit) {
+ // menu!!
modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE;
- else /* menu */
+ } else {
+ // menu
modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE
| MENU_OP_PENDING_MODE;
+ }
}
if (noremap != NULL)
@@ -1201,12 +1303,14 @@ int menu_is_separator(char_u *name)
return name[0] == '-' && name[STRLEN(name) - 1] == '-';
}
-/*
- * Return TRUE if the menu is hidden: Starts with ']'
- */
+
+/// True if a popup menu or starts with \ref MNU_HIDDEN_CHAR
+///
+/// @return true if the menu is hidden
static int menu_is_hidden(char_u *name)
{
- return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL);
+ return (name[0] == MNU_HIDDEN_CHAR)
+ || (menu_is_popup(name) && name[5] != NUL);
}
/*
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index a84b7d812e..5ff979f2bf 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -6,7 +6,9 @@
#include "nvim/types.h" // for char_u and expand_T
#include "nvim/ex_cmds_defs.h" // for exarg_T
-/* Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode */
+/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
+/// \addtogroup MENU_INDEX
+/// @{
#define MENU_INDEX_INVALID -1
#define MENU_INDEX_NORMAL 0
#define MENU_INDEX_VISUAL 1
@@ -16,8 +18,12 @@
#define MENU_INDEX_CMDLINE 5
#define MENU_INDEX_TIP 6
#define MENU_MODES 7
+/// @}
+/// note MENU_INDEX_TIP is not a 'real' mode
-/* Menu modes */
+/// Menu modes
+/// \addtogroup MENU_MODES
+/// @{
#define MENU_NORMAL_MODE (1 << MENU_INDEX_NORMAL)
#define MENU_VISUAL_MODE (1 << MENU_INDEX_VISUAL)
#define MENU_SELECT_MODE (1 << MENU_INDEX_SELECT)
@@ -26,31 +32,30 @@
#define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE)
#define MENU_TIP_MODE (1 << MENU_INDEX_TIP)
#define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1)
-/*note MENU_INDEX_TIP is not a 'real' mode*/
+/// @}
-/* Start a menu name with this to not include it on the main menu bar */
+/// Start a menu name with this to not include it on the main menu bar
#define MNU_HIDDEN_CHAR ']'
typedef struct VimMenu vimmenu_T;
struct VimMenu {
- int modes; /* Which modes is this menu visible for? */
- int enabled; /* for which modes the menu is enabled */
- char_u *name; /* Name of menu, possibly translated */
- char_u *dname; /* Displayed Name ("name" without '&') */
- char_u *en_name; /* "name" untranslated, NULL when "name"
- * was not translated */
- char_u *en_dname; /* "dname" untranslated, NULL when "dname"
- * was not translated */
- int mnemonic; /* mnemonic key (after '&') */
- char_u *actext; /* accelerator text (after TAB) */
- long priority; /* Menu order priority */
- char_u *strings[MENU_MODES]; /* Mapped string for each mode */
- int noremap[MENU_MODES]; /* A REMAP_ flag for each mode */
- bool silent[MENU_MODES]; /* A silent flag for each mode */
- vimmenu_T *children; /* Children of sub-menu */
- vimmenu_T *parent; /* Parent of menu */
- vimmenu_T *next; /* Next item in menu */
+ int modes; ///< Which modes is this menu visible for
+ int enabled; ///< for which modes the menu is enabled
+ char_u *name; ///< Name of menu, possibly translated
+ char_u *dname; ///< Displayed Name ("name" without '&')
+ char_u *en_name; ///< "name" untranslated, NULL when
+ ///< was not translated
+ char_u *en_dname; ///< NULL when "dname" untranslated
+ int mnemonic; ///< mnemonic key (after '&')
+ char_u *actext; ///< accelerator text (after TAB)
+ long priority; ///< Menu order priority
+ char_u *strings[MENU_MODES]; ///< Mapped string for each mode
+ int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode
+ bool silent[MENU_MODES]; ///< A silent flag for each mode
+ vimmenu_T *children; ///< Children of sub-menu
+ vimmenu_T *parent; ///< Parent of menu
+ vimmenu_T *next; ///< Next item in menu
};
diff --git a/src/nvim/move.c b/src/nvim/move.c
index d5be4cb8c3..81d46a7f17 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1763,7 +1763,7 @@ int onepage(int dir, long count)
loff.fill = 0;
if (dir == FORWARD) {
- if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) {
+ if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
/* Vi compatible scrolling */
if (p_window <= 2)
++curwin->w_topline;
@@ -1797,7 +1797,7 @@ int onepage(int dir, long count)
max_topfill();
continue;
}
- if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) {
+ if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
/* Vi compatible scrolling (sort of) */
if (p_window <= 2)
--curwin->w_topline;
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 68ac35bc4e..6fd1af1ba6 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -117,12 +117,15 @@ void channel_teardown(void)
/// Creates an API channel by starting a process and connecting to its
/// stdin/stdout. stderr is handled by the job infrastructure.
///
-/// @param argv The argument vector for the process. [consumed]
-/// @return The channel id (> 0), on success.
-/// 0, on error.
-uint64_t channel_from_process(Process *proc, uint64_t id)
+/// @param proc process object
+/// @param id (optional) channel id
+/// @param source description of source function, rplugin name, TCP addr, etc
+///
+/// @return Channel id (> 0), on success. 0, on error.
+uint64_t channel_from_process(Process *proc, uint64_t id, char *source)
{
- Channel *channel = register_channel(kChannelTypeProc, id, proc->events);
+ Channel *channel = register_channel(kChannelTypeProc, id, proc->events,
+ source);
incref(channel); // process channels are only closed by the exit_cb
channel->data.proc = proc;
@@ -138,7 +141,8 @@ uint64_t channel_from_process(Process *proc, uint64_t id)
/// @param watcher The SocketWatcher ready to accept the connection
void channel_from_connection(SocketWatcher *watcher)
{
- Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeSocket, 0, NULL,
+ watcher->addr);
socket_watcher_accept(watcher, &channel->data.stream);
incref(channel); // close channel only after the stream is closed
channel->data.stream.internal_close_cb = close_cb;
@@ -148,8 +152,9 @@ void channel_from_connection(SocketWatcher *watcher)
rstream_start(&channel->data.stream, receive_msgpack, channel);
}
-uint64_t channel_connect(bool tcp, const char *address,
- int timeout, const char **error)
+/// @param source description of source function, rplugin name, TCP addr, etc
+uint64_t channel_connect(bool tcp, const char *address, int timeout,
+ char *source, const char **error)
{
if (!tcp) {
char *path = fix_fname(address);
@@ -161,7 +166,7 @@ uint64_t channel_connect(bool tcp, const char *address,
xfree(path);
}
- Channel *channel = register_channel(kChannelTypeSocket, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeSocket, 0, NULL, source);
if (!socket_connect(&main_loop, &channel->data.stream,
tcp, address, timeout, error)) {
decref(channel);
@@ -329,11 +334,10 @@ bool channel_close(uint64_t id)
return true;
}
-/// Creates an API channel from stdin/stdout. This is used when embedding
-/// Neovim
+/// Creates an API channel from stdin/stdout. Used to embed Nvim.
void channel_from_stdio(void)
{
- Channel *channel = register_channel(kChannelTypeStdio, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeStdio, 0, NULL, NULL);
incref(channel); // stdio channels are only closed on exit
// read stream
rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE);
@@ -346,7 +350,7 @@ void channel_from_stdio(void)
/// when an instance connects to its own named pipe.
uint64_t channel_create_internal(void)
{
- Channel *channel = register_channel(kChannelTypeInternal, 0, NULL);
+ Channel *channel = register_channel(kChannelTypeInternal, 0, NULL, NULL);
incref(channel); // internal channel lives until process exit
return channel->id;
}
@@ -745,9 +749,12 @@ static void close_cb(Stream *stream, void *data)
decref(data);
}
+/// @param source description of source function, rplugin name, TCP addr, etc
static Channel *register_channel(ChannelType type, uint64_t id,
- MultiQueue *events)
+ MultiQueue *events, char *source)
{
+ // Jobs and channels share the same id namespace.
+ assert(id == 0 || !pmap_get(uint64_t)(channels, id));
Channel *rv = xmalloc(sizeof(Channel));
rv->events = events ? events : multiqueue_new_child(main_loop.events);
rv->type = type;
@@ -761,6 +768,14 @@ static Channel *register_channel(ChannelType type, uint64_t id,
kv_init(rv->call_stack);
kv_init(rv->delayed_notifications);
pmap_put(uint64_t)(channels, rv->id, rv);
+
+ ILOG("new channel %" PRIu64 " (%s): %s", rv->id,
+ (type == kChannelTypeProc ? "proc"
+ : (type == kChannelTypeSocket ? "socket"
+ : (type == kChannelTypeStdio ? "stdio"
+ : (type == kChannelTypeInternal ? "internal" : "?")))),
+ (source ? source : "?"));
+
return rv;
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index d891c74fd2..c1676780d8 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3657,6 +3657,39 @@ nv_gd (
}
}
+// Return true if line[offset] is not inside a C-style comment or string, false
+// otherwise.
+static bool is_ident(char_u *line, int offset)
+{
+ bool incomment = false;
+ int instring = 0;
+ int prev = 0;
+
+ for (int i = 0; i < offset && line[i] != NUL; i++) {
+ if (instring != 0) {
+ if (prev != '\\' && line[i] == instring) {
+ instring = 0;
+ }
+ } else if ((line[i] == '"' || line[i] == '\'') && !incomment) {
+ instring = line[i];
+ } else {
+ if (incomment) {
+ if (prev == '*' && line[i] == '/') {
+ incomment = false;
+ }
+ } else if (prev == '/' && line[i] == '*') {
+ incomment = true;
+ } else if (prev == '/' && line[i] == '/') {
+ return false;
+ }
+ }
+
+ prev = line[i];
+ }
+
+ return incomment == false && instring == 0;
+}
+
/*
* Search for variable declaration of "ptr[len]".
* When "locally" is true in the current function ("gd"), otherwise in the
@@ -3683,6 +3716,7 @@ find_decl (
bool retval = true;
bool incll;
int searchflags = flags_arg;
+ bool valid;
pat = xmalloc(len + 7);
@@ -3717,6 +3751,7 @@ find_decl (
/* Search forward for the identifier, ignore comment lines. */
clearpos(&found_pos);
for (;; ) {
+ valid = false;
t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD,
pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL);
if (curwin->w_cursor.lnum >= old_pos.lnum)
@@ -3747,20 +3782,35 @@ find_decl (
curwin->w_cursor.col = 0;
continue;
}
- if (!locally) /* global search: use first match found */
+ valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col);
+
+ // If the current position is not a valid identifier and a previous match is
+ // present, favor that one instead.
+ if (!valid && found_pos.lnum != 0) {
+ curwin->w_cursor = found_pos;
break;
- if (curwin->w_cursor.lnum >= par_pos.lnum) {
- /* If we previously found a valid position, use it. */
- if (found_pos.lnum != 0)
+ }
+ // global search: use first match found
+ if (valid && !locally) {
+ break;
+ }
+ if (valid && curwin->w_cursor.lnum >= par_pos.lnum) {
+ // If we previously found a valid position, use it.
+ if (found_pos.lnum != 0) {
curwin->w_cursor = found_pos;
+ }
break;
}
- // For finding a local variable and the match is before the "{" search
- // to find a later match. For K&R style function declarations this
- // skips the function header without types. Remove SEARCH_START from
- // flags to avoid getting stuck at one position.
- found_pos = curwin->w_cursor;
+ // For finding a local variable and the match is before the "{" or
+ // inside a comment, continue searching. For K&R style function
+ // declarations this skips the function header without types.
+ if (!valid) {
+ clearpos(&found_pos);
+ } else {
+ found_pos = curwin->w_cursor;
+ }
+ // Remove SEARCH_START from flags to avoid getting stuck at one position.
searchflags &= ~SEARCH_START;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 37b37e2859..98ca72b2bb 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -2457,12 +2457,11 @@ did_set_string_option (
if ((secure || sandbox != 0)
&& (options[opt_idx].flags & P_SECURE)) {
errmsg = e_secure;
- }
- /* Check for a "normal" file name in some options. Disallow a path
- * separator (slash and/or backslash), wildcards and characters that are
- * often illegal in a file name. */
- else if ((options[opt_idx].flags & P_NFNAME)
- && vim_strpbrk(*varp, (char_u *)"/\\*?[|<>") != NULL) {
+ } else if ((options[opt_idx].flags & P_NFNAME)
+ && vim_strpbrk(*varp, (char_u *)"/\\*?[|;&<>\r\n") != NULL) {
+ // Check for a "normal" file name in some options. Disallow a path
+ // separator (slash and/or backslash), wildcards and characters that are
+ // often illegal in a file name.
errmsg = e_invarg;
}
/* 'backupcopy' */
@@ -4078,7 +4077,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
}
/* Change window height NOW */
- if (lastwin != firstwin) {
+ if (!ONE_WINDOW) {
if (pp == &p_wh && curwin->w_height < p_wh)
win_setheight((int)p_wh);
if (pp == &p_hh && curbuf->b_help && curwin->w_height < p_hh)
@@ -4107,7 +4106,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
}
/* Change window width NOW */
- if (lastwin != firstwin && curwin->w_width < p_wiw)
+ if (!ONE_WINDOW && curwin->w_width < p_wiw)
win_setwidth((int)p_wiw);
}
/* 'winminwidth' */
@@ -5239,7 +5238,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
void comp_col(void)
{
- int last_has_status = (p_ls == 2 || (p_ls == 1 && firstwin != lastwin));
+ int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW));
sc_col = 0;
ru_col = 0;
@@ -5718,7 +5717,22 @@ void buf_copy_options(buf_T *buf, int flags)
free_buf_options(buf, TRUE);
buf->b_p_ro = FALSE; /* don't copy readonly */
buf->b_p_fenc = vim_strsave(p_fenc);
- buf->b_p_ff = vim_strsave(p_ff);
+ switch (*p_ffs) {
+ case 'm':
+ buf->b_p_ff = vim_strsave((char_u *)FF_MAC);
+ break;
+ case 'd':
+ buf->b_p_ff = vim_strsave((char_u *)FF_DOS);
+ break;
+ case 'u':
+ buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ break;
+ default:
+ buf->b_p_ff = vim_strsave(p_ff);
+ }
+ if (buf->b_p_ff != NULL) {
+ buf->b_start_ffc = *buf->b_p_ff;
+ }
buf->b_p_bh = empty_option;
buf->b_p_bt = empty_option;
} else
@@ -7039,8 +7053,11 @@ dict_T *get_winbuf_options(const int bufopt)
if (opt->flags & P_STRING) {
tv_dict_add_str(d, opt->fullname, strlen(opt->fullname),
*(const char **)varp);
+ } else if (opt->flags & P_NUM) {
+ tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname),
+ *(long *)varp);
} else {
- tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp);
+ tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *(int *)varp);
}
}
}
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 9d80a43718..32e9a70e57 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -447,7 +447,7 @@ static void out_data_append_to_screen(char *output, size_t remaining,
size_t off = 0;
int last_row = (int)Rows - 1;
- while (off < remaining) {
+ while (output != NULL && off < remaining) {
// Found end of line?
if (output[off] == NL) {
// Can we start a new line or do we need to continue the last one?
@@ -473,7 +473,7 @@ static void out_data_append_to_screen(char *output, size_t remaining,
off++;
}
- if (remaining) {
+ if (output != NULL && remaining) {
if (last_col == 0) {
screen_del_lines(0, 0, 1, (int)Rows, NULL);
}
@@ -496,12 +496,8 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
size_t cnt;
char *ptr = rbuffer_read_ptr(buf, &cnt);
- if (ptr == NULL || cnt == 0) {
- // Nothing to read;
- return;
- }
-
- if (out_data_decide_throttle(cnt)) { // Skip output above a threshold.
+ if (ptr != NULL && cnt > 0
+ && out_data_decide_throttle(cnt)) { // Skip output above a threshold.
// Save the skipped output. If it is the final chunk, we display it later.
out_data_ring(ptr, cnt);
} else {
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index bd5dfa92cc..e6b1e7b95a 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -383,6 +383,8 @@ static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr,
return 0;
}
+static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
+
static void free_efm_list(efm_T **efm_first)
{
for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) {
@@ -390,6 +392,8 @@ static void free_efm_list(efm_T **efm_first)
vim_regfree(efm_ptr->prog);
xfree(efm_ptr);
}
+
+ fmt_start = NULL;
}
// Parse 'errorformat' option
@@ -671,7 +675,6 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen,
efm_T *fmt_first, qffields_T *fields)
{
efm_T *fmt_ptr;
- static efm_T *fmt_start = NULL; // cached across calls
size_t len;
int i;
int idx = 0;
@@ -875,36 +878,38 @@ restofline:
qi->qf_multiignore = false; // reset continuation
} else if (vim_strchr((char_u *)"CZ", idx)
!= NULL) { // continuation of multi-line msg
- qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last;
- if (qfprev == NULL) {
- return QF_FAIL;
- }
- if (*fields->errmsg && !qi->qf_multiignore) {
- size_t len = STRLEN(qfprev->qf_text);
- qfprev->qf_text = xrealloc(qfprev->qf_text,
- len + STRLEN(fields->errmsg) + 2);
- qfprev->qf_text[len] = '\n';
- STRCPY(qfprev->qf_text + len + 1, fields->errmsg);
- }
- if (qfprev->qf_nr == -1) {
- qfprev->qf_nr = fields->enr;
- }
- if (vim_isprintc(fields->type) && !qfprev->qf_type) {
- qfprev->qf_type = fields->type; // only printable chars allowed
- }
- if (!qfprev->qf_lnum) {
- qfprev->qf_lnum = fields->lnum;
- }
- if (!qfprev->qf_col) {
- qfprev->qf_col = fields->col;
- }
- qfprev->qf_viscol = fields->use_viscol;
- if (!qfprev->qf_fnum) {
- qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory,
- *fields->namebuf || qi->qf_directory
- ? fields->namebuf
- : qi->qf_currfile && fields->valid
- ? qi->qf_currfile : 0);
+ if (!qi->qf_multiignore) {
+ qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last;
+ if (qfprev == NULL) {
+ return QF_FAIL;
+ }
+ if (*fields->errmsg && !qi->qf_multiignore) {
+ size_t len = STRLEN(qfprev->qf_text);
+ qfprev->qf_text = xrealloc(qfprev->qf_text,
+ len + STRLEN(fields->errmsg) + 2);
+ qfprev->qf_text[len] = '\n';
+ STRCPY(qfprev->qf_text + len + 1, fields->errmsg);
+ }
+ if (qfprev->qf_nr == -1) {
+ qfprev->qf_nr = fields->enr;
+ }
+ if (vim_isprintc(fields->type) && !qfprev->qf_type) {
+ qfprev->qf_type = fields->type; // only printable chars allowed
+ }
+ if (!qfprev->qf_lnum) {
+ qfprev->qf_lnum = fields->lnum;
+ }
+ if (!qfprev->qf_col) {
+ qfprev->qf_col = fields->col;
+ }
+ qfprev->qf_viscol = fields->use_viscol;
+ if (!qfprev->qf_fnum) {
+ qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory,
+ *fields->namebuf || qi->qf_directory
+ ? fields->namebuf
+ : qi->qf_currfile && fields->valid
+ ? qi->qf_currfile : 0);
+ }
}
if (idx == 'Z') {
qi->qf_multiline = qi->qf_multiignore = false;
@@ -967,6 +972,7 @@ qf_init_ext(
NULL, 0, 0 };
qffields_T fields = { NULL, NULL, 0, 0L, 0, false, NULL, 0, 0, 0 };
qfline_T *old_last = NULL;
+ bool adding = false;
static efm_T *fmt_first = NULL;
char_u *efm;
static char_u *last_efm = NULL;
@@ -992,6 +998,7 @@ qf_init_ext(
qf_new_list(qi, qf_title);
} else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) {
// Adding to existing list, use last entry.
+ adding = true;
old_last = qi->qf_lists[qi->qf_curlist].qf_last;
}
@@ -1108,10 +1115,12 @@ qf_init_ext(
}
EMSG(_(e_readerrf));
error2:
- qf_free(qi, qi->qf_curlist);
- qi->qf_listcount--;
- if (qi->qf_curlist > 0) {
- qi->qf_curlist--;
+ if (!adding) {
+ qf_free(qi, qi->qf_curlist);
+ qi->qf_listcount--;
+ if (qi->qf_curlist > 0) {
+ qi->qf_curlist--;
+ }
}
qf_init_end:
if (state.fd != NULL) {
@@ -1408,7 +1417,7 @@ void copy_loclist(win_T *from, win_T *to)
to->w_llist->qf_curlist = qi->qf_curlist; /* current list */
}
-// Get buffer number for file "directory.fname".
+// Get buffer number for file "directory/fname".
// Also sets the b_has_qf_entry flag.
static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname)
{
@@ -1869,7 +1878,7 @@ win_found:
* If there is only one window and it is the quickfix window, create a
* new one above the quickfix window.
*/
- if (((firstwin == lastwin) && bt_quickfix(curbuf)) || !usable_win) {
+ if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) {
flags = WSP_ABOVE;
if (ll_ref != NULL)
flags |= WSP_NEWLOC;
@@ -2362,7 +2371,9 @@ static void qf_free(qf_info_T *qi, int idx)
qi->qf_lists[idx].qf_index = 0;
qf_clean_dir_stack(&qi->qf_dir_stack);
+ qi->qf_directory = NULL;
qf_clean_dir_stack(&qi->qf_file_stack);
+ qi->qf_currfile = NULL;
}
/*
@@ -3116,7 +3127,7 @@ static char_u *get_mef_name(void)
STRCPY(name, p_mef);
sprintf((char *)name + (p - p_mef), "%d%d", start, off);
STRCAT(name, p + 2);
- // Don't accept a symbolic link, its a security risk.
+ // Don't accept a symbolic link, it's a security risk.
FileInfo file_info;
bool file_or_link_found = os_fileinfo_link((char *)name, &file_info);
if (!file_or_link_found) {
@@ -4025,9 +4036,12 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) {
// Use the specified quickfix/location list
if (di->di_tv.v_type == VAR_NUMBER) {
- qf_idx = (int)di->di_tv.vval.v_number - 1;
- if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
- return FAIL;
+ // for zero use the current list
+ if (di->di_tv.vval.v_number != 0) {
+ qf_idx = (int)di->di_tv.vval.v_number - 1;
+ if (qf_idx < 0 || qf_idx >= qi->qf_listcount) {
+ return FAIL;
+ }
}
flags |= QF_GETLIST_NR;
} else {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 41070aebf4..847b2f273e 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -3172,61 +3172,56 @@ static int need_clear_zsubexpr = FALSE; /* extmatch subexpressions
int regnarrate = 0;
#endif
-/*
- * Internal copy of 'ignorecase'. It is set at each call to vim_regexec().
- * Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern
- * contains '\c' or '\C' the value is overruled.
- */
-static int ireg_ic;
-
-/*
- * Similar to ireg_ic, but only for 'combining' characters. Set with \Z flag
- * in the regexp. Defaults to false, always.
- */
-static int ireg_icombine;
-
-/*
- * Copy of "rmm_maxcol": maximum column to search for a match. Zero when
- * there is no maximum.
- */
-static colnr_T ireg_maxcol;
-
-/*
- * Sometimes need to save a copy of a line. Since alloc()/free() is very
- * slow, we keep one allocated piece of memory and only re-allocate it when
- * it's too small. It's freed in bt_regexec_both() when finished.
- */
+// Sometimes need to save a copy of a line. Since alloc()/free() is very
+// slow, we keep one allocated piece of memory and only re-allocate it when
+// it's too small. It's freed in bt_regexec_both() when finished.
static char_u *reg_tofree = NULL;
static unsigned reg_tofreelen;
-/*
- * These variables are set when executing a regexp to speed up the execution.
- * Which ones are set depends on whether a single-line or multi-line match is
- * done:
- * single-line multi-line
- * reg_match &regmatch_T NULL
- * reg_mmatch NULL &regmmatch_T
- * reg_startp reg_match->startp <invalid>
- * reg_endp reg_match->endp <invalid>
- * reg_startpos <invalid> reg_mmatch->startpos
- * reg_endpos <invalid> reg_mmatch->endpos
- * reg_win NULL window in which to search
- * reg_buf curbuf buffer in which to search
- * reg_firstlnum <invalid> first line in which to search
- * reg_maxline 0 last line nr
- * reg_line_lbr FALSE or TRUE FALSE
- */
-static regmatch_T *reg_match;
-static regmmatch_T *reg_mmatch;
-static char_u **reg_startp = NULL;
-static char_u **reg_endp = NULL;
-static lpos_T *reg_startpos = NULL;
-static lpos_T *reg_endpos = NULL;
-static win_T *reg_win;
-static buf_T *reg_buf;
-static linenr_T reg_firstlnum;
-static linenr_T reg_maxline;
-static int reg_line_lbr; /* "\n" in string is line break */
+// Structure used to store the execution state of the regex engine.
+// Which ones are set depends on whether a single-line or multi-line match is
+// done:
+// single-line multi-line
+// reg_match &regmatch_T NULL
+// reg_mmatch NULL &regmmatch_T
+// reg_startp reg_match->startp <invalid>
+// reg_endp reg_match->endp <invalid>
+// reg_startpos <invalid> reg_mmatch->startpos
+// reg_endpos <invalid> reg_mmatch->endpos
+// reg_win NULL window in which to search
+// reg_buf curbuf buffer in which to search
+// reg_firstlnum <invalid> first line in which to search
+// reg_maxline 0 last line nr
+// reg_line_lbr false or true false
+typedef struct {
+ regmatch_T *reg_match;
+ regmmatch_T *reg_mmatch;
+ char_u **reg_startp;
+ char_u **reg_endp;
+ lpos_T *reg_startpos;
+ lpos_T *reg_endpos;
+ win_T *reg_win;
+ buf_T *reg_buf;
+ linenr_T reg_firstlnum;
+ linenr_T reg_maxline;
+ bool reg_line_lbr; // "\n" in string is line break
+
+ // Internal copy of 'ignorecase'. It is set at each call to vim_regexec().
+ // Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern
+ // contains '\c' or '\C' the value is overruled.
+ bool reg_ic;
+
+ // Similar to rex.reg_ic, but only for 'combining' characters. Set with \Z
+ // flag in the regexp. Defaults to false, always.
+ bool reg_icombine;
+
+ // Copy of "rmm_maxcol": maximum column to search for a match. Zero when
+ // there is no maximum.
+ colnr_T reg_maxcol;
+} regexec_T;
+
+static regexec_T rex;
+static bool rex_in_use = false;
/*
* "regstack" and "backpos" are used by regmatch(). They are kept over calls
@@ -3268,14 +3263,16 @@ void free_regexp_stuff(void)
*/
static char_u *reg_getline(linenr_T lnum)
{
- /* when looking behind for a match/no-match lnum is negative. But we
- * can't go before line 1 */
- if (reg_firstlnum + lnum < 1)
+ // when looking behind for a match/no-match lnum is negative. But we
+ // can't go before line 1
+ if (rex.reg_firstlnum + lnum < 1) {
return NULL;
- if (lnum > reg_maxline)
- /* Must have matched the "\n" in the last line. */
+ }
+ if (lnum > rex.reg_maxline) {
+ // Must have matched the "\n" in the last line.
return (char_u *)"";
- return ml_get_buf(reg_buf, reg_firstlnum + lnum, FALSE);
+ }
+ return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false);
}
static regsave_T behind_pos;
@@ -3285,9 +3282,8 @@ static char_u *reg_endzp[NSUBEXP]; /* and end of \z(...\) matches */
static lpos_T reg_startzpos[NSUBEXP]; /* idem, beginning pos */
static lpos_T reg_endzpos[NSUBEXP]; /* idem, end pos */
-/* TRUE if using multi-line regexp. */
-#define REG_MULTI (reg_match == NULL)
-
+// TRUE if using multi-line regexp.
+#define REG_MULTI (rex.reg_match == NULL)
/*
* Match a regexp against a string.
@@ -3305,15 +3301,15 @@ bt_regexec_nl (
bool line_lbr
)
{
- reg_match = rmp;
- reg_mmatch = NULL;
- reg_maxline = 0;
- reg_line_lbr = line_lbr;
- reg_buf = curbuf;
- reg_win = NULL;
- ireg_ic = rmp->rm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = 0;
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_line_lbr = line_lbr;
+ rex.reg_buf = curbuf;
+ rex.reg_win = NULL;
+ rex.reg_ic = rmp->rm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = 0;
long r = bt_regexec_both(line, col, NULL);
assert(r <= INT_MAX);
@@ -3336,16 +3332,16 @@ bt_regexec_nl (
static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
linenr_T lnum, colnr_T col, proftime_T *tm)
{
- reg_match = NULL;
- reg_mmatch = rmp;
- reg_buf = buf;
- reg_win = win;
- reg_firstlnum = lnum;
- reg_maxline = reg_buf->b_ml.ml_line_count - lnum;
- reg_line_lbr = FALSE;
- ireg_ic = rmp->rmm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = rmp->rmm_maxcol;
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = buf;
+ rex.reg_win = win;
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ rex.reg_ic = rmp->rmm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = rmp->rmm_maxcol;
return bt_regexec_both(NULL, col, tm);
}
@@ -3383,14 +3379,14 @@ static long bt_regexec_both(char_u *line,
}
if (REG_MULTI) {
- prog = (bt_regprog_T *)reg_mmatch->regprog;
+ prog = (bt_regprog_T *)rex.reg_mmatch->regprog;
line = reg_getline((linenr_T)0);
- reg_startpos = reg_mmatch->startpos;
- reg_endpos = reg_mmatch->endpos;
+ rex.reg_startpos = rex.reg_mmatch->startpos;
+ rex.reg_endpos = rex.reg_mmatch->endpos;
} else {
- prog = (bt_regprog_T *)reg_match->regprog;
- reg_startp = reg_match->startp;
- reg_endp = reg_match->endp;
+ prog = (bt_regprog_T *)rex.reg_match->regprog;
+ rex.reg_startp = rex.reg_match->startp;
+ rex.reg_endp = rex.reg_match->endp;
}
/* Be paranoid... */
@@ -3403,19 +3399,22 @@ static long bt_regexec_both(char_u *line,
if (prog_magic_wrong())
goto theend;
- /* If the start column is past the maximum column: no need to try. */
- if (ireg_maxcol > 0 && col >= ireg_maxcol)
+ // If the start column is past the maximum column: no need to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
goto theend;
+ }
- /* If pattern contains "\c" or "\C": overrule value of ireg_ic */
- if (prog->regflags & RF_ICASE)
- ireg_ic = TRUE;
- else if (prog->regflags & RF_NOICASE)
- ireg_ic = FALSE;
+ // If pattern contains "\c" or "\C": overrule value of rex.reg_ic
+ if (prog->regflags & RF_ICASE) {
+ rex.reg_ic = true;
+ } else if (prog->regflags & RF_NOICASE) {
+ rex.reg_ic = false;
+ }
- /* If pattern contains "\Z" overrule value of ireg_icombine */
- if (prog->regflags & RF_ICOMBINE)
- ireg_icombine = TRUE;
+ // If pattern contains "\Z" overrule value of rex.reg_icombine
+ if (prog->regflags & RF_ICOMBINE) {
+ rex.reg_icombine = true;
+ }
/* If there is a "must appear" string, look for it. */
if (prog->regmust != NULL) {
@@ -3429,7 +3428,7 @@ static long bt_regexec_both(char_u *line,
// This is used very often, esp. for ":global". Use two versions of
// the loop to avoid overhead of conditions.
- if (!ireg_ic) {
+ if (!rex.reg_ic) {
while ((s = vim_strchr(s, c)) != NULL) {
if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) {
break; // Found it.
@@ -3463,7 +3462,7 @@ static long bt_regexec_both(char_u *line,
c = regline[col];
if (prog->regstart == NUL
|| prog->regstart == c
- || (ireg_ic
+ || (rex.reg_ic
&& (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c)))
|| (c < 255 && prog->regstart < 255
&& mb_tolower(prog->regstart) == mb_tolower(c))))) {
@@ -3485,8 +3484,8 @@ static long bt_regexec_both(char_u *line,
col = (int)(s - regline);
}
- /* Check for maximum column to try. */
- if (ireg_maxcol > 0 && col >= ireg_maxcol) {
+ // Check for maximum column to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
retval = 0;
break;
}
@@ -3583,21 +3582,24 @@ static long regtry(bt_regprog_T *prog, colnr_T col)
cleanup_subexpr();
if (REG_MULTI) {
- if (reg_startpos[0].lnum < 0) {
- reg_startpos[0].lnum = 0;
- reg_startpos[0].col = col;
+ if (rex.reg_startpos[0].lnum < 0) {
+ rex.reg_startpos[0].lnum = 0;
+ rex.reg_startpos[0].col = col;
+ }
+ if (rex.reg_endpos[0].lnum < 0) {
+ rex.reg_endpos[0].lnum = reglnum;
+ rex.reg_endpos[0].col = (int)(reginput - regline);
+ } else {
+ // Use line number of "\ze".
+ reglnum = rex.reg_endpos[0].lnum;
}
- if (reg_endpos[0].lnum < 0) {
- reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = (int)(reginput - regline);
- } else
- /* Use line number of "\ze". */
- reglnum = reg_endpos[0].lnum;
} else {
- if (reg_startp[0] == NULL)
- reg_startp[0] = regline + col;
- if (reg_endp[0] == NULL)
- reg_endp[0] = reginput;
+ if (rex.reg_startp[0] == NULL) {
+ rex.reg_startp[0] = regline + col;
+ }
+ if (rex.reg_endp[0] == NULL) {
+ rex.reg_endp[0] = reginput;
+ }
}
/* Package any found \z(...\) matches for export. Default is none. */
unref_extmatch(re_extmatch_out);
@@ -3632,36 +3634,33 @@ static long regtry(bt_regprog_T *prog, colnr_T col)
}
-/*
- * Get class of previous character.
- */
+// Get class of previous character.
static int reg_prev_class(void)
{
if (reginput > regline) {
return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline,
reginput - 1),
- reg_buf->b_chartab);
+ rex.reg_buf->b_chartab);
}
return -1;
}
-/*
- * Return TRUE if the current reginput position matches the Visual area.
- */
+// Return TRUE if the current reginput position matches the Visual area.
static int reg_match_visual(void)
{
pos_T top, bot;
linenr_T lnum;
colnr_T col;
- win_T *wp = reg_win == NULL ? curwin : reg_win;
+ win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win;
int mode;
colnr_T start, end;
colnr_T start2, end2;
- /* Check if the buffer is the current buffer. */
- if (reg_buf != curbuf || VIsual.lnum == 0)
- return FALSE;
+ // Check if the buffer is the current buffer.
+ if (rex.reg_buf != curbuf || VIsual.lnum == 0) {
+ return false;
+ }
if (VIsual_active) {
if (lt(VIsual, wp->w_cursor)) {
@@ -3682,9 +3681,10 @@ static int reg_match_visual(void)
}
mode = curbuf->b_visual.vi_mode;
}
- lnum = reglnum + reg_firstlnum;
- if (lnum < top.lnum || lnum > bot.lnum)
- return FALSE;
+ lnum = reglnum + rex.reg_firstlnum;
+ if (lnum < top.lnum || lnum > bot.lnum) {
+ return false;
+ }
if (mode == 'v') {
col = (colnr_T)(reginput - regline);
@@ -3803,11 +3803,11 @@ regmatch (
next = regnext(scan);
op = OP(scan);
- /* Check for character class with NL added. */
- if (!reg_line_lbr && WITH_NL(op) && REG_MULTI
- && *reginput == NUL && reglnum <= reg_maxline) {
+ // Check for character class with NL added.
+ if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI
+ && *reginput == NUL && reglnum <= rex.reg_maxline) {
reg_nextline();
- } else if (reg_line_lbr && WITH_NL(op) && *reginput == '\n') {
+ } else if (rex.reg_line_lbr && WITH_NL(op) && *reginput == '\n') {
ADVANCE_REGINPUT();
} else {
if (WITH_NL(op))
@@ -3828,26 +3828,29 @@ regmatch (
break;
case RE_BOF:
- /* We're not at the beginning of the file when below the first
- * line where we started, not at the start of the line or we
- * didn't start at the first line of the buffer. */
+ // We're not at the beginning of the file when below the first
+ // line where we started, not at the start of the line or we
+ // didn't start at the first line of the buffer.
if (reglnum != 0 || reginput != regline
- || (REG_MULTI && reg_firstlnum > 1))
+ || (REG_MULTI && rex.reg_firstlnum > 1)) {
status = RA_NOMATCH;
+ }
break;
case RE_EOF:
- if (reglnum != reg_maxline || c != NUL)
+ if (reglnum != rex.reg_maxline || c != NUL) {
status = RA_NOMATCH;
+ }
break;
case CURSOR:
- /* Check if the buffer is in a window and compare the
- * reg_win->w_cursor position to the match position. */
- if (reg_win == NULL
- || (reglnum + reg_firstlnum != reg_win->w_cursor.lnum)
- || ((colnr_T)(reginput - regline) != reg_win->w_cursor.col))
+ // Check if the buffer is in a window and compare the
+ // rex.reg_win->w_cursor position to the match position.
+ if (rex.reg_win == NULL
+ || (reglnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum)
+ || ((colnr_T)(reginput - regline) != rex.reg_win->w_cursor.col)) {
status = RA_NOMATCH;
+ }
break;
case RE_MARK:
@@ -3857,19 +3860,20 @@ regmatch (
int cmp = OPERAND(scan)[1];
pos_T *pos;
- pos = getmark_buf(reg_buf, mark, FALSE);
- if (pos == NULL /* mark doesn't exist */
- || pos->lnum <= 0 /* mark isn't set in reg_buf */
- || (pos->lnum == reglnum + reg_firstlnum
+ pos = getmark_buf(rex.reg_buf, mark, false);
+ if (pos == NULL // mark doesn't exist
+ || pos->lnum <= 0 // mark isn't set in reg_buf
+ || (pos->lnum == reglnum + rex.reg_firstlnum
? (pos->col == (colnr_T)(reginput - regline)
? (cmp == '<' || cmp == '>')
: (pos->col < (colnr_T)(reginput - regline)
? cmp != '>'
: cmp != '<'))
- : (pos->lnum < reglnum + reg_firstlnum
+ : (pos->lnum < reglnum + rex.reg_firstlnum
? cmp != '>'
- : cmp != '<')))
+ : cmp != '<'))) {
status = RA_NOMATCH;
+ }
}
break;
@@ -3879,11 +3883,12 @@ regmatch (
break;
case RE_LNUM:
- assert(reglnum + reg_firstlnum >= 0
- && (uintmax_t)(reglnum + reg_firstlnum) <= UINT32_MAX);
- if (!REG_MULTI || !re_num_cmp((uint32_t)(reglnum + reg_firstlnum),
- scan))
+ assert(reglnum + rex.reg_firstlnum >= 0
+ && (uintmax_t)(reglnum + rex.reg_firstlnum) <= UINT32_MAX);
+ if (!REG_MULTI
+ || !re_num_cmp((uint32_t)(reglnum + rex.reg_firstlnum), scan)) {
status = RA_NOMATCH;
+ }
break;
case RE_COL:
@@ -3894,11 +3899,13 @@ regmatch (
break;
case RE_VCOL:
- if (!re_num_cmp(win_linetabsize(reg_win == NULL ? curwin : reg_win,
+ if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
+ ? curwin : rex.reg_win,
regline,
(colnr_T)(reginput - regline)) + 1,
- scan))
+ scan)) {
status = RA_NOMATCH;
+ }
break;
case BOW: /* \<word; reginput points to w */
@@ -3908,17 +3915,18 @@ regmatch (
int this_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
if (this_class <= 1) {
status = RA_NOMATCH; // Not on a word at all.
} else if (reg_prev_class() == this_class) {
status = RA_NOMATCH; // Previous char is in same word.
}
} else {
- if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline
- && vim_iswordc_buf(reginput[-1
- ], reg_buf)))
+ if (!vim_iswordc_buf(c, rex.reg_buf)
+ || (reginput > regline
+ && vim_iswordc_buf(reginput[-1], rex.reg_buf))) {
status = RA_NOMATCH;
+ }
}
break;
@@ -3929,15 +3937,16 @@ regmatch (
int this_class, prev_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
prev_class = reg_prev_class();
if (this_class == prev_class
|| prev_class == 0 || prev_class == 1)
status = RA_NOMATCH;
} else {
- if (!vim_iswordc_buf(reginput[-1], reg_buf)
- || (reginput[0] != NUL && vim_iswordc_buf(c, reg_buf)))
+ if (!vim_iswordc_buf(reginput[-1], rex.reg_buf)
+ || (reginput[0] != NUL && vim_iswordc_buf(c, rex.reg_buf))) {
status = RA_NOMATCH;
+ }
}
break; /* Matched with EOW */
@@ -3964,17 +3973,20 @@ regmatch (
break;
case KWORD:
- if (!vim_iswordp_buf(reginput, reg_buf))
+ if (!vim_iswordp_buf(reginput, rex.reg_buf)) {
status = RA_NOMATCH;
- else
+ } else {
ADVANCE_REGINPUT();
+ }
break;
case SKWORD:
- if (ascii_isdigit(*reginput) || !vim_iswordp_buf(reginput, reg_buf))
+ if (ascii_isdigit(*reginput)
+ || !vim_iswordp_buf(reginput, rex.reg_buf)) {
status = RA_NOMATCH;
- else
+ } else {
ADVANCE_REGINPUT();
+ }
break;
case FNAME:
@@ -4139,7 +4151,7 @@ regmatch (
opnd = OPERAND(scan);
// Inline the first byte, for speed.
if (*opnd != *reginput
- && (!ireg_ic
+ && (!rex.reg_ic
|| (!enc_utf8
&& mb_tolower(*opnd) != mb_tolower(*reginput)))) {
status = RA_NOMATCH;
@@ -4147,8 +4159,8 @@ regmatch (
// match empty string always works; happens when "~" is
// empty.
} else {
- if (opnd[1] == NUL && !(enc_utf8 && ireg_ic)) {
- len = 1; /* matched a single byte above */
+ if (opnd[1] == NUL && !(enc_utf8 && rex.reg_ic)) {
+ len = 1; // matched a single byte above
} else {
// Need to match first byte again for multi-byte.
len = (int)STRLEN(opnd);
@@ -4160,7 +4172,7 @@ regmatch (
// follows (skips over all composing chars).
if (status != RA_NOMATCH && enc_utf8
&& UTF_COMPOSINGLIKE(reginput, reginput + len)
- && !ireg_icombine
+ && !rex.reg_icombine
&& OP(next) != RE_COMPOSING) {
// raaron: This code makes a composing character get
// ignored, which is the correct behavior (sometimes)
@@ -4284,9 +4296,9 @@ regmatch (
status = RA_FAIL;
else {
rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &reg_startpos[no],
- &reg_startp[no]);
- /* We simply continue and handle the result when done. */
+ save_se(&rp->rs_un.sesave, &rex.reg_startpos[no],
+ &rex.reg_startp[no]);
+ // We simply continue and handle the result when done.
}
}
break;
@@ -4336,12 +4348,12 @@ regmatch (
no = op - MCLOSE;
cleanup_subexpr();
rp = regstack_push(RS_MCLOSE, scan);
- if (rp == NULL)
+ if (rp == NULL) {
status = RA_FAIL;
- else {
+ } else {
rp->rs_no = no;
- save_se(&rp->rs_un.sesave, &reg_endpos[no], &reg_endp[no]);
- /* We simply continue and handle the result when done. */
+ save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]);
+ // We simply continue and handle the result when done.
}
}
break;
@@ -4384,41 +4396,40 @@ regmatch (
no = op - BACKREF;
cleanup_subexpr();
- if (!REG_MULTI) { /* Single-line regexp */
- if (reg_startp[no] == NULL || reg_endp[no] == NULL) {
- /* Backref was not set: Match an empty string. */
+ if (!REG_MULTI) { // Single-line regexp
+ if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) {
+ // Backref was not set: Match an empty string.
len = 0;
} else {
- /* Compare current input with back-ref in the same
- * line. */
- len = (int)(reg_endp[no] - reg_startp[no]);
- if (cstrncmp(reg_startp[no], reginput, &len) != 0)
+ // Compare current input with back-ref in the same line.
+ len = (int)(rex.reg_endp[no] - rex.reg_startp[no]);
+ if (cstrncmp(rex.reg_startp[no], reginput, &len) != 0) {
status = RA_NOMATCH;
+ }
}
- } else { /* Multi-line regexp */
- if (reg_startpos[no].lnum < 0 || reg_endpos[no].lnum < 0) {
- /* Backref was not set: Match an empty string. */
+ } else { // Multi-line regexp
+ if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) {
+ // Backref was not set: Match an empty string.
len = 0;
} else {
- if (reg_startpos[no].lnum == reglnum
- && reg_endpos[no].lnum == reglnum) {
- /* Compare back-ref within the current line. */
- len = reg_endpos[no].col - reg_startpos[no].col;
- if (cstrncmp(regline + reg_startpos[no].col,
- reginput, &len) != 0)
+ if (rex.reg_startpos[no].lnum == reglnum
+ && rex.reg_endpos[no].lnum == reglnum) {
+ // Compare back-ref within the current line.
+ len = rex.reg_endpos[no].col - rex.reg_startpos[no].col;
+ if (cstrncmp(regline + rex.reg_startpos[no].col,
+ reginput, &len) != 0) {
status = RA_NOMATCH;
+ }
} else {
- /* Messy situation: Need to compare between two
- * lines. */
- int r = match_with_backref(
- reg_startpos[no].lnum,
- reg_startpos[no].col,
- reg_endpos[no].lnum,
- reg_endpos[no].col,
- &len);
-
- if (r != RA_MATCH)
+ // Messy situation: Need to compare between two lines.
+ int r = match_with_backref(rex.reg_startpos[no].lnum,
+ rex.reg_startpos[no].col,
+ rex.reg_endpos[no].lnum,
+ rex.reg_endpos[no].col,
+ &len);
+ if (r != RA_MATCH) {
status = r;
+ }
}
}
}
@@ -4558,7 +4569,7 @@ regmatch (
*/
if (OP(next) == EXACTLY) {
rst.nextb = *OPERAND(next);
- if (ireg_ic) {
+ if (rex.reg_ic) {
if (mb_isupper(rst.nextb)) {
rst.nextb_ic = mb_tolower(rst.nextb);
} else {
@@ -4665,13 +4676,14 @@ regmatch (
break;
case NEWL:
- if ((c != NUL || !REG_MULTI || reglnum > reg_maxline
- || reg_line_lbr) && (c != '\n' || !reg_line_lbr))
+ if ((c != NUL || !REG_MULTI || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) {
status = RA_NOMATCH;
- else if (reg_line_lbr)
+ } else if (rex.reg_line_lbr) {
ADVANCE_REGINPUT();
- else
+ } else {
reg_nextline();
+ }
break;
case END:
@@ -4710,10 +4722,11 @@ regmatch (
break;
case RS_MOPEN:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- restore_se(&rp->rs_un.sesave, &reg_startpos[rp->rs_no],
- &reg_startp[rp->rs_no]);
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no],
+ &rex.reg_startp[rp->rs_no]);
+ }
regstack_pop(&scan);
break;
@@ -4726,10 +4739,11 @@ regmatch (
break;
case RS_MCLOSE:
- /* Pop the state. Restore pointers when there is no match. */
- if (status == RA_NOMATCH)
- restore_se(&rp->rs_un.sesave, &reg_endpos[rp->rs_no],
- &reg_endp[rp->rs_no]);
+ // Pop the state. Restore pointers when there is no match.
+ if (status == RA_NOMATCH) {
+ restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no],
+ &rex.reg_endp[rp->rs_no]);
+ }
regstack_pop(&scan);
break;
@@ -5109,10 +5123,11 @@ regrepeat (
++count;
mb_ptr_adv(scan);
}
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr || count == maxcount)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr || count == maxcount) {
break;
- ++count; /* count the line-break */
+ }
+ count++; // count the line-break
reg_nextline();
scan = reginput;
if (got_int)
@@ -5130,17 +5145,19 @@ regrepeat (
if (vim_isIDc(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
} else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
if (got_int)
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
+ }
++count;
}
break;
@@ -5152,22 +5169,25 @@ regrepeat (
case SKWORD:
case SKWORD + ADD_NL:
while (count < maxcount) {
- if (vim_iswordp_buf(scan, reg_buf)
+ if (vim_iswordp_buf(scan, rex.reg_buf)
&& (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
} else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
- ++count;
+ }
+ count++;
}
break;
@@ -5181,18 +5201,21 @@ regrepeat (
if (vim_isfilec(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
} else if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
- ++count;
+ }
+ count++;
}
break;
@@ -5204,21 +5227,24 @@ regrepeat (
case SPRINT + ADD_NL:
while (count < maxcount) {
if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
+ }
} else if (vim_isprintc(PTR2CHAR(scan)) == 1
&& (testval || !ascii_isdigit(*scan))) {
mb_ptr_adv(scan);
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
- ++count;
+ }
+ count++;
}
break;
@@ -5229,9 +5255,10 @@ do_class:
while (count < maxcount) {
int l;
if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
if (got_int)
@@ -5240,12 +5267,13 @@ do_class:
if (testval != 0)
break;
scan += l;
- } else if ((class_tab[*scan] & mask) == testval)
- ++scan;
- else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else
+ } else if ((class_tab[*scan] & mask) == testval) {
+ scan++;
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else {
break;
+ }
++count;
}
break;
@@ -5323,10 +5351,10 @@ do_class:
{
int cu, cl;
- /* This doesn't do a multi-byte character, because a MULTIBYTECODE
- * would have been used for it. It does handle single-byte
- * characters, such as latin1. */
- if (ireg_ic) {
+ // This doesn't do a multi-byte character, because a MULTIBYTECODE
+ // would have been used for it. It does handle single-byte
+ // characters, such as latin1.
+ if (rex.reg_ic) {
cu = mb_toupper(*opnd);
cl = mb_tolower(*opnd);
while (count < maxcount && (*scan == cu || *scan == cl)) {
@@ -5350,17 +5378,19 @@ do_class:
/* Safety check (just in case 'encoding' was changed since
* compiling the program). */
if ((len = (*mb_ptr2len)(opnd)) > 1) {
- if (ireg_ic && enc_utf8)
+ if (rex.reg_ic && enc_utf8) {
cf = utf_fold(utf_ptr2char(opnd));
+ }
while (count < maxcount && (*mb_ptr2len)(scan) >= len) {
for (i = 0; i < len; ++i) {
if (opnd[i] != scan[i]) {
break;
}
}
- if (i < len && (!ireg_ic || !enc_utf8
- || utf_fold(utf_ptr2char(scan)) != cf))
+ if (i < len && (!rex.reg_ic || !enc_utf8
+ || utf_fold(utf_ptr2char(scan)) != cf)) {
break;
+ }
scan += len;
++count;
}
@@ -5378,18 +5408,21 @@ do_class:
while (count < maxcount) {
int len;
if (*scan == NUL) {
- if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline
- || reg_line_lbr)
+ if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline
+ || rex.reg_line_lbr) {
break;
+ }
reg_nextline();
scan = reginput;
- if (got_int)
+ if (got_int) {
break;
- } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p)))
- ++scan;
- else if (has_mbyte && (len = (*mb_ptr2len)(scan)) > 1) {
- if ((cstrchr(opnd, (*mb_ptr2char)(scan)) == NULL) == testval)
+ }
+ } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) {
+ scan++;
+ } else if (has_mbyte && (len = (*mb_ptr2len)(scan)) > 1) {
+ if ((cstrchr(opnd, (*mb_ptr2char)(scan)) == NULL) == testval) {
break;
+ }
scan += len;
} else {
if ((cstrchr(opnd, *scan) == NULL) == testval)
@@ -5402,13 +5435,14 @@ do_class:
case NEWL:
while (count < maxcount
- && ((*scan == NUL && reglnum <= reg_maxline && !reg_line_lbr
- && REG_MULTI) || (*scan == '\n' && reg_line_lbr))) {
+ && ((*scan == NUL && reglnum <= rex.reg_maxline && !rex.reg_line_lbr
+ && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) {
count++;
- if (reg_line_lbr)
+ if (rex.reg_line_lbr) {
ADVANCE_REGINPUT();
- else
+ } else {
reg_nextline();
+ }
scan = reginput;
if (got_int)
break;
@@ -5458,10 +5492,11 @@ static int prog_magic_wrong(void)
{
regprog_T *prog;
- prog = REG_MULTI ? reg_mmatch->regprog : reg_match->regprog;
- if (prog->engine == &nfa_regengine)
- /* For NFA matcher we don't check the magic */
- return FALSE;
+ prog = REG_MULTI ? rex.reg_mmatch->regprog : rex.reg_match->regprog;
+ if (prog->engine == &nfa_regengine) {
+ // For NFA matcher we don't check the magic
+ return false;
+ }
if (UCHARAT(((bt_regprog_T *)prog)->program) != REGMAGIC) {
EMSG(_(e_re_corr));
@@ -5479,12 +5514,12 @@ static void cleanup_subexpr(void)
{
if (need_clear_subexpr) {
if (REG_MULTI) {
- /* Use 0xff to set lnum to -1 */
- memset(reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP);
- memset(reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ // Use 0xff to set lnum to -1
+ memset(rex.reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP);
+ memset(rex.reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP);
} else {
- memset(reg_startp, 0, sizeof(char_u *) * NSUBEXP);
- memset(reg_endp, 0, sizeof(char_u *) * NSUBEXP);
+ memset(rex.reg_startp, 0, sizeof(char_u *) * NSUBEXP);
+ memset(rex.reg_endp, 0, sizeof(char_u *) * NSUBEXP);
}
need_clear_subexpr = FALSE;
}
@@ -5513,17 +5548,17 @@ static void save_subexpr(regbehind_T *bp)
{
int i;
- /* When "need_clear_subexpr" is set we don't need to save the values, only
- * remember that this flag needs to be set again when restoring. */
+ // When "need_clear_subexpr" is set we don't need to save the values, only
+ // remember that this flag needs to be set again when restoring.
bp->save_need_clear_subexpr = need_clear_subexpr;
if (!need_clear_subexpr) {
for (i = 0; i < NSUBEXP; ++i) {
if (REG_MULTI) {
- bp->save_start[i].se_u.pos = reg_startpos[i];
- bp->save_end[i].se_u.pos = reg_endpos[i];
+ bp->save_start[i].se_u.pos = rex.reg_startpos[i];
+ bp->save_end[i].se_u.pos = rex.reg_endpos[i];
} else {
- bp->save_start[i].se_u.ptr = reg_startp[i];
- bp->save_end[i].se_u.ptr = reg_endp[i];
+ bp->save_start[i].se_u.ptr = rex.reg_startp[i];
+ bp->save_end[i].se_u.ptr = rex.reg_endp[i];
}
}
}
@@ -5541,11 +5576,11 @@ static void restore_subexpr(regbehind_T *bp)
if (!need_clear_subexpr) {
for (i = 0; i < NSUBEXP; ++i) {
if (REG_MULTI) {
- reg_startpos[i] = bp->save_start[i].se_u.pos;
- reg_endpos[i] = bp->save_end[i].se_u.pos;
+ rex.reg_startpos[i] = bp->save_start[i].se_u.pos;
+ rex.reg_endpos[i] = bp->save_end[i].se_u.pos;
} else {
- reg_startp[i] = bp->save_start[i].se_u.ptr;
- reg_endp[i] = bp->save_end[i].se_u.ptr;
+ rex.reg_startp[i] = bp->save_start[i].se_u.ptr;
+ rex.reg_endp[i] = bp->save_end[i].se_u.ptr;
}
}
}
@@ -5681,10 +5716,12 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e
return RA_NOMATCH; /* doesn't match */
if (bytelen != NULL)
*bytelen += len;
- if (clnum == end_lnum)
- break; /* match and at end! */
- if (reglnum >= reg_maxline)
- return RA_NOMATCH; /* text too short */
+ if (clnum == end_lnum) {
+ break; // match and at end!
+ }
+ if (reglnum >= rex.reg_maxline) {
+ return RA_NOMATCH; // text too short
+ }
/* Advance to next line. */
reg_nextline();
@@ -6232,24 +6269,22 @@ static void mb_decompose(int c, int *c1, int *c2, int *c3)
}
}
-/*
- * Compare two strings, ignore case if ireg_ic set.
- * Return 0 if strings match, non-zero otherwise.
- * Correct the length "*n" when composing characters are ignored.
- */
+// Compare two strings, ignore case if rex.reg_ic set.
+// Return 0 if strings match, non-zero otherwise.
+// Correct the length "*n" when composing characters are ignored.
static int cstrncmp(char_u *s1, char_u *s2, int *n)
{
int result;
- if (!ireg_ic)
+ if (!rex.reg_ic) {
result = STRNCMP(s1, s2, *n);
- else {
+ } else {
assert(*n >= 0);
result = mb_strnicmp(s1, s2, (size_t)*n);
}
- /* if it failed and it's utf8 and we want to combineignore: */
- if (result != 0 && enc_utf8 && ireg_icombine) {
+ // if it failed and it's utf8 and we want to combineignore:
+ if (result != 0 && enc_utf8 && rex.reg_icombine) {
char_u *str1, *str2;
int c1, c2, c11, c12;
int junk;
@@ -6266,14 +6301,15 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
/* decompose the character if necessary, into 'base' characters
* because I don't care about Arabic, I will hard-code the Hebrew
* which I *do* care about! So sue me... */
- if (c1 != c2 && (!ireg_ic || utf_fold(c1) != utf_fold(c2))) {
- /* decomposition necessary? */
+ if (c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) {
+ // decomposition necessary?
mb_decompose(c1, &c11, &junk, &junk);
mb_decompose(c2, &c12, &junk, &junk);
c1 = c11;
c2 = c12;
- if (c11 != c12 && (!ireg_ic || utf_fold(c11) != utf_fold(c12)))
+ if (c11 != c12 && (!rex.reg_ic || utf_fold(c11) != utf_fold(c12))) {
break;
+ }
}
}
result = c2 - c1;
@@ -6291,7 +6327,7 @@ static inline char_u *cstrchr(const char_u *const s, const int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_ALWAYS_INLINE
{
- if (!ireg_ic) {
+ if (!rex.reg_ic) {
return vim_strchr(s, c);
}
@@ -6419,14 +6455,18 @@ char_u *regtilde(char_u *source, int magic)
static int can_f_submatch = FALSE; /* TRUE when submatch() can be used */
-/* These pointers are used instead of reg_match and reg_mmatch for
- * reg_submatch(). Needed for when the substitution string is an expression
- * that contains a call to substitute() and submatch(). */
-static regmatch_T *submatch_match;
-static regmmatch_T *submatch_mmatch;
-static linenr_T submatch_firstlnum;
-static linenr_T submatch_maxline;
-static int submatch_line_lbr;
+// These pointers are used for reg_submatch(). Needed for when the
+// substitution string is an expression that contains a call to substitute()
+// and submatch().
+typedef struct {
+ regmatch_T *sm_match;
+ regmmatch_T *sm_mmatch;
+ linenr_T sm_firstlnum;
+ linenr_T sm_maxline;
+ int sm_line_lbr;
+} regsubmatch_T;
+
+static regsubmatch_T rsm; // can only be used when can_f_submatch is true
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
/// vim_regsub_both().
@@ -6447,11 +6487,11 @@ static int fill_submatch_list(int argc, typval_T *argv, int argcount)
// There are always 10 list items in staticList10_T.
li = argv->vval.v_list->lv_first;
for (i = 0; i < 10; i++) {
- s = submatch_match->startp[i];
- if (s == NULL || submatch_match->endp[i] == NULL) {
+ s = rsm.sm_match->startp[i];
+ if (s == NULL || rsm.sm_match->endp[i] == NULL) {
s = NULL;
} else {
- s = vim_strnsave(s, (int)(submatch_match->endp[i] - s));
+ s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s));
}
li->li_tv.v_type = VAR_STRING;
li->li_tv.vval.v_string = s;
@@ -6488,23 +6528,55 @@ static void clear_submatch_list(staticList10_T *sl)
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
int copy, int magic, int backslash)
{
- reg_match = rmp;
- reg_mmatch = NULL;
- reg_maxline = 0;
- reg_buf = curbuf;
- reg_line_lbr = true;
- return vim_regsub_both(source, expr, dest, copy, magic, backslash);
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_buf = curbuf;
+ rex.reg_line_lbr = true;
+ int result = vim_regsub_both(source, expr, dest, copy, magic, backslash);
+
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
+ return result;
}
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash)
{
- reg_match = NULL;
- reg_mmatch = rmp;
- reg_buf = curbuf; /* always works on the current buffer! */
- reg_firstlnum = lnum;
- reg_maxline = curbuf->b_ml.ml_line_count - lnum;
- reg_line_lbr = false;
- return vim_regsub_both(source, NULL, dest, copy, magic, backslash);
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = curbuf; // always works on the current buffer!
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash);
+
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
+ return result;
}
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
@@ -6533,8 +6605,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
dst = dest;
// When the substitute part starts with "\=" evaluate it as an expression.
- if (expr != NULL || (source[0] == '\\' && source[1] == '='
- && !can_f_submatch)) { // can't do this recursively
+ if (expr != NULL || (source[0] == '\\' && source[1] == '=')) {
// To make sure that the length doesn't change between checking the
// length and copying the string, and to speed up things, the
// resulting string is saved from the call with "copy" == FALSE to the
@@ -6547,24 +6618,23 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
eval_result = NULL;
}
} else {
- win_T *save_reg_win;
- int save_ireg_ic;
- bool prev_can_f_submatch = can_f_submatch;
+ int prev_can_f_submatch = can_f_submatch;
+ regsubmatch_T rsm_save;
xfree(eval_result);
- /* The expression may contain substitute(), which calls us
- * recursively. Make sure submatch() gets the text from the first
- * level. Don't need to save "reg_buf", because
- * vim_regexec_multi() can't be called recursively. */
- submatch_match = reg_match;
- submatch_mmatch = reg_mmatch;
- submatch_firstlnum = reg_firstlnum;
- submatch_maxline = reg_maxline;
- submatch_line_lbr = reg_line_lbr;
- save_reg_win = reg_win;
- save_ireg_ic = ireg_ic;
+ // The expression may contain substitute(), which calls us
+ // recursively. Make sure submatch() gets the text from the first
+ // level.
+ if (can_f_submatch) {
+ rsm_save = rsm;
+ }
can_f_submatch = true;
+ rsm.sm_match = rex.reg_match;
+ rsm.sm_mmatch = rex.reg_mmatch;
+ rsm.sm_firstlnum = rex.reg_firstlnum;
+ rsm.sm_maxline = rex.reg_maxline;
+ rsm.sm_line_lbr = rex.reg_line_lbr;
if (expr != NULL) {
typval_T argv[2];
@@ -6574,29 +6644,25 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL;
- if (prev_can_f_submatch) {
- // can't do this recursively
- } else {
- argv[0].v_type = VAR_LIST;
- argv[0].vval.v_list = &matchList.sl_list;
- matchList.sl_list.lv_len = 0;
- if (expr->v_type == VAR_FUNC) {
- s = expr->vval.v_string;
- call_func(s, (int)STRLEN(s), &rettv, 1, argv,
- fill_submatch_list, 0L, 0L, &dummy,
- true, NULL, NULL);
- } else if (expr->v_type == VAR_PARTIAL) {
- partial_T *partial = expr->vval.v_partial;
-
- s = partial_name(partial);
- call_func(s, (int)STRLEN(s), &rettv, 1, argv,
- fill_submatch_list, 0L, 0L, &dummy,
- true, partial, NULL);
- }
- if (matchList.sl_list.lv_len > 0) {
- // fill_submatch_list() was called.
- clear_submatch_list(&matchList);
- }
+ argv[0].v_type = VAR_LIST;
+ argv[0].vval.v_list = &matchList.sl_list;
+ matchList.sl_list.lv_len = 0;
+ if (expr->v_type == VAR_FUNC) {
+ s = expr->vval.v_string;
+ call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ fill_submatch_list, 0L, 0L, &dummy,
+ true, NULL, NULL);
+ } else if (expr->v_type == VAR_PARTIAL) {
+ partial_T *partial = expr->vval.v_partial;
+
+ s = partial_name(partial);
+ call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ fill_submatch_list, 0L, 0L, &dummy,
+ true, partial, NULL);
+ }
+ if (matchList.sl_list.lv_len > 0) {
+ // fill_submatch_list() was called.
+ clear_submatch_list(&matchList);
}
char buf[NUMBUFLEN];
eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf);
@@ -6612,22 +6678,23 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
int had_backslash = FALSE;
for (s = eval_result; *s != NUL; mb_ptr_adv(s)) {
- /* Change NL to CR, so that it becomes a line break,
- * unless called from vim_regexec_nl().
- * Skip over a backslashed character. */
- if (*s == NL && !submatch_line_lbr)
+ // Change NL to CR, so that it becomes a line break,
+ // unless called from vim_regexec_nl().
+ // Skip over a backslashed character.
+ if (*s == NL && !rsm.sm_line_lbr) {
*s = CAR;
- else if (*s == '\\' && s[1] != NUL) {
- ++s;
+ } else if (*s == '\\' && s[1] != NUL) {
+ s++;
/* Change NL to CR here too, so that this works:
* :s/abc\\\ndef/\="aaa\\\nbbb"/ on text:
* abc\
* def
* Not when called from vim_regexec_nl().
*/
- if (*s == NL && !submatch_line_lbr)
+ if (*s == NL && !rsm.sm_line_lbr) {
*s = CAR;
- had_backslash = TRUE;
+ }
+ had_backslash = true;
}
}
if (had_backslash && backslash) {
@@ -6640,14 +6707,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
dst += STRLEN(eval_result);
}
- reg_match = submatch_match;
- reg_mmatch = submatch_mmatch;
- reg_firstlnum = submatch_firstlnum;
- reg_maxline = submatch_maxline;
- reg_line_lbr = submatch_line_lbr;
- reg_win = save_reg_win;
- ireg_ic = save_ireg_ic;
- can_f_submatch = FALSE;
+ can_f_submatch = prev_can_f_submatch;
+ if (can_f_submatch) {
+ rsm = rsm_save;
+ }
}
} else
while ((c = *src++) != NUL) {
@@ -6745,43 +6808,50 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
dst++;
} else {
if (REG_MULTI) {
- clnum = reg_mmatch->startpos[no].lnum;
- if (clnum < 0 || reg_mmatch->endpos[no].lnum < 0)
+ clnum = rex.reg_mmatch->startpos[no].lnum;
+ if (clnum < 0 || rex.reg_mmatch->endpos[no].lnum < 0) {
s = NULL;
- else {
- s = reg_getline(clnum) + reg_mmatch->startpos[no].col;
- if (reg_mmatch->endpos[no].lnum == clnum)
- len = reg_mmatch->endpos[no].col
- - reg_mmatch->startpos[no].col;
- else
+ } else {
+ s = reg_getline(clnum) + rex.reg_mmatch->startpos[no].col;
+ if (rex.reg_mmatch->endpos[no].lnum == clnum) {
+ len = rex.reg_mmatch->endpos[no].col
+ - rex.reg_mmatch->startpos[no].col;
+ } else {
len = (int)STRLEN(s);
+ }
}
} else {
- s = reg_match->startp[no];
- if (reg_match->endp[no] == NULL)
+ s = rex.reg_match->startp[no];
+ if (rex.reg_match->endp[no] == NULL) {
s = NULL;
- else
- len = (int)(reg_match->endp[no] - s);
+ } else {
+ len = (int)(rex.reg_match->endp[no] - s);
+ }
}
if (s != NULL) {
for (;; ) {
if (len == 0) {
if (REG_MULTI) {
- if (reg_mmatch->endpos[no].lnum == clnum)
+ if (rex.reg_mmatch->endpos[no].lnum == clnum) {
break;
- if (copy)
+ }
+ if (copy) {
*dst = CAR;
- ++dst;
+ }
+ dst++;
s = reg_getline(++clnum);
- if (reg_mmatch->endpos[no].lnum == clnum)
- len = reg_mmatch->endpos[no].col;
- else
+ if (rex.reg_mmatch->endpos[no].lnum == clnum) {
+ len = rex.reg_mmatch->endpos[no].col;
+ } else {
len = (int)STRLEN(s);
- } else
+ }
+ } else {
break;
- } else if (*s == NUL) { /* we hit NUL. */
- if (copy)
+ }
+ } else if (*s == NUL) { // we hit NUL.
+ if (copy) {
EMSG(_(e_re_damg));
+ }
goto exit;
} else {
if (backslash && (*s == CAR || *s == '\\')) {
@@ -6855,16 +6925,16 @@ exit:
static char_u *reg_getline_submatch(linenr_T lnum)
{
char_u *s;
- linenr_T save_first = reg_firstlnum;
- linenr_T save_max = reg_maxline;
+ linenr_T save_first = rex.reg_firstlnum;
+ linenr_T save_max = rex.reg_maxline;
- reg_firstlnum = submatch_firstlnum;
- reg_maxline = submatch_maxline;
+ rex.reg_firstlnum = rsm.sm_firstlnum;
+ rex.reg_maxline = rsm.sm_maxline;
s = reg_getline(lnum);
- reg_firstlnum = save_first;
- reg_maxline = save_max;
+ rex.reg_firstlnum = save_first;
+ rex.reg_maxline = save_max;
return s;
}
@@ -6883,39 +6953,41 @@ char_u *reg_submatch(int no)
if (!can_f_submatch || no < 0)
return NULL;
- if (submatch_match == NULL) {
+ if (rsm.sm_match == NULL) {
ssize_t len;
/*
* First round: compute the length and allocate memory.
* Second round: copy the text.
*/
- for (round = 1; round <= 2; ++round) {
- lnum = submatch_mmatch->startpos[no].lnum;
- if (lnum < 0 || submatch_mmatch->endpos[no].lnum < 0)
+ for (round = 1; round <= 2; round++) {
+ lnum = rsm.sm_mmatch->startpos[no].lnum;
+ if (lnum < 0 || rsm.sm_mmatch->endpos[no].lnum < 0) {
return NULL;
+ }
- s = reg_getline_submatch(lnum) + submatch_mmatch->startpos[no].col;
- if (s == NULL) /* anti-crash check, cannot happen? */
+ s = reg_getline_submatch(lnum) + rsm.sm_mmatch->startpos[no].col;
+ if (s == NULL) { // anti-crash check, cannot happen?
break;
- if (submatch_mmatch->endpos[no].lnum == lnum) {
- /* Within one line: take form start to end col. */
- len = submatch_mmatch->endpos[no].col
- - submatch_mmatch->startpos[no].col;
- if (round == 2)
+ }
+ if (rsm.sm_mmatch->endpos[no].lnum == lnum) {
+ // Within one line: take form start to end col.
+ len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col;
+ if (round == 2) {
STRLCPY(retval, s, len + 1);
- ++len;
+ }
+ len++;
} else {
- /* Multiple lines: take start line from start col, middle
- * lines completely and end line up to end col. */
- len = STRLEN(s);
+ // Multiple lines: take start line from start col, middle
+ // lines completely and end line up to end col.
+ len = (ssize_t)STRLEN(s);
if (round == 2) {
STRCPY(retval, s);
retval[len] = '\n';
}
- ++len;
- ++lnum;
- while (lnum < submatch_mmatch->endpos[no].lnum) {
+ len++;
+ lnum++;
+ while (lnum < rsm.sm_mmatch->endpos[no].lnum) {
s = reg_getline_submatch(lnum++);
if (round == 2)
STRCPY(retval + len, s);
@@ -6924,10 +6996,11 @@ char_u *reg_submatch(int no)
retval[len] = '\n';
++len;
}
- if (round == 2)
+ if (round == 2) {
STRNCPY(retval + len, reg_getline_submatch(lnum),
- submatch_mmatch->endpos[no].col);
- len += submatch_mmatch->endpos[no].col;
+ rsm.sm_mmatch->endpos[no].col);
+ }
+ len += rsm.sm_mmatch->endpos[no].col;
if (round == 2) {
retval[len] = NUL; // -V595
}
@@ -6939,11 +7012,12 @@ char_u *reg_submatch(int no)
}
}
} else {
- s = submatch_match->startp[no];
- if (s == NULL || submatch_match->endp[no] == NULL)
+ s = rsm.sm_match->startp[no];
+ if (s == NULL || rsm.sm_match->endp[no] == NULL) {
retval = NULL;
- else
- retval = vim_strnsave(s, (int)(submatch_match->endp[no] - s));
+ } else {
+ retval = vim_strnsave(s, (int)(rsm.sm_match->endp[no] - s));
+ }
}
return retval;
@@ -6965,15 +7039,15 @@ list_T *reg_submatch_list(int no)
list_T *list;
const char *s;
- if (submatch_match == NULL) {
- slnum = submatch_mmatch->startpos[no].lnum;
- elnum = submatch_mmatch->endpos[no].lnum;
+ if (rsm.sm_match == NULL) {
+ slnum = rsm.sm_mmatch->startpos[no].lnum;
+ elnum = rsm.sm_mmatch->endpos[no].lnum;
if (slnum < 0 || elnum < 0) {
return NULL;
}
- colnr_T scol = submatch_mmatch->startpos[no].col;
- colnr_T ecol = submatch_mmatch->endpos[no].col;
+ colnr_T scol = rsm.sm_mmatch->startpos[no].col;
+ colnr_T ecol = rsm.sm_mmatch->endpos[no].col;
list = tv_list_alloc();
@@ -6990,12 +7064,12 @@ list_T *reg_submatch_list(int no)
tv_list_append_string(list, s, ecol);
}
} else {
- s = (const char *)submatch_match->startp[no];
- if (s == NULL || submatch_match->endp[no] == NULL) {
+ s = (const char *)rsm.sm_match->startp[no];
+ if (s == NULL || rsm.sm_match->endp[no] == NULL) {
return NULL;
}
list = tv_list_alloc();
- tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s);
+ tv_list_append_string(list, s, (const char *)rsm.sm_match->endp[no] - s);
}
return list;
@@ -7149,6 +7223,19 @@ static void report_re_switch(char_u *pat)
/// @return TRUE if there is a match, FALSE if not.
static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
{
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+ rex.reg_startp = NULL;
+ rex.reg_endp = NULL;
+ rex.reg_startpos = NULL;
+ rex.reg_endpos = NULL;
+
int result = rmp->regprog->engine->regexec_nl(rmp, line, col, nl);
// NFA engine aborted because it's very slow, use backtracking engine instead.
@@ -7170,6 +7257,11 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
p_re = save_p_re;
}
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
return result > 0;
}
@@ -7217,6 +7309,15 @@ long vim_regexec_multi(
proftime_T *tm /* timeout limit or NULL */
)
{
+ regexec_T rex_save;
+ bool rex_in_use_save = rex_in_use;
+
+ if (rex_in_use) {
+ // Being called recursively, save the state.
+ rex_save = rex;
+ }
+ rex_in_use = true;
+
int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
tm);
@@ -7240,5 +7341,10 @@ long vim_regexec_multi(
p_re = save_p_re;
}
+ rex_in_use = rex_in_use_save;
+ if (rex_in_use) {
+ rex = rex_save;
+ }
+
return result <= 0 ? 0 : result;
}
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 491693a371..5d708febea 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -4882,7 +4882,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
int c2_len = PTR2LEN(s2);
int c2 = PTR2CHAR(s2);
- if ((c1 != c2 && (!ireg_ic || mb_tolower(c1) != mb_tolower(c2)))
+ if ((c1 != c2 && (!rex.reg_ic || mb_tolower(c1) != mb_tolower(c2)))
|| c1_len != c2_len) {
match = false;
break;
@@ -4895,13 +4895,13 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
&& !(enc_utf8 && utf_iscomposing(PTR2CHAR(s2)))) {
cleanup_subexpr();
if (REG_MULTI) {
- reg_startpos[0].lnum = reglnum;
- reg_startpos[0].col = col;
- reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = s2 - regline;
+ rex.reg_startpos[0].lnum = reglnum;
+ rex.reg_startpos[0].col = col;
+ rex.reg_endpos[0].lnum = reglnum;
+ rex.reg_endpos[0].col = s2 - regline;
} else {
- reg_startp[0] = regline + col;
- reg_endp[0] = s2;
+ rex.reg_startp[0] = regline + col;
+ rex.reg_endp[0] = s2;
}
return 1L;
}
@@ -5116,8 +5116,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_MATCH:
{
// If the match ends before a composing characters and
- // ireg_icombine is not set, that is not really a match.
- if (enc_utf8 && !ireg_icombine && utf_iscomposing(curc)) {
+ // rex.reg_icombine is not set, that is not really a match.
+ if (enc_utf8 && !rex.reg_icombine && utf_iscomposing(curc)) {
break;
}
nfa_match = true;
@@ -5400,15 +5400,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
int this_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
if (this_class <= 1) {
result = false;
} else if (reg_prev_class() == this_class) {
result = false;
}
- } else if (!vim_iswordc_buf(curc, reg_buf)
+ } else if (!vim_iswordc_buf(curc, rex.reg_buf)
|| (reginput > regline
- && vim_iswordc_buf(reginput[-1], reg_buf))) {
+ && vim_iswordc_buf(reginput[-1], rex.reg_buf))) {
result = false;
}
if (result) {
@@ -5425,15 +5425,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
int this_class, prev_class;
// Get class of current and previous char (if it exists).
- this_class = mb_get_class_tab(reginput, reg_buf->b_chartab);
+ this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab);
prev_class = reg_prev_class();
if (this_class == prev_class
|| prev_class == 0 || prev_class == 1) {
result = false;
}
- } else if (!vim_iswordc_buf(reginput[-1], reg_buf)
+ } else if (!vim_iswordc_buf(reginput[-1], rex.reg_buf)
|| (reginput[0] != NUL
- && vim_iswordc_buf(curc, reg_buf))) {
+ && vim_iswordc_buf(curc, rex.reg_buf))) {
result = false;
}
if (result) {
@@ -5444,14 +5444,14 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_BOF:
if (reglnum == 0 && reginput == regline
- && (!REG_MULTI || reg_firstlnum == 1)) {
+ && (!REG_MULTI || rex.reg_firstlnum == 1)) {
add_here = true;
add_state = t->state->out;
}
break;
case NFA_EOF:
- if (reglnum == reg_maxline && curc == NUL) {
+ if (reglnum == rex.reg_maxline && curc == NUL) {
add_here = true;
add_state = t->state->out;
}
@@ -5475,7 +5475,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// (no preceding character).
len += mb_char2len(mc);
}
- if (ireg_icombine && len == 0) {
+ if (rex.reg_icombine && len == 0) {
// If \Z was present, then ignore composing characters.
// When ignoring the base character this always matches.
if (sta->c != curc) {
@@ -5526,14 +5526,14 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
case NFA_NEWL:
- if (curc == NUL && !reg_line_lbr && REG_MULTI
- && reglnum <= reg_maxline) {
+ if (curc == NUL && !rex.reg_line_lbr && REG_MULTI
+ && reglnum <= rex.reg_maxline) {
go_to_nextline = true;
// Pass -1 for the offset, which means taking the position
// at the start of the next line.
add_state = t->state->out;
add_off = -1;
- } else if (curc == '\n' && reg_line_lbr) {
+ } else if (curc == '\n' && rex.reg_line_lbr) {
// match \n as if it is an ordinary character
add_state = t->state->out;
add_off = 1;
@@ -5574,7 +5574,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
result = result_if_matched;
break;
}
- if (ireg_ic) {
+ if (rex.reg_ic) {
int curc_low = mb_tolower(curc);
int done = false;
@@ -5591,7 +5591,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
} else if (state->c < 0 ? check_char_class(state->c, curc)
: (curc == state->c
- || (ireg_ic && mb_tolower(curc)
+ || (rex.reg_ic && mb_tolower(curc)
== mb_tolower(state->c)))) {
result = result_if_matched;
break;
@@ -5639,13 +5639,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break;
case NFA_KWORD: // \k
- result = vim_iswordp_buf(reginput, reg_buf);
+ result = vim_iswordp_buf(reginput, rex.reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_SKWORD: // \K
result = !ascii_isdigit(curc)
- && vim_iswordp_buf(reginput, reg_buf);
+ && vim_iswordp_buf(reginput, rex.reg_buf);
ADD_STATE_IF_MATCH(t->state);
break;
@@ -5760,24 +5760,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
break;
case NFA_LOWER_IC: // [a-z]
- result = ri_lower(curc) || (ireg_ic && ri_upper(curc));
+ result = ri_lower(curc) || (rex.reg_ic && ri_upper(curc));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_NLOWER_IC: // [^a-z]
result = curc != NUL
- && !(ri_lower(curc) || (ireg_ic && ri_upper(curc)));
+ && !(ri_lower(curc) || (rex.reg_ic && ri_upper(curc)));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_UPPER_IC: // [A-Z]
- result = ri_upper(curc) || (ireg_ic && ri_lower(curc));
+ result = ri_upper(curc) || (rex.reg_ic && ri_lower(curc));
ADD_STATE_IF_MATCH(t->state);
break;
case NFA_NUPPER_IC: // [^A-Z]
result = curc != NUL
- && !(ri_upper(curc) || (ireg_ic && ri_lower(curc)));
+ && !(ri_upper(curc) || (rex.reg_ic && ri_lower(curc)));
ADD_STATE_IF_MATCH(t->state);
break;
@@ -5851,13 +5851,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_LNUM_GT:
case NFA_LNUM_LT:
assert(t->state->val >= 0
- && !((reg_firstlnum > 0 && reglnum > LONG_MAX - reg_firstlnum)
- || (reg_firstlnum <0 && reglnum < LONG_MIN + reg_firstlnum))
- && reglnum + reg_firstlnum >= 0);
+ && !((rex.reg_firstlnum > 0
+ && reglnum > LONG_MAX - rex.reg_firstlnum)
+ || (rex.reg_firstlnum < 0
+ && reglnum < LONG_MIN + rex.reg_firstlnum))
+ && reglnum + rex.reg_firstlnum >= 0);
result = (REG_MULTI
&& nfa_re_num_cmp((uintmax_t)t->state->val,
t->state->c - NFA_LNUM,
- (uintmax_t)(reglnum + reg_firstlnum)));
+ (uintmax_t)(reglnum + rex.reg_firstlnum)));
if (result) {
add_here = true;
add_state = t->state->out;
@@ -5893,7 +5895,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
result = false;
- win_T *wp = reg_win == NULL ? curwin : reg_win;
+ win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win;
if (op == 1 && col - 1 > t->state->val && col > 100) {
long ts = wp->w_buffer->b_p_ts;
@@ -5920,18 +5922,18 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
case NFA_MARK_GT:
case NFA_MARK_LT:
{
- pos_T *pos = getmark_buf(reg_buf, t->state->val, FALSE);
+ pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false);
// Compare the mark position to the match position.
result = (pos != NULL // mark doesn't exist
&& pos->lnum > 0 // mark isn't set in reg_buf
- && (pos->lnum == reglnum + reg_firstlnum
+ && (pos->lnum == reglnum + rex.reg_firstlnum
? (pos->col == (colnr_T)(reginput - regline)
? t->state->c == NFA_MARK
: (pos->col < (colnr_T)(reginput - regline)
? t->state->c == NFA_MARK_GT
: t->state->c == NFA_MARK_LT))
- : (pos->lnum < reglnum + reg_firstlnum
+ : (pos->lnum < reglnum + rex.reg_firstlnum
? t->state->c == NFA_MARK_GT
: t->state->c == NFA_MARK_LT)));
if (result) {
@@ -5942,10 +5944,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
}
case NFA_CURSOR:
- result = (reg_win != NULL
- && (reglnum + reg_firstlnum == reg_win->w_cursor.lnum)
+ result = (rex.reg_win != NULL
+ && (reglnum + rex.reg_firstlnum == rex.reg_win->w_cursor.lnum)
&& ((colnr_T)(reginput - regline)
- == reg_win->w_cursor.col));
+ == rex.reg_win->w_cursor.col));
if (result) {
add_here = true;
add_state = t->state->out;
@@ -5995,13 +5997,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
#endif
result = (c == curc);
- if (!result && ireg_ic) {
+ if (!result && rex.reg_ic) {
result = mb_tolower(c) == mb_tolower(curc);
}
- // If ireg_icombine is not set only skip over the character
+ // If rex.reg_icombine is not set only skip over the character
// itself. When it is set skip over composing characters.
- if (result && enc_utf8 && !ireg_icombine) {
+ if (result && enc_utf8 && !rex.reg_icombine) {
clen = utf_ptr2len(reginput);
}
@@ -6109,8 +6111,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
&& ((toplevel
&& reglnum == 0
&& clen != 0
- && (ireg_maxcol == 0
- || (colnr_T)(reginput - regline) < ireg_maxcol))
+ && (rex.reg_maxcol == 0
+ || (colnr_T)(reginput - regline) < rex.reg_maxcol))
|| (nfa_endp != NULL
&& (REG_MULTI
? (reglnum < nfa_endp->se_u.pos.lnum
@@ -6145,7 +6147,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
// Checking if the required start character matches is
// cheaper than adding a state that won't match.
c = PTR2CHAR(reginput + clen);
- if (c != prog->regstart && (!ireg_ic || mb_tolower(c)
+ if (c != prog->regstart && (!rex.reg_ic || mb_tolower(c)
!= mb_tolower(prog->regstart))) {
#ifdef REGEXP_DEBUG
fprintf(log_fd,
@@ -6271,34 +6273,37 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
cleanup_subexpr();
if (REG_MULTI) {
for (i = 0; i < subs.norm.in_use; i++) {
- reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum;
- reg_startpos[i].col = subs.norm.list.multi[i].start_col;
+ rex.reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum;
+ rex.reg_startpos[i].col = subs.norm.list.multi[i].start_col;
- reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum;
- reg_endpos[i].col = subs.norm.list.multi[i].end_col;
+ rex.reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum;
+ rex.reg_endpos[i].col = subs.norm.list.multi[i].end_col;
}
- if (reg_startpos[0].lnum < 0) {
- reg_startpos[0].lnum = 0;
- reg_startpos[0].col = col;
+ if (rex.reg_startpos[0].lnum < 0) {
+ rex.reg_startpos[0].lnum = 0;
+ rex.reg_startpos[0].col = col;
+ }
+ if (rex.reg_endpos[0].lnum < 0) {
+ // pattern has a \ze but it didn't match, use current end
+ rex.reg_endpos[0].lnum = reglnum;
+ rex.reg_endpos[0].col = (int)(reginput - regline);
+ } else {
+ // Use line number of "\ze".
+ reglnum = rex.reg_endpos[0].lnum;
}
- if (reg_endpos[0].lnum < 0) {
- /* pattern has a \ze but it didn't match, use current end */
- reg_endpos[0].lnum = reglnum;
- reg_endpos[0].col = (int)(reginput - regline);
- } else
- /* Use line number of "\ze". */
- reglnum = reg_endpos[0].lnum;
} else {
for (i = 0; i < subs.norm.in_use; i++) {
- reg_startp[i] = subs.norm.list.line[i].start;
- reg_endp[i] = subs.norm.list.line[i].end;
+ rex.reg_startp[i] = subs.norm.list.line[i].start;
+ rex.reg_endp[i] = subs.norm.list.line[i].end;
}
- if (reg_startp[0] == NULL)
- reg_startp[0] = regline + col;
- if (reg_endp[0] == NULL)
- reg_endp[0] = reginput;
+ if (rex.reg_startp[0] == NULL) {
+ rex.reg_startp[0] = regline + col;
+ }
+ if (rex.reg_endp[0] == NULL) {
+ rex.reg_endp[0] = reginput;
+ }
}
/* Package any found \z(...\) matches for export. Default is none. */
@@ -6352,14 +6357,14 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
colnr_T col = startcol;
if (REG_MULTI) {
- prog = (nfa_regprog_T *)reg_mmatch->regprog;
- line = reg_getline((linenr_T)0); /* relative to the cursor */
- reg_startpos = reg_mmatch->startpos;
- reg_endpos = reg_mmatch->endpos;
+ prog = (nfa_regprog_T *)rex.reg_mmatch->regprog;
+ line = reg_getline((linenr_T)0); // relative to the cursor
+ rex.reg_startpos = rex.reg_mmatch->startpos;
+ rex.reg_endpos = rex.reg_mmatch->endpos;
} else {
- prog = (nfa_regprog_T *)reg_match->regprog;
- reg_startp = reg_match->startp;
- reg_endp = reg_match->endp;
+ prog = (nfa_regprog_T *)rex.reg_match->regprog;
+ rex.reg_startp = rex.reg_match->startp;
+ rex.reg_endp = rex.reg_match->endp;
}
/* Be paranoid... */
@@ -6368,15 +6373,17 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
goto theend;
}
- /* If pattern contains "\c" or "\C": overrule value of ireg_ic */
- if (prog->regflags & RF_ICASE)
- ireg_ic = TRUE;
- else if (prog->regflags & RF_NOICASE)
- ireg_ic = FALSE;
+ // If pattern contains "\c" or "\C": overrule value of rex.reg_ic
+ if (prog->regflags & RF_ICASE) {
+ rex.reg_ic = true;
+ } else if (prog->regflags & RF_NOICASE) {
+ rex.reg_ic = false;
+ }
- /* If pattern contains "\Z" overrule value of ireg_icombine */
- if (prog->regflags & RF_ICOMBINE)
- ireg_icombine = TRUE;
+ // If pattern contains "\Z" overrule value of rex.reg_icombine
+ if (prog->regflags & RF_ICOMBINE) {
+ rex.reg_icombine = true;
+ }
regline = line;
reglnum = 0; /* relative to line */
@@ -6405,17 +6412,17 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
if (skip_to_start(prog->regstart, &col) == FAIL)
return 0L;
- /* If match_text is set it contains the full text that must match.
- * Nothing else to try. Doesn't handle combining chars well. */
- if (prog->match_text != NULL
- && !ireg_icombine
- )
+ // If match_text is set it contains the full text that must match.
+ // Nothing else to try. Doesn't handle combining chars well.
+ if (prog->match_text != NULL && !rex.reg_icombine) {
return find_match_text(col, prog->regstart, prog->match_text);
+ }
}
- /* If the start column is past the maximum column: no need to try. */
- if (ireg_maxcol > 0 && col >= ireg_maxcol)
+ // If the start column is past the maximum column: no need to try.
+ if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) {
goto theend;
+ }
nstate = prog->nstate;
for (i = 0; i < nstate; ++i) {
@@ -6567,15 +6574,15 @@ nfa_regexec_nl (
bool line_lbr
)
{
- reg_match = rmp;
- reg_mmatch = NULL;
- reg_maxline = 0;
- reg_line_lbr = line_lbr;
- reg_buf = curbuf;
- reg_win = NULL;
- ireg_ic = rmp->rm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = 0;
+ rex.reg_match = rmp;
+ rex.reg_mmatch = NULL;
+ rex.reg_maxline = 0;
+ rex.reg_line_lbr = line_lbr;
+ rex.reg_buf = curbuf;
+ rex.reg_win = NULL;
+ rex.reg_ic = rmp->rm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = 0;
return nfa_regexec_both(line, col, NULL);
}
@@ -6616,16 +6623,16 @@ nfa_regexec_nl (
static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
linenr_T lnum, colnr_T col, proftime_T *tm)
{
- reg_match = NULL;
- reg_mmatch = rmp;
- reg_buf = buf;
- reg_win = win;
- reg_firstlnum = lnum;
- reg_maxline = reg_buf->b_ml.ml_line_count - lnum;
- reg_line_lbr = FALSE;
- ireg_ic = rmp->rmm_ic;
- ireg_icombine = FALSE;
- ireg_maxcol = rmp->rmm_maxcol;
+ rex.reg_match = NULL;
+ rex.reg_mmatch = rmp;
+ rex.reg_buf = buf;
+ rex.reg_win = win;
+ rex.reg_firstlnum = lnum;
+ rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum;
+ rex.reg_line_lbr = false;
+ rex.reg_ic = rmp->rmm_ic;
+ rex.reg_icombine = false;
+ rex.reg_maxcol = rmp->rmm_maxcol;
return nfa_regexec_both(NULL, col, tm);
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index a8353153fd..bcc996679b 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -991,7 +991,7 @@ static void win_update(win_T *wp)
* first. */
if (mid_start == 0) {
mid_end = wp->w_height;
- if (lastwin == firstwin) {
+ if (ONE_WINDOW) {
/* Clear the screen when it was not done by win_del_lines() or
* win_ins_lines() above, "screen_cleared" is FALSE or MAYBE
* then. */
@@ -7160,7 +7160,7 @@ static int fillchar_status(int *attr, win_T *wp)
* window differs, or the fillchars differ, or this is not the
* current window */
if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
- || !is_curwin || firstwin == lastwin)
+ || !is_curwin || ONE_WINDOW)
|| (fill_stl != fill_stlnc))) {
return fill;
}
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 117ba52eb6..39ffabc024 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -79,7 +79,11 @@ let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
func RunTheTest(test)
echo 'Executing ' . a:test
if exists("*SetUp")
- call SetUp()
+ try
+ call SetUp()
+ catch
+ call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
endif
call add(s:messages, 'Executing ' . a:test)
@@ -94,7 +98,11 @@ func RunTheTest(test)
endtry
if exists("*TearDown")
- call TearDown()
+ try
+ call TearDown()
+ catch
+ call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
+ endtry
endif
" Close any extra windows and make the current one not modified.
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 1103778107..d55170c27c 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -14,6 +14,7 @@ source test_float_func.vim
source test_functions.vim
source test_goto.vim
source test_jumps.vim
+source test_fileformat.vim
source test_lambda.vim
source test_menu.vim
source test_mapping.vim
diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim
index 5c916e2dd7..1c9350c416 100644
--- a/src/nvim/testdir/test_bufwintabinfo.vim
+++ b/src/nvim/testdir/test_bufwintabinfo.vim
@@ -87,9 +87,17 @@ function Test_get_buf_options()
endfunc
function Test_get_win_options()
+ if has('folding')
+ set foldlevel=999
+ endif
+ set list
let opts = getwinvar(1, '&')
call assert_equal(v:t_dict, type(opts))
call assert_equal(0, opts.linebreak)
+ call assert_equal(1, opts.list)
+ if has('folding')
+ call assert_equal(999, opts.foldlevel)
+ endif
if has('signs')
call assert_equal('auto', opts.signcolumn)
endif
@@ -97,7 +105,12 @@ function Test_get_win_options()
let opts = gettabwinvar(1, 1, '&')
call assert_equal(v:t_dict, type(opts))
call assert_equal(0, opts.linebreak)
+ call assert_equal(1, opts.list)
if has('signs')
call assert_equal('auto', opts.signcolumn)
endif
+ set list&
+ if has('folding')
+ set foldlevel=0
+ endif
endfunc
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 0c9d1297d6..c3bfae1b7b 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -130,6 +130,11 @@ func Test_getcompletion()
let l = getcompletion('dark', 'highlight')
call assert_equal([], l)
+ let l = getcompletion('', 'messages')
+ call assert_true(index(l, 'clear') >= 0)
+ let l = getcompletion('not', 'messages')
+ call assert_equal([], l)
+
if has('cscope')
let l = getcompletion('', 'cscope')
let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show']
@@ -204,3 +209,26 @@ func Test_expand_star_star()
bwipe!
call delete('a', 'rf')
endfunc
+
+func Test_paste_in_cmdline()
+ let @a = "def"
+ call feedkeys(":abc \<C-R>a ghi\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"abc def ghi', @:)
+
+ new
+ call setline(1, 'asdf.x /tmp/some verylongword a;b-c*d ')
+
+ call feedkeys(":aaa \<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa asdf bbb', @:)
+
+ call feedkeys("ft:aaa \<C-R>\<C-F> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa /tmp/some bbb', @:)
+
+ set incsearch
+ call feedkeys("fy:aaa veryl\<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa verylongword bbb', @:)
+
+ call feedkeys("f;:aaa \<C-R>\<C-A> bbb\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"aaa a;b-c*d bbb', @:)
+ bwipe!
+endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 82c5d21bd0..d32facaa98 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -384,9 +384,10 @@ func Test_substitute_expr()
\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
func Recurse()
- return substitute('yyy', 'y*', {-> g:val}, '')
+ return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
endfunc
- call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
+ " recursive call works
+ call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
endfunc
func Test_invalid_submatch()
@@ -418,6 +419,9 @@ func Test_function_with_funcref()
let s:fref = function(s:f)
call assert_equal(v:t_string, s:fref('x'))
call assert_fails("call function('s:f')", 'E700:')
+
+ call assert_fails("call function('foo()')", 'E475:')
+ call assert_fails("call function('foo()')", 'foo()')
endfunc
func Test_funcref()
diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim
new file mode 100644
index 0000000000..584f20cdfc
--- /dev/null
+++ b/src/nvim/testdir/test_fileformat.vim
@@ -0,0 +1,17 @@
+" Test behavior of fileformat after bwipeout of last buffer
+
+func Test_fileformat_after_bw()
+ bwipeout
+ set fileformat&
+ if &fileformat == 'dos'
+ let test_fileformats = 'unix'
+ elseif &fileformat == 'unix'
+ let test_fileformats = 'mac'
+ else " must be mac
+ let test_fileformats = 'dos'
+ endif
+ exec 'set fileformats='.test_fileformats
+ bwipeout!
+ call assert_equal(test_fileformats, &fileformat)
+ set fileformats&
+endfunc
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index b6ac5720c3..2573401707 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -1,22 +1,277 @@
" Test commands that jump somewhere.
-func Test_geeDEE()
+" Create a new buffer using "lines" and place the cursor on the word after the
+" first occurrence of return and invoke "cmd". The cursor should now be
+" positioned at the given line and col.
+func XTest_goto_decl(cmd, lines, line, col)
new
- call setline(1, ["Filename x;", "", "int Filename", "int func() {", "Filename y;"])
- /y;/
- normal gD
- call assert_equal(1, line('.'))
+ call setline(1, a:lines)
+ /return/
+ normal! W
+ execute 'norm! ' . a:cmd
+ call assert_equal(a:line, line('.'))
+ call assert_equal(a:col, col('.'))
quit!
endfunc
-func Test_gee_dee()
- new
- call setline(1, ["int x;", "", "int func(int x)", "{", " return x;", "}"])
- /return/
- normal $hgd
- call assert_equal(3, line('.'))
- call assert_equal(14, col('.'))
- quit!
+func Test_gD()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 5)
+endfunc
+
+func Test_gD_too()
+ let lines = [
+ \ 'Filename x;',
+ \ '',
+ \ 'int Filename',
+ \ 'int func() {',
+ \ ' Filename x;',
+ \ ' return x;',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 10)
+endfunc
+
+func Test_gD_comment()
+ let lines = [
+ \ '/* int x; */',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_inline_comment()
+ let lines = [
+ \ 'int y /* , x */;',
+ \ 'int x;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_string()
+ let lines = [
+ \ 'char *s[] = "x";',
+ \ 'int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gD_string_same_line()
+ let lines = [
+ \ 'char *s[] = "x", int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 1, 22)
+endfunc
+
+func Test_gD_char()
+ let lines = [
+ \ "char c = 'x';",
+ \ 'int x = 1;',
+ \ '',
+ \ 'int func(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gD', lines, 2, 5)
+endfunc
+
+func Test_gd()
+ let lines = [
+ \ 'int x;',
+ \ '',
+ \ 'int func(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 14)
+endfunc
+
+func Test_gd_not_local()
+ let lines = [
+ \ 'int func1(void)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ '',
+ \ 'int func2(int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gd_kr_style()
+ let lines = [
+ \ 'int func(x)',
+ \ ' int x;',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 2, 7)
+endfunc
+
+func Test_gd_missing_braces()
+ let lines = [
+ \ 'def func1(a)',
+ \ ' a + 1',
+ \ 'end',
+ \ '',
+ \ 'a = 1',
+ \ '',
+ \ 'def func2()',
+ \ ' return a',
+ \ 'end',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 11)
+endfunc
+
+func Test_gd_comment()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* int x; */',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gd_comment_in_string()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s ="//"; int x;',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 3, 22)
+endfunc
+
+func Test_gd_string_in_comment()
+ set comments=
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' /* " */ int x;',
+ \ ' int x;',
+ \ ' return x;',
+ \ '}',
+ \]
+ call XTest_goto_decl('gd', lines, 3, 15)
+ set comments&
+endfunc
+
+func Test_gd_inline_comment()
+ let lines = [
+ \ 'int func(/* x is an int */ int x)',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 32)
+endfunc
+
+func Test_gd_inline_comment_only()
+ let lines = [
+ \ 'int func(void) /* one lonely x */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 3, 10)
+endfunc
+
+func Test_gd_inline_comment_body()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' int y /* , x */;',
+ \ '',
+ \ ' for (/* int x = 0 */; y < 2; y++);',
+ \ '',
+ \ ' int x = 0;',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 7, 7)
+endfunc
+
+func Test_gd_trailing_multiline_comment()
+ let lines = [
+ \ 'int func(int x) /* x is an int */',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
+endfunc
+
+func Test_gd_trailing_comment()
+ let lines = [
+ \ 'int func(int x) // x is an int',
+ \ '{',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 1, 14)
+endfunc
+
+func Test_gd_string()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s = "x";',
+ \ ' int x = 1;',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 4, 7)
+endfunc
+
+func Test_gd_string_only()
+ let lines = [
+ \ 'int func(void)',
+ \ '{',
+ \ ' char *s = "x";',
+ \ '',
+ \ ' return x;',
+ \ '}',
+ \ ]
+ call XTest_goto_decl('gd', lines, 5, 10)
endfunc
" Check that setting 'cursorline' does not change curswant
diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim
index c788689e33..c11f1a84a9 100644
--- a/src/nvim/testdir/test_matchadd_conceal.vim
+++ b/src/nvim/testdir/test_matchadd_conceal.vim
@@ -277,6 +277,7 @@ function! Test_matchadd_and_syn_conceal()
call assert_notequal(screenattr(1, 11) , screenattr(1, 12))
call assert_equal(screenattr(1, 11) , screenattr(1, 32))
call matchadd('CheckedByCoq', '\%<2l\%>9c\%<16c')
+ redraw!
call assert_equal(expect, s:screenline(1))
call assert_notequal(screenattr(1, 10) , screenattr(1, 11))
call assert_notequal(screenattr(1, 11) , screenattr(1, 12))
diff --git a/src/nvim/testdir/test_nested_function.vim b/src/nvim/testdir/test_nested_function.vim
index f881730529..7e301ed33e 100644
--- a/src/nvim/testdir/test_nested_function.vim
+++ b/src/nvim/testdir/test_nested_function.vim
@@ -1,32 +1,42 @@
"Tests for nested functions
"
-function! NestedFunc()
- fu! Func1()
+func NestedFunc()
+ func! Func1()
let g:text .= 'Func1 '
- endfunction
+ endfunc
call Func1()
- fu! s:func2()
+ func! s:func2()
let g:text .= 's:func2 '
- endfunction
+ endfunc
call s:func2()
- fu! s:_func3()
+ func! s:_func3()
let g:text .= 's:_func3 '
- endfunction
+ endfunc
call s:_func3()
let fn = 'Func4'
- fu! {fn}()
+ func! {fn}()
let g:text .= 'Func4 '
- endfunction
+ endfunc
call {fn}()
let fn = 'func5'
- fu! s:{fn}()
+ func! s:{fn}()
let g:text .= 's:func5'
- endfunction
+ endfunc
call s:{fn}()
-endfunction
+endfunc
-function! Test_nested_functions()
+func Test_nested_functions()
let g:text = ''
call NestedFunc()
call assert_equal('Func1 s:func2 s:_func3 Func4 s:func5', g:text)
endfunction
+
+func Test_nested_argument()
+ func g:X()
+ let g:Y = function('sort')
+ endfunc
+ let g:Y = function('sort')
+ echo g:Y([], g:X())
+ delfunc g:X
+ unlet g:Y
+endfunc
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 6261625801..4747d5704d 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1255,21 +1255,27 @@ func! Test_normal22_zet()
" Test for ZZ
" let shell = &shell
" let &shell = 'sh'
- call writefile(['1', '2'], 'Xfile')
+
+ " Remove any stale test files from previous run.
+ for file in ['Xfile_Test_normal22_zet']
+ call delete(file)
+ endfor
+
+ call writefile(['1', '2'], 'Xfile_Test_normal22_zet')
let args = ' --headless -u NONE -N -U NONE -i NONE --noplugins'
- call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile')
- let a = readfile('Xfile')
+ call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet')
+ let a = readfile('Xfile_Test_normal22_zet')
call assert_equal([], a)
" Test for ZQ
- call writefile(['1', '2'], 'Xfile')
- call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile')
- let a = readfile('Xfile')
+ call writefile(['1', '2'], 'Xfile_Test_normal22_zet')
+ call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet')
+ let a = readfile('Xfile_Test_normal22_zet')
call assert_equal(['1', '2'], a)
- " clean up
- for file in ['Xfile']
- call delete(file)
- endfor
+ " Nvim: This sometimes hangs the TSAN build.
+ " for file in ['Xfile_Test_normal22_zet']
+ " call delete(file)
+ " endfor
" let &shell = shell
endfunc
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 75ab01f013..aff5fc2eed 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -6,7 +6,7 @@ endif
set encoding=utf-8
-function! s:setup_commands(cchar)
+func s:setup_commands(cchar)
if a:cchar == 'c'
command! -nargs=* -bang Xlist <mods>clist<bang> <args>
command! -nargs=* Xgetexpr <mods>cgetexpr <args>
@@ -68,10 +68,10 @@ function! s:setup_commands(cchar)
let g:Xgetlist = function('getloclist', [0])
let g:Xsetlist = function('setloclist', [0])
endif
-endfunction
+endfunc
" Tests for the :clist and :llist commands
-function XlistTests(cchar)
+func XlistTests(cchar)
call s:setup_commands(a:cchar)
" With an empty list, command should return error
@@ -128,17 +128,17 @@ function XlistTests(cchar)
let l = split(result, "\n")
call assert_equal([' 2 Xtestfile1:1 col 3: Line1',
\ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l)
-endfunction
+endfunc
-function Test_clist()
+func Test_clist()
call XlistTests('c')
call XlistTests('l')
-endfunction
+endfunc
" Tests for the :colder, :cnewer, :lolder and :lnewer commands
" Note that this test assumes that a quickfix/location list is
" already set by the caller.
-function XageTests(cchar)
+func XageTests(cchar)
call s:setup_commands(a:cchar)
" Jumping to a non existent list should return error
@@ -171,20 +171,20 @@ function XageTests(cchar)
Xnewer 2
let l = g:Xgetlist()
call assert_equal('Line3', l[0].text)
-endfunction
+endfunc
-function Test_cage()
+func Test_cage()
let list = [{'bufnr': 1, 'lnum': 1}]
call setqflist(list)
call XageTests('c')
call setloclist(0, list)
call XageTests('l')
-endfunction
+endfunc
" Tests for the :cwindow, :lwindow :cclose, :lclose, :copen and :lopen
" commands
-function XwindowTests(cchar)
+func XwindowTests(cchar)
call s:setup_commands(a:cchar)
" Create a list with no valid entries
@@ -227,16 +227,16 @@ function XwindowTests(cchar)
" Calling cwindow should close the quickfix window with no valid errors
Xwindow
call assert_true(winnr('$') == 1)
-endfunction
+endfunc
-function Test_cwindow()
+func Test_cwindow()
call XwindowTests('c')
call XwindowTests('l')
-endfunction
+endfunc
" Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile
" commands.
-function XfileTests(cchar)
+func XfileTests(cchar)
call s:setup_commands(a:cchar)
call writefile(['Xtestfile1:700:10:Line 700',
@@ -275,16 +275,16 @@ function XfileTests(cchar)
\ l[1].lnum == 333 && l[1].col == 88 && l[1].text ==# 'Line 333')
call delete('Xqftestfile1')
-endfunction
+endfunc
-function Test_cfile()
+func Test_cfile()
call XfileTests('c')
call XfileTests('l')
-endfunction
+endfunc
" Tests for the :cbuffer, :lbuffer, :caddbuffer, :laddbuffer, :cgetbuffer and
" :lgetbuffer commands.
-function XbufferTests(cchar)
+func XbufferTests(cchar)
call s:setup_commands(a:cchar)
enew!
@@ -316,26 +316,26 @@ function XbufferTests(cchar)
\ l[3].lnum == 750 && l[3].col == 25 && l[3].text ==# 'Line 750')
enew!
-endfunction
+endfunc
-function Test_cbuffer()
+func Test_cbuffer()
call XbufferTests('c')
call XbufferTests('l')
-endfunction
+endfunc
-function XexprTests(cchar)
+func XexprTests(cchar)
call s:setup_commands(a:cchar)
call assert_fails('Xexpr 10', 'E777:')
-endfunction
+endfunc
-function Test_cexpr()
+func Test_cexpr()
call XexprTests('c')
call XexprTests('l')
-endfunction
+endfunc
" Tests for :cnext, :cprev, :cfirst, :clast commands
-function Xtest_browse(cchar)
+func Xtest_browse(cchar)
call s:setup_commands(a:cchar)
call s:create_test_file('Xqftestfile1')
@@ -366,14 +366,14 @@ function Xtest_browse(cchar)
call delete('Xqftestfile1')
call delete('Xqftestfile2')
-endfunction
+endfunc
-function Test_browse()
+func Test_browse()
call Xtest_browse('c')
call Xtest_browse('l')
-endfunction
+endfunc
-function! s:test_xhelpgrep(cchar)
+func s:test_xhelpgrep(cchar)
call s:setup_commands(a:cchar)
Xhelpgrep quickfix
Xopen
@@ -385,9 +385,9 @@ function! s:test_xhelpgrep(cchar)
call assert_true(w:quickfix_title =~ title_text, w:quickfix_title)
" This wipes out the buffer, make sure that doesn't cause trouble.
Xclose
-endfunction
+endfunc
-function Test_helpgrep()
+func Test_helpgrep()
call s:test_xhelpgrep('c')
helpclose
call s:test_xhelpgrep('l')
@@ -425,7 +425,7 @@ func Test_vimgreptitle()
augroup! QfBufWinEnter
endfunc
-function XqfTitleTests(cchar)
+func XqfTitleTests(cchar)
call s:setup_commands(a:cchar)
Xgetexpr ['file:1:1:message']
@@ -444,16 +444,16 @@ function XqfTitleTests(cchar)
endif
call assert_equal(title, w:quickfix_title)
Xclose
-endfunction
+endfunc
" Tests for quickfix window's title
-function Test_qf_title()
+func Test_qf_title()
call XqfTitleTests('c')
call XqfTitleTests('l')
-endfunction
+endfunc
" Tests for 'errorformat'
-function Test_efm()
+func Test_efm()
let save_efm = &efm
set efm=%EEEE%m,%WWWW%m,%+CCCC%.%#,%-GGGG%.%#
cgetexpr ['WWWW', 'EEEE', 'CCCC']
@@ -466,7 +466,7 @@ function Test_efm()
let l = strtrans(string(map(getqflist(), '[v:val.text, v:val.valid]')))
call assert_equal("[['W', 1], ['ZZZZ', 0], ['E^@CCCC', 1], ['YYYY', 0]]", l)
let &efm = save_efm
-endfunction
+endfunc
" This will test for problems in quickfix:
" A. incorrectly copying location lists which caused the location list to show
@@ -477,7 +477,7 @@ endfunction
" window it belongs to.
"
" Set up the test environment:
-function! ReadTestProtocol(name)
+func ReadTestProtocol(name)
let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '')
let word = substitute(base, '\v(.*)\..*', '\1', '')
@@ -496,9 +496,9 @@ function! ReadTestProtocol(name)
setl nomodifiable
setl readonly
exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '')
-endfunction
+endfunc
-function Test_locationlist()
+func Test_locationlist()
enew
augroup testgroup
@@ -578,15 +578,15 @@ function Test_locationlist()
wincmd n | only
augroup! testgroup
-endfunction
+ endfunc
-function Test_locationlist_curwin_was_closed()
+func Test_locationlist_curwin_was_closed()
augroup testgroup
au!
autocmd BufReadCmd test_curwin.txt call R(expand("<amatch>"))
augroup END
- function! R(n)
+ func! R(n)
quit
endfunc
@@ -597,9 +597,9 @@ function Test_locationlist_curwin_was_closed()
call assert_fails('lrewind', 'E924:')
augroup! testgroup
-endfunction
+ endfunc
-function Test_locationlist_cross_tab_jump()
+func Test_locationlist_cross_tab_jump()
call writefile(['loclistfoo'], 'loclistfoo')
call writefile(['loclistbar'], 'loclistbar')
set switchbuf=usetab
@@ -613,10 +613,10 @@ function Test_locationlist_cross_tab_jump()
set switchbuf&vim
call delete('loclistfoo')
call delete('loclistbar')
-endfunction
+endfunc
" More tests for 'errorformat'
-function! Test_efm1()
+func Test_efm1()
if !has('unix')
" The 'errorformat' setting is different on non-Unix systems.
" This test works only on Unix-like systems.
@@ -734,10 +734,10 @@ function! Test_efm1()
call delete('Xerrorfile1')
call delete('Xerrorfile2')
call delete('Xtestfile')
-endfunction
+ endfunc
" Test for quickfix directory stack support
-function! s:dir_stack_tests(cchar)
+func s:dir_stack_tests(cchar)
call s:setup_commands(a:cchar)
let save_efm=&efm
@@ -779,10 +779,10 @@ function! s:dir_stack_tests(cchar)
call assert_equal(5, qf[11].lnum)
let &efm=save_efm
-endfunction
+endfunc
" Tests for %D and %X errorformat options
-function! Test_efm_dirstack()
+func Test_efm_dirstack()
" Create the directory stack and files
call mkdir('dir1')
call mkdir('dir1/a')
@@ -814,10 +814,33 @@ function! Test_efm_dirstack()
call delete('dir1', 'rf')
call delete('dir2', 'rf')
call delete('habits1.txt')
-endfunction
+endfunc
+
+" Test for resync after continuing an ignored message
+func Xefm_ignore_continuations(cchar)
+ call s:setup_commands(a:cchar)
+
+ let save_efm = &efm
+
+ let &efm =
+ \ '%Eerror %m %l,' .
+ \ '%-Wignored %m %l,' .
+ \ '%+Cmore ignored %m %l,' .
+ \ '%Zignored end'
+ Xgetexpr ['ignored warning 1', 'more ignored continuation 2', 'ignored end', 'error resync 4']
+ let l = map(g:Xgetlist(), '[v:val.text, v:val.valid, v:val.lnum, v:val.type]')
+ call assert_equal([['resync', 1, 4, 'E']], l)
+
+ let &efm = save_efm
+endfunc
+
+func Test_efm_ignore_continuations()
+ call Xefm_ignore_continuations('c')
+ call Xefm_ignore_continuations('l')
+endfunc
" Tests for invalid error format specifies
-function Xinvalid_efm_Tests(cchar)
+func Xinvalid_efm_Tests(cchar)
call s:setup_commands(a:cchar)
let save_efm = &efm
@@ -850,17 +873,17 @@ function Xinvalid_efm_Tests(cchar)
call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:')
let &efm = save_efm
-endfunction
+endfunc
-function Test_invalid_efm()
+func Test_invalid_efm()
call Xinvalid_efm_Tests('c')
call Xinvalid_efm_Tests('l')
-endfunction
+endfunc
" TODO:
" Add tests for the following formats in 'errorformat'
" %r %O
-function! Test_efm2()
+func Test_efm2()
let save_efm = &efm
" Test for %s format in efm
@@ -946,19 +969,19 @@ function! Test_efm2()
call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr))
let &efm = save_efm
-endfunction
+endfunc
-function XquickfixChangedByAutocmd(cchar)
+func XquickfixChangedByAutocmd(cchar)
call s:setup_commands(a:cchar)
if a:cchar == 'c'
let ErrorNr = 'E925'
- function! ReadFunc()
+ func! ReadFunc()
colder
cgetexpr []
endfunc
else
let ErrorNr = 'E926'
- function! ReadFunc()
+ func! ReadFunc()
lolder
lgetexpr []
endfunc
@@ -981,10 +1004,10 @@ function XquickfixChangedByAutocmd(cchar)
augroup! testgroup
endfunc
-function Test_quickfix_was_changed_by_autocmd()
+func Test_quickfix_was_changed_by_autocmd()
call XquickfixChangedByAutocmd('c')
call XquickfixChangedByAutocmd('l')
-endfunction
+endfunc
func Test_caddbuffer_to_empty()
helpgr quickfix
@@ -1006,7 +1029,7 @@ func Test_cgetexpr_works()
endfunc
" Tests for the setqflist() and setloclist() functions
-function SetXlistTests(cchar, bnum)
+func SetXlistTests(cchar, bnum)
call s:setup_commands(a:cchar)
call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1},
@@ -1041,9 +1064,9 @@ function SetXlistTests(cchar, bnum)
call g:Xsetlist([])
let l = g:Xgetlist()
call assert_equal(0, len(l))
-endfunction
+endfunc
-function Test_setqflist()
+func Test_setqflist()
new Xtestfile | only
let bnum = bufnr('%')
call setline(1, range(1,5))
@@ -1053,9 +1076,9 @@ function Test_setqflist()
enew!
call delete('Xtestfile')
-endfunction
+endfunc
-function Xlist_empty_middle(cchar)
+func Xlist_empty_middle(cchar)
call s:setup_commands(a:cchar)
" create three quickfix lists
@@ -1078,12 +1101,12 @@ function Xlist_empty_middle(cchar)
call assert_equal(matchlen, len(g:Xgetlist()))
endfunc
-function Test_setqflist_empty_middle()
+func Test_setqflist_empty_middle()
call Xlist_empty_middle('c')
call Xlist_empty_middle('l')
-endfunction
+endfunc
-function Xlist_empty_older(cchar)
+func Xlist_empty_older(cchar)
call s:setup_commands(a:cchar)
" create three quickfix lists
@@ -1104,14 +1127,14 @@ function Xlist_empty_older(cchar)
call assert_equal(twolen, len(g:Xgetlist()))
Xnewer
call assert_equal(threelen, len(g:Xgetlist()))
-endfunction
+endfunc
-function Test_setqflist_empty_older()
+func Test_setqflist_empty_older()
call Xlist_empty_older('c')
call Xlist_empty_older('l')
-endfunction
+endfunc
-function! XquickfixSetListWithAct(cchar)
+func XquickfixSetListWithAct(cchar)
call s:setup_commands(a:cchar)
let list1 = [{'filename': 'fnameA', 'text': 'A'},
@@ -1185,12 +1208,12 @@ function! XquickfixSetListWithAct(cchar)
call assert_fails("call g:Xsetlist(list1, 0)", 'E928:')
endfunc
-function Test_quickfix_set_list_with_act()
+func Test_quickfix_set_list_with_act()
call XquickfixSetListWithAct('c')
call XquickfixSetListWithAct('l')
-endfunction
+endfunc
-function XLongLinesTests(cchar)
+func XLongLinesTests(cchar)
let l = g:Xgetlist()
call assert_equal(4, len(l))
@@ -1208,9 +1231,9 @@ function XLongLinesTests(cchar)
call assert_equal(10, len(l[3].text))
call g:Xsetlist([], 'r')
-endfunction
+endfunc
-function s:long_lines_tests(cchar)
+func s:long_lines_tests(cchar)
call s:setup_commands(a:cchar)
let testfile = 'samples/quickfix.txt'
@@ -1231,22 +1254,22 @@ function s:long_lines_tests(cchar)
exe 'edit' testfile
exe 'Xbuffer' bufnr('%')
call XLongLinesTests(a:cchar)
-endfunction
+endfunc
-function Test_long_lines()
+func Test_long_lines()
call s:long_lines_tests('c')
call s:long_lines_tests('l')
-endfunction
+endfunc
-function! s:create_test_file(filename)
+func s:create_test_file(filename)
let l = []
for i in range(1, 20)
call add(l, 'Line' . i)
endfor
call writefile(l, a:filename)
-endfunction
+endfunc
-function! Test_switchbuf()
+func Test_switchbuf()
call s:create_test_file('Xqftestfile1')
call s:create_test_file('Xqftestfile2')
call s:create_test_file('Xqftestfile3')
@@ -1333,9 +1356,9 @@ function! Test_switchbuf()
call delete('Xqftestfile1')
call delete('Xqftestfile2')
call delete('Xqftestfile3')
-endfunction
+endfunc
-function! Xadjust_qflnum(cchar)
+func Xadjust_qflnum(cchar)
call s:setup_commands(a:cchar)
enew | only
@@ -1360,17 +1383,17 @@ function! Xadjust_qflnum(cchar)
enew!
call delete(fname)
-endfunction
+endfunc
-function! Test_adjust_lnum()
+func Test_adjust_lnum()
call setloclist(0, [])
call Xadjust_qflnum('c')
call setqflist([])
call Xadjust_qflnum('l')
-endfunction
+endfunc
" Tests for the :grep/:lgrep and :grepadd/:lgrepadd commands
-function! s:test_xgrep(cchar)
+func s:test_xgrep(cchar)
call s:setup_commands(a:cchar)
" The following lines are used for the grep test. Don't remove.
@@ -1389,9 +1412,9 @@ function! s:test_xgrep(cchar)
set makeef=Temp_File_##
silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim
call assert_true(len(g:Xgetlist()) == 6)
-endfunction
+endfunc
-function! Test_grep()
+func Test_grep()
if !has('unix')
" The grepprg may not be set on non-Unix systems
return
@@ -1399,9 +1422,9 @@ function! Test_grep()
call s:test_xgrep('c')
call s:test_xgrep('l')
-endfunction
+endfunc
-function! Test_two_windows()
+func Test_two_windows()
" Use one 'errorformat' for two windows. Add an expression to each of them,
" make sure they each keep their own state.
set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f'
@@ -1427,12 +1450,10 @@ function! Test_two_windows()
laddexpr 'one.txt:3:one one one'
let loc_one = getloclist(one_id)
-echo string(loc_one)
call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr))
call assert_equal(3, loc_one[1].lnum)
let loc_two = getloclist(two_id)
-echo string(loc_two)
call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr))
call assert_equal(5, loc_two[1].lnum)
@@ -1444,7 +1465,7 @@ echo string(loc_two)
call delete('Xtwo', 'rf')
endfunc
-function XbottomTests(cchar)
+func XbottomTests(cchar)
call s:setup_commands(a:cchar)
call g:Xsetlist([{'filename': 'foo', 'lnum': 42}])
@@ -1460,12 +1481,12 @@ function XbottomTests(cchar)
endfunc
" Tests for the :cbottom and :lbottom commands
-function Test_cbottom()
+func Test_cbottom()
call XbottomTests('c')
call XbottomTests('l')
-endfunction
+endfunc
-function HistoryTest(cchar)
+func HistoryTest(cchar)
call s:setup_commands(a:cchar)
call assert_fails(a:cchar . 'older 99', 'E380:')
@@ -1505,7 +1526,7 @@ func Test_duplicate_buf()
endfunc
" Quickfix/Location list set/get properties tests
-function Xproperty_tests(cchar)
+func Xproperty_tests(cchar)
call s:setup_commands(a:cchar)
" Error cases
@@ -1532,6 +1553,11 @@ function Xproperty_tests(cchar)
call assert_equal('N1', g:Xgetlist({'all':1}).title)
call g:Xsetlist([], ' ', {'title' : 'N2'})
call assert_equal(qfnr + 1, g:Xgetlist({'all':1}).nr)
+
+ let res = g:Xgetlist({'nr': 0})
+ call assert_equal(qfnr + 1, res.nr)
+ call assert_equal(['nr'], keys(res))
+
call g:Xsetlist([], ' ', {'title' : 'N3'})
call assert_equal('N2', g:Xgetlist({'nr':2, 'title':1}).title)
@@ -1544,21 +1570,21 @@ function Xproperty_tests(cchar)
call assert_equal({}, g:Xgetlist({'abc':1}))
if a:cchar == 'l'
- call assert_equal({}, getloclist(99, ['title']))
+ call assert_equal({}, getloclist(99, {'title': 1}))
endif
-endfunction
+ endfunc
-function Test_qf_property()
+func Test_qf_property()
call Xproperty_tests('c')
call Xproperty_tests('l')
-endfunction
+ endfunc
" Tests for the QuickFixCmdPre/QuickFixCmdPost autocommands
-function QfAutoCmdHandler(loc, cmd)
+func QfAutoCmdHandler(loc, cmd)
call add(g:acmds, a:loc . a:cmd)
-endfunction
+endfunc
-function Test_Autocmd()
+func Test_Autocmd()
autocmd QuickFixCmdPre * call QfAutoCmdHandler('pre', expand('<amatch>'))
autocmd QuickFixCmdPost * call QfAutoCmdHandler('post', expand('<amatch>'))
@@ -1586,9 +1612,9 @@ function Test_Autocmd()
\ 'precaddbuffer',
\ 'postcaddbuffer']
call assert_equal(l, g:acmds)
-endfunction
+endfunc
-function! Test_Autocmd_Exception()
+func Test_Autocmd_Exception()
set efm=%m
lgetexpr '?'
@@ -1603,4 +1629,47 @@ function! Test_Autocmd_Exception()
call assert_equal('1', getloclist(0)[0].text)
set efm&vim
-endfunction
+endfunc
+
+func Test_caddbuffer_wrong()
+ " This used to cause a memory access in freed memory.
+ let save_efm = &efm
+ set efm=%EEEE%m,%WWWW,%+CCCC%>%#,%GGGG%.#
+ cgetexpr ['WWWW', 'EEEE', 'CCCC']
+ let &efm = save_efm
+ caddbuffer
+ bwipe!
+endfunc
+
+func Test_caddexpr_wrong()
+ " This used to cause a memory access in freed memory.
+ cbuffer
+ cbuffer
+ copen
+ let save_efm = &efm
+ set efm=%
+ call assert_fails('caddexpr ""', 'E376:')
+ let &efm = save_efm
+endfunc
+
+func Test_dirstack_cleanup()
+ " This used to cause a memory access in freed memory.
+ let save_efm = &efm
+ lexpr '0'
+ lopen
+ fun X(c)
+ let save_efm=&efm
+ set efm=%D%f
+ if a:c == 'c'
+ caddexpr '::'
+ else
+ laddexpr ':0:0'
+ endif
+ let &efm=save_efm
+ endfun
+ call X('c')
+ call X('l')
+ call setqflist([], 'r')
+ caddbuffer
+ let &efm = save_efm
+endfunc
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index ab78a72909..c29ec09638 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1007,7 +1007,7 @@ static void tui_flush(UI *ui)
size_t nrevents = loop_size(data->loop);
if (nrevents > TOO_MANY_EVENTS) {
- ILOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
+ WLOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
// Back-pressure: UI events may accumulate much faster than the terminal
// device can serve them. Even if SIGINT/CTRL-C is received, user must still
// wait for the TUI event-queue to drain, and if there are ~millions of
@@ -1537,11 +1537,11 @@ static void augment_terminfo(TUIData *data, const char *term,
bool teraterm = terminfo_is_term_family(term, "teraterm");
bool putty = terminfo_is_term_family(term, "putty");
bool screen = terminfo_is_term_family(term, "screen");
+ bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
bool iterm = terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iTerm.app");
// None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env;
- bool tmux_wrap = screen && !!os_getenv("TMUX");
const char * xterm_version = os_getenv("XTERM_VERSION");
bool true_xterm = xterm && !!xterm_version;
@@ -1573,12 +1573,11 @@ static void augment_terminfo(TUIData *data, const char *term,
// specific ones.
// can use colons like ISO 8613-6:1994/ITU T.416:1993 says.
- bool has_colon_rgb = false
- // per GNOME bug #685759 and bug #704449
- || (vte_version >= 3600)
- || iterm || iterm_pretending_xterm // per analysis of VT100Terminal.m
- // per http://invisible-island.net/xterm/xterm.log.html#xterm_282
- || true_xterm;
+ bool has_colon_rgb = !tmux && !screen
+ && ((vte_version >= 3600) // per GNOME bug #685759, #704449
+ || iterm || iterm_pretending_xterm // per analysis of VT100Terminal.m
+ // per http://invisible-island.net/xterm/xterm.log.html#xterm_282
+ || true_xterm);
data->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf");
if (-1 == data->unibi_ext.set_rgb_foreground) {
@@ -1606,7 +1605,7 @@ static void augment_terminfo(TUIData *data, const char *term,
// all panes, which is not particularly desirable. A better approach
// would use a tmux control sequence and an extra if(screen) test.
data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(
- ut, NULL, TMUX_WRAP(tmux_wrap, "\033]Pl%p1%06x\033\\"));
+ ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
} else if (xterm || (vte_version != 0) || rxvt) {
// This seems to be supported for a long time in VTE
// urxvt also supports this
@@ -1690,7 +1689,7 @@ static const char *tui_get_stty_erase(void)
if (tcgetattr(input_global_fd(), &t) != -1) {
stty_erase[0] = (char)t.c_cc[VERASE];
stty_erase[1] = '\0';
- ILOG("stty/termios:erase=%s", stty_erase);
+ DLOG("stty/termios:erase=%s", stty_erase);
}
#endif
return stty_erase;
@@ -1707,12 +1706,12 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value,
}
if (strequal(name, "key_backspace")) {
- ILOG("libtermkey:kbs=%s", value);
+ DLOG("libtermkey:kbs=%s", value);
if (stty_erase[0] != 0) {
return stty_erase;
}
} else if (strequal(name, "key_dc")) {
- ILOG("libtermkey:kdch1=%s", value);
+ DLOG("libtermkey:kdch1=%s", value);
// Vim: "If <BS> and <DEL> are now the same, redefine <DEL>."
if (value != NULL && strequal(stty_erase, value)) {
return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 0a2154438f..a60c061949 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -8,6 +8,7 @@
#include <limits.h>
#include "nvim/vim.h"
+#include "nvim/log.h"
#include "nvim/ui.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
@@ -59,6 +60,27 @@ static int busy = 0;
static int height, width;
static int old_mode_idx = -1;
+#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
+# define UI_LOG(funname, ...)
+#else
+static size_t uilog_seen = 0;
+static char uilog_last_event[1024] = { 0 };
+# define UI_LOG(funname, ...) \
+ do { \
+ if (strequal(uilog_last_event, STR(funname))) { \
+ uilog_seen++; \
+ } else { \
+ if (uilog_seen > 0) { \
+ do_log(DEBUG_LOG_LEVEL, "ui", 0, true, \
+ "%s (+%zu times...)", uilog_last_event, uilog_seen); \
+ } \
+ DLOG("ui: " STR(funname)); \
+ uilog_seen = 0; \
+ xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
+ } \
+ } while (0)
+#endif
+
// UI_CALL invokes a function on all registered UI instances. The functions can
// have 0-5 arguments (configurable by SELECT_NTH).
//
@@ -67,6 +89,7 @@ static int old_mode_idx = -1;
# define UI_CALL(funname, ...) \
do { \
flush_cursor_update(); \
+ UI_LOG(funname, 0); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
UI_CALL_MORE(funname, __VA_ARGS__); \
@@ -76,6 +99,7 @@ static int old_mode_idx = -1;
# define UI_CALL(...) \
do { \
flush_cursor_update(); \
+ UI_LOG(__VA_ARGS__, 0); \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
@@ -85,6 +109,7 @@ static int old_mode_idx = -1;
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
+// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 0165db7c0c..5585886612 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -24,30 +24,10 @@
#define UI(b) (((UIBridgeData *)b)->ui)
-#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
-static size_t uilog_seen = 0;
-static argv_callback uilog_event = NULL;
-#define UI_CALL(ui, name, argc, ...) \
- do { \
- if (uilog_event == ui_bridge_##name##_event) { \
- uilog_seen++; \
- } else { \
- if (uilog_seen > 0) { \
- DLOG("UI bridge: ...%zu times", uilog_seen); \
- } \
- DLOG("UI bridge: " STR(name)); \
- uilog_seen = 0; \
- uilog_event = ui_bridge_##name##_event; \
- } \
- ((UIBridgeData *)ui)->scheduler( \
- event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \
- } while (0)
-#else
// Schedule a function call on the UI bridge thread.
-#define UI_CALL(ui, name, argc, ...) \
+#define UI_BRIDGE_CALL(ui, name, argc, ...) \
((UIBridgeData *)ui)->scheduler( \
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
-#endif
#define INT2PTR(i) ((void *)(intptr_t)i)
#define PTR2INT(p) ((Integer)(intptr_t)p)
@@ -128,7 +108,7 @@ static void ui_bridge_stop(UI *b)
{
UIBridgeData *bridge = (UIBridgeData *)b;
bool stopped = bridge->stopped = false;
- UI_CALL(b, stop, 1, b);
+ UI_BRIDGE_CALL(b, stop, 1, b);
for (;;) {
uv_mutex_lock(&bridge->mutex);
stopped = bridge->stopped;
@@ -154,7 +134,7 @@ static void ui_bridge_highlight_set(UI *b, HlAttrs attrs)
{
HlAttrs *a = xmalloc(sizeof(HlAttrs));
*a = attrs;
- UI_CALL(b, highlight_set, 2, b, a);
+ UI_BRIDGE_CALL(b, highlight_set, 2, b, a);
}
static void ui_bridge_highlight_set_event(void **argv)
{
@@ -167,7 +147,7 @@ static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;
uv_mutex_lock(&data->mutex);
- UI_CALL(b, suspend, 1, b);
+ UI_BRIDGE_CALL(b, suspend, 1, b);
data->ready = false;
// suspend the main thread until CONTINUE is called by the UI thread
while (!data->ready) {
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 44e9d5e26c..6662dff437 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -605,12 +605,12 @@ static const int included_patches[] = {
127,
// 126,
// 125,
- // 124,
+ 124,
// 123 NA
// 122 NA
121,
// 120 NA
- // 119,
+ 119,
// 118,
// 117 NA
116,
@@ -629,7 +629,7 @@ static const int included_patches[] = {
// 103 NA
// 102,
// 101,
- // 100,
+ 100,
99,
// 98 NA
// 97 NA
@@ -641,33 +641,33 @@ static const int included_patches[] = {
// 91,
// 90,
// 89 NA
- // 88,
+ 88,
// 87 NA
// 86,
- // 85,
- // 84,
+ 85,
+ 84,
83,
// 82 NA
// 81,
// 80 NA
- // 79,
- // 78,
+ 79,
+ 78,
// 77 NA
// 76 NA
- // 75,
+ 75,
// 74,
- // 73,
+ 73,
// 72 NA
// 71 NA
// 70 NA
- // 69,
+ 69,
68,
// 67 NA
66,
// 65 NA
64,
- // 63,
- // 62,
+ // 63 NA
+ 62,
// 61 NA
60,
// 59 NA
@@ -677,13 +677,13 @@ static const int included_patches[] = {
// 55 NA
// 54 NA
53,
- // 52,
+ 52,
// 51 NA
// 50 NA
49,
// 48 NA
47,
- // 46,
+ 46,
// 45 NA
// 44,
43,
@@ -691,33 +691,33 @@ static const int included_patches[] = {
41,
40,
// 39 NA
- // 38,
+ 38,
37,
// 36 NA
35,
- // 34,
+ 34,
33,
32,
- // 31,
+ 31,
// 30 NA
// 29 NA
// 28 NA
// 27 NA
- // 26,
- // 25,
+ 26,
+ 25,
// 24 NA
- // 23,
+ 23,
// 22 NA
// 21,
- // 20,
+ 20,
19,
// 18,
- // 17,
+ 17,
// 16 NA
// 15 NA
// 14 NA
// 13 NA
- // 12,
+ 12,
// 11 NA
// 10 NA
// 9 NA
@@ -725,8 +725,8 @@ static const int included_patches[] = {
// 7 NA
6,
// 5 NA
- // 4,
- // 3,
+ 4,
+ 3,
2,
1,
0
@@ -993,7 +993,7 @@ void intro_message(int colon)
static char *(lines[]) = {
N_(NVIM_VERSION_LONG),
"",
- N_("by Bram Moolenaar et al."),
+ N_("by al."),
N_("Nvim is open source and freely distributable"),
N_("https://neovim.io/community"),
"",
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 5d2c27a2f4..62ffc7433e 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -163,6 +163,7 @@ enum {
EXPAND_SYNTIME,
EXPAND_USER_ADDR_TYPE,
EXPAND_PACKADD,
+ EXPAND_MESSAGES,
};
@@ -319,7 +320,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// Lowest number used for window ID. Cannot have this many windows per tab.
#define LOWEST_WIN_ID 1000
-#if defined(__FreeBSD__) && defined(S_ISCHR)
+#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) && defined(S_ISCHR)
# define OPEN_CHR_FILES
#endif
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 29f5412ba0..faf5bceb56 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -193,7 +193,7 @@ newwindow:
/* cursor to previous window with wrap around */
case 'W':
CHECK_CMDWIN
- if (firstwin == lastwin && Prenum != 1) /* just one window */
+ if (ONE_WINDOW && Prenum != 1) /* just one window */
beep_flush();
else {
if (Prenum) { /* go to specified window */
@@ -574,7 +574,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
oldwin = curwin;
/* add a status line when p_ls == 1 and splitting the first window */
- if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) {
+ if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) {
if (oldwin->w_height <= p_wmh && new_wp == NULL) {
EMSG(_(e_noroom));
return FAIL;
@@ -1182,7 +1182,7 @@ static void win_exchange(long Prenum)
win_T *wp2;
int temp;
- if (lastwin == firstwin) { /* just one window */
+ if (ONE_WINDOW) { /* just one window */
beep_flush();
return;
}
@@ -1271,7 +1271,7 @@ static void win_rotate(int upwards, int count)
frame_T *frp;
int n;
- if (firstwin == lastwin) { /* nothing to do */
+ if (ONE_WINDOW) { /* nothing to do */
beep_flush();
return;
}
@@ -1343,7 +1343,7 @@ static void win_totop(int size, int flags)
int dir;
int height = curwin->w_height;
- if (lastwin == firstwin) {
+ if (ONE_WINDOW) {
beep_flush();
return;
}
@@ -1728,7 +1728,7 @@ void close_windows(buf_T *buf, int keep_curwin)
++RedrawingDisabled;
- for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) {
+ for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW; ) {
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
if (win_close(wp, false) == FAIL) {
@@ -1810,7 +1810,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf,
tabpage_T *prev_curtab)
FUNC_ATTR_NONNULL_ARG(1)
{
- if (firstwin != lastwin) {
+ if (!ONE_WINDOW) {
return false;
}
buf_T *old_curbuf = curbuf;
@@ -2194,7 +2194,7 @@ winframe_remove (
/*
* If there is only one window there is nothing to remove.
*/
- if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin)
+ if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
return NULL;
/*
@@ -2331,7 +2331,7 @@ win_altframe (
frame_T *frp;
int b;
- if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin)
+ if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin)
/* Last window in this tab page, will go to next tab page. */
return alt_tabpage()->tp_curwin->w_frame;
@@ -2851,7 +2851,7 @@ close_others (
win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer));
}
- if (message && lastwin != firstwin)
+ if (message && !ONE_WINDOW)
EMSG(_("E445: Other window contains changes"));
}
@@ -5173,7 +5173,7 @@ last_status (
{
/* Don't make a difference between horizontal or vertical split. */
last_status_rec(topframe, (p_ls == 2
- || (p_ls == 1 && (morewin || lastwin != firstwin))));
+ || (p_ls == 1 && (morewin || !ONE_WINDOW))));
}
static void last_status_rec(frame_T *fr, int statusline)
diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua
index 57198600b9..55da8da8dc 100644
--- a/test/functional/ex_cmds/menu_spec.lua
+++ b/test/functional/ex_cmds/menu_spec.lua
@@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each)
local clear, command, nvim = helpers.clear, helpers.command, helpers.nvim
local expect, feed = helpers.expect, helpers.feed
local eq, eval = helpers.eq, helpers.eval
+local funcs = helpers.funcs
+
describe(':emenu', function()
@@ -56,3 +58,326 @@ describe(':emenu', function()
eq('thiscmdmode', eval('getcmdline()'))
end)
end)
+
+describe('menu_get', function()
+
+ before_each(function()
+ clear()
+ command('nnoremenu &Test.Test inormal<ESC>')
+ command('inoremenu Test.Test insert')
+ command('vnoremenu Test.Test x')
+ command('cnoremenu Test.Test cmdmode')
+ command('menu Test.Nested.test level1')
+ command('menu Test.Nested.Nested2 level2')
+
+ command('nnoremenu <script> Export.Script p')
+ command('tmenu Export.Script This is the tooltip')
+ command('menu ]Export.hidden thisoneshouldbehidden')
+
+ command('nnoremenu Edit.Paste p')
+ command('cnoremenu Edit.Paste <C-R>"')
+ end)
+
+ it("path='', modes='a'", function()
+ local m = funcs.menu_get("","a");
+ -- HINT: To print the expected table and regenerate the tests:
+ -- print(require('pl.pretty').dump(m))
+ local expected = {
+ {
+ shortcut = "T",
+ hidden = 0,
+ submenus = {
+ {
+ mappings = {
+ i = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "insert",
+ silent = 0
+ },
+ s = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "x",
+ silent = 0
+ },
+ n = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "inormal\27",
+ silent = 0
+ },
+ v = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "x",
+ silent = 0
+ },
+ c = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "cmdmode",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "Test",
+ hidden = 0
+ },
+ {
+ priority = 500,
+ name = "Nested",
+ submenus = {
+ {
+ mappings = {
+ o = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level1",
+ silent = 0
+ },
+ v = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level1",
+ silent = 0
+ },
+ s = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level1",
+ silent = 0
+ },
+ n = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level1",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "test",
+ hidden = 0
+ },
+ {
+ mappings = {
+ o = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level2",
+ silent = 0
+ },
+ v = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level2",
+ silent = 0
+ },
+ s = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level2",
+ silent = 0
+ },
+ n = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "level2",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "Nested2",
+ hidden = 0
+ }
+ },
+ hidden = 0
+ }
+ },
+ priority = 500,
+ name = "Test"
+ },
+ {
+ priority = 500,
+ name = "Export",
+ submenus = {
+ {
+ tooltip = "This is the tooltip",
+ hidden = 0,
+ name = "Script",
+ priority = 500,
+ mappings = {
+ n = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "p",
+ silent = 0
+ }
+ }
+ }
+ },
+ hidden = 0
+ },
+ {
+ priority = 500,
+ name = "Edit",
+ submenus = {
+ {
+ mappings = {
+ c = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "\18\"",
+ silent = 0
+ },
+ n = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "p",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "Paste",
+ hidden = 0
+ }
+ },
+ hidden = 0
+ },
+ {
+ priority = 500,
+ name = "]Export",
+ submenus = {
+ {
+ mappings = {
+ o = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "thisoneshouldbehidden",
+ silent = 0
+ },
+ v = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "thisoneshouldbehidden",
+ silent = 0
+ },
+ s = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "thisoneshouldbehidden",
+ silent = 0
+ },
+ n = {
+ sid = 0,
+ noremap = 0,
+ enabled = 1,
+ rhs = "thisoneshouldbehidden",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "hidden",
+ hidden = 0
+ }
+ },
+ hidden = 1
+ }
+ }
+ eq(expected, m)
+ end)
+
+ it('matching path, default modes', function()
+ local m = funcs.menu_get("Export", "a")
+ local expected = {
+ {
+ tooltip = "This is the tooltip",
+ hidden = 0,
+ name = "Script",
+ priority = 500,
+ mappings = {
+ n = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "p",
+ silent = 0
+ }
+ }
+ }
+ }
+ eq(expected, m)
+ end)
+
+ it('no path, matching modes', function()
+ local m = funcs.menu_get("","i")
+ local expected = {
+ {
+ shortcut = "T",
+ hidden = 0,
+ submenus = {
+ {
+ mappings = {
+ i = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "insert",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "Test",
+ hidden = 0
+ },
+ {
+ }
+ },
+ priority = 500,
+ name = "Test"
+ }
+ }
+ eq(expected, m)
+ end)
+
+ it('matching path and modes', function()
+ local m = funcs.menu_get("Test","i")
+ local expected = {
+ {
+ mappings = {
+ i = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "insert",
+ silent = 0
+ }
+ },
+ priority = 500,
+ name = "Test",
+ hidden = 0
+ }
+ }
+ eq(expected, m)
+ end)
+
+end)
diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua
index 2f342ec9a3..d646e8dbf4 100644
--- a/test/functional/legacy/assert_spec.lua
+++ b/test/functional/legacy/assert_spec.lua
@@ -253,6 +253,11 @@ describe('assert function:', function()
"Expected range 5 - 7, but got 8",
})
end)
+
+ it('assert_inrange(1, 1) returns E119', function()
+ eq('Vim(call):E119: Not enough arguments for function: assert_inrange',
+ exc_exec("call assert_inrange(1, 1)"))
+ end)
end)
-- assert_report({msg})
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 35af34015d..3daf92eea0 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -13,9 +13,6 @@ describe('ui/mouse/input', function()
clear()
meths.set_option('mouse', 'a')
meths.set_option('listchars', 'eol:$')
- -- set mousetime to very high value to ensure that even in valgrind/travis,
- -- nvim will still pick multiple clicks
- meths.set_option('mousetime', 5000)
screen = Screen.new(25, 5)
screen:attach()
screen:set_default_attr_ids({
@@ -119,7 +116,6 @@ describe('ui/mouse/input', function()
sel = { bold=true },
fill = { reverse=true }
})
- screen.timeout = 15000
end)
it('in tabline on filler space moves tab to the end', function()