Merge branch 'master' into colored-cmdline

This commit is contained in:
ZyX 2017-07-31 02:05:02 +03:00
commit fbe60af538
76 changed files with 2808 additions and 1509 deletions

3
.gitignore vendored
View File

@ -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/

View File

@ -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()

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -68,4 +68,6 @@
#cmakedefine ORDER_BIG_ENDIAN
#define ENDIAN_INCLUDE_FILE <@ENDIAN_INCLUDE_FILE@>
#cmakedefine HAVE_EXECINFO_BACKTRACE
#endif // AUTO_CONFIG_H

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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')
" Mappings: {{{1
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*')
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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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=

View File

@ -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*

View File

@ -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.
Nvim emits true (24-bit) colours in the terminal, if 'termguicolors' is set.
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.)
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).
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.
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 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.
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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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."
}
}

View File

@ -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
"expect" lines check that the contents of the line are identical to some preset text
(like in the exercises above).
\---> TEXT {expect:TEXT}
These elements are specified in separate JSON files like this
is satisfied, but
~~~ json
{
"expect": {
"1": "This is how this line should look.",
"2": "This is how this line should look.",
"3": -1
}
}
~~~
\---> OTHER TEXT {expect:TEXT}
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).
is not.
This is an "expect" line that is always satisfied. Try changing it.
13. Make both lines the same:
---> 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
---> this is free text {expect:ANYTHING}
14. Turn the line below into free text:
---> 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).

View File

@ -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
}
}

View File

@ -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
--------------
Let's understand what a Vim-like program does by analyzing the workflow of
a typical editing session:
Following describes how Nvim processes input.
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:

View File

@ -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);

View File

@ -793,6 +793,7 @@ static digr_T digraphdefault[] =
{ '/', '-', 0x2020 },
{ '/', '=', 0x2021 },
{ '.', '.', 0x2025 },
{ ',', '.', 0x2026 },
{ '%', '0', 0x2030 },
{ '1', '\'', 0x2032 },
{ '2', '\'', 0x2033 },

View File

@ -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));
}

View File

@ -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}},

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -89,27 +89,28 @@ 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

View File

@ -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 },

View File

@ -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')

View File

@ -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 */

View File

@ -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.

View File

@ -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, ...)

View File

@ -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

View File

@ -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;

View File

@ -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. */

View File

@ -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);
}
/*

View File

@ -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
};

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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 {

View File

@ -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 {

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -1255,21 +1255,27 @@ func! Test_normal22_zet()
" Test for ZZ
" let shell = &shell
" let &shell = 'sh'
call writefile(['1', '2'], 'Xfile')
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 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 assert_equal(['1', '2'], a)
" clean up
for file in ['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_Test_normal22_zet')
let a = readfile('Xfile_Test_normal22_zet')
call assert_equal([], a)
" Test for ZQ
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)
" Nvim: This sometimes hangs the TSAN build.
" for file in ['Xfile_Test_normal22_zet']
" call delete(file)
" endfor
" let &shell = shell
endfunc

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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) {

View File

@ -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"),
"",

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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})

View File

@ -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()