Merge branch 'master' into expression-parser

This commit is contained in:
ZyX 2017-10-15 21:16:27 +03:00
commit 76f0466536
155 changed files with 3675 additions and 1624 deletions

View File

@ -7,6 +7,10 @@ end_of_line = lf
insert_final_newline = true insert_final_newline = true
charset = utf_8 charset = utf_8
[runtime/doc/*.txt]
indent_style = tab
indent_size = 8
[Makefile] [Makefile]
indent_style = tab indent_style = tab
tab_width = 4 tab_width = 4

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# Build/deps dir # Build/deps dir
/build/ /build/
/cmake-build-debug/
/dist/ /dist/
/.deps/ /.deps/
/tmp/ /tmp/

4
BSDmakefile Normal file
View File

@ -0,0 +1,4 @@
.DONE:
@echo "Please use GNU Make (gmake) to build neovim"
.DEFAULT:
@echo "Please use GNU Make (gmake) to build neovim"

View File

@ -254,7 +254,10 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.5") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8.5")
# Array-bounds testing is broken in some GCC versions before 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 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56273
add_definitions(-Wno-array-bounds) check_c_compiler_flag(-Wno-array-bounds HAS_NO_ARRAY_BOUNDS_FLAG)
if(HAS_NO_ARRAY_BOUNDS_FLAG)
add_definitions(-Wno-array-bounds)
endif()
endif() endif()
endif() endif()
@ -344,6 +347,21 @@ if(FEAT_TUI)
find_package(Unibilium REQUIRED) find_package(Unibilium REQUIRED)
include_directories(SYSTEM ${UNIBILIUM_INCLUDE_DIRS}) include_directories(SYSTEM ${UNIBILIUM_INCLUDE_DIRS})
list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
check_c_source_compiles("
#include <unibilium.h>
int
main(void)
{
return unibi_num_from_var(unibi_var_from_num(0));
}
" UNIBI_HAS_VAR_FROM)
if(UNIBI_HAS_VAR_FROM)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_UNIBI_HAS_VAR_FROM")
endif()
find_package(LibTermkey REQUIRED) find_package(LibTermkey REQUIRED)
include_directories(SYSTEM ${LIBTERMKEY_INCLUDE_DIRS}) include_directories(SYSTEM ${LIBTERMKEY_INCLUDE_DIRS})
endif() endif()

View File

@ -48,17 +48,24 @@ and [more](https://github.com/neovim/neovim/wiki/Installing-Neovim)!
Project layout Project layout
-------------- --------------
├─ ci/ Build server scripts ├─ ci/ build automation
├─ cmake/ Build scripts ├─ cmake/ build scripts
├─ runtime/ User plugins/docs ├─ runtime/ user plugins/docs
├─ src/ Source code ├─ src/ application source code (see src/nvim/README.md)
├─ third-party/ CMake subproject to build dependencies │ ├─ api/ API subsystem
└─ test/ Test code │ ├─ eval/ VimL subsystem
│ ├─ event/ event-loop subsystem
│ ├─ generators/ code generation (pre-compilation)
│ ├─ lib/ generic data structures
│ ├─ lua/ lua subsystem
│ ├─ msgpack_rpc/ RPC subsystem
│ ├─ os/ low-level platform code
│ └─ tui/ built-in UI
├─ third-party/ cmake subproject to build dependencies
└─ test/ tests (see test/README.md)
- `third-party/` is activated if `USE_BUNDLED_DEPS` is undefined or the - To disable `third-party/` specify `USE_BUNDLED_DEPS=NO` or `USE_BUNDLED=NO`
`USE_BUNDLED` CMake option is true. (CMake option).
- [Source README](src/nvim/README.md)
- [Test README](test/README.md)
Features Features
-------- --------

View File

@ -1,4 +1,6 @@
version: '{build}' version: '{build}'
environment:
APPVEYOR_CACHE_ENTRY_ZIP_ARGS: "-t7z -m0=lzma -mx=9"
configuration: configuration:
- MINGW_64 - MINGW_64
- MINGW_32 - MINGW_32

View File

@ -11,6 +11,18 @@ if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
brew update brew update
fi fi
echo 'python info:'
(
2>&1 python --version || true
2>&1 python2 --version || true
2>&1 python3 --version || true
2>&1 pip --version || true
2>&1 pip2 --version || true
2>&1 pip3 --version || true
echo 'pyenv versions:'
2>&1 pyenv versions || true
) | sed 's/^/ /'
echo "Upgrade Python 2 pip." echo "Upgrade Python 2 pip."
pip2.7 -q install --user --upgrade pip pip2.7 -q install --user --upgrade pip
@ -21,5 +33,7 @@ if [[ "${TRAVIS_OS_NAME}" == osx ]]; then
pip3 -q install --user --upgrade pip pip3 -q install --user --upgrade pip
else else
echo "Upgrade Python 3 pip." echo "Upgrade Python 3 pip."
pip3 -q install --user --upgrade pip # Allow failure. pyenv pip3 on travis is broken:
# https://github.com/travis-ci/travis-ci/issues/8363
pip3 -q install --user --upgrade pip || true
fi fi

View File

@ -17,7 +17,9 @@ echo "Install neovim module and coveralls for Python 2."
CC=cc pip2.7 -q install --user --upgrade neovim cpp-coveralls CC=cc pip2.7 -q install --user --upgrade neovim cpp-coveralls
echo "Install neovim module for Python 3." echo "Install neovim module for Python 3."
CC=cc pip3 -q install --user --upgrade neovim # Allow failure. pyenv pip3 on travis is broken:
# https://github.com/travis-ci/travis-ci/issues/8363
CC=cc pip3 -q install --user --upgrade neovim || true
echo "Install neovim RubyGem." echo "Install neovim RubyGem."
gem install --no-document --version ">= 0.2.0" neovim gem install --no-document --version ">= 0.2.0" neovim

View File

@ -46,6 +46,7 @@ check_c_source_compiles("
int main(int argc, char** argv) { int main(int argc, char** argv) {
gettext(\"foo\"); gettext(\"foo\");
ngettext(\"foo\", \"bar\", 1);
bindtextdomain(\"foo\", \"bar\"); bindtextdomain(\"foo\", \"bar\");
bind_textdomain_codeset(\"foo\", \"bar\"); bind_textdomain_codeset(\"foo\", \"bar\");
textdomain(\"foo\"); textdomain(\"foo\");

View File

@ -71,6 +71,7 @@ foreach(DF ${DOCFILES})
endforeach() endforeach()
add_custom_target(helptags add_custom_target(helptags
COMMAND ${CMAKE_COMMAND} -E remove_directory ${GENERATED_RUNTIME_DIR}/doc
COMMAND ${CMAKE_COMMAND} -E copy_directory COMMAND ${CMAKE_COMMAND} -E copy_directory
${PROJECT_SOURCE_DIR}/runtime/doc ${GENERATED_RUNTIME_DIR}/doc ${PROJECT_SOURCE_DIR}/runtime/doc ${GENERATED_RUNTIME_DIR}/doc
COMMAND "${PROJECT_BINARY_DIR}/bin/nvim" COMMAND "${PROJECT_BINARY_DIR}/bin/nvim"

View File

@ -1,15 +1,15 @@
function! s:enhance_syntax() abort function! s:enhance_syntax() abort
syntax case match syntax case match
syntax keyword healthError ERROR syntax keyword healthError ERROR[:]
\ containedin=markdownCodeBlock,mkdListItemLine \ containedin=markdownCodeBlock,mkdListItemLine
highlight link healthError Error highlight link healthError Error
syntax keyword healthWarning WARNING syntax keyword healthWarning WARNING[:]
\ containedin=markdownCodeBlock,mkdListItemLine \ containedin=markdownCodeBlock,mkdListItemLine
highlight link healthWarning WarningMsg highlight link healthWarning WarningMsg
syntax keyword healthSuccess SUCCESS syntax keyword healthSuccess OK[:]
\ containedin=markdownCodeBlock,mkdListItemLine \ containedin=markdownCodeBlock,mkdListItemLine
highlight healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232 highlight healthSuccess guibg=#5fff00 guifg=#080808 ctermbg=82 ctermfg=232
@ -90,27 +90,27 @@ endfunction
" Changes ':h clipboard' to ':help |clipboard|'. " Changes ':h clipboard' to ':help |clipboard|'.
function! s:help_to_link(s) abort function! s:help_to_link(s) abort
return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n]+)', ':help |\1|', 'g') return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n ]+)', ':help |\1|', 'g')
endfunction endfunction
" Format a message for a specific report item " Format a message for a specific report item
function! s:format_report_message(status, msg, ...) abort " {{{ function! s:format_report_message(status, msg, ...) abort " {{{
let output = ' - ' . a:status . ': ' . s:indent_after_line1(a:msg, 4) let output = ' - ' . a:status . ': ' . s:indent_after_line1(a:msg, 4)
let suggestions = [] let advice = []
" Optional parameters " Optional parameters
if a:0 > 0 if a:0 > 0
let suggestions = type(a:1) == type("") ? [a:1] : a:1 let advice = type(a:1) == type("") ? [a:1] : a:1
if type(suggestions) != type([]) if type(advice) != type([])
echoerr "Expected String or List" throw "Expected String or List"
endif endif
endif endif
" Report each suggestion " Report each suggestion
if len(suggestions) > 0 if len(advice) > 0
let output .= "\n - SUGGESTIONS:" let output .= "\n - ADVICE:"
endif endif
for suggestion in suggestions for suggestion in advice
let output .= "\n - " . s:indent_after_line1(suggestion, 10) let output .= "\n - " . s:indent_after_line1(suggestion, 10)
endfor endfor
@ -124,7 +124,7 @@ endfunction " }}}
" Reports a successful healthcheck. " Reports a successful healthcheck.
function! health#report_ok(msg) abort " {{{ function! health#report_ok(msg) abort " {{{
echo s:format_report_message('SUCCESS', a:msg) echo s:format_report_message('OK', a:msg)
endfunction " }}} endfunction " }}}
" Reports a health warning. " Reports a health warning.

View File

@ -10,6 +10,12 @@ function! s:check_config() abort
\ [ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'", \ [ "Use the 'guicursor' option to configure cursor shape. :help 'guicursor'",
\ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ]) \ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ])
endif endif
if &paste
let ok = v:false
call health#report_error("'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.",
\ [ 'Remove `set paste` from your init.vim, if applicable.',
\ 'Check `:verbose set paste?` to see if a plugin or script set the option.', ])
endif
if ok if ok
call health#report_ok('no issues found') call health#report_ok('no issues found')

View File

@ -121,14 +121,14 @@ function! s:check_clipboard() abort
call health#report_start('Clipboard (optional)') call health#report_start('Clipboard (optional)')
let clipboard_tool = provider#clipboard#Executable() let clipboard_tool = provider#clipboard#Executable()
if empty(clipboard_tool) if exists('g:clipboard') && empty(clipboard_tool)
call health#report_warn(
\ 'No clipboard tool found. Clipboard registers will not work.',
\ [':help clipboard'])
elseif exists('g:clipboard') && (type({}) != type(g:clipboard)
\ || !has_key(g:clipboard, 'copy') || !has_key(g:clipboard, 'paste'))
call health#report_error( call health#report_error(
\ 'g:clipboard exists but is malformed. It must be a dictionary with the keys documented at :help g:clipboard') \ provider#clipboard#Error(),
\ ["Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."])
elseif empty(clipboard_tool)
call health#report_warn(
\ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.',
\ [':help clipboard'])
else else
call health#report_ok('Clipboard tool found: '. clipboard_tool) call health#report_ok('Clipboard tool found: '. clipboard_tool)
endif endif
@ -239,7 +239,7 @@ function! s:check_python(version) abort
let pyname = 'python'.(a:version == 2 ? '' : '3') let pyname = 'python'.(a:version == 2 ? '' : '3')
let pyenv = resolve(exepath('pyenv')) let pyenv = resolve(exepath('pyenv'))
let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : 'n' let pyenv_root = exists('$PYENV_ROOT') ? resolve($PYENV_ROOT) : ''
let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : '' let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
let host_prog_var = pyname.'_host_prog' let host_prog_var = pyname.'_host_prog'
let loaded_var = 'g:loaded_'.pyname.'_provider' let loaded_var = 'g:loaded_'.pyname.'_provider'
@ -251,6 +251,19 @@ function! s:check_python(version) abort
return return
endif endif
if !empty(pyenv)
if empty(pyenv_root)
call health#report_warn(
\ 'pyenv was found, but $PYENV_ROOT is not set.',
\ ['Did you follow the final install instructions?',
\ 'If you use a shell "framework" like Prezto or Oh My Zsh, try without.',
\ 'Try a different shell (bash).']
\ )
else
call health#report_ok(printf('pyenv found: "%s"', pyenv))
endif
endif
if exists('g:'.host_prog_var) if exists('g:'.host_prog_var)
call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var))) call health#report_info(printf('Using: g:%s = "%s"', host_prog_var, get(g:, host_prog_var)))
endif endif
@ -282,15 +295,6 @@ function! s:check_python(version) abort
endif endif
if !empty(pyenv) if !empty(pyenv)
if empty(pyenv_root)
call health#report_warn(
\ 'pyenv was found, but $PYENV_ROOT is not set.',
\ ['Did you follow the final install instructions?']
\ )
else
call health#report_ok(printf('pyenv found: "%s"', pyenv))
endif
let python_bin = s:trim(s:system([pyenv, 'which', pyname], '', 1)) let python_bin = s:trim(s:system([pyenv, 'which', pyname], '', 1))
if empty(python_bin) if empty(python_bin)
@ -320,9 +324,8 @@ function! s:check_python(version) abort
if python_bin =~# '\<shims\>' if python_bin =~# '\<shims\>'
call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_bin), [ call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_bin), [
\ 'The `pyenv` executable is not in $PATH,', \ '`pyenv` is not in $PATH, your pyenv installation is broken. '
\ 'Your pyenv installation is broken. You should set ' \ .'Set `g:'.host_prog_var.'` to avoid surprises.',
\ . '`g:'.host_prog_var.'` to avoid surprises.',
\ ]) \ ])
endif endif
endif endif
@ -335,7 +338,7 @@ function! s:check_python(version) abort
call health#report_warn('pyenv is not set up optimally.', [ call health#report_warn('pyenv is not set up optimally.', [
\ printf('Create a virtualenv specifically ' \ printf('Create a virtualenv specifically '
\ . 'for Neovim using pyenv, and set `g:%s`. This will avoid ' \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid '
\ . 'the need to install Neovim''s Python module in each ' \ . 'the need to install the Neovim Python module in each '
\ . 'version/virtualenv.', host_prog_var) \ . 'version/virtualenv.', host_prog_var)
\ ]) \ ])
elseif !empty(venv) && exists('g:'.host_prog_var) elseif !empty(venv) && exists('g:'.host_prog_var)

View File

@ -2,7 +2,7 @@
let s:stderr = {} let s:stderr = {}
function! provider#stderr_collector(chan_id, data, event) dict function! provider#stderr_collector(chan_id, data, event)
let stderr = get(s:stderr, a:chan_id, ['']) let stderr = get(s:stderr, a:chan_id, [''])
let stderr[-1] .= a:data[0] let stderr[-1] .= a:data[0]
call extend(stderr, a:data[1:]) call extend(stderr, a:data[1:])
@ -10,7 +10,9 @@ function! provider#stderr_collector(chan_id, data, event) dict
endfunction endfunction
function! provider#clear_stderr(chan_id) function! provider#clear_stderr(chan_id)
silent! call delete(s:stderr, a:chan_id) if has_key(s:stderr, a:chan_id)
call remove(s:stderr, a:chan_id)
endif
endfunction endfunction
function! provider#get_stderr(chan_id) function! provider#get_stderr(chan_id)

View File

@ -3,6 +3,7 @@
" available. " available.
let s:copy = {} let s:copy = {}
let s:paste = {} let s:paste = {}
let s:clipboard = {}
" When caching is enabled, store the jobid of the xclip/xsel process keeping " 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. " ownership of the selection, so we know how long the cache is valid.
@ -23,7 +24,7 @@ function! s:selection.on_exit(jobid, data, event) abort
call provider#clear_stderr(a:jobid) call provider#clear_stderr(a:jobid)
endfunction endfunction
let s:selections = { '*': s:selection, '+': copy(s:selection)} let s:selections = { '*': s:selection, '+': copy(s:selection) }
function! s:try_cmd(cmd, ...) abort function! s:try_cmd(cmd, ...) abort
let argv = split(a:cmd, " ") let argv = split(a:cmd, " ")
@ -31,7 +32,7 @@ function! s:try_cmd(cmd, ...) abort
if v:shell_error if v:shell_error
if !exists('s:did_error_try_cmd') if !exists('s:did_error_try_cmd')
echohl WarningMsg echohl WarningMsg
echomsg "clipboard: error: ".(len(out) ? out[0] : '') echomsg "clipboard: error: ".(len(out) ? out[0] : v:shell_error)
echohl None echohl None
let s:did_error_try_cmd = 1 let s:did_error_try_cmd = 1
endif endif
@ -55,9 +56,15 @@ endfunction
function! provider#clipboard#Executable() abort function! provider#clipboard#Executable() abort
if exists('g:clipboard') if exists('g:clipboard')
if type({}) isnot# type(g:clipboard)
\ || type({}) isnot# type(get(g:clipboard, 'copy', v:null))
\ || type({}) isnot# type(get(g:clipboard, 'paste', v:null))
let s:err = 'clipboard: invalid g:clipboard'
return ''
endif
let s:copy = get(g:clipboard, 'copy', { '+': v:null, '*': v:null }) let s:copy = get(g:clipboard, 'copy', { '+': v:null, '*': v:null })
let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null }) let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null })
let s:cache_enabled = get(g:clipboard, 'cache_enabled', 1) let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0)
return get(g:clipboard, 'name', 'g:clipboard') return get(g:clipboard, 'name', 'g:clipboard')
elseif has('mac') && executable('pbcopy') elseif has('mac') && executable('pbcopy')
let s:copy['+'] = 'pbcopy' let s:copy['+'] = 'pbcopy'
@ -104,16 +111,17 @@ function! provider#clipboard#Executable() abort
return 'tmux' return 'tmux'
endif endif
let s:err = 'clipboard: No clipboard tool available. :help clipboard' let s:err = 'clipboard: No clipboard tool. :help clipboard'
return '' return ''
endfunction endfunction
if empty(provider#clipboard#Executable()) if empty(provider#clipboard#Executable())
" provider#clipboard#Call() *must not* be defined if the provider is broken.
" Otherwise eval_has_provider() thinks the clipboard provider is
" functioning, and eval_call_provider() will happily call it.
finish finish
endif endif
let s:clipboard = {}
function! s:clipboard.get(reg) abort function! s:clipboard.get(reg) abort
if s:selections[a:reg].owner > 0 if s:selections[a:reg].owner > 0
return s:selections[a:reg].data return s:selections[a:reg].data
@ -154,9 +162,19 @@ function! s:clipboard.set(lines, regtype, reg) abort
echohl WarningMsg echohl WarningMsg
echomsg 'clipboard: failed to execute: '.(s:copy[a:reg]) echomsg 'clipboard: failed to execute: '.(s:copy[a:reg])
echohl None echohl None
return 0
endif endif
return 1
endfunction endfunction
function! provider#clipboard#Call(method, args) abort function! provider#clipboard#Call(method, args) abort
return call(s:clipboard[a:method],a:args,s:clipboard) if get(s:, 'here', v:false) " Clipboard provider must not recurse. #7184
return 0
endif
let s:here = v:true
try
return call(s:clipboard[a:method],a:args,s:clipboard)
finally
let s:here = v:false
endtry
endfunction endfunction

View File

@ -89,7 +89,8 @@ endfunction
function! remote#define#AutocmdOnHost(host, method, sync, name, opts) function! remote#define#AutocmdOnHost(host, method, sync, name, opts)
let group = s:GetNextAutocmdGroup() let group = s:GetNextAutocmdGroup()
let forward = '"doau '.group.' '.a:name.' ".'.'expand("<amatch>")' let forward = '"doau '.group.' '.a:name.' ".'
\ . 'fnameescape(expand("<amatch>"))'
let a:opts.group = group let a:opts.group = group
let bootstrap_def = s:GetAutocmdPrefix(a:name, a:opts) let bootstrap_def = s:GetAutocmdPrefix(a:name, a:opts)
\ .' call remote#define#AutocmdBootstrap("'.a:host.'"' \ .' call remote#define#AutocmdBootstrap("'.a:host.'"'

View File

@ -171,8 +171,8 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
Parameters:~ Parameters:~
{str} String to be converted. {str} String to be converted.
{from_part} Legacy Vim parameter. Usually true. {from_part} Legacy Vim parameter. Usually true.
{do_lt} Also translate <lt>. Does nothing if {do_lt} Also translate <lt>. Ignored if `special` is
`special` is false. false.
{special} Replace |keycodes|, e.g. <CR> becomes a "\n" {special} Replace |keycodes|, e.g. <CR> becomes a "\n"
char. char.
@ -309,20 +309,24 @@ nvim_set_option({name}, {value}) *nvim_set_option()*
{value} New option value {value} New option value
nvim_out_write({str}) *nvim_out_write()* nvim_out_write({str}) *nvim_out_write()*
Writes a message to vim output buffer Writes a message to the Vim output buffer. Does not append
"\n", the message is buffered (won't display) until a linefeed
is written.
Parameters:~ Parameters:~
{str} Message {str} Message
nvim_err_write({str}) *nvim_err_write()* nvim_err_write({str}) *nvim_err_write()*
Writes a message to vim error buffer Writes a message to the Vim error buffer. Does not append
"\n", the message is buffered (won't display) until a linefeed
is written.
Parameters:~ Parameters:~
{str} Message {str} Message
nvim_err_writeln({str}) *nvim_err_writeln()* nvim_err_writeln({str}) *nvim_err_writeln()*
Writes a message to vim error buffer. Appends a linefeed to Writes a message to the Vim error buffer. Appends "\n", so the
ensure all contents are written. buffer is flushed (and displayed).
Parameters:~ Parameters:~
{str} Message {str} Message

View File

@ -1271,7 +1271,7 @@ Commands for changing the working directory can be suffixed with a bang "!"
*:lcd-* *:lcd-*
:lcd[!] - Change to the previous current directory (before the :lcd[!] - Change to the previous current directory (before the
previous ":tcd {path}" command). previous ":lcd {path}" command).
*:pw* *:pwd* *E187* *:pw* *:pwd* *E187*
:pw[d] Print the current directory name. :pw[d] Print the current directory name.

View File

@ -1522,14 +1522,16 @@ v:errors Errors found by assert functions, such as |assert_true()|.
*v:event* *event-variable* *v:event* *event-variable*
v:event Dictionary of event data for the current |autocommand|. Valid v:event Dictionary of event data for the current |autocommand|. Valid
only during the autocommand lifetime: storing or passing only during the event lifetime; storing or passing v:event is
`v:event` is invalid. Copy it instead: > invalid! Copy it instead: >
au TextYankPost * let g:foo = deepcopy(v:event) au TextYankPost * let g:foo = deepcopy(v:event)
< Keys vary by event; see the documentation for the specific < Keys vary by event; see the documentation for the specific
event, e.g. |TextYankPost|. event, e.g. |DirChanged| or |TextYankPost|.
KEY DESCRIPTION ~ KEY DESCRIPTION ~
operator The current |operator|. Also set for cwd Current working directory
Ex commands (unlike |v:operator|). For scope Event-specific scope name.
operator Current |operator|. Also set for Ex
commands (unlike |v:operator|). For
example if |TextYankPost| is triggered example if |TextYankPost| is triggered
by the |:yank| Ex command then by the |:yank| Ex command then
`v:event['operator']` is "y". `v:event['operator']` is "y".
@ -4726,7 +4728,8 @@ input({opts})
"-complete=" argument. Refer to |:command-completion| for "-complete=" argument. Refer to |:command-completion| for
more information. Example: > more information. Example: >
let fname = input("File: ", "", "file") let fname = input("File: ", "", "file")
< *E5400* *E5402*
< *input()-highlight* *E5400* *E5402*
The optional `highlight` key allows specifying function which The optional `highlight` key allows specifying function which
will be used for highlighting user input. This function will be used for highlighting user input. This function
receives user input as its only argument and must return receives user input as its only argument and must return
@ -4744,6 +4747,30 @@ input({opts})
sections must be ordered so that next hl_start_col is greater sections must be ordered so that next hl_start_col is greater
then or equal to previous hl_end_col. then or equal to previous hl_end_col.
Example (try some input with parentheses): >
highlight RBP1 guibg=Red ctermbg=red
highlight RBP2 guibg=Yellow ctermbg=yellow
highlight RBP3 guibg=Green ctermbg=green
highlight RBP4 guibg=Blue ctermbg=blue
let g:rainbow_levels = 4
function! RainbowParens(cmdline)
let ret = []
let i = 0
let lvl = 0
while i < len(a:cmdline)
if a:cmdline[i] is# '('
call add(ret, [i, i + 1, 'RBP' . ((lvl % g:rainbow_levels) + 1)])
let lvl += 1
elseif a:cmdline[i] is# ')'
let lvl -= 1
call add(ret, [i, i + 1, 'RBP' . ((lvl % g:rainbow_levels) + 1)])
endif
let i += 1
endwhile
return ret
endfunction
call input({'prompt':'>','highlight':'RainbowParens'})
<
Highlight function is called at least once for each new Highlight function is called at least once for each new
displayed input string, before command-line is redrawn. It is displayed input string, before command-line is redrawn. It is
expected that function is pure for the duration of one input() expected that function is pure for the duration of one input()

View File

@ -449,12 +449,6 @@ changed. To avoid the message reset the 'warn' option.
Something inside Vim went wrong and resulted in a NULL pointer. If you know Something inside Vim went wrong and resulted in a NULL pointer. If you know
how to reproduce this problem, please report it. |bugs| how to reproduce this problem, please report it. |bugs|
*E172* >
Only one file name allowed
The ":edit" command only accepts one file name. When you want to specify
several files for editing use ":next" |:next|.
*E41* *E82* *E83* *E342* > *E41* *E82* *E83* *E342* >
Out of memory! Out of memory!
Out of memory! (allocating {number} bytes) Out of memory! (allocating {number} bytes)

View File

@ -6267,11 +6267,11 @@ A jump table for the options with a short description can be found at |Q_op|.
when part of a command has been typed. when part of a command has been typed.
*'title'* *'notitle'* *'title'* *'notitle'*
'title' boolean (default off, on when title can be restored) 'title' boolean (default off)
global global
When on, the title of the window will be set to the value of When on, the title of the window will be set to the value of
'titlestring' (if it is not empty), or to: 'titlestring' (if it is not empty), or to:
filename [+=-] (path) - VIM filename [+=-] (path) - NVIM
Where: Where:
filename the name of the file being edited filename the name of the file being edited
- indicates the file cannot be modified, 'ma' off - indicates the file cannot be modified, 'ma' off
@ -6279,7 +6279,7 @@ A jump table for the options with a short description can be found at |Q_op|.
= indicates the file is read-only = indicates the file is read-only
=+ indicates the file is read-only and modified =+ indicates the file is read-only and modified
(path) is the path of the file being edited (path) is the path of the file being edited
- VIM the server name |v:servername| or "VIM" - NVIM the server name |v:servername| or "NVIM"
*'titlelen'* *'titlelen'*
'titlelen' number (default 85) 'titlelen' number (default 85)
@ -6295,11 +6295,10 @@ A jump table for the options with a short description can be found at |Q_op|.
'titlelen' is also used for the 'titlestring' option. 'titlelen' is also used for the 'titlestring' option.
*'titleold'* *'titleold'*
'titleold' string (default "Thanks for flying Vim") 'titleold' string (default "")
global global
This option will be used for the window title when exiting Vim if the If not empty, this option will be used to set the window title when
original title cannot be restored. Only happens if 'title' is on or exiting. Only if 'title' is enabled.
'titlestring' is not empty.
This option cannot be set from a |modeline| or in the |sandbox|, for This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons. security reasons.
*'titlestring'* *'titlestring'*
@ -6748,19 +6747,19 @@ A jump table for the options with a short description can be found at |Q_op|.
*'winhighlight'* *'winhl'* *'winhighlight'* *'winhl'*
'winhighlight' 'winhl' string (default empty) 'winhighlight' 'winhl' string (default empty)
local to window local to window
Window-local highlights. Comma-delimited list of |group-name| pairs Window-local highlights. Comma-delimited list of highlight
"{hl-builtin}:{hl-group},..." where each {hl-builtin} is a group (from |group-name| pairs "{hl-builtin}:{hl},..." where each {hl-builtin} is
|highlight-groups|) to be overridden by {hl-group} in the window where a built-in |highlight-groups| item to be overridden by {hl} group in
this option was set. Only builting ui highlights are supported, not the window. Only built-in |highlight-groups| are supported, not
syntax highlighting. For that purpose, use |:ownsyntax|. syntax highlighting (use |:ownsyntax| for that).
Most highlights occuring within the frame of a window are supported.
Highlights of vertical separators are determined by the window to the Highlights of vertical separators are determined by the window to the
left of the separator. The highlight of a tabpage in |tabline| is left of the separator. The highlight of a tabpage in |tabline| is
determined by the last focused window in the tabpage. Highlights of determine by the last-focused window of the tabpage. Highlights of
the popupmenu are determined by the current window. Highlights in the the popupmenu are determined by the current window. Highlights in the
message area are not overridable. Example for overriding the message area cannot be overridden.
backgrond color: >
Example: show a different color for non-current windows: >
set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC
< <
*'winfixheight'* *'wfh'* *'nowinfixheight'* *'nowfh'* *'winfixheight'* *'wfh'* *'nowinfixheight'* *'nowfh'*

View File

@ -64,11 +64,11 @@ health#report_info({msg}) *health#report_info*
health#report_ok({msg}) *health#report_ok* health#report_ok({msg}) *health#report_ok*
Displays a "success" message. Displays a "success" message.
health#report_warn({msg}, [{suggestions}]) *health#report_warn* health#report_warn({msg}, [{advice}]) *health#report_warn*
Displays a warning. {suggestions} is an optional List of suggestions. Displays a warning. {advice} is an optional List of suggestions.
health#report_error({msg}, [{suggestions}]) *health#report_error* health#report_error({msg}, [{advice}]) *health#report_error*
Displays an error. {suggestions} is an optional List of suggestions. Displays an error. {advice} is an optional List of suggestions.
health#{plugin}#check() *health.user_checker* health#{plugin}#check() *health.user_checker*
This is the form of a healthcheck definition. Call the above functions This is the form of a healthcheck definition. Call the above functions

View File

@ -3270,11 +3270,10 @@ improve screen updating rates (see |:syn-sync| for more on this). >
The g:vimsyn_embed option allows users to select what, if any, types of The g:vimsyn_embed option allows users to select what, if any, types of
embedded script highlighting they wish to have. > embedded script highlighting they wish to have. >
g:vimsyn_embed == 0 : don't embed any scripts g:vimsyn_embed == 0 : disable (don't embed any scripts)
g:vimsyn_embed =~ 'P' : support embedded python g:vimsyn_embed == 'lPr' : support embedded lua, python and ruby
< <
By default, g:vimsyn_embed is a string supporting interpreters that your vim This option is disabled by default.
itself supports.
*g:vimsyn_folding* *g:vimsyn_folding*
Some folding is now supported with syntax/vim.vim: > Some folding is now supported with syntax/vim.vim: >

View File

@ -41,16 +41,17 @@ $TERM is also important because it is mirrored by SSH to the remote session,
unlike other common client-end environment variables ($COLORTERM, unlike other common client-end environment variables ($COLORTERM,
$XTERM_VERSION, $VTE_VERSION, $KONSOLE_PROFILE_NAME, $TERM_PROGRAM, ...). $XTERM_VERSION, $VTE_VERSION, $KONSOLE_PROFILE_NAME, $TERM_PROGRAM, ...).
For this terminal Set $TERM to |builtin-terms|? For this terminal Set $TERM to |builtin-terms|
-------------------------------------------------------------------------
iTerm.app "iterm" or "iTerm.app" Y iTerm (original) iterm, iTerm.app N
anything libvte based "vte" or "vte-256color" Y iTerm2 (new capabilities) iterm2, iTerm2.app Y
(e.g. GNOME Terminal) (aliases: "gnome", "gnome-256color") anything libvte-based vte, vte-256color Y
tmux "tmux" or "tmux-256color" Y (e.g. GNOME Terminal) (aliases: gnome, gnome-256color)
screen "screen" or "screen-256color" Y tmux tmux, tmux-256color Y
PuTTY "putty" or "putty-256color" Y screen screen, screen-256color Y
Terminal.app "nsterm" N PuTTY putty, putty-256color Y
Linux virtual terminal "linux" or "linux-256color" Y Terminal.app nsterm N
Linux virtual terminal linux, linux-256color Y
*builtin-terms* *builtin_terms* *builtin-terms* *builtin_terms*
If a |terminfo| database is not available, or no entry for the terminal type is If a |terminfo| database is not available, or no entry for the terminal type is

View File

@ -212,7 +212,6 @@ g8 Print the hex values of the bytes used in the
Equivalent to: > Equivalent to: >
:enew :enew
:call termopen('{cmd}') :call termopen('{cmd}')
:startinsert
< <
See |termopen()|. See |termopen()|.

View File

@ -6,9 +6,8 @@
Differences between Nvim and Vim *vim-differences* Differences between Nvim and Vim *vim-differences*
Throughout the help files, differences between Nvim and Vim are indicated via Nvim differs from Vim in many ways, big and small. This document is
the "{Nvim}" tag. This document is a complete and centralized list of all a complete and centralized reference of those differences.
these differences.
Type <M-]> to see the table of contents. Type <M-]> to see the table of contents.
@ -72,12 +71,18 @@ Clipboard integration |provider-clipboard|
USER EXPERIENCE ~ USER EXPERIENCE ~
Working intuitively and consistently is a major goal of Nvim. Examples: Working intuitively and consistently is a major goal of Nvim.
- Nvim does not have `-X`, a platform-specific option "sometimes" available in *feature-compile*
Vim (with potential surprises: http://stackoverflow.com/q/14635295). Nvim - Nvim always includes ALL features, in contrast to Vim (which ships with
avoids features that cannot be provided on all platforms--instead that is various combinations of 100+ optional features). Think of it as a leaner
delegated to external plugins/extensions. version of Vim's "HUGE" build. This reduces surface area for bugs, and
removes a common source of confusion and friction for users.
- Nvim avoids features that cannot be provided on all platforms; instead that
is delegated to external plugins/extensions. E.g. the `-X` platform-specific
option is "sometimes" available in Vim (with potential surprises:
http://stackoverflow.com/q/14635295).
- Vim's internal test functions (test_autochdir(), test_settime(), etc.) are - Vim's internal test functions (test_autochdir(), test_settime(), etc.) are
not exposed (nor implemented); instead Nvim has a robust API. not exposed (nor implemented); instead Nvim has a robust API.
@ -268,13 +273,16 @@ Lua interface (|if_lua.txt|):
- Lua has direct access to Nvim |API| via `vim.api`. - Lua has direct access to Nvim |API| via `vim.api`.
- Lua package.path and package.cpath are automatically updated according to - Lua package.path and package.cpath are automatically updated according to
'runtimepath': |lua-require|. 'runtimepath': |lua-require|.
- Currently, most legacy Vim features are missing.
|input()| and |inputdialog()| gained support for each others features (return |input()| and |inputdialog()| support for each others features (return on
on cancel and completion respectively) via dictionary argument (replaces all cancel and completion respectively) via dictionary argument (replaces all
other arguments if used). other arguments if used).
|input()| and |inputdialog()| now support user-defined cmdline highlighting. |input()| and |inputdialog()| support user-defined cmdline highlighting.
Highlight groups:
|hl-ColorColumn|, |hl-CursorColumn|, |hl-CursorLine| are lower priority than
(overridden by) most other highlight groups.
============================================================================== ==============================================================================
5. Missing legacy features *nvim-features-missing* 5. Missing legacy features *nvim-features-missing*
@ -282,7 +290,7 @@ other arguments if used).
Some legacy Vim features are not implemented: Some legacy Vim features are not implemented:
- |if_py|: vim.bindeval() and vim.Function() are not supported - |if_py|: vim.bindeval() and vim.Function() are not supported
- |if_lua|: the `vim` object currently only supports `vim.api` - |if_lua|: the `vim` object is missing most legacy methods
- *if_perl* - *if_perl*
- *if_mzscheme* - *if_mzscheme*
- *if_tcl* - *if_tcl*
@ -290,7 +298,7 @@ Some legacy Vim features are not implemented:
============================================================================== ==============================================================================
6. Removed features *nvim-features-removed* 6. Removed features *nvim-features-removed*
These features are in Vim, but have been intentionally removed from Nvim. These Vim features were intentionally removed from Nvim.
*'cp'* *'nocompatible'* *'nocp'* *'compatible'* *'cp'* *'nocompatible'* *'nocp'* *'compatible'*
Nvim is always "non-compatible" with Vi. Nvim is always "non-compatible" with Vi.

View File

@ -3,8 +3,8 @@
" ############################################################################# " #############################################################################
" ############################################################################# " #############################################################################
" Note: Be careful when merging the upstream version of this file. " Note: Be careful when merging the upstream version of this file.
" Much of this is generated by scripts/genvimvim.lua (result is installed " Much of this is generated by scripts/genvimvim.lua
" to: $VIMRUNTIME/syntax/vim/generated.vim) " (installs to $VIMRUNTIME/syntax/vim/generated.vim)
" ############################################################################# " #############################################################################
" ############################################################################# " #############################################################################
@ -128,6 +128,13 @@ if exists("g:vimsyntax_noerror")
let g:vimsyn_noerror= g:vimsyntax_noerror let g:vimsyn_noerror= g:vimsyntax_noerror
endif endif
" Variable options {{{2
if exists("g:vim_maxlines")
let s:vimsyn_maxlines= g:vim_maxlines
else
let s:vimsyn_maxlines= 60
endif
" Numbers {{{2 " Numbers {{{2
" ======= " =======
syn match vimNumber "\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=" skipwhite nextgroup=vimGlobal,vimSubst,vimCommand syn match vimNumber "\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=" skipwhite nextgroup=vimGlobal,vimSubst,vimCommand
@ -601,7 +608,7 @@ syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' e
" g:vimsyn_embed =~# 'r' : embed ruby " g:vimsyn_embed =~# 'r' : embed ruby
" g:vimsyn_embed =~# 't' : embed tcl " g:vimsyn_embed =~# 't' : embed tcl
if !exists("g:vimsyn_embed") if !exists("g:vimsyn_embed")
let g:vimsyn_embed= "lpPr" let g:vimsyn_embed= 0
endif endif
" [-- lua --] {{{3 " [-- lua --] {{{3
@ -744,9 +751,10 @@ if !filereadable(s:mzschemepath)
endif endif
if g:vimsyn_embed =~# 'm' && filereadable(s:mzschemepath) if g:vimsyn_embed =~# 'm' && filereadable(s:mzschemepath)
unlet! b:current_syntax unlet! b:current_syntax
let iskKeep= &isk let s:iskKeep= &isk
exe "syn include @vimMzSchemeScript ".s:mzschemepath exe "syn include @vimMzSchemeScript ".s:mzschemepath
let &isk= iskKeep let &isk= s:iskKeep
unlet s:iskKeep
VimFoldm syn region vimMzSchemeRegion matchgroup=vimScriptDelim start=+mz\%[scheme]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimMzSchemeScript VimFoldm syn region vimMzSchemeRegion matchgroup=vimScriptDelim start=+mz\%[scheme]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimMzSchemeScript
VimFoldm syn region vimMzSchemeRegion matchgroup=vimScriptDelim start=+mz\%[scheme]\s*<<\s*$+ end=+\.$+ contains=@vimMzSchemeScript VimFoldm syn region vimMzSchemeRegion matchgroup=vimScriptDelim start=+mz\%[scheme]\s*<<\s*$+ end=+\.$+ contains=@vimMzSchemeScript
syn cluster vimFuncBodyList add=vimMzSchemeRegion syn cluster vimFuncBodyList add=vimMzSchemeRegion
@ -761,11 +769,7 @@ unlet s:mzschemepath
if exists("g:vimsyn_minlines") if exists("g:vimsyn_minlines")
exe "syn sync minlines=".g:vimsyn_minlines exe "syn sync minlines=".g:vimsyn_minlines
endif endif
if exists("g:vimsyn_maxlines") exe "syn sync maxlines=".s:vimsyn_maxlines
exe "syn sync maxlines=".g:vimsyn_maxlines
else
syn sync maxlines=60
endif
syn sync linecont "^\s\+\\" syn sync linecont "^\s\+\\"
syn sync match vimAugroupSyncA groupthere NONE "\<aug\%[roup]\>\s\+[eE][nN][dD]" syn sync match vimAugroupSyncA groupthere NONE "\<aug\%[roup]\>\s\+[eE][nN][dD]"
@ -778,7 +782,7 @@ if !exists("skip_vim_syntax_inits")
hi def link vimBehaveError vimError hi def link vimBehaveError vimError
hi def link vimCollClassErr vimError hi def link vimCollClassErr vimError
hi def link vimErrSetting vimError hi def link vimErrSetting vimError
hi def link vimEmbedError vimError hi def link vimEmbedError Normal
hi def link vimFTError vimError hi def link vimFTError vimError
hi def link vimFunctionError vimError hi def link vimFunctionError vimError
hi def link vimFunc vimError hi def link vimFunc vimError
@ -935,6 +939,11 @@ if !exists("skip_vim_syntax_inits")
hi def link vimUserFunc Normal hi def link vimUserFunc Normal
hi def link vimVar Identifier hi def link vimVar Identifier
hi def link vimWarn WarningMsg hi def link vimWarn WarningMsg
hi def link nvimAutoEvent vimAutoEvent
hi def link nvimHLGroup vimHLGroup
hi def link nvimMap vimMap
hi def link nvimUnmap vimUnmap
endif endif
" Current Syntax Variable: {{{2 " Current Syntax Variable: {{{2

View File

@ -23,7 +23,7 @@ relevant help (hopefully), and pressing K over any word will try to do so too.
Sometimes you will be required to modify text like Sometimes you will be required to modify text like
this here this here
Once you have done the changes correctly, the ✗ sign at the left will change 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 ;) 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): Other times, you'll be prompted to run a command (I'll explain this later):
~~~ cmd ~~~ cmd
:help <Enter> :help <Enter>
@ -36,30 +36,30 @@ or press a sequence of keys
Text within <'s and >'s (like `<Enter>`{normal}) describes a key to press instead of text Text within <'s and >'s (like `<Enter>`{normal}) describes a key to press instead of text
to type. to type.
Now, move to the next lesson (remember, use j). Now, move to the next lesson (remember, use the `j`{normal} key to scroll down).
## Lesson 1.1: MOVING THE CURSOR ## Lesson 1.1: MOVING THE CURSOR
** To move the cursor, press the `h`, `j`, `k`, `l` keys as indicated. ** ** To move the cursor, press the `h`, `j`, `k`, `l` keys as indicated. **
k Hint: The h key is at the left and moves left. k Hint: The `h`{normal} key is at the left and moves left.
← h l → The l key is at the right and moves right. ← h l → The `l`{normal} key is at the right and moves right.
j The j key looks like a down arrow. j The `j`{normal} key looks like a down arrow.
1. Move the cursor around the screen until you are comfortable. 1. Move the cursor around the screen until you are comfortable.
2. Hold down the down key (j) until it repeats. 2. Hold down the down key (`j`{normal}) until it repeats.
Now you know how to move to the next lesson. Now you know how to move to the next lesson.
3. Using the down key, move to Lesson 1.2. 3. Using the down key, move to Lesson 1.2.
NOTE: If you are ever unsure about something you typed, press <Esc> to place NOTE: If you are ever unsure about something you typed, press <Esc> to place
you in Normal mode. Then retype the command you wanted. you in Normal mode. Then retype the command you wanted.
NOTE: The cursor keys should also work. But using hjkl you will be able to NOTE: The cursor keys should also work. But using hjkl you will be able to
move around much faster, once you get used to it. Really! move around much faster, once you get used to it. Really!
# Lesson 1.2: EXITING VIM # Lesson 1.2: EXITING VIM
@ -81,7 +81,7 @@ NOTE: The cursor keys should also work. But using hjkl you will be able to
4. If you have these steps memorized and are confident, execute steps 4. If you have these steps memorized and are confident, execute steps
1 through 3 to exit and re-enter the editor. 1 through 3 to exit and re-enter the editor.
NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you
will learn how to save the changes to a file. will learn how to save the changes to a file.
5. Move the cursor down to Lesson 1.3. 5. Move the cursor down to Lesson 1.3.
@ -90,18 +90,18 @@ NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you
** Press `x`{normal} to delete the character under the cursor. ** ** Press `x`{normal} to delete the character under the cursor. **
1. Move the cursor to the line below marked --->. 1. Move the cursor to the line below marked --->.
2. To fix the errors, move the cursor until it is on top of the 2. To fix the errors, move the cursor until it is on top of the
character to be deleted. character to be deleted.
3. Press [the x key](x) to delete the unwanted character. 3. Press [the x key](x) to delete the unwanted character.
4. Repeat steps 2 through 4 until the sentence is correct. 4. Repeat steps 2 through 4 until the sentence is correct.
The ccow jumpedd ovverr thhe mooon. The ccow jumpedd ovverr thhe mooon.
5. Now that the line is correct, go on to Lesson 1.4. 5. Now that the line is correct, go on to Lesson 1.4.
NOTE: As you go through this tutor, do not try to memorize, learn by usage. NOTE: As you go through this tutor, do not try to memorize, learn by usage.
@ -114,15 +114,15 @@ NOTE: As you go through this tutor, do not try to memorize, learn by usage.
2. To make the first line the same as the second, move the cursor on top 2. To make the first line the same as the second, move the cursor on top
of the first character AFTER where the text is to be inserted. of the first character AFTER where the text is to be inserted.
3. Press i and type in the necessary additions. 3. Press `i`{normal} and type in the necessary additions.
4. As each error is fixed press <Esc> to return to Normal mode. 4. As each error is fixed press `<Esc>`{normal} to return to Normal mode.
Repeat steps 2 through 4 to correct the sentence. Repeat steps 2 through 4 to correct the sentence.
There is text misng this . There is text misng this .
There is some text missing from this line. There is some text missing from this line.
5. When you are comfortable inserting text move to lesson 1.5. 5. When you are comfortable inserting text move to Lesson 1.5.
# Lesson 1.5: TEXT EDITING: APPENDING # Lesson 1.5: TEXT EDITING: APPENDING
@ -131,9 +131,9 @@ There is some text missing from this line.
1. Move the cursor to the first line below marked --->. 1. Move the cursor to the first line below marked --->.
It does not matter on what character the cursor is in that line. It does not matter on what character the cursor is in that line.
2. Press [A](A) and type in the necessary additions. 2. Press [A](A) and type in the necessary additions.
3. As the text has been appended press <Esc> to return to Normal mode. 3. As the text has been appended press `<Esc>`{normal} to return to Normal mode.
4. Move the cursor to the second line marked ---> and repeat 4. Move the cursor to the second line marked ---> and repeat
steps 2 and 3 to correct this sentence. steps 2 and 3 to correct this sentence.
@ -143,7 +143,7 @@ There is some text missing from this line.
There is also some text miss There is also some text miss
There is also some text missing here. There is also some text missing here.
5. When you are comfortable appending text move to lesson 1.6. 5. When you are comfortable appending text move to Lesson 1.6.
# Lesson 1.6: EDITING A FILE # Lesson 1.6: EDITING A FILE
@ -151,7 +151,7 @@ There is also some text missing here.
!! NOTE: Before executing any of the steps below, read this entire lesson !! !! NOTE: Before executing any of the steps below, read this entire lesson !!
1. Exit this tutor as you did in lesson 1.2: :q! 1. Exit this tutor as you did in Lesson 1.2: `:q!`{vim}
Or, if you have access to another terminal, do the following there. Or, if you have access to another terminal, do the following there.
2. At the shell prompt type this command: 2. At the shell prompt type this command:
@ -159,7 +159,7 @@ There is also some text missing here.
$ vim tutor $ vim tutor
~~~ ~~~
'vim' is the command to start the Vim editor, 'tutor' is the name of the 'vim' is the command to start the Vim editor, 'tutor' is the name of the
file you wish to edit. Use a file that may be changed. file you wish to edit. Use a file that may be changed.
3. Insert and delete text as you learned in the previous lessons. 3. Insert and delete text as you learned in the previous lessons.
@ -186,14 +186,14 @@ There is also some text missing here.
$ vim FILENAME $ vim FILENAME
~~~ ~~~
3. To exit Vim type: <Esc> :q! <Enter> to trash all changes. 3. To exit Vim type: `<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} to trash all changes.
OR type: <Esc> :wq <Enter> to save the changes. OR type: `<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} to save the changes.
4. To delete the character at the cursor type: `x`{normal} 4. To delete the character at the cursor type: `x`{normal}
5. To insert or append text type: 5. To insert or append text type:
`i`{normal} type inserted text `<Esc>`{normal} insert before the cursor `i`{normal} insert text `<Esc>`{normal} insert before the cursor.
`A`{normal} type appended text `<Esc>`{normal} append after the line `A`{normal} append text `<Esc>`{normal} append after the line.
NOTE: Pressing `<Esc>`{normal} will place you in Normal mode or will cancel NOTE: Pressing `<Esc>`{normal} will place you in Normal mode or will cancel
an unwanted and partially completed command. an unwanted and partially completed command.
@ -210,7 +210,7 @@ Now continue with Lesson 2.
3. Move the cursor to the beginning of a word that needs to be deleted. 3. Move the cursor to the beginning of a word that needs to be deleted.
4. Type [d](d)[w](w) to make the word disappear. 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. There are a some words fun that don't belong paper in this sentence.
@ -218,9 +218,9 @@ There are a some words fun that don't belong paper in this sentence.
# Lesson 2.2: MORE DELETION COMMANDS # Lesson 2.2: MORE DELETION COMMANDS
** Type `d$`{normal} to delete to the end of the line. ** ** Type `d$`{normal} to delete to the end of the line. **
1. Press <Esc> to make sure you are in Normal mode. 1. Press `<Esc>`{normal} to make sure you are in Normal mode.
2. Move the cursor to the line below marked --->. 2. Move the cursor to the line below marked --->.
@ -236,7 +236,7 @@ Somebody typed the end of this line twice. end of this line twice.
Many commands that change text are made from an [operator](operator) and a [motion](navigation). Many commands that change text are made from an [operator](operator) and a [motion](navigation).
The format for a delete command with the [d](d) delete operator is as follows: The format for a delete command with the [d](d) delete operator is as follows:
d motion d motion
@ -249,7 +249,7 @@ The format for a delete command with the [d](d) delete operator is as follows:
[e](e) - to the end of the current word, INCLUDING the last character. [e](e) - to the end of the current word, INCLUDING the last character.
[$]($) - to the end of the line, INCLUDING the last character. [$]($) - to the end of the line, INCLUDING the last character.
Thus typing `de`{normal} will delete from the cursor to the end of the word. Thus typing `de`{normal} will delete from the cursor to the end of the word.
NOTE: Pressing just the motion while in Normal mode without an operator will NOTE: Pressing just the motion while in Normal mode without an operator will
move the cursor as specified. move the cursor as specified.
@ -260,11 +260,11 @@ NOTE: Pressing just the motion while in Normal mode without an operator will
1. Move the cursor to the start of the line marked ---> below. 1. Move the cursor to the start of the line marked ---> below.
2. Type `2w`{normal} to move the cursor two words forward. 2. Type `2w`{normal} to move the cursor two words forward.
3. Type `3e`{normal} to move the cursor to the end of the third word forward. 3. Type `3e`{normal} to move the cursor to the end of the third word forward.
4. Type `0`{normal} ([zero](0)) to move to the start of the line. 4. Type `0`{normal} ([zero](0)) to move to the start of the line.
5. Repeat steps 2 and 3 with different numbers. 5. Repeat steps 2 and 3 with different numbers.
@ -282,22 +282,22 @@ insert a count before the motion to delete more:
1. Move the cursor to the first UPPER CASE word in the line marked --->. 1. Move the cursor to the first UPPER CASE word in the line marked --->.
2. Type `d2w`{normal} to delete the two UPPER CASE words 2. Type `d2w`{normal} to delete the two UPPER CASE words
3. Repeat steps 1 and 2 with a different count to delete the consecutive 3. Repeat steps 1 and 2 with a different count to delete the consecutive
UPPER CASE words with one command UPPER CASE words with one command
this ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up. This ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up.
# Lesson 2.6: OPERATING ON LINES # Lesson 2.6: OPERATING ON LINES
** Type dd to delete a whole line. ** ** Type `dd`{normal} to delete a whole line. **
Due to the frequency of whole line deletion, the designers of Vi decided Due to the frequency of whole line deletion, the designers of Vi decided
it would be easier to simply type two d's to delete a line. it would be easier to simply type two d's to delete a line.
1. Move the cursor to the second line in the phrase below. 1. Move the cursor to the second line in the phrase below.
2. Type [dd](dd) to delete the line. 2. Type [dd](dd) to delete the line.
3. Now move to the fourth line. 3. Now move to the fourth line.
4. Type `2dd`{normal} to delete two lines. 4. Type `2dd`{normal} to delete two lines.
@ -311,20 +311,20 @@ this ABC DE line FGHI JK LMN OP of words is Q RS TUV cleaned up.
# Lesson 2.7: THE UNDO COMMAND # Lesson 2.7: THE UNDO COMMAND
** Press u to undo the last commands, U to fix a whole line. ** ** Press `u`{normal} to undo the last commands, `U`{normal} to fix a whole line. **
1. Move the cursor to the line below marked ---> and place it on the 1. Move the cursor to the line below marked ---> and place it on the
first error. first error.
2. Type `x`{normal} to delete the first unwanted character. 2. Type `x`{normal} to delete the first unwanted character.
3. Now type `u`{normal} to undo the last command executed. 3. Now type `u`{normal} to undo the last command executed.
4. This time fix all the errors on the line using the `x`{normal} command. 4. This time fix all the errors on the line using the `x`{normal} command.
5. Now type a capital `U`{normal} to return the line to its original state. 5. Now type a capital `U`{normal} to return the line to its original state.
6. Now type `u`{normal} a few times to undo the U and preceding commands. 6. Now type `u`{normal} a few times to undo the `U`{normal} and preceding commands.
7. Now type `<Ctrl-r>`{normal} a few times to redo the commands (undo the undo's). 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. 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. 8. These are very useful commands. Now move on to the Lesson 2 Summary.
# Lesson 2 SUMMARY # Lesson 2 SUMMARY
@ -336,13 +336,13 @@ Fiix the errors oon thhis line and reeplace them witth undo.
5. The format for a change command is: 5. The format for a change command is:
operator [number] motion operator [number] motion
where: where:
operator - is what to do, such as [d](d) for delete operator - is what to do, such as [d](d) for delete
[number] - is an optional count to repeat the motion [number] - is an optional count to repeat the motion
motion - moves over the text to operate on, such as: motion - moves over the text to operate on, such as:
[w](w) (word), [w](w) (word),
[$]($) (to the end of line), etc. [$]($) (to the end of line), etc.
6. To move to the start of the line use a zero: [0](0) 6. To move to the start of the line use a zero: [0](0)
7. To undo previous actions, type: `u`{normal} (lowercase u) 7. To undo previous actions, type: `u`{normal} (lowercase u)
To undo all the changes on a line, type: `U`{normal} (capital U) To undo all the changes on a line, type: `U`{normal} (capital U)
@ -350,15 +350,15 @@ Fiix the errors oon thhis line and reeplace them witth undo.
# Lesson 3.1: THE PUT COMMAND # Lesson 3.1: THE PUT COMMAND
** Type p to put previously deleted text after the cursor. ** ** Type `p`{normal} to put previously deleted text after the cursor. **
1. Move the cursor to the first ---> line below. 1. Move the cursor to the first ---> line below.
2. Type `dd`{normal} to delete the line and store it in a Vim register. 2. Type `dd`{normal} to delete the line and store it in a Vim register.
3. Move the cursor to the c) line, ABOVE where the deleted line should go. 3. Move the cursor to the c) line, ABOVE where the deleted line should go.
4. Type `p`{normal} to put the line below the cursor. 4. Type `p`{normal} to put the line below the cursor.
5. Repeat steps 2 through 4 to put all the lines in correct order. 5. Repeat steps 2 through 4 to put all the lines in correct order.
@ -388,11 +388,11 @@ NOTE: Remember that you should be learning by doing, not memorization.
# Lesson 3.3: THE CHANGE OPERATOR # Lesson 3.3: THE CHANGE OPERATOR
** To change until the end of a word, type `ce`{normal} ** ** To change until the end of a word, type `ce`{normal}. **
1. Move the cursor to the first line below marked --->. 1. Move the cursor to the first line below marked --->.
2. Place the cursor on the "u" in "lubw". 2. Place the cursor on the "u" in "lubw".
3. Type `ce`{normal} and the correct word (in this case, type "ine" ). 3. Type `ce`{normal} and the correct word (in this case, type "ine" ).
@ -405,11 +405,11 @@ 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. Notice that [c](c)e deletes the word and places you in Insert mode.
# Lesson 3.4: MORE CHANGES USING c # Lesson 3.4: MORE CHANGES USING `c`{normal}
** The change operator is used with the same motions as delete. ** ** The change operator is used with the same motions as delete. **
1. The change operator works in the same way as delete. The format is: 1. The change operator works in the same way as delete. The format is:
c [number] motion c [number] motion
@ -422,13 +422,13 @@ 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}. 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. 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. The end of this line needs to be corrected using the `c$`{normal} command.
NOTE: You can use the Backspace key to correct mistakes while typing. NOTE: You can use the Backspace key to correct mistakes while typing.
# Lesson 3 SUMMARY # Lesson 3 SUMMARY
1. To put back text that has just been deleted, type [p](p). This puts the 1. To put back text that has just been deleted, type [p](p). This puts the
deleted text AFTER the cursor (if a line was deleted it will go on the deleted text AFTER the cursor (if a line was deleted it will go on the
line below the cursor). line below the cursor).
@ -436,8 +436,8 @@ NOTE: You can use the Backspace key to correct mistakes while typing.
character you want to have there. character you want to have there.
3. The [change operator](c) allows you to change from the cursor to where the 3. The [change operator](c) allows you to change from the cursor to where the
motion takes you. eg. Type `ce`{normal} to change from the cursor to the end of motion takes you. Type `ce`{normal} to change from the cursor to the end of
the word, `c$`{normal} to change to the end of a line. the word, `c$`{normal} to change to the end of a line.
4. The format for change is: 4. The format for change is:
@ -448,21 +448,21 @@ Now go on to the next lesson.
# Lesson 4.1: CURSOR LOCATION AND FILE STATUS # Lesson 4.1: CURSOR LOCATION AND FILE STATUS
** Type `<Ctrl-g>`{normal} to show your location in the file and the file status. ** Type `<Ctrl-g>`{normal} to show your location in the file and the file status.
Type `G`{normal} to move to a line in the file. ** Type `G`{normal} to move to a line in the file. **
NOTE: Read this entire lesson before executing any of the steps!! NOTE: Read this entire lesson before executing any of the steps!!
1. Hold down the `<Ctrl>`{normal} key and press `g`{normal}. We call this `<Ctrl-g>`{normal}. 1. Hold down the `<Ctrl>`{normal} key and press `g`{normal}. We call this `<Ctrl-g>`{normal}.
A message will appear at the bottom of the page with the filename and the A message will appear at the bottom of the page with the filename and the
position in the file. Remember the line number for Step 3. position in the file. Remember the line number for Step 3.
NOTE: You may see the cursor position in the lower right corner of the screen NOTE: You may see the cursor position in the lower right corner of the screen
This happens when the ['ruler']('ruler') option is set (see :help 'ruler' ) This happens when the ['ruler']('ruler') option is set (see `:help 'ruler'`{vim} ).
2. Press [G](G) to move you to the bottom of the file. 2. Press [G](G) to move you to the bottom of the file.
Type [gg](gg) to move you to the start of the file. Type [gg](gg) to move you to the start of the file.
3. Type the number of the line you were on and then `G`{normal} . This will 3. Type the number of the line you were on and then `G`{normal}. This will
return you to the line you were on when you first pressed `<Ctrl-g>`{normal}. return you to the line you were on when you first pressed `<Ctrl-g>`{normal}.
4. If you feel confident to do this, execute steps 1 through 3. 4. If you feel confident to do this, execute steps 1 through 3.
@ -471,20 +471,20 @@ NOTE: You may see the cursor position in the lower right corner of the screen
** Type `/`{normal} followed by a phrase to search for the phrase. ** ** Type `/`{normal} followed by a phrase to search for the phrase. **
1. In Normal mode type the `/`{normal} character. Notice that it and the cursor 1. In Normal mode type the `/`{normal} character. Notice that it and the cursor
appear at the bottom of the screen as with the : command. appear at the bottom of the screen as with the `:`{normal} command.
2. Now type 'errroor' `<Enter>`{normal}. This is the word you want to search for. 2. Now type 'errroor' `<Enter>`{normal}. This is the word you want to search for.
3. To search for the same phrase again, simply type [n](n) . 3. To search for the same phrase again, simply type [n](n).
To search for the same phrase in the opposite direction, type [N](N) . To search for the same phrase in the opposite direction, type [N](N).
4. To search for a phrase in the backward direction, use [?](?) instead of / . 4. To search for a phrase in the backward direction, use [?](?) instead of `/`{normal}.
5. To go back to where you came from press `<Ctrl-o>`{normal} (Keep Ctrl down while 5. To go back to where you came from press `<Ctrl-o>`{normal} (keep `<Ctrl>`{normal} pressed down while
pressing the letter o). Repeat to go back further. `<Ctrl-i>`{normal} goes forward. pressing the letter `o`{normal}). Repeat to go back further. `<Ctrl-i>`{normal} goes forward.
"errroor" is not the way to spell error; errroor is an error. "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 NOTE: When the search reaches the end of the file it will continue at the
start, unless the ['wrapscan']('wrapscan') option has been reset. start, unless the ['wrapscan']('wrapscan') option has been reset.
@ -495,7 +495,7 @@ NOTE: When the search reaches the end of the file it will continue at the
1. Place the cursor on any (, [, or { in the line below marked --->. 1. Place the cursor on any (, [, or { in the line below marked --->.
2. Now type the [%](%) character. 2. Now type the [%](%) character.
3. The cursor will move to the matching parenthesis or bracket. 3. The cursor will move to the matching parenthesis or bracket.
@ -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 Adding the g [flag](:s_flags) means to substitute globally in the line, change
all occurrences of "thee" in the line. all occurrences of "thee" in the line.
thee best time to see thee flowers is in thee spring. Usually thee best time to see thee flowers is in thee spring.
4. To change every occurrence of a character string between two lines, type 4. To change every occurrence of a character string between two lines, type
~~~ cmd ~~~ cmd
@ -589,20 +589,20 @@ thee best time to see thee flowers is in thee spring.
** Type `:!`{vim} followed by an external command to execute that command. ** ** Type `:!`{vim} followed by an external command to execute that command. **
1. Type the familiar command `:`{normal} to set the cursor at the bottom of the 1. Type the familiar command `:`{normal} to set the cursor at the bottom of the
screen. This allows you to enter a command-line command. screen. This allows you to enter a command-line command.
2. Now type the [!](!cmd) (exclamation point) character. This allows you to 2. Now type the [!](!cmd) (exclamation point) character. This allows you to
execute any external shell command. execute any external shell command.
3. As an example type "ls" following the "!" and then hit `<Enter>`{normal}. This 3. As an example type "ls" following the "!" and then hit `<Enter>`{normal}. This
will show you a listing of your directory, just as if you were at the will show you a listing of your directory, just as if you were at the
shell prompt. shell prompt.
NOTE: It is possible to execute any external command this way, also with NOTE: It is possible to execute any external command this way, also with
arguments. arguments.
NOTE: All : commands must be finished by hitting <Enter> NOTE: All `:`{vim} commands must be finished by hitting `<Enter>`{normal}.
From here on we will not always mention it. From here on we will not always mention it.
# Lesson 5.2: MORE ON WRITING FILES # Lesson 5.2: MORE ON WRITING FILES
@ -622,7 +622,7 @@ NOTE: All : commands must be finished by hitting <Enter>
4. This saves the whole file (the Vim Tutor) under the name TEST. 4. This saves the whole file (the Vim Tutor) under the name TEST.
To verify this, type `:!ls`{vim} again to see your directory. To verify this, type `:!ls`{vim} again to see your directory.
NOTE: If you were to exit Vim and start it again with vim TEST , the file NOTE: If you were to exit Vim and start it again with `nvim TEST`, the file
would be an exact copy of the tutor when you saved it. would be an exact copy of the tutor when you saved it.
5. Now remove the file by typing: 5. Now remove the file by typing:
@ -632,14 +632,14 @@ NOTE: If you were to exit Vim and start it again with vim TEST , the file
# Lesson 5.3: SELECTING TEXT TO WRITE # Lesson 5.3: SELECTING TEXT TO WRITE
** To save part of the file, type `v`{normal} motion `:w FILENAME`{vim} ** ** To save part of the file, type `v`{normal} motion `:w FILENAME`{vim}. **
1. Move the cursor to this line. 1. Move the cursor to this line.
2. Press [v](v) and move the cursor to the fifth item below. Notice that the 2. Press [v](v) and move the cursor to the fifth item below. Notice that the
text is highlighted. text is highlighted.
3. Press the `:`{normal} character. At the bottom of the screen 3. Press the `:`{normal} character. At the bottom of the screen
:'<,'> :'<,'>
@ -649,27 +649,27 @@ NOTE: If you were to exit Vim and start it again with vim TEST , the file
`:w TEST`{vim} `:w TEST`{vim}
where TEST is a filename that does not exist yet. Verify that you see where TEST is a filename that does not exist yet. Verify that you see
`:'<,'>w TEST`{vim} `:'<,'>w TEST`{vim}
before you press `<Enter>`{normal}. before you press `<Enter>`{normal}.
5. Vim will write the selected lines to the file TEST. Use `:!ls`{vim} to see it. 5. Vim will write the selected lines to the file TEST. Use `:!ls`{vim} to see it.
Do not remove it yet! We will use it in the next lesson. Do not remove it yet! We will use it in the next lesson.
NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move
the cursor around to make the selection bigger or smaller. Then you can the cursor around to make the selection bigger or smaller. Then you can
use an operator to do something with the text. For example, `d`{normal} deletes use an operator to do something with the text. For example, `d`{normal} deletes
the text. the text.
# Lesson 5.4: RETRIEVING AND MERGING FILES # Lesson 5.4: RETRIEVING AND MERGING FILES
** To insert the contents of a file, type `:r FILENAME`{vim} ** ** To insert the contents of a file, type `:r FILENAME`{vim}. **
1. Place the cursor just above this line. 1. Place the cursor just above this line.
NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move
DOWN to see this lesson again. DOWN to see this lesson again.
2. Now retrieve your TEST file using the command 2. Now retrieve your TEST file using the command
@ -682,31 +682,31 @@ NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move
3. To verify that a file was retrieved, cursor back and notice that there 3. To verify that a file was retrieved, cursor back and notice that there
are now two copies of Lesson 5.3, the original and the file version. are now two copies of Lesson 5.3, the original and the file version.
NOTE: You can also read the output of an external command. For example, NOTE: You can also read the output of an external command. For example,
`:r !ls`{vim} `:r !ls`{vim}
reads the output of the `ls` command and puts it below the cursor. reads the output of the `ls` command and puts it below the cursor.
# Lesson 5 SUMMARY # Lesson 5 SUMMARY
1. [:!command](:!cmd) executes an external command. 1. [:!command](:!cmd) executes an external command.
Some useful examples are: Some useful examples are:
`:!ls`{vim} - shows a directory listing `:!ls`{vim} - shows a directory listing
`:!rm FILENAME`{vim} - removes file FILENAME `:!rm FILENAME`{vim} - removes file FILENAME
2. [:w](:w) FILENAME writes the current Vim file to disk with 2. [:w](:w) FILENAME writes the current Vim file to disk with
name FILENAME. name FILENAME.
3. [v](v) motion :w FILENAME saves the Visually selected lines in file 3. [v](v) motion :w FILENAME saves the Visually selected lines in file
FILENAME. FILENAME.
4. [:r](:r) FILENAME retrieves disk file FILENAME and puts it 4. [:r](:r) FILENAME retrieves disk file FILENAME and puts it
below the cursor position. below the cursor position.
5. [:r !dir](:r!) reads the output of the dir command and 5. [:r !dir](:r!) reads the output of the dir command and
puts it below the cursor position. puts it below the cursor position.
# Lesson 6.1: THE OPEN COMMAND # Lesson 6.1: THE OPEN COMMAND
@ -719,10 +719,10 @@ 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. 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. After typing `o`{normal} 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 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. 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. Open up a line above this by typing O while the cursor is on this line.
@ -734,9 +734,9 @@ Open up a line above this by typing O while the cursor is on this line.
2. Press `e`{normal} until the cursor is on the end of "li". 2. Press `e`{normal} until the cursor is on the end of "li".
3. Type an `a`{normal} (lowercase) to [append](a) text AFTER the cursor. 3. Type the lowercase letter `a`{normal} to [append](a) text AFTER the cursor.
4. Complete the word like the line below it. Press `<Esc>`{normal} to exit Insert 4. Complete the word like the line below it. Press `<Esc>`{normal} to exit Insert
mode. mode.
5. Use `e`{normal} to move to the next incomplete word and repeat steps 3 and 4. 5. Use `e`{normal} to move to the next incomplete word and repeat steps 3 and 4.
@ -744,20 +744,20 @@ Open up a line above this by typing O while the cursor is on this line.
This li will allow you to pract appendi 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. 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 NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only difference is where
the characters are inserted. the characters are inserted.
# Lesson 6.3: ANOTHER WAY TO REPLACE # Lesson 6.3: ANOTHER WAY TO REPLACE
** Type a capital `R`{normal} to replace more than one character. ** ** Type a capital `R`{normal} to replace more than one character. **
1. Move the cursor to the first line below marked --->. Move the cursor to 1. Move the cursor to the first line below marked --->. Move the cursor to
the beginning of the first "xxx". the beginning of the first "xxx".
2. Now press `R`{normal} ([capital R](R)) and type the number below it in the second line, so that it 2. Now press `R`{normal} ([capital R](R)) and type the number below it in the second line, so that it
replaces the "xxx". replaces the "xxx".
3. Press `<Esc>`{normal} to leave [Replace mode](mode-replace). Notice that the rest of the line 3. Press `<Esc>`{normal} to leave [Replace mode](mode-replace). Notice that the rest of the line
remains unmodified. remains unmodified.
4. Repeat the steps to replace the remaining "xxx". 4. Repeat the steps to replace the remaining "xxx".
@ -765,12 +765,12 @@ NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only differ
Adding 123 to xxx gives you xxx. Adding 123 to xxx gives you xxx.
Adding 123 to 456 gives you 579. Adding 123 to 456 gives you 579.
NOTE: Replace mode is like Insert mode, but every typed character deletes an NOTE: Replace mode is like Insert mode, but every typed character deletes an
existing character. existing character.
# Lesson 6.4: COPY AND PASTE TEXT # Lesson 6.4: COPY AND PASTE TEXT
** Use the `y`{normal} operator to copy text and `p`{normal} to paste it ** ** Use the `y`{normal} operator to copy text and `p`{normal} to paste it. **
1. Go to the line marked with ---> below and place the cursor after "a)". 1. Go to the line marked with ---> below and place the cursor after "a)".
@ -780,35 +780,37 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an
4. Move the cursor to the end of the next line: `j$`{normal} 4. Move the cursor to the end of the next line: `j$`{normal}
5. Type `p`{normal} to [put](put) (paste) the text. Then type: "a second"`<Esc>`{normal}. 5. Type `p`{normal} to [put](put) (paste) the text.
6. Use Visual mode to select " item.", yank it with `y`{normal}, move to the end of 6. Press `a`{normal} and then type "second". Press `<Esc>`{normal} to leave Insert mode.
7. 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}. the next line with `j$`{normal} and put the text there with `p`{normal}.
a) this is the first item. a) This is the first item.
b) b)
NOTE: you can also use `y`{normal} as an operator; `yw`{normal} yanks one word. NOTE: you can also use `y`{normal} as an operator; `yw`{normal} yanks one word.
# Lesson 6.5: SET OPTION # Lesson 6.5: SET OPTION
** Set an option so a search or substitute ignores case ** ** Set an option so a search or substitute ignores case. **
1. Search for 'ignore' by entering: `/ignore` 1. Search for 'ignore' by entering: `/ignore`
Repeat several times by pressing `n`{normal}. Repeat several times by pressing `n`{normal}.
2. Set the 'ic' (Ignore case) option by entering: 2. Set the 'ic' (Ignore case) option by entering:
~~~ cmd ~~~ cmd
:set ic :set ic
~~~ ~~~
3. Now search for 'ignore' again by pressing n 3. Now search for 'ignore' again by pressing `n`{normal}.
Notice that Ignore and IGNORE are now also found. Notice that Ignore and IGNORE are now also found.
4. Set the 'hlsearch' and 'incsearch' options: 4. Set the 'hlsearch' and 'incsearch' options:
~~~ cmd ~~~ cmd
:set hls is :set hls is
~~~ ~~~
5. Now type the search command again and see what happens: /ignore <Enter> 5. Now type the search command again and see what happens: /ignore <Enter>
6. To disable ignoring case enter: 6. To disable ignoring case enter:
~~~ cmd ~~~ cmd
@ -818,12 +820,12 @@ a) this is the first item.
~~~ cmd ~~~ cmd
:set invic :set invic
~~~ ~~~
NOTE: To remove the highlighting of matches enter: NOTE: To remove the highlighting of matches enter:
~~~ cmd ~~~ cmd
:nohlsearch :nohlsearch
~~~ ~~~
NOTE: If you want to ignore case for just one search command, use [\c](/\c) NOTE: If you want to ignore case for just one search command, use [\c](/\c)
in the phrase: /ignore\c <Enter> in the phrase: /ignore\c <Enter>
# Lesson 6 SUMMARY # Lesson 6 SUMMARY
@ -839,7 +841,7 @@ NOTE: If you want to ignore case for just one search command, use [\c](/\c)
5. Typing a capital `R`{normal} enters Replace mode until `<Esc>`{normal} is pressed. 5. Typing a capital `R`{normal} enters Replace mode until `<Esc>`{normal} is pressed.
6. Typing "[:set](:set) xxx" sets the option "xxx". Some options are: 6. Typing "[:set](:set) xxx" sets the option "xxx". Some options are:
'ic' 'ignorecase' ignore upper/lower case when searching 'ic' 'ignorecase' ignore upper/lower case when searching
'is' 'incsearch' show partial matches for a search phrase 'is' 'incsearch' show partial matches for a search phrase
@ -858,9 +860,9 @@ NOTE: If you want to ignore case for just one search command, use [\c](/\c)
# Lesson 7.1: GETTING HELP # Lesson 7.1: GETTING HELP
** Use the on-line help system ** ** Use the on-line help system. **
Vim has a comprehensive on-line help system. To get started, try one of Vim has a comprehensive on-line help system. To get started, try one of
these three: these three:
- press the `<HELP>`{normal} key (if you have one) - press the `<HELP>`{normal} key (if you have one)
- press the `<F1>`{normal} key (if you have one) - press the `<F1>`{normal} key (if you have one)
@ -872,7 +874,7 @@ Type `<Ctrl-w><Ctrl-w>`{normal} to jump from one window to another.
Type `:q`{vim} to close the help window. Type `:q`{vim} to close the help window.
You can find help on just about any subject, by giving an argument to the You can find help on just about any subject, by giving an argument to the
":help" command. Try these (don't forget pressing <Enter>): ":help" command. Try these (don't forget pressing <Enter>):
~~~ cmd ~~~ cmd
:help w :help w
:help c_CTRL-D :help c_CTRL-D
@ -881,13 +883,13 @@ You can find help on just about any subject, by giving an argument to the
~~~ ~~~
# Lesson 7.2: CREATE A STARTUP SCRIPT # Lesson 7.2: CREATE A STARTUP SCRIPT
** Enable Vim features ** ** Enable Vim features. **
Vim has many more features than Vi, but most of them are disabled by Vim has many more features than Vi, but most of them are disabled by
default. To start using more features you have to create a "vimrc" file. default. To start using more features you have to create a "vimrc" file.
1. Start editing the "vimrc" file. This depends on your system: 1. Start editing the "vimrc" file. This depends on your system:
`:e ~/.config/nvim/init.vim`{vim} for Unix-like systems `:e ~/.config/nvim/init.vim`{vim} for Unix-like systems
2. Now read the example "vimrc" file contents: 2. Now read the example "vimrc" file contents:
`:r $VIMRUNTIME/vimrc_example.vim`{vim} `:r $VIMRUNTIME/vimrc_example.vim`{vim}
@ -897,15 +899,15 @@ default. To start using more features you have to create a "vimrc" file.
The next time you start Vim it will use syntax highlighting. The next time you start Vim it will use syntax highlighting.
You can add all your preferred settings to this "vimrc" file. You can add all your preferred settings to this "vimrc" file.
For more information type :help vimrc-intro For more information type `:help vimrc-intro`{vim}.
# Lesson 7.3: COMPLETION # Lesson 7.3: COMPLETION
** Command line completion with `<Ctrl-d>`{normal} and `<Tab>`{normal} ** ** Command line completion with `<Ctrl-d>`{normal} and `<Tab>`{normal}. **
1. Look what files exist in the directory: `:!ls`{vim} 1. Look what files exist in the directory: `:!ls`{vim}
2. Type the start of a command: `:e`{vim} 2. Type the start of a command: `:e`{vim}
3. Press `<Ctrl-d>`{normal} and Vim will show a list of commands that start with "e". 3. Press `<Ctrl-d>`{normal} and Vim will show a list of commands that start with "e".
@ -913,20 +915,20 @@ default. To start using more features you have to create a "vimrc" file.
5. Now add a space and the start of an existing file name: `:edit FIL`{vim} 5. Now add a space and the start of an existing file name: `:edit FIL`{vim}
6. Press `<Tab>`{normal}. Vim will complete the name (if it is unique). 6. Press `<Tab>`{normal}. Vim will complete the name (if it is unique).
NOTE: Completion works for many commands. It is especially useful for `:help`{vim}. NOTE: Completion works for many commands. It is especially useful for `:help`{vim}.
# Lesson 7 SUMMARY # Lesson 7 SUMMARY
1. Type `:help`{vim} 1. Type `:help`{vim}
or press `<F1>`{normal} or `<Help>`{normal} to open a help window. or press `<F1>`{normal} or `<Help>`{normal} to open a help window.
2. Type `:help TOPIC`{vim} to find help on TOPIC. 2. Type `:help TOPIC`{vim} to find help on TOPIC.
3. Type `<Ctrl-w><Ctrl-w>`{normal} to jump to another window 3. Type `<Ctrl-w><Ctrl-w>`{normal} to jump to another window
4. Type `:q`{vim} to close the help window 4. Type `:q`{vim} to close the help window
5. Create a vimrc startup script to keep your preferred settings. 5. Create a vimrc startup script to keep your preferred settings.
@ -937,24 +939,24 @@ NOTE: Completion works for many commands. It is especially useful for `:help`{v
This was intended to give a brief overview of the Vim editor, just enough to This was intended to give a brief overview of the Vim editor, just enough to
allow you to use the editor fairly easily. It is far from complete as Vim has allow you to use the editor fairly easily. It is far from complete as Vim has
many many more commands. Consult the help often. many many more commands. Consult the help often.
There are many resources online to learn more about vim. Here's a bunch of them: There are many resources online to learn more about vim. Here's a bunch of them:
- *Learn Vim Progressively*: http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/ - *Learn Vim Progressively*: http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/
- *Learning Vim in 2014*: http://benmccormick.org/learning-vim-in-2014/ - *Learning Vim in 2014*: http://benmccormick.org/learning-vim-in-2014/
- Vimcasts: http://vimcasts.org/ - *Vimcasts*: http://vimcasts.org/
- Vim Video-Tutorials by Derek Wyatt: http://derekwyatt.org/vim/tutorials/ - *Vim Video-Tutorials by Derek Wyatt*: http://derekwyatt.org/vim/tutorials/
- *Learn Vimscript the Hard Way*: http://learnvimscriptthehardway.stevelosh.com/ - *Learn Vimscript the Hard Way*: http://learnvimscriptthehardway.stevelosh.com/
- *7 Habits of Effective Text Editing*: http://www.moolenaar.net/habits.html - *7 Habits of Effective Text Editing*: http://www.moolenaar.net/habits.html
- *vim-galore*: https://github.com/mhinz/vim-galore - *vim-galore*: https://github.com/mhinz/vim-galore
If you prefer a book, *Practical Vim* by Drew Neil is recommended often (the sequel, *Modern If you prefer a book, *Practical Vim* by Drew Neil is recommended often (the sequel, *Modern
Vim*, includes material specific to nvim!). Vim*, includes material specific to nvim).
This tutorial was written by Michael C. Pierce and Robert K. Ware, Colorado This tutorial was written by Michael C. Pierce and Robert K. Ware, Colorado
School of Mines using ideas supplied by Charles Smith, Colorado State School of Mines using ideas supplied by Charles Smith, Colorado State
University. E-mail: bware@mines.colorado.edu. University. E-mail: bware@mines.colorado.edu.
Modified for Vim by Bram Moolenaar. Modified for Vim by Bram Moolenaar.
Modified for vim-tutor-mode by Felipe Morales. Modified for vim-tutor-mode by Felipe Morales.

View File

@ -11,7 +11,7 @@
"215": "There are some words that don't belong in this sentence.", "215": "There are some words that don't belong in this sentence.",
"231": "Somebody typed the end of this line twice.", "231": "Somebody typed the end of this line twice.",
"271": -1, "271": -1,
"290": "this line of words is cleaned up.", "290": "This line of words is cleaned up.",
"304": -1, "304": -1,
"305": -1, "305": -1,
"306": -1, "306": -1,
@ -32,14 +32,14 @@
"425": "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, "487": -1,
"506": -1, "506": -1,
"531": "the best time to see the flowers is in the spring.", "531": "Usually the best time to see the flowers is in the spring.",
"722": -1, "722": -1,
"727": -1, "727": -1,
"744": "This line will allow you to practice appending text to a line.", "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.", "745": "This line will allow you to practice appending text to a line.",
"765": "Adding 123 to 456 gives you 579.", "765": "Adding 123 to 456 gives you 579.",
"766": "Adding 123 to 456 gives you 579.", "766": "Adding 123 to 456 gives you 579.",
"788": "a) this is the first item.", "790": "a) This is the first item.",
"789": " b) this is the second item." "791": " b) This is the second item."
} }
} }

View File

@ -133,7 +133,7 @@ preprocess_patch() {
# Remove *.proto, Make*, gui_*, some if_* # Remove *.proto, Make*, gui_*, some if_*
local na_src='proto\|Make*\|gui_*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv' local na_src='proto\|Make*\|gui_*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%('${na_src}'\)@norm! d/\v(^diff)|%$ ' +w +q "$file" 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%(testdir/\)\@<!\%('${na_src}'\)@norm! d/\v(^diff)|%$ ' +w +q "$file"
# Remove channel.txt, netbeans.txt, os_*.txt, term.txt, todo.txt, version*.txt, tags # Remove channel.txt, netbeans.txt, os_*.txt, term.txt, todo.txt, version*.txt, tags
local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|tags' local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|tags'

View File

@ -10,6 +10,11 @@ if(USE_GCOV)
endif() endif()
endif() endif()
if(WIN32)
# tell MinGW compiler to enable wmain
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode")
endif()
set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)
set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators) set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)

View File

@ -215,6 +215,7 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error)
#undef UI_EXT_OPTION #undef UI_EXT_OPTION
} }
/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
static void push_call(UI *ui, char *name, Array args) static void push_call(UI *ui, char *name, Array args)
{ {
Array call = ARRAY_DICT_INIT; Array call = ARRAY_DICT_INIT;
@ -241,39 +242,7 @@ static void push_call(UI *ui, char *name, Array args)
static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) static void remote_ui_highlight_set(UI *ui, HlAttrs attrs)
{ {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
Dictionary hl = ARRAY_DICT_INIT; Dictionary hl = hlattrs2dict(attrs);
if (attrs.bold) {
PUT(hl, "bold", BOOLEAN_OBJ(true));
}
if (attrs.underline) {
PUT(hl, "underline", BOOLEAN_OBJ(true));
}
if (attrs.undercurl) {
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
}
if (attrs.italic) {
PUT(hl, "italic", BOOLEAN_OBJ(true));
}
if (attrs.reverse) {
PUT(hl, "reverse", BOOLEAN_OBJ(true));
}
if (attrs.foreground != -1) {
PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground));
}
if (attrs.background != -1) {
PUT(hl, "background", INTEGER_OBJ(attrs.background));
}
if (attrs.special != -1) {
PUT(hl, "special", INTEGER_OBJ(attrs.special));
}
ADD(args, DICTIONARY_OBJ(hl)); ADD(args, DICTIONARY_OBJ(hl));
push_call(ui, "highlight_set", args); push_call(ui, "highlight_set", args);

View File

@ -55,6 +55,47 @@ void nvim_command(String command, Error *err)
try_end(err); try_end(err);
} }
/// Gets a highlight definition by name.
///
/// @param name Highlight group name
/// @param rgb Export RGB colors
/// @param[out] err Error details, if any
/// @return Highlight definition map
/// @see nvim_get_hl_by_id
Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
FUNC_API_SINCE(3)
{
Dictionary result = ARRAY_DICT_INIT;
int id = syn_name2id((const char_u *)name.data);
if (id == 0) {
api_set_error(err, kErrorTypeException, "Invalid highlight name: %s",
name.data);
return result;
}
result = nvim_get_hl_by_id(id, rgb, err);
return result;
}
/// Gets a highlight definition by id. |hlID()|
///
/// @param hl_id Highlight id as returned by |hlID()|
/// @param rgb Export RGB colors
/// @param[out] err Error details, if any
/// @return Highlight definition map
/// @see nvim_get_hl_by_name
Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err)
FUNC_API_SINCE(3)
{
Dictionary dic = ARRAY_DICT_INIT;
if (syn_get_final_id((int)hl_id) == 0) {
api_set_error(err, kErrorTypeException, "Invalid highlight id: %d", hl_id);
return dic;
}
int attrcode = syn_id2attr((int)hl_id);
return hl_get_attr_by_id(attrcode, rgb, err);
}
/// Passes input keys to Nvim. /// Passes input keys to Nvim.
/// On VimL error: Does not fail, but updates v:errmsg. /// On VimL error: Does not fail, but updates v:errmsg.
/// ///
@ -255,12 +296,11 @@ free_vim_args:
return rv; return rv;
} }
/// Execute lua code. Parameters might be passed, they are available inside /// Execute lua code. Parameters (if any) are available as `...` inside the
/// the chunk as `...`. The chunk can return a value. /// chunk. The chunk can return a value.
/// ///
/// To evaluate an expression, it must be prefixed with "return ". For /// Only statements are executed. To evaluate an expression, prefix it
/// instance, to call a lua function with arguments sent in and get its /// with `return`: return my_function(...)
/// return value back, use the code "return my_function(...)".
/// ///
/// @param code lua code to execute /// @param code lua code to execute
/// @param args Arguments to the code /// @param args Arguments to the code
@ -423,29 +463,18 @@ void nvim_del_var(String name, Error *err)
dict_set_var(&globvardict, name, NIL, true, false, err); dict_set_var(&globvardict, name, NIL, true, false, err);
} }
/// Sets a global variable
///
/// @deprecated /// @deprecated
/// /// @see nvim_set_var
/// @param name Variable name
/// @param value Variable value
/// @param[out] err Error details, if any
/// @return Old value or nil if there was no previous value. /// @return Old value or nil if there was no previous value.
/// /// @warning May return nil if there was no previous value
/// @warning It may return nil if there was no previous value /// OR if previous value was `v:null`.
/// or if previous value was `v:null`.
Object vim_set_var(String name, Object value, Error *err) Object vim_set_var(String name, Object value, Error *err)
{ {
return dict_set_var(&globvardict, name, value, false, true, err); return dict_set_var(&globvardict, name, value, false, true, err);
} }
/// Removes a global variable
///
/// @deprecated /// @deprecated
/// /// @see nvim_del_var
/// @param name Variable name
/// @param[out] err Error details, if any
/// @return Old value
Object vim_del_var(String name, Error *err) Object vim_del_var(String name, Error *err)
{ {
return dict_set_var(&globvardict, name, NIL, true, true, err); return dict_set_var(&globvardict, name, NIL, true, true, err);
@ -484,7 +513,8 @@ void nvim_set_option(String name, Object value, Error *err)
set_option_to(NULL, SREQ_GLOBAL, name, value, err); set_option_to(NULL, SREQ_GLOBAL, name, value, err);
} }
/// Writes a message to vim output buffer /// Writes a message to the Vim output buffer. Does not append "\n", the
/// message is buffered (won't display) until a linefeed is written.
/// ///
/// @param str Message /// @param str Message
void nvim_out_write(String str) void nvim_out_write(String str)
@ -493,7 +523,8 @@ void nvim_out_write(String str)
write_msg(str, false); write_msg(str, false);
} }
/// Writes a message to vim error buffer /// Writes a message to the Vim error buffer. Does not append "\n", the
/// message is buffered (won't display) until a linefeed is written.
/// ///
/// @param str Message /// @param str Message
void nvim_err_write(String str) void nvim_err_write(String str)
@ -502,8 +533,8 @@ void nvim_err_write(String str)
write_msg(str, true); write_msg(str, true);
} }
/// Writes a message to vim error buffer. Appends a linefeed to ensure all /// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
/// contents are written. /// flushed (and displayed).
/// ///
/// @param str Message /// @param str Message
/// @see nvim_err_write() /// @see nvim_err_write()

41
src/nvim/aucmd.c Normal file
View File

@ -0,0 +1,41 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include "nvim/os/os.h"
#include "nvim/fileio.h"
#include "nvim/vim.h"
#include "nvim/main.h"
#include "nvim/ui.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "aucmd.c.generated.h"
#endif
static void focusgained_event(void **argv)
{
bool *gainedp = argv[0];
do_autocmd_focusgained(*gainedp);
xfree(gainedp);
}
void aucmd_schedule_focusgained(bool gained)
{
bool *gainedp = xmalloc(sizeof(*gainedp));
*gainedp = gained;
loop_schedule_deferred(&main_loop,
event_create(focusgained_event, 1, gainedp));
}
static void do_autocmd_focusgained(bool gained)
FUNC_ATTR_NONNULL_ALL
{
static bool recursive = false;
if (recursive) {
return; // disallow recursion
}
recursive = true;
apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST),
NULL, NULL, false, curbuf);
recursive = false;
}

9
src/nvim/aucmd.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef NVIM_AUCMD_H
#define NVIM_AUCMD_H
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "aucmd.h.generated.h"
#endif
#endif // NVIM_AUCMD_H

View File

@ -3069,8 +3069,8 @@ static bool ti_change(char_u *str, char_u **last)
/// Set current window title /// Set current window title
void resettitle(void) void resettitle(void)
{ {
ui_call_set_title(cstr_as_string((char *)lasttitle));
ui_call_set_icon(cstr_as_string((char *)lasticon)); ui_call_set_icon(cstr_as_string((char *)lasticon));
ui_call_set_title(cstr_as_string((char *)lasttitle));
ui_flush(); ui_flush();
} }

View File

@ -762,7 +762,7 @@ bool vim_isIDc(int c)
} }
/// Check that "c" is a keyword character: /// Check that "c" is a keyword character:
/// Letters and characters from 'iskeyword' option for current buffer. /// Letters and characters from 'iskeyword' option for the current buffer.
/// For multi-byte characters mb_get_class() is used (builtin rules). /// For multi-byte characters mb_get_class() is used (builtin rules).
/// ///
/// @param c character to check /// @param c character to check

View File

@ -135,6 +135,20 @@ void diff_buf_add(buf_T *buf)
EMSGN(_("E96: Cannot diff more than %" PRId64 " buffers"), DB_COUNT); EMSGN(_("E96: Cannot diff more than %" PRId64 " buffers"), DB_COUNT);
} }
///
/// Remove all buffers to make diffs for.
///
static void diff_buf_clear(void)
{
for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
curtab->tp_diffbuf[i] = NULL;
curtab->tp_diff_invalid = true;
diff_redraw(true);
}
}
}
/// Find buffer "buf" in the list of diff buffers for the current tab page. /// Find buffer "buf" in the list of diff buffers for the current tab page.
/// ///
/// @param buf The buffer to find. /// @param buf The buffer to find.
@ -1175,6 +1189,11 @@ void ex_diffoff(exarg_T *eap)
diffwin |= wp->w_p_diff; diffwin |= wp->w_p_diff;
} }
// Also remove hidden buffers from the list.
if (eap->forceit) {
diff_buf_clear();
}
// Remove "hor" from from 'scrollopt' if there are no diff windows left. // Remove "hor" from from 'scrollopt' if there are no diff windows left.
if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) { if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) {
do_cmdline_cmd("set sbo-=hor"); do_cmdline_cmd("set sbo-=hor");

View File

@ -974,14 +974,6 @@ static int insert_handle_key(InsertState *s)
multiqueue_process_events(main_loop.events); multiqueue_process_events(main_loop.events);
break; break;
case K_FOCUSGAINED: // Neovim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
break;
case K_FOCUSLOST: // Neovim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
break;
case K_HOME: // <Home> case K_HOME: // <Home>
case K_KHOME: case K_KHOME:
case K_S_HOME: case K_S_HOME:
@ -2406,6 +2398,7 @@ void set_completion(colnr_T startcol, list_T *list)
ins_compl_prep(' '); ins_compl_prep(' ');
} }
ins_compl_clear(); ins_compl_clear();
ins_compl_free();
compl_direction = FORWARD; compl_direction = FORWARD;
if (startcol > curwin->w_cursor.col) if (startcol > curwin->w_cursor.col)
@ -3166,8 +3159,7 @@ static bool ins_compl_prep(int c)
/* Ignore end of Select mode mapping and mouse scroll buttons. */ /* Ignore end of Select mode mapping and mouse scroll buttons. */
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
|| c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT) {
|| c == K_FOCUSGAINED || c == K_FOCUSLOST) {
return retval; return retval;
} }

View File

@ -6733,6 +6733,39 @@ static void prepare_assert_error(garray_T *gap)
} }
} }
// Append "str" to "gap", escaping unprintable characters.
// Changes NL to \n, CR to \r, etc.
static void ga_concat_esc(garray_T *gap, char_u *str)
{
char_u *p;
char_u buf[NUMBUFLEN];
if (str == NULL) {
ga_concat(gap, (char_u *)"NULL");
return;
}
for (p = str; *p != NUL; p++) {
switch (*p) {
case BS: ga_concat(gap, (char_u *)"\\b"); break;
case ESC: ga_concat(gap, (char_u *)"\\e"); break;
case FF: ga_concat(gap, (char_u *)"\\f"); break;
case NL: ga_concat(gap, (char_u *)"\\n"); break;
case TAB: ga_concat(gap, (char_u *)"\\t"); break;
case CAR: ga_concat(gap, (char_u *)"\\r"); break;
case '\\': ga_concat(gap, (char_u *)"\\\\"); break;
default:
if (*p < ' ') {
vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
ga_concat(gap, buf);
} else {
ga_append(gap, *p);
}
break;
}
}
}
// Fill "gap" with information about an assert error. // Fill "gap" with information about an assert error.
static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
char_u *exp_str, typval_T *exp_tv, char_u *exp_str, typval_T *exp_tv,
@ -6747,28 +6780,30 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv,
} else { } else {
if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
ga_concat(gap, (char_u *)"Pattern "); ga_concat(gap, (char_u *)"Pattern ");
} else if (atype == ASSERT_NOTEQUAL) {
ga_concat(gap, (char_u *)"Expected not equal to ");
} else { } else {
ga_concat(gap, (char_u *)"Expected "); ga_concat(gap, (char_u *)"Expected ");
} }
if (exp_str == NULL) { if (exp_str == NULL) {
tofree = (char_u *) encode_tv2string(exp_tv, NULL); tofree = (char_u *)encode_tv2string(exp_tv, NULL);
ga_concat(gap, tofree); ga_concat_esc(gap, tofree);
xfree(tofree); xfree(tofree);
} else { } else {
ga_concat(gap, exp_str); ga_concat_esc(gap, exp_str);
} }
tofree = (char_u *)encode_tv2string(got_tv, NULL); if (atype != ASSERT_NOTEQUAL) {
if (atype == ASSERT_MATCH) { if (atype == ASSERT_MATCH) {
ga_concat(gap, (char_u *)" does not match "); ga_concat(gap, (char_u *)" does not match ");
} else if (atype == ASSERT_NOTMATCH) { } else if (atype == ASSERT_NOTMATCH) {
ga_concat(gap, (char_u *)" does match "); ga_concat(gap, (char_u *)" does match ");
} else if (atype == ASSERT_NOTEQUAL) { } else {
ga_concat(gap, (char_u *)" differs from "); ga_concat(gap, (char_u *)" but got ");
} else { }
ga_concat(gap, (char_u *)" but got "); tofree = (char_u *)encode_tv2string(got_tv, NULL);
ga_concat_esc(gap, tofree);
xfree(tofree);
} }
ga_concat(gap, tofree);
xfree(tofree);
} }
} }
@ -17493,7 +17528,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
} }
/// Writes list of strings to file /// Write "list" of strings to file "fd".
/// ///
/// @param fp File to write to. /// @param fp File to write to.
/// @param[in] list List to write. /// @param[in] list List to write.
@ -22775,7 +22810,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
bool eval_has_provider(const char *name) bool eval_has_provider(const char *name)
{ {
#define check_provider(name) \ #define CHECK_PROVIDER(name) \
if (has_##name == -1) { \ if (has_##name == -1) { \
has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \ has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \
if (!has_##name) { \ if (!has_##name) { \
@ -22791,17 +22826,17 @@ bool eval_has_provider(const char *name)
static int has_python3 = -1; static int has_python3 = -1;
static int has_ruby = -1; static int has_ruby = -1;
if (!strcmp(name, "clipboard")) { if (strequal(name, "clipboard")) {
check_provider(clipboard); CHECK_PROVIDER(clipboard);
return has_clipboard; return has_clipboard;
} else if (!strcmp(name, "python3")) { } else if (strequal(name, "python3")) {
check_provider(python3); CHECK_PROVIDER(python3);
return has_python3; return has_python3;
} else if (!strcmp(name, "python")) { } else if (strequal(name, "python")) {
check_provider(python); CHECK_PROVIDER(python);
return has_python; return has_python;
} else if (!strcmp(name, "ruby")) { } else if (strequal(name, "ruby")) {
check_provider(ruby); CHECK_PROVIDER(ruby);
return has_ruby; return has_ruby;
} }

View File

@ -59,7 +59,14 @@ void loop_poll_events(Loop *loop, int ms)
multiqueue_process_events(loop->fast_events); multiqueue_process_events(loop->fast_events);
} }
// Schedule an event from another thread /// Schedules an event from another thread.
///
/// @note Event is queued into `fast_events`, which is processed outside of the
/// primary `events` queue by loop_poll_events(). For `main_loop`, that
/// means `fast_events` is NOT processed in an "editor mode"
/// (VimState.execute), so redraw and other side-effects are likely to be
/// skipped.
/// @see loop_schedule_deferred
void loop_schedule(Loop *loop, Event event) void loop_schedule(Loop *loop, Event event)
{ {
uv_mutex_lock(&loop->mutex); uv_mutex_lock(&loop->mutex);
@ -68,6 +75,24 @@ void loop_schedule(Loop *loop, Event event)
uv_mutex_unlock(&loop->mutex); uv_mutex_unlock(&loop->mutex);
} }
/// Schedules an event from another thread. Unlike loop_schedule(), the event
/// is forwarded to `Loop.events`, instead of being processed immediately.
///
/// @see loop_schedule
void loop_schedule_deferred(Loop *loop, Event event)
{
Event *eventp = xmalloc(sizeof(*eventp));
*eventp = event;
loop_schedule(loop, event_create(loop_deferred_event, 2, loop, eventp));
}
static void loop_deferred_event(void **argv)
{
Loop *loop = argv[0];
Event *eventp = argv[1];
multiqueue_put_event(loop->events, *eventp);
xfree(eventp);
}
void loop_on_put(MultiQueue *queue, void *data) void loop_on_put(MultiQueue *queue, void *data)
{ {
Loop *loop = data; Loop *loop = data;

View File

@ -16,10 +16,28 @@ KLIST_INIT(WatcherPtr, WatcherPtr, _noop)
typedef struct loop { typedef struct loop {
uv_loop_t uv; uv_loop_t uv;
MultiQueue *events, *fast_events, *thread_events; MultiQueue *events;
MultiQueue *thread_events;
// Immediate events:
// "Events that should be processed after exiting uv_run() (to avoid
// recursion), but before returning from loop_poll_events()."
// 502aee690c980fcb3cfcb3f211dcfad06103db46
// Practical consequence: these events are processed by
// state_enter()..os_inchar()
// whereas "regular" (main_loop.events) events are processed by
// state_enter()..VimState.execute()
// But state_enter()..os_inchar() can be "too early" if you want the event
// to trigger UI updates and other user-activity-related side-effects.
MultiQueue *fast_events;
// used by process/job-control subsystem
klist_t(WatcherPtr) *children; klist_t(WatcherPtr) *children;
uv_signal_t children_watcher; uv_signal_t children_watcher;
uv_timer_t children_kill_timer, poll_timer; uv_timer_t children_kill_timer;
// generic timer, used by loop_poll_events()
uv_timer_t poll_timer;
size_t children_stop_requests; size_t children_stop_requests;
uv_async_t async; uv_async_t async;
uv_mutex_t mutex; uv_mutex_t mutex;

View File

@ -233,8 +233,7 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
switch (proc->type) { switch (proc->type) {
case kProcessTypeUv: case kProcessTypeUv:
// Close the process's stdin. If the process doesn't close its own // Close the process's stdin. If the process doesn't close its own
// stdout/stderr, they will be closed when it exits(possibly due to being // stdout/stderr, they will be closed when it exits (voluntarily or not).
// terminated after a timeout)
process_close_in(proc); process_close_in(proc);
ILOG("Sending SIGTERM to pid %d", proc->pid); ILOG("Sending SIGTERM to pid %d", proc->pid);
uv_kill(proc->pid, SIGTERM); uv_kill(proc->pid, SIGTERM);

View File

@ -118,7 +118,7 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
// to `alloc_cb` will return the same unused pointer(`rbuffer_produced` // to `alloc_cb` will return the same unused pointer(`rbuffer_produced`
// won't be called) // won't be called)
&& cnt != 0) { && cnt != 0) {
DLOG("Closing Stream (%p): %s (%s)", stream, DLOG("closing Stream: %p: %s (%s)", stream,
uv_err_name((int)cnt), os_strerror((int)cnt)); uv_err_name((int)cnt), os_strerror((int)cnt));
// Read error or EOF, either way stop the stream and invoke the callback // Read error or EOF, either way stop the stream and invoke the callback
// with eof == true // with eof == true

View File

@ -7,6 +7,7 @@
#include <uv.h> #include <uv.h>
#include "nvim/log.h"
#include "nvim/rbuffer.h" #include "nvim/rbuffer.h"
#include "nvim/macros.h" #include "nvim/macros.h"
#include "nvim/event/stream.h" #include "nvim/event/stream.h"
@ -81,6 +82,7 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
assert(!stream->closed); assert(!stream->closed);
DLOG("closing Stream: %p", stream);
stream->closed = true; stream->closed = true;
stream->close_cb = on_stream_close; stream->close_cb = on_stream_close;
stream->close_cb_data = data; stream->close_cb_data = data;

View File

@ -8,6 +8,7 @@
#include <uv.h> #include <uv.h>
#include "nvim/log.h"
#include "nvim/event/loop.h" #include "nvim/event/loop.h"
#include "nvim/event/wstream.h" #include "nvim/event/wstream.h"
#include "nvim/vim.h" #include "nvim/vim.h"

View File

@ -3332,10 +3332,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
sub = regtilde(sub, p_magic); sub = regtilde(sub, p_magic);
// Check for a match on each line. // Check for a match on each line.
// If preview: limit to max('cmdwinheight', viewport).
linenr_T line2 = eap->line2; linenr_T line2 = eap->line2;
for (linenr_T lnum = eap->line1; for (linenr_T lnum = eap->line1;
lnum <= line2 && !(got_quit || aborting()) lnum <= line2 && !got_quit && !aborting()
&& (!preview || matched_lines.size <= (size_t)p_cwh); && (!preview || matched_lines.size < (size_t)p_cwh
|| lnum <= curwin->w_botline);
lnum++) { lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL); (colnr_T)0, NULL);
@ -3500,6 +3502,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
setmouse(); /* disable mouse in xterm */ setmouse(); /* disable mouse in xterm */
curwin->w_cursor.col = regmatch.startpos[0].col; curwin->w_cursor.col = regmatch.startpos[0].col;
if (curwin->w_p_crb) {
do_check_cursorbind();
}
/* When 'cpoptions' contains "u" don't sync undo when /* When 'cpoptions' contains "u" don't sync undo when
* asking for confirmation. */ * asking for confirmation. */
if (vim_strchr(p_cpo, CPO_UNDO) != NULL) if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
@ -3659,6 +3665,42 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
* use "\=col("."). */ * use "\=col("."). */
curwin->w_cursor.col = regmatch.startpos[0].col; curwin->w_cursor.col = regmatch.startpos[0].col;
// When the match included the "$" of the last line it may
// go beyond the last line of the buffer.
if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) {
nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1;
skip_match = true;
}
#define ADJUST_SUB_FIRSTLNUM() \
do { \
/* For a multi-line match, make a copy of the last matched */ \
/* line and continue in that one. */ \
if (nmatch > 1) { \
sub_firstlnum += nmatch - 1; \
xfree(sub_firstline); \
sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \
/* When going beyond the last line, stop substituting. */ \
if (sub_firstlnum <= line2) { \
do_again = true; \
} else { \
subflags.do_all = false; \
} \
} \
if (skip_match) { \
/* Already hit end of the buffer, sub_firstlnum is one */ \
/* less than what it ought to be. */ \
xfree(sub_firstline); \
sub_firstline = vim_strsave((char_u *)""); \
copycol = 0; \
} \
} while (0)
if (preview && !has_second_delim) {
ADJUST_SUB_FIRSTLNUM();
goto skip;
}
// 3. Substitute the string. During 'inccommand' preview only do this if // 3. Substitute the string. During 'inccommand' preview only do this if
// there is a replace pattern. // there is a replace pattern.
if (!preview || has_second_delim) { if (!preview || has_second_delim) {
@ -3685,13 +3727,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
goto skip; goto skip;
} }
// When the match included the "$" of the last line it may
// go beyond the last line of the buffer.
if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) {
nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1;
skip_match = true;
}
// Need room for: // Need room for:
// - result so far in new_start (not for first sub in line) // - result so far in new_start (not for first sub in line)
// - original text up to match // - original text up to match
@ -3722,30 +3757,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
// is beyond the end of the line after the substitution. // is beyond the end of the line after the substitution.
curwin->w_cursor.col = 0; curwin->w_cursor.col = 0;
// For a multi-line match, make a copy of the last matched
// line and continue in that one.
if (nmatch > 1) {
sub_firstlnum += nmatch - 1;
xfree(sub_firstline);
sub_firstline = vim_strsave(ml_get(sub_firstlnum));
// When going beyond the last line, stop substituting.
if (sub_firstlnum <= line2) {
do_again = true;
} else {
subflags.do_all = false;
}
}
// Remember next character to be copied. // Remember next character to be copied.
copycol = regmatch.endpos[0].col; copycol = regmatch.endpos[0].col;
if (skip_match) { ADJUST_SUB_FIRSTLNUM();
// Already hit end of the buffer, sub_firstlnum is one
// less than what it ought to be.
xfree(sub_firstline);
sub_firstline = vim_strsave((char_u *)"");
copycol = 0;
}
// Now the trick is to replace CTRL-M chars with a real line // Now the trick is to replace CTRL-M chars with a real line
// break. This would make it impossible to insert a CTRL-M in // break. This would make it impossible to insert a CTRL-M in
@ -4002,6 +4017,7 @@ skip:
kv_destroy(matched_lines); kv_destroy(matched_lines);
return preview_buf; return preview_buf;
#undef ADJUST_SUB_FIRSTLNUM
} // NOLINT(readability/fn_size) } // NOLINT(readability/fn_size)
/* /*

View File

@ -2319,16 +2319,6 @@ static void source_callback(char_u *fname, void *cookie)
(void)do_source(fname, false, DOSO_NONE); (void)do_source(fname, false, DOSO_NONE);
} }
/// Source the file "name" from all directories in 'runtimepath'.
/// "name" can contain wildcards.
/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
///
/// return FAIL when no file could be sourced, OK otherwise.
int source_runtime(char_u *name, int flags)
{
return do_in_runtimepath(name, flags, source_callback, NULL);
}
/// Find the file "name" in all directories in "path" and invoke /// Find the file "name" in all directories in "path" and invoke
/// "callback(fname, cookie)". /// "callback(fname, cookie)".
/// "name" can contain wildcards. /// "name" can contain wildcards.
@ -2434,21 +2424,21 @@ int do_in_path(char_u *path, char_u *name, int flags,
return did_one ? OK : FAIL; return did_one ? OK : FAIL;
} }
/// Find "name" in 'runtimepath'. When found, invoke the callback function for /// Find "name" in "path". When found, invoke the callback function for
/// it: callback(fname, "cookie") /// it: callback(fname, "cookie")
/// When "flags" has DIP_ALL repeat for all matches, otherwise only the first /// When "flags" has DIP_ALL repeat for all matches, otherwise only the first
/// one is used. /// one is used.
/// Returns OK when at least one match found, FAIL otherwise. /// Returns OK when at least one match found, FAIL otherwise.
/// If "name" is NULL calls callback for each entry in runtimepath. Cookie is /// If "name" is NULL calls callback for each entry in "path". Cookie is
/// passed by reference in this case, setting it to NULL indicates that callback /// passed by reference in this case, setting it to NULL indicates that callback
/// has done its job. /// has done its job.
int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, int do_in_path_and_pp(char_u *path, char_u *name, int flags,
void *cookie) DoInRuntimepathCB callback, void *cookie)
{ {
int done = FAIL; int done = FAIL;
if ((flags & DIP_NORTP) == 0) { if ((flags & DIP_NORTP) == 0) {
done = do_in_path(p_rtp, name, flags, callback, cookie); done = do_in_path(path, name, flags, callback, cookie);
} }
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) { if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) {
@ -2476,6 +2466,29 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
return done; return done;
} }
/// Just like do_in_path_and_pp(), using 'runtimepath' for "path".
int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
void *cookie)
{
return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
}
/// Source the file "name" from all directories in 'runtimepath'.
/// "name" can contain wildcards.
/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
///
/// return FAIL when no file could be sourced, OK otherwise.
int source_runtime(char_u *name, int flags)
{
return source_in_path(p_rtp, name, flags);
}
/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
int source_in_path(char_u *path, char_u *name, int flags)
{
return do_in_path_and_pp(path, name, flags, source_callback, NULL);
}
// Expand wildcards in "pat" and invoke do_source() for each match. // Expand wildcards in "pat" and invoke do_source() for each match.
static void source_all_matches(char_u *pat) static void source_all_matches(char_u *pat)
{ {
@ -2498,6 +2511,7 @@ static int APP_BOTH;
static void add_pack_plugin(char_u *fname, void *cookie) static void add_pack_plugin(char_u *fname, void *cookie)
{ {
char_u *p4, *p3, *p2, *p1, *p; char_u *p4, *p3, *p2, *p1, *p;
char_u *buf = NULL;
char *const ffname = fix_fname((char *)fname); char *const ffname = fix_fname((char *)fname);
@ -2525,26 +2539,30 @@ static void add_pack_plugin(char_u *fname, void *cookie)
// Find "ffname" in "p_rtp", ignoring '/' vs '\' differences // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences
size_t fname_len = strlen(ffname); size_t fname_len = strlen(ffname);
const char *insp = (const char *)p_rtp; const char *insp = (const char *)p_rtp;
for (;;) { buf = try_malloc(MAXPATHL);
if (path_fnamencmp(insp, ffname, fname_len) == 0) { if (buf == NULL) {
goto theend;
}
while (*insp != NUL) {
copy_option_part((char_u **)&insp, buf, MAXPATHL, ",");
add_pathsep((char *)buf);
char *const rtp_ffname = fix_fname((char *)buf);
if (rtp_ffname == NULL) {
goto theend;
}
bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
xfree(rtp_ffname);
if (match) {
break; break;
} }
insp = strchr(insp, ',');
if (insp == NULL) {
break;
}
insp++;
} }
if (insp == NULL) { if (*insp == NUL) {
// not found, append at the end // not found, append at the end
insp = (const char *)p_rtp + STRLEN(p_rtp); insp = (const char *)p_rtp + STRLEN(p_rtp);
} else { } else {
// append after the matching directory. // append after the matching directory.
insp += strlen(ffname); insp--;
while (*insp != NUL && *insp != ',') {
insp++;
}
} }
*p4 = c; *p4 = c;
@ -2614,26 +2632,35 @@ static void add_pack_plugin(char_u *fname, void *cookie)
} }
theend: theend:
xfree(buf);
xfree(ffname); xfree(ffname);
} }
static bool did_source_packages = false; /// Add all packages in the "start" directory to 'runtimepath'.
void add_pack_start_dirs(void)
{
do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
add_pack_plugin, &APP_ADD_DIR);
}
/// Load plugins from all packages in the "start" directory.
void load_start_packages(void)
{
did_source_packages = true;
do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
add_pack_plugin, &APP_LOAD);
}
// ":packloadall" // ":packloadall"
// Find plugins in the package directories and source them. // Find plugins in the package directories and source them.
// "eap" is NULL when invoked during startup.
void ex_packloadall(exarg_T *eap) void ex_packloadall(exarg_T *eap)
{ {
if (!did_source_packages || (eap != NULL && eap->forceit)) { if (!did_source_packages || eap->forceit) {
did_source_packages = true;
// First do a round to add all directories to 'runtimepath', then load // First do a round to add all directories to 'runtimepath', then load
// the plugins. This allows for plugins to use an autoload directory // the plugins. This allows for plugins to use an autoload directory
// of another plugin. // of another plugin.
do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT add_pack_start_dirs();
add_pack_plugin, &APP_ADD_DIR); load_start_packages();
do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
add_pack_plugin, &APP_LOAD);
} }
} }

View File

@ -1668,8 +1668,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (*ea.cmd == ';') { if (*ea.cmd == ';') {
if (!ea.skip) { if (!ea.skip) {
curwin->w_cursor.lnum = ea.line2; curwin->w_cursor.lnum = ea.line2;
// Don't leave the cursor on an illegal line (caused by ';') // don't leave the cursor on an illegal line or column
check_cursor_lnum(); check_cursor();
} }
} else if (*ea.cmd != ',') { } else if (*ea.cmd != ',') {
break; break;
@ -1813,7 +1813,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (text_locked() && !(ea.argt & CMDWIN) if (text_locked() && !(ea.argt & CMDWIN)
&& !IS_USER_CMDIDX(ea.cmdidx)) { && !IS_USER_CMDIDX(ea.cmdidx)) {
// Command not allowed when editing the command line. // Command not allowed when editing the command line.
errormsg = get_text_locked_msg(); errormsg = (char_u *)_(get_text_locked_msg());
goto doend; goto doend;
} }
/* Disallow editing another buffer when "curbuf_lock" is set. /* Disallow editing another buffer when "curbuf_lock" is set.
@ -8810,11 +8810,12 @@ makeopens (
&& buf->b_fname != NULL && buf->b_fname != NULL
&& buf->b_p_bl) { && buf->b_p_bl) {
if (fprintf(fd, "badd +%" PRId64 " ", if (fprintf(fd, "badd +%" PRId64 " ",
buf->b_wininfo == NULL ? buf->b_wininfo == NULL
(int64_t)1L : ? (int64_t)1L
(int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags) == FAIL) || ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
return FAIL; return FAIL;
}
} }
} }
@ -8885,11 +8886,13 @@ makeopens (
&& !bt_nofile(wp->w_buffer) && !bt_nofile(wp->w_buffer)
) { ) {
if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0 if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, &ssop_flags) == FAIL) || ses_fname(fd, wp->w_buffer, &ssop_flags, true) == FAIL) {
return FAIL; return FAIL;
need_tabnew = FALSE; }
if (!wp->w_arg_idx_invalid) need_tabnew = false;
if (!wp->w_arg_idx_invalid) {
edited_win = wp; edited_win = wp;
}
break; break;
} }
} }
@ -8933,6 +8936,8 @@ makeopens (
// resized when moving between windows. // resized when moving between windows.
// Do this before restoring the view, so that the topline and the // Do this before restoring the view, so that the topline and the
// cursor can be set. This is done again below. // cursor can be set. This is done again below.
// winminheight and winminwidth need to be set to avoid an error if the
// user has set winheight or winwidth.
if (put_line(fd, "set winminheight=1 winminwidth=1 winheight=1 winwidth=1") if (put_line(fd, "set winminheight=1 winminwidth=1 winheight=1 winwidth=1")
== FAIL) { == FAIL) {
return FAIL; return FAIL;
@ -9221,24 +9226,35 @@ put_view (
if (wp->w_buffer->b_ffname != NULL if (wp->w_buffer->b_ffname != NULL
&& (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal) && (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal)
) { ) {
/* // Editing a file in this buffer: use ":edit file".
* Editing a file in this buffer: use ":edit file". // This may have side effects! (e.g., compressed or network file).
* This may have side effects! (e.g., compressed or network file). //
*/ // Note, if a buffer for that file already exists, use :badd to
if (fputs("edit ", fd) < 0 // edit that buffer, to not lose folding information (:edit resets
|| ses_fname(fd, wp->w_buffer, flagp) == FAIL) // folds in other buffers)
if (fputs("if bufexists('", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
|| fputs("') | buffer ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
|| fputs(" | else | edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
|| fputs(" | endif", fd) < 0
|| put_eol(fd) == FAIL) {
return FAIL; return FAIL;
} else {
/* No file in this buffer, just make it empty. */
if (put_line(fd, "enew") == FAIL)
return FAIL;
if (wp->w_buffer->b_ffname != NULL) {
/* The buffer does have a name, but it's not a file name. */
if (fputs("file ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp) == FAIL)
return FAIL;
} }
do_cursor = FALSE; } else {
// No file in this buffer, just make it empty.
if (put_line(fd, "enew") == FAIL) {
return FAIL;
}
if (wp->w_buffer->b_ffname != NULL) {
// The buffer does have a name, but it's not a file name.
if (fputs("file ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, true) == FAIL) {
return FAIL;
}
}
do_cursor = false;
} }
} }
@ -9378,7 +9394,7 @@ ses_arglist (
(void)vim_FullName((char *)s, (char *)buf, MAXPATHL, FALSE); (void)vim_FullName((char *)s, (char *)buf, MAXPATHL, FALSE);
s = buf; s = buf;
} }
if (fputs("argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL if (fputs("$argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL
|| put_eol(fd) == FAIL) { || put_eol(fd) == FAIL) {
xfree(buf); xfree(buf);
return FAIL; return FAIL;
@ -9389,12 +9405,10 @@ ses_arglist (
return OK; return OK;
} }
/* /// Write a buffer name to the session file.
* Write a buffer name to the session file. /// Also ends the line, if "add_eol" is true.
* Also ends the line. /// Returns FAIL if writing fails.
* Returns FAIL if writing fails. static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol)
*/
static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp)
{ {
char_u *name; char_u *name;
@ -9411,8 +9425,10 @@ static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp)
name = buf->b_sfname; name = buf->b_sfname;
else else
name = buf->b_ffname; name = buf->b_ffname;
if (ses_put_fname(fd, name, flagp) == FAIL || put_eol(fd) == FAIL) if (ses_put_fname(fd, name, flagp) == FAIL
|| (add_eol && put_eol(fd) == FAIL)) {
return FAIL; return FAIL;
}
return OK; return OK;
} }
@ -9835,7 +9851,7 @@ static void ex_terminal(exarg_T *eap)
if (*eap->arg != NUL) { // Run {cmd} in 'shell'. if (*eap->arg != NUL) { // Run {cmd} in 'shell'.
char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");
snprintf(ex_cmd, sizeof(ex_cmd), snprintf(ex_cmd, sizeof(ex_cmd),
":enew%s | call termopen(\"%s\") | startinsert", ":enew%s | call termopen(\"%s\")",
eap->forceit ? "!" : "", name); eap->forceit ? "!" : "", name);
xfree(name); xfree(name);
} else { // No {cmd}: run the job with tokenized 'shell'. } else { // No {cmd}: run the job with tokenized 'shell'.
@ -9857,7 +9873,7 @@ static void ex_terminal(exarg_T *eap)
shell_free_argv(argv); shell_free_argv(argv);
snprintf(ex_cmd, sizeof(ex_cmd), snprintf(ex_cmd, sizeof(ex_cmd),
":enew%s | call termopen([%s]) | startinsert", ":enew%s | call termopen([%s])",
eap->forceit ? "!" : "", shell_argv + 1); eap->forceit ? "!" : "", shell_argv + 1);
} }

View File

@ -1620,14 +1620,6 @@ static int command_line_handle_key(CommandLineState *s)
} }
return command_line_not_changed(s); return command_line_not_changed(s);
case K_FOCUSGAINED: // Neovim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
return command_line_not_changed(s);
case K_FOCUSLOST: // Neovim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
return command_line_not_changed(s);
default: default:
// Normal character with no special meaning. Just set mod_mask // Normal character with no special meaning. Just set mod_mask
// to 0x0 so that typing Shift-Space in the GUI doesn't enter // to 0x0 so that typing Shift-Space in the GUI doesn't enter
@ -1871,9 +1863,13 @@ char *getcmdline_prompt(const char firstc, const char *const prompt,
ccline.input_fn = (firstc == '@'); ccline.input_fn = (firstc == '@');
ccline.highlight_callback = highlight_callback; ccline.highlight_callback = highlight_callback;
int msg_silent_saved = msg_silent;
msg_silent = 0;
char *const ret = (char *)getcmdline(firstc, 1L, 0); char *const ret = (char *)getcmdline(firstc, 1L, 0);
restore_cmdline(&save_ccline); restore_cmdline(&save_ccline);
msg_silent = msg_silent_saved;
// Restore msg_col, the prompt from input() may have changed it. // Restore msg_col, the prompt from input() may have changed it.
// But only if called recursively and the commandline is therefore being // But only if called recursively and the commandline is therefore being
// restored to an old one; if not, the input() prompt stays on the screen, // restored to an old one; if not, the input() prompt stays on the screen,
@ -5722,6 +5718,7 @@ static int ex_window(void)
i = RedrawingDisabled; i = RedrawingDisabled;
RedrawingDisabled = 0; RedrawingDisabled = 0;
int save_count = save_batch_count();
/* /*
* Call the main loop until <CR> or CTRL-C is typed. * Call the main loop until <CR> or CTRL-C is typed.
@ -5730,6 +5727,7 @@ static int ex_window(void)
normal_enter(true, false); normal_enter(true, false);
RedrawingDisabled = i; RedrawingDisabled = i;
restore_batch_count(save_count);
int save_KeyTyped = KeyTyped; int save_KeyTyped = KeyTyped;

View File

@ -92,17 +92,15 @@ static int typeahead_char = 0; /* typeahead char that's not flushed */
*/ */
static int block_redo = FALSE; static int block_redo = FALSE;
/* // Make a hash value for a mapping.
* Make a hash value for a mapping. // "mode" is the lower 4 bits of the State for the mapping.
* "mode" is the lower 4 bits of the State for the mapping. // "c1" is the first character of the "lhs".
* "c1" is the first character of the "lhs". // Returns a value between 0 and 255, index in maphash.
* Returns a value between 0 and 255, index in maphash. // Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
* Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
*/
#define MAP_HASH(mode, \ #define MAP_HASH(mode, \
c1) (((mode) & \ c1) (((mode) & \
(NORMAL + VISUAL + SELECTMODE + \ (NORMAL + VISUAL + SELECTMODE + \
OP_PENDING)) ? (c1) : ((c1) ^ 0x80)) OP_PENDING + TERM_FOCUS)) ? (c1) : ((c1) ^ 0x80))
// Each mapping is put in one of the MAX_MAPHASH hash lists, // Each mapping is put in one of the MAX_MAPHASH hash lists,
// to speed up finding it. // to speed up finding it.
@ -870,20 +868,15 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent)
addlen = (int)STRLEN(str); addlen = (int)STRLEN(str);
/*
* Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off]
*/
if (offset == 0 && addlen <= typebuf.tb_off) { if (offset == 0 && addlen <= typebuf.tb_off) {
// Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off]
typebuf.tb_off -= addlen; typebuf.tb_off -= addlen;
memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen);
} } else {
/* // Need to allocate a new buffer.
* Need to allocate a new buffer. // In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4
* In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 // characters. We add some extra room to avoid having to allocate too
* characters. We add some extra room to avoid having to allocate too // often.
* often.
*/
else {
newoff = MAXMAPLEN + 4; newoff = MAXMAPLEN + 4;
newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4); newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4);
if (newlen < 0) { /* string is getting too long */ if (newlen < 0) { /* string is getting too long */
@ -1665,10 +1658,10 @@ static int vgetorpeek(int advance)
} }
if (c != NUL && !got_int) { if (c != NUL && !got_int) {
if (advance) { if (advance) {
/* KeyTyped = FALSE; When the command that stuffed something // KeyTyped = FALSE; When the command that stuffed something
* was typed, behave like the stuffed command was typed. // was typed, behave like the stuffed command was typed.
* needed for CTRL-W CTRl-] to open a fold, for example. */ // needed for CTRL-W CTRL-] to open a fold, for example.
KeyStuffed = TRUE; KeyStuffed = true;
} }
if (typebuf.tb_no_abbr_cnt == 0) if (typebuf.tb_no_abbr_cnt == 0)
typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */ typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */

View File

@ -13,6 +13,7 @@
#else #else
# define _(x) ((char *)(x)) # define _(x) ((char *)(x))
# define N_(x) x # define N_(x) x
# define ngettext(x, xs, n) ((n) == 1 ? (x) : (xs))
# define bindtextdomain(x, y) // empty # define bindtextdomain(x, y) // empty
# define bind_textdomain_codeset(x, y) // empty # define bind_textdomain_codeset(x, y) // empty
# define textdomain(x) // empty # define textdomain(x) // empty

View File

@ -407,6 +407,9 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);
/* ID of script being sourced or was sourced to define the current function. */ /* ID of script being sourced or was sourced to define the current function. */
EXTERN scid_T current_SID INIT(= 0); EXTERN scid_T current_SID INIT(= 0);
EXTERN bool did_source_packages INIT(= false);
// Scope information for the code that indirectly triggered the current // Scope information for the code that indirectly triggered the current
// provider function call // provider function call
EXTERN struct caller_scope { EXTERN struct caller_scope {
@ -1120,8 +1123,9 @@ EXTERN char_u e_winheight[] INIT(= N_(
EXTERN char_u e_winwidth[] INIT(= N_( EXTERN char_u e_winwidth[] INIT(= N_(
"E592: 'winwidth' cannot be smaller than 'winminwidth'")); "E592: 'winwidth' cannot be smaller than 'winminwidth'"));
EXTERN char_u e_write[] INIT(= N_("E80: Error while writing")); EXTERN char_u e_write[] INIT(= N_("E80: Error while writing"));
EXTERN char_u e_zerocount[] INIT(= N_("Zero count")); EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required"));
EXTERN char_u e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context")); EXTERN char_u e_usingsid[] INIT(= N_(
"E81: Using <SID> not in a script context"));
EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s"));
EXTERN char_u e_maxmempat[] INIT(= N_( EXTERN char_u e_maxmempat[] INIT(= N_(
"E363: pattern uses more memory than 'maxmempattern'")); "E363: pattern uses more memory than 'maxmempattern'"));

View File

@ -515,34 +515,41 @@ int cin_isscopedecl(char_u *s)
/* Maximum number of lines to search back for a "namespace" line. */ /* Maximum number of lines to search back for a "namespace" line. */
#define FIND_NAMESPACE_LIM 20 #define FIND_NAMESPACE_LIM 20
/* // Recognize a "namespace" scope declaration.
* Recognize a "namespace" scope declaration. static bool cin_is_cpp_namespace(char_u *s)
*/
static int cin_is_cpp_namespace(char_u *s)
{ {
char_u *p; char_u *p;
int has_name = FALSE; bool has_name = false;
bool has_name_start = false;
s = cin_skipcomment(s); s = cin_skipcomment(s);
if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) { if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) {
p = cin_skipcomment(skipwhite(s + 9)); p = cin_skipcomment(skipwhite(s + 9));
while (*p != NUL) { while (*p != NUL) {
if (ascii_iswhite(*p)) { if (ascii_iswhite(*p)) {
has_name = TRUE; /* found end of a name */ has_name = true; // found end of a name
p = cin_skipcomment(skipwhite(p)); p = cin_skipcomment(skipwhite(p));
} else if (*p == '{') { } else if (*p == '{') {
break; break;
} else if (vim_iswordc(*p)) { } else if (vim_iswordc(*p)) {
if (has_name) has_name_start = true;
return FALSE; /* word character after skipping past name */ if (has_name) {
++p; return false; // word character after skipping past name
}
p++;
} else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) {
if (!has_name_start || has_name) {
return false;
}
// C++ 17 nested namespace
p += 3;
} else { } else {
return FALSE; return false;
} }
} }
return TRUE; return true;
} }
return FALSE; return false;
} }
/* /*
@ -727,16 +734,20 @@ static int cin_ispreproc(char_u *s)
return FALSE; return FALSE;
} }
/* /// Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a
* Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a /// continuation line of a preprocessor statement. Decrease "*lnump" to the
* continuation line of a preprocessor statement. Decrease "*lnump" to the /// start and return the line in "*pp".
* start and return the line in "*pp". /// Put the amount of indent in "*amount".
*/ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount)
static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump)
{ {
char_u *line = *pp; char_u *line = *pp;
linenr_T lnum = *lnump; linenr_T lnum = *lnump;
int retval = FALSE; int retval = false;
int candidate_amount = *amount;
if (*line != NUL && line[STRLEN(line) - 1] == '\\') {
candidate_amount = get_indent_lnum(lnum);
}
for (;; ) { for (;; ) {
if (cin_ispreproc(line)) { if (cin_ispreproc(line)) {
@ -751,8 +762,12 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump)
break; break;
} }
if (lnum != *lnump) if (lnum != *lnump) {
*pp = ml_get(*lnump); *pp = ml_get(*lnump);
}
if (retval) {
*amount = candidate_amount;
}
return retval; return retval;
} }
@ -1987,10 +2002,12 @@ int get_c_indent(void)
amount = -1; amount = -1;
for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) { for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) {
l = skipwhite(ml_get(lnum)); l = skipwhite(ml_get(lnum));
if (cin_nocode(l)) /* skip comment lines */ if (cin_nocode(l)) { // skip comment lines
continue; continue;
if (cin_ispreproc_cont(&l, &lnum)) }
continue; /* ignore #define, #if, etc. */ if (cin_ispreproc_cont(&l, &lnum, &amount)) {
continue; // ignore #define, #if, etc.
}
curwin->w_cursor.lnum = lnum; curwin->w_cursor.lnum = lnum;
/* Skip a comment or raw string. XXX */ /* Skip a comment or raw string. XXX */
@ -2346,15 +2363,14 @@ int get_c_indent(void)
* up with it. * up with it.
*/ */
if (curwin->w_cursor.lnum <= ourscope) { if (curwin->w_cursor.lnum <= ourscope) {
/* we reached end of scope: // We reached end of scope:
* if looking for an enum or structure initialization // If looking for a enum or structure initialization
* go further back: // go further back:
* if it is an initializer (enum xxx or xxx =), then // If it is an initializer (enum xxx or xxx =), then
* don't add ind_continuation, otherwise it is a variable // don't add ind_continuation, otherwise it is a variable
* declaration: // declaration:
* int x, // int x,
* here; <-- add ind_continuation // here; <-- add ind_continuation
*/
if (lookfor == LOOKFOR_ENUM_OR_INIT) { if (lookfor == LOOKFOR_ENUM_OR_INIT) {
if (curwin->w_cursor.lnum == 0 if (curwin->w_cursor.lnum == 0
|| curwin->w_cursor.lnum || curwin->w_cursor.lnum
@ -2382,11 +2398,12 @@ int get_c_indent(void)
continue; continue;
} }
/* //
* Skip preprocessor directives and blank lines. // Skip preprocessor directives and blank lines.
*/ //
if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) {
continue; continue;
}
if (cin_nocode(l)) if (cin_nocode(l))
continue; continue;
@ -2490,9 +2507,10 @@ int get_c_indent(void)
continue; continue;
} }
/* Skip preprocessor directives and blank lines. */ // Skip preprocessor directives and blank lines.
if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) {
continue; continue;
}
/* Finally the actual check for "namespace". */ /* Finally the actual check for "namespace". */
if (cin_is_cpp_namespace(l)) { if (cin_is_cpp_namespace(l)) {
@ -2655,9 +2673,10 @@ int get_c_indent(void)
* unlocked it) * unlocked it)
*/ */
l = get_cursor_line_ptr(); l = get_cursor_line_ptr();
if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum) if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)
|| cin_nocode(l)) || cin_nocode(l)) {
continue; continue;
}
/* /*
* Are we at the start of a cpp base class declaration or * Are we at the start of a cpp base class declaration or
@ -3302,11 +3321,12 @@ term_again:
break; break;
} }
/* //
* Skip preprocessor directives and blank lines. // Skip preprocessor directives and blank lines.
*/ //
if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) {
continue; continue;
}
if (cin_nocode(l)) if (cin_nocode(l))
continue; continue;
@ -3398,9 +3418,10 @@ term_again:
while (curwin->w_cursor.lnum > 1) { while (curwin->w_cursor.lnum > 1) {
look = ml_get(--curwin->w_cursor.lnum); look = ml_get(--curwin->w_cursor.lnum);
if (!(cin_nocode(look) || cin_ispreproc_cont( if (!(cin_nocode(look)
&look, &curwin->w_cursor.lnum))) || cin_ispreproc_cont(&look, &curwin->w_cursor.lnum, &amount))) {
break; break;
}
} }
if (curwin->w_cursor.lnum > 0 if (curwin->w_cursor.lnum > 0
&& cin_ends_in(look, (char_u *)"}", NULL)) && cin_ends_in(look, (char_u *)"}", NULL))

View File

@ -139,154 +139,153 @@ static char_u modifier_keys_table[] =
}; };
static const struct key_name_entry { static const struct key_name_entry {
int key; ///< Special key code or ASCII value. int key; // Special key code or ascii value
const char *name; ///< Name of the key const char *name; // Name of key
} key_names_table[] = { } key_names_table[] =
{' ', "Space"}, {
{TAB, "Tab"}, { ' ', "Space" },
{K_TAB, "Tab"}, { TAB, "Tab" },
{NL, "NL"}, { K_TAB, "Tab" },
{NL, "NewLine"}, // Alternative name { NL, "NL" },
{NL, "LineFeed"}, // Alternative name { NL, "NewLine" }, // Alternative name
{NL, "LF"}, // Alternative name { NL, "LineFeed" }, // Alternative name
{CAR, "CR"}, { NL, "LF" }, // Alternative name
{CAR, "Return"}, // Alternative name { CAR, "CR" },
{CAR, "Enter"}, // Alternative name { CAR, "Return" }, // Alternative name
{K_BS, "BS"}, { CAR, "Enter" }, // Alternative name
{K_BS, "BackSpace"}, // Alternative name { K_BS, "BS" },
{ESC, "Esc"}, { K_BS, "BackSpace" }, // Alternative name
{CSI, "CSI"}, { ESC, "Esc" },
{K_CSI, "xCSI"}, { CSI, "CSI" },
{'|', "Bar"}, { K_CSI, "xCSI" },
{'\\', "Bslash"}, { '|', "Bar" },
{K_DEL, "Del"}, { '\\', "Bslash" },
{K_DEL, "Delete"}, // Alternative name { K_DEL, "Del" },
{K_KDEL, "kDel"}, { K_DEL, "Delete" }, // Alternative name
{K_UP, "Up"}, { K_KDEL, "kDel" },
{K_DOWN, "Down"}, { K_UP, "Up" },
{K_LEFT, "Left"}, { K_DOWN, "Down" },
{K_RIGHT, "Right"}, { K_LEFT, "Left" },
{K_XUP, "xUp"}, { K_RIGHT, "Right" },
{K_XDOWN, "xDown"}, { K_XUP, "xUp" },
{K_XLEFT, "xLeft"}, { K_XDOWN, "xDown" },
{K_XRIGHT, "xRight"}, { K_XLEFT, "xLeft" },
{ K_XRIGHT, "xRight" },
{K_F1, "F1"}, { K_F1, "F1" },
{K_F2, "F2"}, { K_F2, "F2" },
{K_F3, "F3"}, { K_F3, "F3" },
{K_F4, "F4"}, { K_F4, "F4" },
{K_F5, "F5"}, { K_F5, "F5" },
{K_F6, "F6"}, { K_F6, "F6" },
{K_F7, "F7"}, { K_F7, "F7" },
{K_F8, "F8"}, { K_F8, "F8" },
{K_F9, "F9"}, { K_F9, "F9" },
{K_F10, "F10"}, { K_F10, "F10" },
{K_F11, "F11"}, { K_F11, "F11" },
{K_F12, "F12"}, { K_F12, "F12" },
{K_F13, "F13"}, { K_F13, "F13" },
{K_F14, "F14"}, { K_F14, "F14" },
{K_F15, "F15"}, { K_F15, "F15" },
{K_F16, "F16"}, { K_F16, "F16" },
{K_F17, "F17"}, { K_F17, "F17" },
{K_F18, "F18"}, { K_F18, "F18" },
{K_F19, "F19"}, { K_F19, "F19" },
{K_F20, "F20"}, { K_F20, "F20" },
{K_F21, "F21"}, { K_F21, "F21" },
{K_F22, "F22"}, { K_F22, "F22" },
{K_F23, "F23"}, { K_F23, "F23" },
{K_F24, "F24"}, { K_F24, "F24" },
{K_F25, "F25"}, { K_F25, "F25" },
{K_F26, "F26"}, { K_F26, "F26" },
{K_F27, "F27"}, { K_F27, "F27" },
{K_F28, "F28"}, { K_F28, "F28" },
{K_F29, "F29"}, { K_F29, "F29" },
{K_F30, "F30"}, { K_F30, "F30" },
{K_F31, "F31"}, { K_F31, "F31" },
{K_F32, "F32"}, { K_F32, "F32" },
{K_F33, "F33"}, { K_F33, "F33" },
{K_F34, "F34"}, { K_F34, "F34" },
{K_F35, "F35"}, { K_F35, "F35" },
{K_F36, "F36"}, { K_F36, "F36" },
{K_F37, "F37"}, { K_F37, "F37" },
{K_XF1, "xF1"}, { K_XF1, "xF1" },
{K_XF2, "xF2"}, { K_XF2, "xF2" },
{K_XF3, "xF3"}, { K_XF3, "xF3" },
{K_XF4, "xF4"}, { K_XF4, "xF4" },
{K_HELP, "Help"}, { K_HELP, "Help" },
{K_UNDO, "Undo"}, { K_UNDO, "Undo" },
{K_INS, "Insert"}, { K_INS, "Insert" },
{K_INS, "Ins"}, // Alternative name { K_INS, "Ins" }, // Alternative name
{K_KINS, "kInsert"}, { K_KINS, "kInsert" },
{K_HOME, "Home"}, { K_HOME, "Home" },
{K_KHOME, "kHome"}, { K_KHOME, "kHome" },
{K_XHOME, "xHome"}, { K_XHOME, "xHome" },
{K_ZHOME, "zHome"}, { K_ZHOME, "zHome" },
{K_END, "End"}, { K_END, "End" },
{K_KEND, "kEnd"}, { K_KEND, "kEnd" },
{K_XEND, "xEnd"}, { K_XEND, "xEnd" },
{K_ZEND, "zEnd"}, { K_ZEND, "zEnd" },
{K_PAGEUP, "PageUp"}, { K_PAGEUP, "PageUp" },
{K_PAGEDOWN, "PageDown"}, { K_PAGEDOWN, "PageDown" },
{K_KPAGEUP, "kPageUp"}, { K_KPAGEUP, "kPageUp" },
{K_KPAGEDOWN, "kPageDown"}, { K_KPAGEDOWN, "kPageDown" },
{K_KPLUS, "kPlus"}, { K_KPLUS, "kPlus" },
{K_KMINUS, "kMinus"}, { K_KMINUS, "kMinus" },
{K_KDIVIDE, "kDivide"}, { K_KDIVIDE, "kDivide" },
{K_KMULTIPLY, "kMultiply"}, { K_KMULTIPLY, "kMultiply" },
{K_KENTER, "kEnter"}, { K_KENTER, "kEnter" },
{K_KPOINT, "kPoint"}, { K_KPOINT, "kPoint" },
{K_K0, "k0"}, { K_K0, "k0" },
{K_K1, "k1"}, { K_K1, "k1" },
{K_K2, "k2"}, { K_K2, "k2" },
{K_K3, "k3"}, { K_K3, "k3" },
{K_K4, "k4"}, { K_K4, "k4" },
{K_K5, "k5"}, { K_K5, "k5" },
{K_K6, "k6"}, { K_K6, "k6" },
{K_K7, "k7"}, { K_K7, "k7" },
{K_K8, "k8"}, { K_K8, "k8" },
{K_K9, "k9"}, { K_K9, "k9" },
{'<', "lt"}, { '<', "lt" },
{K_MOUSE, "Mouse"}, { K_MOUSE, "Mouse" },
{K_LEFTMOUSE, "LeftMouse"}, { K_LEFTMOUSE, "LeftMouse" },
{K_LEFTMOUSE_NM, "LeftMouseNM"}, { K_LEFTMOUSE_NM, "LeftMouseNM" },
{K_LEFTDRAG, "LeftDrag"}, { K_LEFTDRAG, "LeftDrag" },
{K_LEFTRELEASE, "LeftRelease"}, { K_LEFTRELEASE, "LeftRelease" },
{K_LEFTRELEASE_NM, "LeftReleaseNM"}, { K_LEFTRELEASE_NM, "LeftReleaseNM" },
{K_MIDDLEMOUSE, "MiddleMouse"}, { K_MIDDLEMOUSE, "MiddleMouse" },
{K_MIDDLEDRAG, "MiddleDrag"}, { K_MIDDLEDRAG, "MiddleDrag" },
{K_MIDDLERELEASE, "MiddleRelease"}, { K_MIDDLERELEASE, "MiddleRelease" },
{K_RIGHTMOUSE, "RightMouse"}, { K_RIGHTMOUSE, "RightMouse" },
{K_RIGHTDRAG, "RightDrag"}, { K_RIGHTDRAG, "RightDrag" },
{K_RIGHTRELEASE, "RightRelease"}, { K_RIGHTRELEASE, "RightRelease" },
{K_MOUSEDOWN, "ScrollWheelUp"}, { K_MOUSEDOWN, "ScrollWheelUp" },
{K_MOUSEUP, "ScrollWheelDown"}, { K_MOUSEUP, "ScrollWheelDown" },
{K_MOUSELEFT, "ScrollWheelRight"}, { K_MOUSELEFT, "ScrollWheelRight" },
{K_MOUSERIGHT, "ScrollWheelLeft"}, { K_MOUSERIGHT, "ScrollWheelLeft" },
{K_MOUSEDOWN, "MouseDown"}, // OBSOLETE: Use ScrollWheelXXX instead { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use
{K_MOUSEUP, "MouseUp"}, // Same { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead
{K_X1MOUSE, "X1Mouse"}, { K_X1MOUSE, "X1Mouse" },
{K_X1DRAG, "X1Drag"}, { K_X1DRAG, "X1Drag" },
{K_X1RELEASE, "X1Release"}, { K_X1RELEASE, "X1Release" },
{K_X2MOUSE, "X2Mouse"}, { K_X2MOUSE, "X2Mouse" },
{K_X2DRAG, "X2Drag"}, { K_X2DRAG, "X2Drag" },
{K_X2RELEASE, "X2Release"}, { K_X2RELEASE, "X2Release" },
{K_DROP, "Drop"}, { K_DROP, "Drop" },
{K_ZERO, "Nul"}, { K_ZERO, "Nul" },
{K_SNR, "SNR"}, { K_SNR, "SNR" },
{K_PLUG, "Plug"}, { K_PLUG, "Plug" },
{K_PASTE, "Paste"}, { K_PASTE, "Paste" },
{K_FOCUSGAINED, "FocusGained"}, { 0, NULL }
{K_FOCUSLOST, "FocusLost"},
{0, NULL}
}; };
static struct mousetable { static struct mousetable {
@ -720,8 +719,8 @@ int get_special_key_code(const char_u *name)
for (int i = 0; key_names_table[i].name != NULL; i++) { for (int i = 0; key_names_table[i].name != NULL; i++) {
const char *const table_name = key_names_table[i].name; const char *const table_name = key_names_table[i].name;
int j; int j;
for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++) { for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++)
if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) { if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j]))
break; break;
} }
} }

View File

@ -428,8 +428,6 @@ enum key_extra {
#define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN) #define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN)
#define K_DROP TERMCAP2KEY(KS_EXTRA, KE_DROP) #define K_DROP TERMCAP2KEY(KS_EXTRA, KE_DROP)
#define K_FOCUSGAINED TERMCAP2KEY(KS_EXTRA, KE_FOCUSGAINED)
#define K_FOCUSLOST TERMCAP2KEY(KS_EXTRA, KE_FOCUSLOST)
#define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT) #define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT)
#define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE) #define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE)

View File

@ -95,8 +95,12 @@ void log_unlock(void)
uv_mutex_unlock(&mutex); uv_mutex_unlock(&mutex);
} }
bool do_log(int log_level, const char *func_name, int line_num, bool eol, /// @param context description of a shared context or subsystem
const char* fmt, ...) FUNC_ATTR_UNUSED /// @param func_name function name, or NULL
/// @param line_num source line number, or -1
bool do_log(int log_level, const char *context, const char *func_name,
int line_num, bool eol, const char *fmt, ...)
FUNC_ATTR_UNUSED
{ {
if (log_level < MIN_LOG_LEVEL) { if (log_level < MIN_LOG_LEVEL) {
return false; return false;
@ -112,8 +116,8 @@ bool do_log(int log_level, const char *func_name, int line_num, bool eol,
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol, ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num,
fmt, args); eol, fmt, args);
va_end(args); va_end(args);
if (log_file != stderr && log_file != stdout) { if (log_file != stderr && log_file != stdout) {
@ -151,7 +155,7 @@ FILE *open_log_file(void)
static bool opening_log_file = false; static bool opening_log_file = false;
// check if it's a recursive call // check if it's a recursive call
if (opening_log_file) { if (opening_log_file) {
do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Cannot LOG() recursively."); "Cannot LOG() recursively.");
return stderr; return stderr;
} }
@ -171,7 +175,7 @@ FILE *open_log_file(void)
// - LOG() is called before early_init() // - LOG() is called before early_init()
// - Directory does not exist // - Directory does not exist
// - File is not writable // - File is not writable
do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true,
"Logging to stderr, failed to open $" LOG_FILE_ENV ": %s", "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s",
log_file_path); log_file_path);
return stderr; return stderr;
@ -201,7 +205,7 @@ void log_callstack_to_file(FILE *log_file, const char *const func_name,
// Now we have a command string like: // Now we have a command string like:
// addr2line -e /path/to/exe -f -p 0x123 0x456 ... // addr2line -e /path/to/exe -f -p 0x123 0x456 ...
do_log_to_file(log_file, DEBUG_LOG_LEVEL, func_name, line_num, true, do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true,
"trace:"); "trace:");
FILE *fp = popen(cmdbuf, "r"); FILE *fp = popen(cmdbuf, "r");
char linebuf[IOSIZE]; char linebuf[IOSIZE];
@ -230,27 +234,28 @@ end:
} }
#endif #endif
static bool do_log_to_file(FILE *log_file, int log_level, static bool do_log_to_file(FILE *log_file, int log_level, const char *context,
const char *func_name, int line_num, bool eol, const char *func_name, int line_num, bool eol,
const char* fmt, ...) const char* fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
bool ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol, bool ret = v_do_log_to_file(log_file, log_level, context, func_name,
fmt, args); line_num, eol, fmt, args);
va_end(args); va_end(args);
return ret; return ret;
} }
static bool v_do_log_to_file(FILE *log_file, int log_level, static bool v_do_log_to_file(FILE *log_file, int log_level,
const char *func_name, int line_num, bool eol, const char *context, const char *func_name,
const char* fmt, va_list args) int line_num, bool eol, const char *fmt,
va_list args)
{ {
static const char *log_levels[] = { static const char *log_levels[] = {
[DEBUG_LOG_LEVEL] = "DEBUG", [DEBUG_LOG_LEVEL] = "DEBUG",
[INFO_LOG_LEVEL] = "INFO ", [INFO_LOG_LEVEL] = "INFO ",
[WARNING_LOG_LEVEL] = "WARN ", [WARN_LOG_LEVEL] = "WARN ",
[ERROR_LOG_LEVEL] = "ERROR", [ERROR_LOG_LEVEL] = "ERROR",
}; };
assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL); assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL);
@ -268,8 +273,15 @@ static bool v_do_log_to_file(FILE *log_file, int log_level,
// print the log message prefixed by the current timestamp and pid // print the log message prefixed by the current timestamp and pid
int64_t pid = os_get_pid(); int64_t pid = os_get_pid();
if (fprintf(log_file, "%s %s %" PRId64 "/%s:%d: ", date_time, int rv = (line_num == -1 || func_name == NULL)
log_levels[log_level], pid, func_name, line_num) < 0) { ? fprintf(log_file, "%s %s %" PRId64 " %s", date_time,
log_levels[log_level], pid,
(context == NULL ? "?:" : context))
: fprintf(log_file, "%s %s %" PRId64 " %s%s:%d: ", date_time,
log_levels[log_level], pid,
(context == NULL ? "" : context),
func_name, line_num);
if (rv < 0) {
return false; return false;
} }
if (vfprintf(log_file, fmt, args) < 0) { if (vfprintf(log_file, fmt, args) < 0) {

View File

@ -6,7 +6,7 @@
#define DEBUG_LOG_LEVEL 0 #define DEBUG_LOG_LEVEL 0
#define INFO_LOG_LEVEL 1 #define INFO_LOG_LEVEL 1
#define WARNING_LOG_LEVEL 2 #define WARN_LOG_LEVEL 2
#define ERROR_LOG_LEVEL 3 #define ERROR_LOG_LEVEL 3
#define DLOG(...) #define DLOG(...)
@ -22,42 +22,42 @@
# define MIN_LOG_LEVEL INFO_LOG_LEVEL # define MIN_LOG_LEVEL INFO_LOG_LEVEL
#endif #endif
#define LOG(level, ...) do_log((level), __func__, __LINE__, true, \ #define LOG(level, ...) do_log((level), NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
# undef DLOG # undef DLOG
# undef DLOGN # undef DLOGN
# define DLOG(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, true, \ # define DLOG(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define DLOGN(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, false, \ # define DLOGN(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif
#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL #if MIN_LOG_LEVEL <= INFO_LOG_LEVEL
# undef ILOG # undef ILOG
# undef ILOGN # undef ILOGN
# define ILOG(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, true, \ # define ILOG(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define ILOGN(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, false, \ # define ILOGN(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif
#if MIN_LOG_LEVEL <= WARNING_LOG_LEVEL #if MIN_LOG_LEVEL <= WARN_LOG_LEVEL
# undef WLOG # undef WLOG
# undef WLOGN # undef WLOGN
# define WLOG(...) do_log(WARNING_LOG_LEVEL, __func__, __LINE__, true, \ # define WLOG(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define WLOGN(...) do_log(WARNING_LOG_LEVEL, __func__, __LINE__, false, \ # define WLOGN(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif
#if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL #if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL
# undef ELOG # undef ELOG
# undef ELOGN # undef ELOGN
# define ELOG(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, true, \ # define ELOG(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \
__VA_ARGS__) __VA_ARGS__)
# define ELOGN(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, false, \ # define ELOGN(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \
__VA_ARGS__) __VA_ARGS__)
#endif #endif

View File

@ -7,6 +7,11 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#ifdef WIN32
# include <wchar.h>
# include <winnls.h>
#endif
#include <msgpack.h> #include <msgpack.h>
#include "nvim/ascii.h" #include "nvim/ascii.h"
@ -215,10 +220,22 @@ void early_init(void)
#ifdef MAKE_LIB #ifdef MAKE_LIB
int nvim_main(int argc, char **argv) int nvim_main(int argc, char **argv)
#elif defined(WIN32)
int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
#else #else
int main(int argc, char **argv) int main(int argc, char **argv)
#endif #endif
{ {
#if defined(WIN32) && !defined(MAKE_LIB)
char *argv[argc];
for (int i = 0; i < argc; i++) {
char *buf = NULL;
utf16_to_utf8(argv_w[i], &buf);
assert(buf);
argv[i] = buf;
}
#endif
argv0 = argv[0]; argv0 = argv[0];
char_u *fname = NULL; // file name from command line char_u *fname = NULL; // file name from command line
@ -632,6 +649,11 @@ void getout(int exitval)
/* Position the cursor again, the autocommands may have moved it */ /* Position the cursor again, the autocommands may have moved it */
ui_cursor_goto((int)Rows - 1, 0); ui_cursor_goto((int)Rows - 1, 0);
// Apply 'titleold'.
if (p_title && *p_titleold != NUL) {
ui_call_set_title(cstr_as_string((char *)p_titleold));
}
#if defined(USE_ICONV) && defined(DYNAMIC_ICONV) #if defined(USE_ICONV) && defined(DYNAMIC_ICONV)
iconv_end(); iconv_end();
#endif #endif
@ -1291,10 +1313,29 @@ static void set_window_layout(mparm_T *paramp)
static void load_plugins(void) static void load_plugins(void)
{ {
if (p_lpl) { if (p_lpl) {
source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); // NOLINT char_u *rtp_copy = NULL;
TIME_MSG("loading plugins");
ex_packloadall(NULL); // First add all package directories to 'runtimepath', so that their
// autoload directories can be found. Only if not done already with a
// :packloadall command.
// Make a copy of 'runtimepath', so that source_runtime does not use the
// pack directories.
if (!did_source_packages) {
rtp_copy = vim_strsave(p_rtp);
add_pack_start_dirs();
}
source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy,
(char_u *)"plugin/**/*.vim", // NOLINT
DIP_ALL | DIP_NOAFTER);
TIME_MSG("loading plugins");
xfree(rtp_copy);
// Only source "start" packages if not done already with a :packloadall
// command.
if (!did_source_packages) {
load_start_packages();
}
TIME_MSG("loading packages"); TIME_MSG("loading packages");
source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER);
@ -1878,54 +1919,47 @@ static void usage(void)
signal_stop(); // kill us with CTRL-C here, if you like signal_stop(); // kill us with CTRL-C here, if you like
mch_msg(_("Usage:\n")); mch_msg(_("Usage:\n"));
mch_msg(_(" nvim [arguments] [file ...] Edit specified file(s)\n")); mch_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
mch_msg(_(" nvim [arguments] - Read text from stdin\n")); mch_msg(_(" nvim [options] - Read text from stdin\n"));
mch_msg(_(" nvim [arguments] -t <tag> Edit file where tag is defined\n")); mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
mch_msg(_(" nvim [arguments] -q [errorfile] Edit file with first error\n")); mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
mch_msg(_("\nArguments:\n")); mch_msg(_("\nOptions:\n"));
mch_msg(_(" -- Only file names after this\n")); mch_msg(_(" -- Only file names after this\n"));
mch_msg(_(" + Start at end of file\n"));
mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n"));
mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"));
mch_msg("\n");
mch_msg(_(" -b Binary mode\n"));
mch_msg(_(" -d Diff mode\n"));
mch_msg(_(" -e, -E Ex mode, Improved Ex mode\n"));
mch_msg(_(" -es Silent (batch) mode\n"));
mch_msg(_(" -h, --help Print this help message\n"));
mch_msg(_(" -i <shada> Use this shada file\n"));
mch_msg(_(" -m Modifications (writing files) not allowed\n"));
mch_msg(_(" -M Modifications in text not allowed\n"));
mch_msg(_(" -n No swap file, use memory only\n"));
mch_msg(_(" -o[N] Open N windows (default: one per file)\n"));
mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n"));
mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n"));
mch_msg(_(" -r, -L List swap files\n"));
mch_msg(_(" -r <file> Recover edit state for this file\n"));
mch_msg(_(" -R Read-only mode\n"));
mch_msg(_(" -S <session> Source <session> after loading the first file\n"));
mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
mch_msg(_(" -u <config> Use this config file\n"));
mch_msg(_(" -v, --version Print version information\n"));
mch_msg(_(" -V[N][file] Verbose [level][file]\n"));
mch_msg(_(" -Z Restricted mode\n"));
mch_msg("\n");
mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
#if !defined(UNIX) #if !defined(UNIX)
mch_msg(_(" --literal Don't expand wildcards\n")); mch_msg(_(" --literal Don't expand wildcards\n"));
#endif #endif
mch_msg(_(" -e Ex mode\n")); mch_msg(_(" --noplugin Don't load plugins\n"));
mch_msg(_(" -E Improved Ex mode\n"));
mch_msg(_(" -s Silent (batch) mode (only for ex mode)\n"));
mch_msg(_(" -d Diff mode\n"));
mch_msg(_(" -R Read-only mode\n"));
mch_msg(_(" -Z Restricted mode\n"));
mch_msg(_(" -m Modifications (writing files) not allowed\n"));
mch_msg(_(" -M Modifications in text not allowed\n"));
mch_msg(_(" -b Binary mode\n"));
mch_msg(_(" -l Lisp mode\n"));
mch_msg(_(" -A Arabic mode\n"));
mch_msg(_(" -F Farsi mode\n"));
mch_msg(_(" -H Hebrew mode\n"));
mch_msg(_(" -V[N][file] Be verbose [level N][log messages to file]\n"));
mch_msg(_(" -D Debugging mode\n"));
mch_msg(_(" -n No swap file, use memory only\n"));
mch_msg(_(" -r, -L List swap files and exit\n"));
mch_msg(_(" -r <file> Recover crashed session\n"));
mch_msg(_(" -u <vimrc> Use <vimrc> instead of the default\n"));
mch_msg(_(" -i <shada> Use <shada> instead of the default\n"));
mch_msg(_(" --noplugin Don't load plugin scripts\n"));
mch_msg(_(" -o[N] Open N windows (default: one for each file)\n"));
mch_msg(_(" -O[N] Like -o but split vertically\n"));
mch_msg(_(" -p[N] Open N tab pages (default: one for each file)\n"));
mch_msg(_(" + Start at end of file\n"));
mch_msg(_(" +<linenum> Start at line <linenum>\n"));
mch_msg(_(" +/<pattern> Start at first occurrence of <pattern>\n"));
mch_msg(_(" --cmd <command> Execute <command> before loading any vimrc\n"));
mch_msg(_(" -c <command> Execute <command> after loading the first file\n"));
mch_msg(_(" -S <session> Source <session> after loading the first file\n"));
mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
mch_msg(_(" -w <scriptout> Append all typed characters to <scriptout>\n"));
mch_msg(_(" -W <scriptout> Write all typed characters to <scriptout>\n"));
mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n")); mch_msg(_("\nSee \":help startup-options\" for all options.\n"));
mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
mch_msg(_(" --headless Don't start a user interface\n"));
mch_msg(_(" -v, --version Print version information and exit\n"));
mch_msg(_(" -h, --help Print this help message and exit\n"));
} }

View File

@ -2164,16 +2164,21 @@ void do_check_cursorbind(void)
int restart_edit_save = restart_edit; int restart_edit_save = restart_edit;
restart_edit = true; restart_edit = true;
check_cursor(); check_cursor();
if (curwin->w_p_cul || curwin->w_p_cuc) {
validate_cursor();
}
restart_edit = restart_edit_save; restart_edit = restart_edit_save;
} }
/* Correct cursor for multi-byte character. */ // Correct cursor for multi-byte character.
if (has_mbyte) if (has_mbyte) {
mb_adjust_cursor(); mb_adjust_cursor();
}
redraw_later(VALID); redraw_later(VALID);
/* Only scroll when 'scrollbind' hasn't done this. */ // Only scroll when 'scrollbind' hasn't done this.
if (!curwin->w_p_scb) if (!curwin->w_p_scb) {
update_topline(); update_topline();
}
curwin->w_redr_status = true; curwin->w_redr_status = true;
} }
} }

View File

@ -62,7 +62,7 @@ typedef struct {
ChannelType type; ChannelType type;
msgpack_unpacker *unpacker; msgpack_unpacker *unpacker;
union { union {
Stream stream; Stream stream; // bidirectional (socket)
Process *proc; Process *proc;
struct { struct {
Stream in; Stream in;
@ -133,6 +133,9 @@ uint64_t channel_from_process(Process *proc, uint64_t id, char *source)
rstream_init(proc->out, 0); rstream_init(proc->out, 0);
rstream_start(proc->out, receive_msgpack, channel); rstream_start(proc->out, receive_msgpack, channel);
DLOG("ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id, proc->in,
proc->out);
return channel->id; return channel->id;
} }
@ -150,6 +153,9 @@ void channel_from_connection(SocketWatcher *watcher)
wstream_init(&channel->data.stream, 0); wstream_init(&channel->data.stream, 0);
rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE); rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE);
rstream_start(&channel->data.stream, receive_msgpack, channel); rstream_start(&channel->data.stream, receive_msgpack, channel);
DLOG("ch %" PRIu64 " in/out-stream=%p", channel->id,
&channel->data.stream);
} }
/// @param source description of source function, rplugin name, TCP addr, etc /// @param source description of source function, rplugin name, TCP addr, etc
@ -182,12 +188,11 @@ uint64_t channel_connect(bool tcp, const char *address, int timeout,
return channel->id; return channel->id;
} }
/// Sends event/arguments to channel /// Publishes an event to a channel.
/// ///
/// @param id The channel id. If 0, the event will be sent to all /// @param id Channel id. 0 means "broadcast to all subscribed channels"
/// channels that have subscribed to the event type /// @param name Event name (application-defined)
/// @param name The event name, an arbitrary string /// @param args Array of event arguments
/// @param args Array with event arguments
/// @return True if the event was sent successfully, false otherwise. /// @return True if the event was sent successfully, false otherwise.
bool channel_send_event(uint64_t id, const char *name, Array args) bool channel_send_event(uint64_t id, const char *name, Array args)
{ {
@ -209,7 +214,6 @@ bool channel_send_event(uint64_t id, const char *name, Array args)
send_event(channel, name, args); send_event(channel, name, args);
} }
} else { } else {
// TODO(tarruda): Implement event broadcasting in vimscript
broadcast_event(name, args); broadcast_event(name, args);
} }
@ -344,6 +348,9 @@ void channel_from_stdio(void)
rstream_start(&channel->data.std.in, receive_msgpack, channel); rstream_start(&channel->data.std.in, receive_msgpack, channel);
// write stream // write stream
wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0); wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0);
DLOG("ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id,
&channel->data.std.in, &channel->data.std.out);
} }
/// Creates a loopback channel. This is used to avoid deadlock /// Creates a loopback channel. This is used to avoid deadlock
@ -363,6 +370,7 @@ void channel_process_exit(uint64_t id, int status)
decref(channel); decref(channel);
} }
// rstream.c:read_event() invokes this as stream->read_cb().
static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c,
void *data, bool eof) void *data, bool eof)
{ {
@ -374,12 +382,24 @@ static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c,
char buf[256]; char buf[256];
snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client", snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client",
channel->id); channel->id);
call_set_error(channel, buf, WARNING_LOG_LEVEL); call_set_error(channel, buf, WARN_LOG_LEVEL);
goto end;
}
if ((chan_wstream(channel) != NULL && chan_wstream(channel)->closed)
|| (chan_rstream(channel) != NULL && chan_rstream(channel)->closed)) {
char buf[256];
snprintf(buf, sizeof(buf),
"ch %" PRIu64 ": stream closed unexpectedly. "
"closing channel",
channel->id);
call_set_error(channel, buf, WARN_LOG_LEVEL);
goto end; goto end;
} }
size_t count = rbuffer_size(rbuf); size_t count = rbuffer_size(rbuf);
DLOG("parsing %u bytes of msgpack data from Stream(%p)", count, stream); DLOG("ch %" PRIu64 ": parsing %u bytes from msgpack Stream: %p",
channel->id, count, stream);
// Feed the unpacker with data // Feed the unpacker with data
msgpack_unpacker_reserve_buffer(channel->unpacker, count); msgpack_unpacker_reserve_buffer(channel->unpacker, count);
@ -435,8 +455,8 @@ static void parse_msgpack(Channel *channel)
// causes for this error(search for 'goto _failed') // causes for this error(search for 'goto _failed')
// //
// A not so uncommon cause for this might be deserializing objects with // A not so uncommon cause for this might be deserializing objects with
// a high nesting level: msgpack will break when it's internal parse stack // a high nesting level: msgpack will break when its internal parse stack
// size exceeds MSGPACK_EMBED_STACK_SIZE(defined as 32 by default) // size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default)
send_error(channel, 0, "Invalid msgpack payload. " send_error(channel, 0, "Invalid msgpack payload. "
"This error can also happen when deserializing " "This error can also happen when deserializing "
"an object with high level of nesting"); "an object with high level of nesting");
@ -534,6 +554,39 @@ static void on_request_event(void **argv)
api_clear_error(&error); api_clear_error(&error);
} }
/// Returns the Stream that a Channel writes to.
static Stream *chan_wstream(Channel *chan)
{
switch (chan->type) {
case kChannelTypeSocket:
return &chan->data.stream;
case kChannelTypeProc:
return chan->data.proc->in;
case kChannelTypeStdio:
return &chan->data.std.out;
case kChannelTypeInternal:
return NULL;
}
abort();
}
/// Returns the Stream that a Channel reads from.
static Stream *chan_rstream(Channel *chan)
{
switch (chan->type) {
case kChannelTypeSocket:
return &chan->data.stream;
case kChannelTypeProc:
return chan->data.proc->out;
case kChannelTypeStdio:
return &chan->data.std.in;
case kChannelTypeInternal:
return NULL;
}
abort();
}
static bool channel_write(Channel *channel, WBuffer *buffer) static bool channel_write(Channel *channel, WBuffer *buffer)
{ {
bool success = false; bool success = false;
@ -545,13 +598,9 @@ static bool channel_write(Channel *channel, WBuffer *buffer)
switch (channel->type) { switch (channel->type) {
case kChannelTypeSocket: case kChannelTypeSocket:
success = wstream_write(&channel->data.stream, buffer);
break;
case kChannelTypeProc: case kChannelTypeProc:
success = wstream_write(channel->data.proc->in, buffer);
break;
case kChannelTypeStdio: case kChannelTypeStdio:
success = wstream_write(&channel->data.std.out, buffer); success = wstream_write(chan_wstream(channel), buffer);
break; break;
case kChannelTypeInternal: case kChannelTypeInternal:
incref(channel); incref(channel);
@ -565,8 +614,8 @@ static bool channel_write(Channel *channel, WBuffer *buffer)
char buf[256]; char buf[256];
snprintf(buf, snprintf(buf,
sizeof(buf), sizeof(buf),
"Before returning from a RPC call, ch %" PRIu64 " was " "ch %" PRIu64 ": stream write failed. "
"closed due to a failed write", "RPC canceled; closing channel",
channel->id); channel->id);
call_set_error(channel, buf, ERROR_LOG_LEVEL); call_set_error(channel, buf, ERROR_LOG_LEVEL);
} }
@ -817,6 +866,7 @@ static void call_set_error(Channel *channel, char *msg, int loglevel)
ChannelCallFrame *frame = kv_A(channel->call_stack, i); ChannelCallFrame *frame = kv_A(channel->call_stack, i);
frame->returned = true; frame->returned = true;
frame->errored = true; frame->errored = true;
api_free_object(frame->result);
frame->result = STRING_OBJ(cstr_to_string(msg)); frame->result = STRING_OBJ(cstr_to_string(msg));
} }

View File

@ -88,7 +88,12 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg)
{ {
bool ret = true; bool ret = true;
kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE; kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE;
kv_push(stack, ((MPToAPIObjectStackItem) { obj, arg, false, 0 })); kv_push(stack, ((MPToAPIObjectStackItem) {
.mobj = obj,
.aobj = arg,
.container = false,
.idx = 0,
}));
while (ret && kv_size(stack)) { while (ret && kv_size(stack)) {
MPToAPIObjectStackItem cur = kv_last(stack); MPToAPIObjectStackItem cur = kv_last(stack);
if (!cur.container) { if (!cur.container) {
@ -361,7 +366,7 @@ typedef struct {
size_t idx; size_t idx;
} APIToMPObjectStackItem; } APIToMPObjectStackItem;
/// Convert type used by Neovim API to msgpack /// Convert type used by Nvim API to msgpack type.
/// ///
/// @param[in] result Object to convert. /// @param[in] result Object to convert.
/// @param[out] res Structure that defines where conversion results are saved. /// @param[out] res Structure that defines where conversion results are saved.

View File

@ -13,6 +13,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "nvim/log.h"
#include "nvim/vim.h" #include "nvim/vim.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/normal.h" #include "nvim/normal.h"
@ -344,8 +345,6 @@ static const struct nv_cmd {
{ K_F8, farsi_f8, 0, 0 }, { K_F8, farsi_f8, 0, 0 },
{ K_F9, farsi_f9, 0, 0 }, { K_F9, farsi_f9, 0, 0 },
{ K_EVENT, nv_event, NV_KEEPREG, 0 }, { K_EVENT, nv_event, NV_KEEPREG, 0 },
{ K_FOCUSGAINED, nv_focusgained, NV_KEEPREG, 0 },
{ K_FOCUSLOST, nv_focuslost, NV_KEEPREG, 0 },
}; };
/* Number of commands in nv_cmds[]. */ /* Number of commands in nv_cmds[]. */
@ -1943,8 +1942,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
* the lines. */ * the lines. */
auto_format(false, true); auto_format(false, true);
if (restart_edit == 0) if (restart_edit == 0) {
restart_edit = restart_edit_save; restart_edit = restart_edit_save;
} else {
cap->retval |= CA_COMMAND_BUSY;
}
} }
break; break;
@ -7957,18 +7959,7 @@ static void nv_event(cmdarg_T *cap)
may_garbage_collect = false; may_garbage_collect = false;
multiqueue_process_events(main_loop.events); multiqueue_process_events(main_loop.events);
cap->retval |= CA_COMMAND_BUSY; // don't call edit() now cap->retval |= CA_COMMAND_BUSY; // don't call edit() now
} finish_op = false;
/// Trigger FocusGained event.
static void nv_focusgained(cmdarg_T *cap)
{
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
}
/// Trigger FocusLost event.
static void nv_focuslost(cmdarg_T *cap)
{
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
} }
/* /*

View File

@ -55,12 +55,11 @@ static yankreg_T y_regs[NUM_REGISTERS];
static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */ static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */
static bool clipboard_didwarn_unnamed = false;
// for behavior between start_batch_changes() and end_batch_changes()) // for behavior between start_batch_changes() and end_batch_changes())
static bool clipboard_delay_update = false; // delay clipboard update
static int batch_change_count = 0; // inside a script static int batch_change_count = 0; // inside a script
static bool clipboard_delay_update = false; // delay clipboard update
static bool clipboard_needs_update = false; // clipboard was updated static bool clipboard_needs_update = false; // clipboard was updated
static bool clipboard_didwarn = false;
/* /*
* structure used by block_prep, op_delete and op_yank for blockwise operators * structure used by block_prep, op_delete and op_yank for blockwise operators
@ -2061,7 +2060,7 @@ void op_insert(oparg_T *oap, long count1)
} }
t1 = oap->start; t1 = oap->start;
edit(NUL, false, (linenr_T)count1); (void)edit(NUL, false, (linenr_T)count1);
// When a tab was inserted, and the characters in front of the tab // When a tab was inserted, and the characters in front of the tab
// have been converted to a tab as well, the column of the cursor // have been converted to a tab as well, the column of the cursor
@ -5524,7 +5523,7 @@ int get_default_register_name(void)
} }
/// Determine if register `*name` should be used as a clipboard. /// Determine if register `*name` should be used as a clipboard.
/// In an unnammed operation, `*name` is `NUL` and will be adjusted to `'*'/'+'` if /// In an unnamed operation, `*name` is `NUL` and will be adjusted to */+ if
/// `clipboard=unnamed[plus]` is set. /// `clipboard=unnamed[plus]` is set.
/// ///
/// @param name The name of register, or `NUL` if unnamed. /// @param name The name of register, or `NUL` if unnamed.
@ -5535,33 +5534,41 @@ int get_default_register_name(void)
/// if the register isn't a clipboard or provider isn't available. /// if the register isn't a clipboard or provider isn't available.
static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
{ {
if (*name == '*' || *name == '+') { #define MSG_NO_CLIP "clipboard: No provider. " \
if(!eval_has_provider("clipboard")) { "Try \":CheckHealth\" or \":h clipboard\"."
if (!quiet) {
EMSG("clipboard: No provider. Try \":CheckHealth\" or " yankreg_T *target = NULL;
"\":h clipboard\"."); bool explicit_cb_reg = (*name == '*' || *name == '+');
} bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK);
return NULL; if (!explicit_cb_reg && !implicit_cb_reg) {
} goto end;
return &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; }
} else if ((*name == NUL) && (cb_flags & CB_UNNAMEDMASK)) {
if(!eval_has_provider("clipboard")) { if (!eval_has_provider("clipboard")) {
if (!quiet && !clipboard_didwarn_unnamed) { if (batch_change_count == 1 && !quiet
msg((char_u *)"clipboard: No provider. Try \":CheckHealth\" or " && (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) {
"\":h clipboard\"."); clipboard_didwarn = true;
clipboard_didwarn_unnamed = true; // Do NOT error (emsg()) here--if it interrupts :redir we get into
} // a weird state, stuck in "redirect mode".
return NULL; msg((char_u *)MSG_NO_CLIP);
} }
// ... else, be silent (don't flood during :while, :redir, etc.).
goto end;
}
if (explicit_cb_reg) {
target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
goto end;
} else { // unnamed register: "implicit" clipboard
if (writing && clipboard_delay_update) { if (writing && clipboard_delay_update) {
// For "set" (copy), defer the clipboard call.
clipboard_needs_update = true; clipboard_needs_update = true;
return NULL; goto end;
} else if (!writing && clipboard_needs_update) { } else if (!writing && clipboard_needs_update) {
// use the internal value // For "get" (paste), use the internal value.
return NULL; goto end;
} }
yankreg_T *target;
if (cb_flags & CB_UNNAMEDPLUS) { if (cb_flags & CB_UNNAMEDPLUS) {
*name = (cb_flags & CB_UNNAMED && writing) ? '"': '+'; *name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';
target = &y_regs[PLUS_REGISTER]; target = &y_regs[PLUS_REGISTER];
@ -5569,10 +5576,11 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
*name = '*'; *name = '*';
target = &y_regs[STAR_REGISTER]; target = &y_regs[STAR_REGISTER];
} }
return target; // unnamed register goto end;
} }
// don't do anything for other register names
return NULL; end:
return target;
} }
static bool get_clipboard(int name, yankreg_T **target, bool quiet) static bool get_clipboard(int name, yankreg_T **target, bool quiet)
@ -5740,17 +5748,16 @@ static void set_clipboard(int name, yankreg_T *reg)
(void)eval_call_provider("clipboard", "set", args); (void)eval_call_provider("clipboard", "set", args);
} }
/// Avoid clipboard (slow) during batch operations (i.e., a script). /// Avoid slow things (clipboard) during batch operations (while/for-loops).
void start_batch_changes(void) void start_batch_changes(void)
{ {
if (++batch_change_count > 1) { if (++batch_change_count > 1) {
return; return;
} }
clipboard_delay_update = true; clipboard_delay_update = true;
clipboard_needs_update = false;
} }
/// Update the clipboard after batch changes finished. /// Counterpart to start_batch_changes().
void end_batch_changes(void) void end_batch_changes(void)
{ {
if (--batch_change_count > 0) { if (--batch_change_count > 0) {
@ -5759,11 +5766,37 @@ void end_batch_changes(void)
} }
clipboard_delay_update = false; clipboard_delay_update = false;
if (clipboard_needs_update) { if (clipboard_needs_update) {
set_clipboard(NUL, y_previous); // must be before, as set_clipboard will invoke
// start/end_batch_changes recursively
clipboard_needs_update = false; clipboard_needs_update = false;
// unnamed ("implicit" clipboard)
set_clipboard(NUL, y_previous);
} }
} }
int save_batch_count(void)
{
int save_count = batch_change_count;
batch_change_count = 0;
clipboard_delay_update = false;
if (clipboard_needs_update) {
clipboard_needs_update = false;
// unnamed ("implicit" clipboard)
set_clipboard(NUL, y_previous);
}
return save_count;
}
void restore_batch_count(int save_count)
{
assert(batch_change_count == 0);
batch_change_count = save_count;
if (batch_change_count > 0) {
clipboard_delay_update = true;
}
}
/// Check whether register is empty /// Check whether register is empty
static inline bool reg_empty(const yankreg_T *const reg) static inline bool reg_empty(const yankreg_T *const reg)
FUNC_ATTR_PURE FUNC_ATTR_PURE

View File

@ -1749,7 +1749,7 @@ do_set (
if (flags & P_FLAGLIST) { if (flags & P_FLAGLIST) {
// Remove flags that appear twice. // Remove flags that appear twice.
for (s = newval; *s; s++) { for (s = newval; *s;) {
// if options have P_FLAGLIST and P_ONECOMMA such as // if options have P_FLAGLIST and P_ONECOMMA such as
// 'whichwrap' // 'whichwrap'
if (flags & P_ONECOMMA) { if (flags & P_ONECOMMA) {
@ -1757,15 +1757,16 @@ do_set (
&& vim_strchr(s + 2, *s) != NULL) { && vim_strchr(s + 2, *s) != NULL) {
// Remove the duplicated value and the next comma. // Remove the duplicated value and the next comma.
STRMOVE(s, s + 2); STRMOVE(s, s + 2);
s -= 2; continue;
} }
} else { } else {
if ((!(flags & P_COMMA) || *s != ',') if ((!(flags & P_COMMA) || *s != ',')
&& vim_strchr(s + 1, *s) != NULL) { && vim_strchr(s + 1, *s) != NULL) {
STRMOVE(s, s + 1); STRMOVE(s, s + 1);
s--; continue;
} }
} }
s++;
} }
} }
@ -2996,9 +2997,10 @@ did_set_string_option (
if (s[-1] == 'k' || s[-1] == 's') { if (s[-1] == 'k' || s[-1] == 's') {
/* skip optional filename after 'k' and 's' */ /* skip optional filename after 'k' and 's' */
while (*s && *s != ',' && *s != ' ') { while (*s && *s != ',' && *s != ' ') {
if (*s == '\\') if (*s == '\\' && s[1] != NUL) {
++s; s++;
++s; }
s++;
} }
} else { } else {
if (errbuf != NULL) { if (errbuf != NULL) {

View File

@ -2498,7 +2498,7 @@ return {
no_mkrc=true, no_mkrc=true,
vi_def=true, vi_def=true,
varname='p_titleold', varname='p_titleold',
defaults={if_true={vi=N_("Thanks for flying Vim")}} defaults={if_true={vi=N_("")}}
}, },
{ {
full_name='titlestring', full_name='titlestring',

View File

@ -1690,6 +1690,9 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (strlen(fname) > (len - 1)) { if (strlen(fname) > (len - 1)) {
xstrlcpy(buf, fname, len); // truncate xstrlcpy(buf, fname, len); // truncate
#ifdef WIN32
slash_adjust(buf);
#endif
return FAIL; return FAIL;
} }
@ -1702,6 +1705,9 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
if (rv == FAIL) { if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename xstrlcpy(buf, fname, len); // something failed; use the filename
} }
#ifdef WIN32
slash_adjust(buf);
#endif
return rv; return rv;
} }
@ -2196,11 +2202,11 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf,
// expand it if forced or not an absolute path // expand it if forced or not an absolute path
if (force || !path_is_absolute_path(fname)) { if (force || !path_is_absolute_path(fname)) {
if ((p = vim_strrchr(fname, '/')) != NULL) { if ((p = vim_strrchr(fname, PATHSEP)) != NULL) {
// relative to root // relative to root
if (p == fname) { if (p == fname) {
// only one path component // only one path component
relative_directory[0] = '/'; relative_directory[0] = PATHSEP;
relative_directory[1] = NUL; relative_directory[1] = NUL;
} else { } else {
assert(p >= fname); assert(p >= fname);

View File

@ -3316,6 +3316,47 @@ bt_regexec_nl (
return (int)r; return (int)r;
} }
/// Wrapper around strchr which accounts for case-insensitive searches and
/// non-ASCII characters.
///
/// This function is used a lot for simple searches, keep it fast!
///
/// @param s string to search
/// @param c character to find in @a s
///
/// @return NULL if no match, otherwise pointer to the position in @a s
static inline char_u *cstrchr(const char_u *const s, const int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_ALWAYS_INLINE
{
if (!rex.reg_ic) {
return vim_strchr(s, c);
}
// Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is
// expected to be highly optimized.
if (c > 0x80) {
const int folded_c = utf_fold(c);
for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) {
if (utf_fold(utf_ptr2char(p)) == folded_c) {
return (char_u *)p;
}
}
return NULL;
}
int cc;
if (ASCII_ISUPPER(c)) {
cc = TOLOWER_ASC(c);
} else if (ASCII_ISLOWER(c)) {
cc = TOUPPER_ASC(c);
} else {
return vim_strchr(s, c);
}
char tofind[] = { (char)c, (char)cc, NUL };
return (char_u *)strpbrk((const char *)s, tofind);
}
/// Matches a regexp against multiple lines. /// Matches a regexp against multiple lines.
/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). /// "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
@ -6320,42 +6361,6 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n)
return result; return result;
} }
/*
* cstrchr: This function is used a lot for simple searches, keep it fast!
*/
static inline char_u *cstrchr(const char_u *const s, const int c)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_ALWAYS_INLINE
{
if (!rex.reg_ic) {
return vim_strchr(s, c);
}
// Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is
// expected to be highly optimized.
if (c > 0x80) {
const int folded_c = utf_fold(c);
for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) {
if (utf_fold(utf_ptr2char(p)) == folded_c) {
return (char_u *)p;
}
}
return NULL;
}
int cc;
if (ASCII_ISUPPER(c)) {
cc = TOLOWER_ASC(c);
} else if (ASCII_ISLOWER(c)) {
cc = TOUPPER_ASC(c);
} else {
return vim_strchr(s, c);
}
char tofind[] = { (char)c, (char)cc, NUL };
return (char_u *)strpbrk((const char *)s, tofind);
}
/*************************************************************** /***************************************************************
* regsub stuff * * regsub stuff *
***************************************************************/ ***************************************************************/

View File

@ -2201,16 +2201,17 @@ win_line (
int change_end = -1; /* last col of changed area */ int change_end = -1; /* last col of changed area */
colnr_T trailcol = MAXCOL; /* start of trailing spaces */ colnr_T trailcol = MAXCOL; /* start of trailing spaces */
int need_showbreak = false; // overlong line, skip first x chars int need_showbreak = false; // overlong line, skip first x chars
int line_attr = 0; /* attribute for the whole line */ int line_attr = 0; // attribute for the whole line
matchitem_T *cur; /* points to the match list */ int line_attr_low_priority = 0; // current line, lowest priority
match_T *shl; /* points to search_hl or a match */ matchitem_T *cur; // points to the match list
int shl_flag; /* flag to indicate whether search_hl match_T *shl; // points to search_hl or a match
has been processed or not */ int shl_flag; // flag to indicate whether search_hl
int prevcol_hl_flag; /* flag to indicate whether prevcol // has been processed or not
equals startcol of search_hl or one int prevcol_hl_flag; // flag to indicate whether prevcol
of the matches */ // equals startcol of search_hl or one
int prev_c = 0; /* previous Arabic character */ // of the matches
int prev_c1 = 0; /* first composing char for prev_c */ int prev_c = 0; // previous Arabic character
int prev_c1 = 0; // first composing char for prev_c
int did_line_attr = 0; int did_line_attr = 0;
bool search_attr_from_match = false; // if search_attr is from :match bool search_attr_from_match = false; // if search_attr is from :match
@ -2427,10 +2428,17 @@ win_line (
filler_lines = wp->w_topfill; filler_lines = wp->w_topfill;
filler_todo = filler_lines; filler_todo = filler_lines;
/* If this line has a sign with line highlighting set line_attr. */ // 'cursorline' highlighting for the current window. Not when Visual mode is
// active, because it's not clear what is selected then.
if (wp->w_p_cul && lnum == wp->w_cursor.lnum
&& !(wp == curwin && VIsual_active)) {
line_attr_low_priority = win_hl_attr(wp, HLF_CUL);
}
v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL); v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
if (v != 0) if (v != 0) {
line_attr = sign_get_attr((int)v, TRUE); line_attr = sign_get_attr((int)v, true);
}
// Highlight the current line in the quickfix window. // Highlight the current line in the quickfix window.
if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) { if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
@ -2441,7 +2449,7 @@ win_line (
line_attr = hl_combine_attr(wp->w_hl_attr_normal, line_attr); line_attr = hl_combine_attr(wp->w_hl_attr_normal, line_attr);
} }
if (line_attr != 0) { if (line_attr_low_priority || line_attr) {
area_highlighting = true; area_highlighting = true;
} }
@ -2663,20 +2671,6 @@ win_line (
cur = cur->next; cur = cur->next;
} }
/* Cursor line highlighting for 'cursorline' in the current window. Not
* when Visual mode is active, because it's not clear what is selected
* then. */
if (wp->w_p_cul && lnum == wp->w_cursor.lnum
&& !(wp == curwin && VIsual_active)) {
if (line_attr != 0 && !(State & INSERT) && bt_quickfix(wp->w_buffer)
&& qf_current_entry(wp) == lnum) {
line_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), line_attr);
} else {
line_attr = win_hl_attr(wp, HLF_CUL);
}
area_highlighting = true;
}
off = (unsigned)(current_ScreenLine - ScreenLines); off = (unsigned)(current_ScreenLine - ScreenLines);
col = 0; col = 0;
if (wp->w_p_rl) { if (wp->w_p_rl) {
@ -3594,15 +3588,15 @@ win_line (
&& lcs_eol_one > 0) { && lcs_eol_one > 0) {
// Display a '$' after the line or highlight an extra // Display a '$' after the line or highlight an extra
// character if the line break is included. // character if the line break is included.
// For a diff line the highlighting continues after the // For a diff line the highlighting continues after the "$".
// "$". if (diff_hlf == (hlf_T)0
if (diff_hlf == (hlf_T)0 && line_attr == 0) { && line_attr == 0
/* In virtualedit, visual selections may extend && line_attr_low_priority == 0) {
* beyond end of line. */ // In virtualedit, visual selections may extend beyond end of line.
if (area_highlighting && virtual_active() if (area_highlighting && virtual_active()
&& tocol != MAXCOL && vcol < tocol) && tocol != MAXCOL && vcol < tocol) {
n_extra = 0; n_extra = 0;
else { } else {
p_extra = at_end_str; p_extra = at_end_str;
n_extra = 1; n_extra = 1;
c_extra = NUL; c_extra = NUL;
@ -3661,7 +3655,7 @@ win_line (
(col < wp->w_width))) { (col < wp->w_width))) {
c = ' '; c = ' ';
ptr--; // put it back at the NUL ptr--; // put it back at the NUL
} else if ((diff_hlf != (hlf_T)0 || line_attr != 0) } else if ((diff_hlf != (hlf_T)0 || line_attr_low_priority || line_attr)
&& (wp->w_p_rl && (wp->w_p_rl
? (col >= 0) ? (col >= 0)
: (col - boguscols < wp->w_width))) { : (col - boguscols < wp->w_width))) {
@ -3673,7 +3667,8 @@ win_line (
did_line_attr++; did_line_attr++;
// don't do search HL for the rest of the line // don't do search HL for the rest of the line
if (line_attr != 0 && char_attr == search_attr && col > 0) { if ((line_attr_low_priority || line_attr)
&& char_attr == search_attr && col > 0) {
char_attr = line_attr; char_attr = line_attr;
} }
if (diff_hlf == HLF_TXD) { if (diff_hlf == HLF_TXD) {
@ -4035,13 +4030,16 @@ win_line (
if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
&& lnum != wp->w_cursor.lnum) { && lnum != wp->w_cursor.lnum) {
vcol_save_attr = char_attr; vcol_save_attr = char_attr;
char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_CUC)); char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
} else if (draw_color_col && VCOL_HLC == *color_cols) { } else if (draw_color_col && VCOL_HLC == *color_cols) {
vcol_save_attr = char_attr; vcol_save_attr = char_attr;
char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_MC)); char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
} }
} }
// Apply `line_attr_low_priority` now, so that everthing can override it.
char_attr = hl_combine_attr(line_attr_low_priority, char_attr);
/* /*
* Store character to be displayed. * Store character to be displayed.
* Skip characters that are left of the screen for 'nowrap'. * Skip characters that are left of the screen for 'nowrap'.
@ -5847,7 +5845,7 @@ static void screen_start_highlight(int attr)
ui_start_highlight(attr); ui_start_highlight(attr);
} }
void screen_stop_highlight(void) static void screen_stop_highlight(void)
{ {
ui_stop_highlight(); ui_stop_highlight();
screen_attr = 0; screen_attr = 0;

View File

@ -3557,11 +3557,15 @@ extend:
--start_lnum; --start_lnum;
if (VIsual_active) { if (VIsual_active) {
/* Problem: when doing "Vipipip" nothing happens in a single white // Problem: when doing "Vipipip" nothing happens in a single white
* line, we get stuck there. Trap this here. */ // line, we get stuck there. Trap this here.
if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) {
goto extend; goto extend;
VIsual.lnum = start_lnum; }
if (VIsual.lnum != start_lnum) {
VIsual.lnum = start_lnum;
VIsual.col = 0;
}
VIsual_mode = 'V'; VIsual_mode = 'V';
redraw_curbuf_later(INVERTED); /* update the inversion */ redraw_curbuf_later(INVERTED); /* update the inversion */
showmode(); showmode();

View File

@ -26,10 +26,11 @@ void state_enter(VimState *s)
int check_result = s->check ? s->check(s) : 1; int check_result = s->check ? s->check(s) : 1;
if (!check_result) { if (!check_result) {
break; break; // Terminate this state.
} else if (check_result == -1) { } else if (check_result == -1) {
continue; continue; // check() again.
} }
// Execute this state.
int key; int key;
@ -48,11 +49,13 @@ getkey:
ui_flush(); ui_flush();
// Call `os_inchar` directly to block for events or user input without // Call `os_inchar` directly to block for events or user input without
// consuming anything from `input_buffer`(os/input.c) or calling the // consuming anything from `input_buffer`(os/input.c) or calling the
// mapping engine. If an event was put into the queue, we send K_EVENT // mapping engine.
// directly.
(void)os_inchar(NULL, 0, -1, 0); (void)os_inchar(NULL, 0, -1, 0);
input_disable_events(); input_disable_events();
key = !multiqueue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); // If an event was put into the queue, we send K_EVENT directly.
key = !multiqueue_empty(main_loop.events)
? K_EVENT
: safe_vgetc();
} }
if (key == K_EVENT) { if (key == K_EVENT) {

View File

@ -42,6 +42,7 @@
#include "nvim/ui.h" #include "nvim/ui.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
#include "nvim/os/time.h" #include "nvim/os/time.h"
#include "nvim/api/private/helpers.h"
static bool did_syntax_onoff = false; static bool did_syntax_onoff = false;
@ -81,7 +82,10 @@ struct hl_group {
// highlight groups for 'highlight' option // highlight groups for 'highlight' option
static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; static garray_T highlight_ga = GA_EMPTY_INIT_VALUE;
#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data))) static inline struct hl_group * HL_TABLE(void)
{
return ((struct hl_group *)((highlight_ga.ga_data)));
}
#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */ #define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
@ -100,10 +104,8 @@ static int include_none = 0; /* when 1 include "nvim/None" */
static int include_default = 0; /* when 1 include "nvim/default" */ static int include_default = 0; /* when 1 include "nvim/default" */
static int include_link = 0; /* when 2 include "nvim/link" and "clear" */ static int include_link = 0; /* when 2 include "nvim/link" and "clear" */
/* /// The "term", "cterm" and "gui" arguments can be any combination of the
* The "term", "cterm" and "gui" arguments can be any combination of the /// following names, separated by commas (but no spaces!).
* following names, separated by commas (but no spaces!).
*/
static char *(hl_name_table[]) = static char *(hl_name_table[]) =
{"bold", "standout", "underline", "undercurl", {"bold", "standout", "underline", "undercurl",
"italic", "reverse", "inverse", "NONE"}; "italic", "reverse", "inverse", "NONE"};
@ -1775,8 +1777,9 @@ syn_current_attr (
cur_si->si_trans_id = CUR_STATE( cur_si->si_trans_id = CUR_STATE(
current_state.ga_len - 2).si_trans_id; current_state.ga_len - 2).si_trans_id;
} }
} else } else {
cur_si->si_attr = syn_id2attr(syn_id); cur_si->si_attr = syn_id2attr(syn_id);
}
cur_si->si_cont_list = NULL; cur_si->si_cont_list = NULL;
cur_si->si_next_list = next_list; cur_si->si_next_list = next_list;
check_keepend(); check_keepend();
@ -5252,12 +5255,10 @@ get_id_list (
/* /*
* Handle full group name. * Handle full group name.
*/ */
if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) {
id = syn_check_group(name + 1, (int)(end - p)); id = syn_check_group(name + 1, (int)(end - p));
else { } else {
/* // Handle match of regexp with group names.
* Handle match of regexp with group names.
*/
*name = '^'; *name = '^';
STRCAT(name, "$"); STRCAT(name, "$");
regmatch.regprog = vim_regcomp(name, RE_MAGIC); regmatch.regprog = vim_regcomp(name, RE_MAGIC);
@ -5567,8 +5568,10 @@ bool syntax_present(win_T *win)
static enum { static enum {
EXP_SUBCMD, /* expand ":syn" sub-commands */ EXP_SUBCMD, // expand ":syn" sub-commands
EXP_CASE /* expand ":syn case" arguments */ EXP_CASE, // expand ":syn case" arguments
EXP_SPELL, // expand ":syn spell" arguments
EXP_SYNC // expand ":syn sync" arguments
} expand_what; } expand_what;
/* /*
@ -5612,6 +5615,10 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
xp->xp_context = EXPAND_NOTHING; xp->xp_context = EXPAND_NOTHING;
} else if (STRNICMP(arg, "case", p - arg) == 0) { } else if (STRNICMP(arg, "case", p - arg) == 0) {
expand_what = EXP_CASE; expand_what = EXP_CASE;
} else if (STRNICMP(arg, "spell", p - arg) == 0) {
expand_what = EXP_SPELL;
} else if (STRNICMP(arg, "sync", p - arg) == 0) {
expand_what = EXP_SYNC;
} else if (STRNICMP(arg, "keyword", p - arg) == 0 } else if (STRNICMP(arg, "keyword", p - arg) == 0
|| STRNICMP(arg, "region", p - arg) == 0 || STRNICMP(arg, "region", p - arg) == 0
|| STRNICMP(arg, "match", p - arg) == 0 || STRNICMP(arg, "match", p - arg) == 0
@ -5624,17 +5631,33 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
} }
} }
static char *(case_args[]) = {"match", "ignore", NULL};
/* /*
* Function given to ExpandGeneric() to obtain the list syntax names for * Function given to ExpandGeneric() to obtain the list syntax names for
* expansion. * expansion.
*/ */
char_u *get_syntax_name(expand_T *xp, int idx) char_u *get_syntax_name(expand_T *xp, int idx)
{ {
if (expand_what == EXP_SUBCMD) switch (expand_what) {
return (char_u *)subcommands[idx].name; case EXP_SUBCMD:
return (char_u *)case_args[idx]; return (char_u *)subcommands[idx].name;
case EXP_CASE: {
static char *case_args[] = { "match", "ignore", NULL };
return (char_u *)case_args[idx];
}
case EXP_SPELL: {
static char *spell_args[] =
{ "toplevel", "notoplevel", "default", NULL };
return (char_u *)spell_args[idx];
}
case EXP_SYNC: {
static char *sync_args[] =
{ "ccomment", "clear", "fromstart",
"linebreaks=", "linecont", "lines=", "match",
"maxlines=", "minlines=", "region", NULL };
return (char_u *)sync_args[idx];
}
}
return NULL;
} }
@ -5845,9 +5868,12 @@ static void syntime_report(void)
} }
} }
/* sort on total time */ // Sort on total time. Skip if there are no items to avoid passing NULL
qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), // pointer to qsort().
syn_compare_syntime); if (ga.ga_len > 1) {
qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
syn_compare_syntime);
}
MSG_PUTS_TITLE(_( MSG_PUTS_TITLE(_(
" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
@ -5958,6 +5984,7 @@ static char *highlight_init_light[] =
"Title ctermfg=DarkMagenta gui=bold guifg=Magenta", "Title ctermfg=DarkMagenta gui=bold guifg=Magenta",
"Visual guibg=LightGrey", "Visual guibg=LightGrey",
"WarningMsg ctermfg=DarkRed guifg=Red", "WarningMsg ctermfg=DarkRed guifg=Red",
"Normal gui=NONE",
NULL NULL
}; };
@ -5991,23 +6018,25 @@ static char *highlight_init_dark[] =
"Title ctermfg=LightMagenta gui=bold guifg=Magenta", "Title ctermfg=LightMagenta gui=bold guifg=Magenta",
"Visual guibg=DarkGrey", "Visual guibg=DarkGrey",
"WarningMsg ctermfg=LightRed guifg=Red", "WarningMsg ctermfg=LightRed guifg=Red",
"Normal gui=NONE",
NULL NULL
}; };
void
init_highlight ( /// Load colors from a file if "g:colors_name" is set, otherwise load builtin
int both, /* include groups where 'bg' doesn't matter */ /// colors
int reset /* clear group first */ ///
) /// @param both include groups where 'bg' doesn't matter
/// @param reset clear groups first
void
init_highlight(int both, int reset)
{ {
int i; int i;
char **pp; char **pp;
static int had_both = FALSE; static int had_both = FALSE;
/* // Try finding the color scheme file. Used when a color file was loaded
* Try finding the color scheme file. Used when a color file was loaded // and 'background' or 't_Co' is changed.
* and 'background' or 't_Co' is changed.
*/
char_u *p = get_var_value("g:colors_name"); char_u *p = get_var_value("g:colors_name");
if (p != NULL) { if (p != NULL) {
// Value of g:colors_name could be freed in load_colors() and make // Value of g:colors_name could be freed in load_colors() and make
@ -6026,33 +6055,34 @@ init_highlight (
if (both) { if (both) {
had_both = TRUE; had_both = TRUE;
pp = highlight_init_both; pp = highlight_init_both;
for (i = 0; pp[i] != NULL; ++i) for (i = 0; pp[i] != NULL; i++) {
do_highlight((char_u *)pp[i], reset, TRUE); do_highlight((char_u *)pp[i], reset, true);
} else if (!had_both) }
/* Don't do anything before the call with both == TRUE from main(). } else if (!had_both) {
* Not everything has been setup then, and that call will overrule // Don't do anything before the call with both == TRUE from main().
* everything anyway. */ // Not everything has been setup then, and that call will overrule
// everything anyway.
return; return;
}
if (*p_bg == 'l') pp = (*p_bg == 'l') ? highlight_init_light : highlight_init_dark;
pp = highlight_init_light;
else for (i = 0; pp[i] != NULL; i++) {
pp = highlight_init_dark; do_highlight((char_u *)pp[i], reset, true);
for (i = 0; pp[i] != NULL; ++i) }
do_highlight((char_u *)pp[i], reset, TRUE);
/* Reverse looks ugly, but grey may not work for 8 colors. Thus let it /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
* depend on the number of colors available. * depend on the number of colors available.
* With 8 colors brown is equal to yellow, need to use black for Search fg * With 8 colors brown is equal to yellow, need to use black for Search fg
* to avoid Statement highlighted text disappears. * to avoid Statement highlighted text disappears.
* Clear the attributes, needed when changing the t_Co value. */ * Clear the attributes, needed when changing the t_Co value. */
if (t_colors > 8) if (t_colors > 8) {
do_highlight( do_highlight(
(char_u *)(*p_bg == 'l' (char_u *)(*p_bg == 'l'
? "Visual cterm=NONE ctermbg=LightGrey" ? "Visual cterm=NONE ctermbg=LightGrey"
: "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, : "Visual cterm=NONE ctermbg=DarkGrey"), false,
TRUE); true);
else { } else {
do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE", do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
FALSE, TRUE); FALSE, TRUE);
if (*p_bg == 'l') if (*p_bg == 'l')
@ -6112,12 +6142,7 @@ int load_colors(char_u *name)
/// "forceit" and "init" both TRUE. /// "forceit" and "init" both TRUE.
/// @param init TRUE when called for initializing /// @param init TRUE when called for initializing
void void
do_highlight( do_highlight(char_u *line, int forceit, int init) {
char_u *line,
int forceit,
int init
)
{
char_u *name_end; char_u *name_end;
char_u *linep; char_u *linep;
char_u *key_start; char_u *key_start;
@ -6134,15 +6159,16 @@ do_highlight(
int dolink = FALSE; int dolink = FALSE;
int error = FALSE; int error = FALSE;
int color; int color;
int is_normal_group = FALSE; /* "Normal" group */ bool is_normal_group = false; // "Normal" group
/* /*
* If no argument, list current highlighting. * If no argument, list current highlighting.
*/ */
if (ends_excmd(*line)) { if (ends_excmd(*line)) {
for (int i = 1; i <= highlight_ga.ga_len && !got_int; ++i) for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
/* TODO: only call when the group has attributes set */ // todo(vim): only call when the group has attributes set
highlight_list_one(i); highlight_list_one(i);
}
return; return;
} }
@ -6270,12 +6296,12 @@ do_highlight(
return; return;
idx = id - 1; /* index is ID minus one */ idx = id - 1; /* index is ID minus one */
/* Return if "default" was used and the group already has settings. */ // Return if "default" was used and the group already has settings
if (dodefault && hl_has_settings(idx, TRUE)) if (dodefault && hl_has_settings(idx, true)) {
return; return;
}
if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0);
is_normal_group = TRUE;
/* Clear the highlighting for ":hi clear {group}" and ":hi clear". */ /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
if (doclear || (forceit && init)) { if (doclear || (forceit && init)) {
@ -6284,7 +6310,7 @@ do_highlight(
HL_TABLE()[idx].sg_set = 0; HL_TABLE()[idx].sg_set = 0;
} }
if (!doclear) if (!doclear) {
while (!ends_excmd(*linep)) { while (!ends_excmd(*linep)) {
key_start = linep; key_start = linep;
if (*linep == '=') { if (*linep == '=') {
@ -6390,12 +6416,12 @@ do_highlight(
} }
} }
} else if (STRCMP(key, "FONT") == 0) { } else if (STRCMP(key, "FONT") == 0) {
/* in non-GUI fonts are simply ignored */ // in non-GUI fonts are simply ignored
} else if (STRCMP(key, } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
"CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {
if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) {
if (!init) if (!init) {
HL_TABLE()[idx].sg_set |= SG_CTERM; HL_TABLE()[idx].sg_set |= SG_CTERM;
}
/* When setting the foreground color, and previously the "bold" /* When setting the foreground color, and previously the "bold"
* flag was set for a light color, reset it now */ * flag was set for a light color, reset it now */
@ -6489,9 +6515,10 @@ do_highlight(
* colors (on some terminals, e.g. "linux") */ * colors (on some terminals, e.g. "linux") */
if (color & 8) { if (color & 8) {
HL_TABLE()[idx].sg_cterm |= HL_BOLD; HL_TABLE()[idx].sg_cterm |= HL_BOLD;
HL_TABLE()[idx].sg_cterm_bold = TRUE; HL_TABLE()[idx].sg_cterm_bold = true;
} else } else {
HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
}
} }
color &= 7; // truncate to 8 colors color &= 7; // truncate to 8 colors
} else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) { } else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) {
@ -6603,38 +6630,40 @@ do_highlight(
/* /*
* When highlighting has been given for a group, don't link it. * When highlighting has been given for a group, don't link it.
*/ */
if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) {
HL_TABLE()[idx].sg_link = 0; HL_TABLE()[idx].sg_link = 0;
}
/* /*
* Continue with next argument. * Continue with next argument.
*/ */
linep = skipwhite(linep); linep = skipwhite(linep);
} }
}
/* /*
* If there is an error, and it's a new entry, remove it from the table. * If there is an error, and it's a new entry, remove it from the table.
*/ */
if (error && idx == highlight_ga.ga_len) if (error && idx == highlight_ga.ga_len) {
syn_unadd_group(); syn_unadd_group();
else { } else {
if (is_normal_group) { if (is_normal_group) {
HL_TABLE()[idx].sg_attr = 0;
// Need to update all groups, because they might be using "bg" and/or // Need to update all groups, because they might be using "bg" and/or
// "fg", which have been changed now. // "fg", which have been changed now.
highlight_attr_set_all(); highlight_attr_set_all();
// If the normal group has changed, it is simpler to refresh every UI // If the normal group has changed, it is simpler to refresh every UI
ui_refresh(); ui_refresh();
} else } else {
set_hl_attr(idx); set_hl_attr(idx);
}
HL_TABLE()[idx].sg_scriptID = current_SID; HL_TABLE()[idx].sg_scriptID = current_SID;
redraw_all_later(NOT_VALID); redraw_all_later(NOT_VALID);
} }
xfree(key); xfree(key);
xfree(arg); xfree(arg);
/* Only call highlight_changed() once, after sourcing a syntax file */ // Only call highlight_changed() once, after sourcing a syntax file
need_highlight_changed = TRUE; need_highlight_changed = true;
} }
#if defined(EXITFREE) #if defined(EXITFREE)
@ -6707,14 +6736,15 @@ static void highlight_clear(int idx)
} }
/* /// Table with the specifications for an attribute number.
* Table with the specifications for an attribute number. /// Note that this table is used by ALL buffers. This is required because the
* Note that this table is used by ALL buffers. This is required because the /// GUI can redraw at any time for any buffer.
* GUI can redraw at any time for any buffer.
*/
static garray_T attr_table = GA_EMPTY_INIT_VALUE; static garray_T attr_table = GA_EMPTY_INIT_VALUE;
#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] static inline attrentry_T * ATTR_ENTRY(int idx)
{
return &((attrentry_T *)attr_table.ga_data)[idx];
}
/// Return the attr number for a set of colors and font. /// Return the attr number for a set of colors and font.
@ -6804,7 +6834,7 @@ int hl_combine_attr(int char_attr, int prim_attr)
{ {
attrentry_T *char_aep = NULL; attrentry_T *char_aep = NULL;
attrentry_T *spell_aep; attrentry_T *spell_aep;
attrentry_T new_en; attrentry_T new_en = ATTRENTRY_INIT;
if (char_attr == 0) { if (char_attr == 0) {
return prim_attr; return prim_attr;
@ -6820,8 +6850,6 @@ int hl_combine_attr(int char_attr, int prim_attr)
if (char_aep != NULL) { if (char_aep != NULL) {
// Copy all attributes from char_aep to the new entry // Copy all attributes from char_aep to the new entry
new_en = *char_aep; new_en = *char_aep;
} else {
memset(&new_en, 0, sizeof(new_en));
} }
spell_aep = syn_cterm_attr2entry(prim_attr); spell_aep = syn_cterm_attr2entry(prim_attr);
@ -6852,17 +6880,25 @@ int hl_combine_attr(int char_attr, int prim_attr)
return get_attr_entry(&new_en); return get_attr_entry(&new_en);
} }
/// \note this function does not apply exclusively to cterm attr contrary
/// to what its name implies
/// \warn don't call it with attr 0 (i.e., the null attribute)
attrentry_T *syn_cterm_attr2entry(int attr) attrentry_T *syn_cterm_attr2entry(int attr)
{ {
attr -= ATTR_OFF; attr -= ATTR_OFF;
if (attr >= attr_table.ga_len) /* did ":syntax clear" */ if (attr >= attr_table.ga_len) {
// did ":syntax clear"
return NULL; return NULL;
return &(ATTR_ENTRY(attr)); }
return ATTR_ENTRY(attr);
} }
/// \addtogroup LIST_XXX
/// @{
#define LIST_ATTR 1 #define LIST_ATTR 1
#define LIST_STRING 2 #define LIST_STRING 2
#define LIST_INT 3 #define LIST_INT 3
/// @}
static void highlight_list_one(int id) static void highlight_list_one(int id)
{ {
@ -6901,7 +6937,13 @@ static void highlight_list_one(int id)
last_set_msg(sgp->sg_scriptID); last_set_msg(sgp->sg_scriptID);
} }
static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name) /// Outputs a highlight when doing ":hi MyHighlight"
///
/// @param type one of \ref LIST_XXX
/// @param iarg integer argument used if \p type == LIST_INT
/// @param sarg string used if \p type == LIST_STRING
static int highlight_list_arg(int id, int didh, int type, int iarg,
char_u *sarg, const char *name)
{ {
char_u buf[100]; char_u buf[100];
char_u *ts; char_u *ts;
@ -7041,24 +7083,23 @@ const char *highlight_color(const int id, const char *const what,
return NULL; return NULL;
} }
/* /// Output the syntax list header.
* Output the syntax list header. ///
* Return TRUE when started a new line. /// @param did_header did header already
*/ /// @param outlen length of string that comes
static int /// @param id highlight group id
syn_list_header ( /// @return true when started a new line.
int did_header, /* did header already */ static int
int outlen, /* length of string that comes */ syn_list_header(int did_header, int outlen, int id)
int id /* highlight group id */
)
{ {
int endcol = 19; int endcol = 19;
int newline = TRUE; int newline = TRUE;
if (!did_header) { if (!did_header) {
msg_putchar('\n'); msg_putchar('\n');
if (got_int) if (got_int) {
return TRUE; return true;
}
msg_outtrans(HL_TABLE()[id - 1].sg_name); msg_outtrans(HL_TABLE()[id - 1].sg_name);
endcol = 15; endcol = 15;
} else if (msg_col + outlen + 1 >= Columns) { } else if (msg_col + outlen + 1 >= Columns) {
@ -7086,21 +7127,14 @@ syn_list_header (
return newline; return newline;
} }
/* /// Set the attribute numbers for a highlight group.
* Set the attribute numbers for a highlight group. /// Called after one of the attributes has changed.
* Called after one of the attributes has changed. /// @param idx corrected highlight index
*/ static void set_hl_attr(int idx)
static void
set_hl_attr (
int idx /* index in array */
)
{ {
attrentry_T at_en; attrentry_T at_en = ATTRENTRY_INIT;
struct hl_group *sgp = HL_TABLE() + idx; struct hl_group *sgp = HL_TABLE() + idx;
/* The "Normal" group doesn't need an attribute number */
if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
return;
at_en.cterm_ae_attr = sgp->sg_cterm; at_en.cterm_ae_attr = sgp->sg_cterm;
at_en.cterm_fg_color = sgp->sg_cterm_fg; at_en.cterm_fg_color = sgp->sg_cterm_fg;
@ -7124,10 +7158,10 @@ set_hl_attr (
} }
} }
/* /// Lookup a highlight group name and return its ID.
* Lookup a highlight group name and return it's ID. ///
* If it is not found, 0 is returned. /// @param highlight name e.g. 'Cursor', 'Normal'
*/ /// @return the highlight id, else 0 if \p name does not exist
int syn_name2id(const char_u *name) int syn_name2id(const char_u *name)
{ {
int i; int i;
@ -7176,7 +7210,7 @@ int syn_namen2id(char_u *linep, int len)
return id; return id;
} }
/// Find highlight group name in the table and return it's ID. /// Find highlight group name in the table and return its ID.
/// If it doesn't exist yet, a new entry is created. /// If it doesn't exist yet, a new entry is created.
/// ///
/// @param pp Highlight group name /// @param pp Highlight group name
@ -7195,11 +7229,11 @@ int syn_check_group(char_u *pp, int len)
return id; return id;
} }
/* /// Add new highlight group and return it's ID.
* Add new highlight group and return it's ID. ///
* "name" must be an allocated string, it will be consumed. /// @param name must be an allocated string, it will be consumed.
* Return 0 for failure. /// @return 0 for failure, else the allocated group id
*/ /// @see syn_check_group syn_unadd_group
static int syn_add_group(char_u *name) static int syn_add_group(char_u *name)
{ {
char_u *p; char_u *p;
@ -7237,25 +7271,26 @@ static int syn_add_group(char_u *name)
struct hl_group* hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga); struct hl_group* hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga);
memset(hlgp, 0, sizeof(*hlgp)); memset(hlgp, 0, sizeof(*hlgp));
hlgp->sg_name = name; hlgp->sg_name = name;
hlgp->sg_rgb_bg = -1;
hlgp->sg_rgb_fg = -1;
hlgp->sg_rgb_sp = -1;
hlgp->sg_name_u = vim_strsave_up(name); hlgp->sg_name_u = vim_strsave_up(name);
return highlight_ga.ga_len; /* ID is index plus one */ return highlight_ga.ga_len; /* ID is index plus one */
} }
/* /// When, just after calling syn_add_group(), an error is discovered, this
* When, just after calling syn_add_group(), an error is discovered, this /// function deletes the new name.
* function deletes the new name.
*/
static void syn_unadd_group(void) static void syn_unadd_group(void)
{ {
--highlight_ga.ga_len; highlight_ga.ga_len--;
xfree(HL_TABLE()[highlight_ga.ga_len].sg_name); xfree(HL_TABLE()[highlight_ga.ga_len].sg_name);
xfree(HL_TABLE()[highlight_ga.ga_len].sg_name_u); xfree(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
} }
/*
* Translate a group ID to highlight attributes. /// Translate a group ID to highlight attributes.
*/ /// @see syn_cterm_attr2entry
int syn_id2attr(int hl_id) int syn_id2attr(int hl_id)
{ {
struct hl_group *sgp; struct hl_group *sgp;
@ -8208,6 +8243,30 @@ RgbValue name_to_color(const uint8_t *name)
return -1; return -1;
} }
/// Gets highlight description for id `attr_id` as a map.
Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err)
{
HlAttrs attrs = HLATTRS_INIT;
Dictionary dic = ARRAY_DICT_INIT;
if (attr_id == 0) {
goto end;
}
attrentry_T *aep = syn_cterm_attr2entry((int)attr_id);
if (!aep) {
api_set_error(err, kErrorTypeException,
"Invalid attribute id: %d", attr_id);
return dic;
}
attrs = attrentry2hlattrs(aep, rgb);
end:
return hlattrs2dict(attrs);
}
/************************************** /**************************************
* End of Highlighting stuff * * End of Highlighting stuff *
**************************************/ **************************************/

View File

@ -73,4 +73,14 @@ typedef struct attr_entry {
int cterm_fg_color, cterm_bg_color; int cterm_fg_color, cterm_bg_color;
} attrentry_T; } attrentry_T;
#define ATTRENTRY_INIT { \
.rgb_ae_attr = 0, \
.cterm_ae_attr = 0, \
.rgb_fg_color = -1, \
.rgb_bg_color = -1, \
.rgb_sp_color = -1, \
.cterm_fg_color = 0, \
.cterm_bg_color = 0, \
}
#endif // NVIM_SYNTAX_DEFS_H #endif // NVIM_SYNTAX_DEFS_H

View File

@ -432,14 +432,6 @@ static int terminal_execute(VimState *state, int key)
TerminalState *s = (TerminalState *)state; TerminalState *s = (TerminalState *)state;
switch (key) { switch (key) {
case K_FOCUSGAINED: // nvim has been given focus
apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf);
break;
case K_FOCUSLOST: // nvim has lost focus
apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf);
break;
// Temporary fix until paste events gets implemented // Temporary fix until paste events gets implemented
case K_PASTE: case K_PASTE:
break; break;
@ -530,6 +522,12 @@ void terminal_send(Terminal *term, char *data, size_t size)
void terminal_send_key(Terminal *term, int c) void terminal_send_key(Terminal *term, int c)
{ {
VTermModifier mod = VTERM_MOD_NONE; VTermModifier mod = VTERM_MOD_NONE;
// Convert K_ZERO back to ASCII
if (c == K_ZERO) {
c = Ctrl_AT;
}
VTermKey key = convert_key(c, &mod); VTermKey key = convert_key(c, &mod);
if (key) { if (key) {
@ -783,26 +781,60 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data)
// }}} // }}}
// input handling {{{ // input handling {{{
static void convert_modifiers(VTermModifier *statep) static void convert_modifiers(int key, VTermModifier *statep)
{ {
if (mod_mask & MOD_MASK_SHIFT) { *statep |= VTERM_MOD_SHIFT; } if (mod_mask & MOD_MASK_SHIFT) { *statep |= VTERM_MOD_SHIFT; }
if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; } if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; }
if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; } if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; }
switch (key) {
case K_S_TAB:
case K_S_UP:
case K_S_DOWN:
case K_S_LEFT:
case K_S_RIGHT:
case K_S_F1:
case K_S_F2:
case K_S_F3:
case K_S_F4:
case K_S_F5:
case K_S_F6:
case K_S_F7:
case K_S_F8:
case K_S_F9:
case K_S_F10:
case K_S_F11:
case K_S_F12:
*statep |= VTERM_MOD_SHIFT;
break;
case K_C_LEFT:
case K_C_RIGHT:
*statep |= VTERM_MOD_CTRL;
break;
}
} }
static VTermKey convert_key(int key, VTermModifier *statep) static VTermKey convert_key(int key, VTermModifier *statep)
{ {
convert_modifiers(statep); convert_modifiers(key, statep);
switch (key) { switch (key) {
case K_BS: return VTERM_KEY_BACKSPACE; case K_BS: return VTERM_KEY_BACKSPACE;
case K_S_TAB: // FALLTHROUGH
case TAB: return VTERM_KEY_TAB; case TAB: return VTERM_KEY_TAB;
case Ctrl_M: return VTERM_KEY_ENTER; case Ctrl_M: return VTERM_KEY_ENTER;
case ESC: return VTERM_KEY_ESCAPE; case ESC: return VTERM_KEY_ESCAPE;
case K_S_UP: // FALLTHROUGH
case K_UP: return VTERM_KEY_UP; case K_UP: return VTERM_KEY_UP;
case K_S_DOWN: // FALLTHROUGH
case K_DOWN: return VTERM_KEY_DOWN; case K_DOWN: return VTERM_KEY_DOWN;
case K_S_LEFT: // FALLTHROUGH
case K_C_LEFT: // FALLTHROUGH
case K_LEFT: return VTERM_KEY_LEFT; case K_LEFT: return VTERM_KEY_LEFT;
case K_S_RIGHT: // FALLTHROUGH
case K_C_RIGHT: // FALLTHROUGH
case K_RIGHT: return VTERM_KEY_RIGHT; case K_RIGHT: return VTERM_KEY_RIGHT;
case K_INS: return VTERM_KEY_INS; case K_INS: return VTERM_KEY_INS;
@ -812,22 +844,22 @@ static VTermKey convert_key(int key, VTermModifier *statep)
case K_PAGEUP: return VTERM_KEY_PAGEUP; case K_PAGEUP: return VTERM_KEY_PAGEUP;
case K_PAGEDOWN: return VTERM_KEY_PAGEDOWN; case K_PAGEDOWN: return VTERM_KEY_PAGEDOWN;
case K_K0: case K_K0: // FALLTHROUGH
case K_KINS: return VTERM_KEY_KP_0; case K_KINS: return VTERM_KEY_KP_0;
case K_K1: case K_K1: // FALLTHROUGH
case K_KEND: return VTERM_KEY_KP_1; case K_KEND: return VTERM_KEY_KP_1;
case K_K2: return VTERM_KEY_KP_2; case K_K2: return VTERM_KEY_KP_2;
case K_K3: case K_K3: // FALLTHROUGH
case K_KPAGEDOWN: return VTERM_KEY_KP_3; case K_KPAGEDOWN: return VTERM_KEY_KP_3;
case K_K4: return VTERM_KEY_KP_4; case K_K4: return VTERM_KEY_KP_4;
case K_K5: return VTERM_KEY_KP_5; case K_K5: return VTERM_KEY_KP_5;
case K_K6: return VTERM_KEY_KP_6; case K_K6: return VTERM_KEY_KP_6;
case K_K7: case K_K7: // FALLTHROUGH
case K_KHOME: return VTERM_KEY_KP_7; case K_KHOME: return VTERM_KEY_KP_7;
case K_K8: return VTERM_KEY_KP_8; case K_K8: return VTERM_KEY_KP_8;
case K_K9: case K_K9: // FALLTHROUGH
case K_KPAGEUP: return VTERM_KEY_KP_9; case K_KPAGEUP: return VTERM_KEY_KP_9;
case K_KDEL: case K_KDEL: // FALLTHROUGH
case K_KPOINT: return VTERM_KEY_KP_PERIOD; case K_KPOINT: return VTERM_KEY_KP_PERIOD;
case K_KENTER: return VTERM_KEY_KP_ENTER; case K_KENTER: return VTERM_KEY_KP_ENTER;
case K_KPLUS: return VTERM_KEY_KP_PLUS; case K_KPLUS: return VTERM_KEY_KP_PLUS;
@ -835,6 +867,57 @@ static VTermKey convert_key(int key, VTermModifier *statep)
case K_KMULTIPLY: return VTERM_KEY_KP_MULT; case K_KMULTIPLY: return VTERM_KEY_KP_MULT;
case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE; case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE;
case K_S_F1: // FALLTHROUGH
case K_F1: return VTERM_KEY_FUNCTION(1);
case K_S_F2: // FALLTHROUGH
case K_F2: return VTERM_KEY_FUNCTION(2);
case K_S_F3: // FALLTHROUGH
case K_F3: return VTERM_KEY_FUNCTION(3);
case K_S_F4: // FALLTHROUGH
case K_F4: return VTERM_KEY_FUNCTION(4);
case K_S_F5: // FALLTHROUGH
case K_F5: return VTERM_KEY_FUNCTION(5);
case K_S_F6: // FALLTHROUGH
case K_F6: return VTERM_KEY_FUNCTION(6);
case K_S_F7: // FALLTHROUGH
case K_F7: return VTERM_KEY_FUNCTION(7);
case K_S_F8: // FALLTHROUGH
case K_F8: return VTERM_KEY_FUNCTION(8);
case K_S_F9: // FALLTHROUGH
case K_F9: return VTERM_KEY_FUNCTION(9);
case K_S_F10: // FALLTHROUGH
case K_F10: return VTERM_KEY_FUNCTION(10);
case K_S_F11: // FALLTHROUGH
case K_F11: return VTERM_KEY_FUNCTION(11);
case K_S_F12: // FALLTHROUGH
case K_F12: return VTERM_KEY_FUNCTION(12);
case K_F13: return VTERM_KEY_FUNCTION(13);
case K_F14: return VTERM_KEY_FUNCTION(14);
case K_F15: return VTERM_KEY_FUNCTION(15);
case K_F16: return VTERM_KEY_FUNCTION(16);
case K_F17: return VTERM_KEY_FUNCTION(17);
case K_F18: return VTERM_KEY_FUNCTION(18);
case K_F19: return VTERM_KEY_FUNCTION(19);
case K_F20: return VTERM_KEY_FUNCTION(20);
case K_F21: return VTERM_KEY_FUNCTION(21);
case K_F22: return VTERM_KEY_FUNCTION(22);
case K_F23: return VTERM_KEY_FUNCTION(23);
case K_F24: return VTERM_KEY_FUNCTION(24);
case K_F25: return VTERM_KEY_FUNCTION(25);
case K_F26: return VTERM_KEY_FUNCTION(26);
case K_F27: return VTERM_KEY_FUNCTION(27);
case K_F28: return VTERM_KEY_FUNCTION(28);
case K_F29: return VTERM_KEY_FUNCTION(29);
case K_F30: return VTERM_KEY_FUNCTION(30);
case K_F31: return VTERM_KEY_FUNCTION(31);
case K_F32: return VTERM_KEY_FUNCTION(32);
case K_F33: return VTERM_KEY_FUNCTION(33);
case K_F34: return VTERM_KEY_FUNCTION(34);
case K_F35: return VTERM_KEY_FUNCTION(35);
case K_F36: return VTERM_KEY_FUNCTION(36);
case K_F37: return VTERM_KEY_FUNCTION(37);
default: return VTERM_KEY_NONE; default: return VTERM_KEY_NONE;
} }
} }
@ -1176,6 +1259,10 @@ static void redraw(bool restore_cursor)
update_screen(0); update_screen(0);
} }
if (need_maketitle) { // Update title in terminal-mode. #7248
maketitle();
}
if (restore_cursor) { if (restore_cursor) {
ui_cursor_goto(save_row, save_col); ui_cursor_goto(save_row, save_col);
} else if (term) { } else if (term) {

View File

@ -58,6 +58,8 @@ NEW_TESTS ?= \
test_match.res \ test_match.res \
test_matchadd_conceal.res \ test_matchadd_conceal.res \
test_matchadd_conceal_utf8.res \ test_matchadd_conceal_utf8.res \
test_mksession.res \
test_mksession_utf8.res \
test_nested_function.res \ test_nested_function.res \
test_normal.res \ test_normal.res \
test_quickfix.res \ test_quickfix.res \

View File

@ -230,12 +230,41 @@ func Test_paste_in_cmdline()
call feedkeys("f;:aaa \<C-R>\<C-A> bbb\<C-B>\"\<CR>", 'tx') call feedkeys("f;:aaa \<C-R>\<C-A> bbb\<C-B>\"\<CR>", 'tx')
call assert_equal('"aaa a;b-c*d bbb', @:) call assert_equal('"aaa a;b-c*d bbb', @:)
call feedkeys(":\<C-\>etoupper(getline(1))\<CR>\<C-B>\"\<CR>", 'tx')
call assert_equal('"ASDF.X /TMP/SOME VERYLONGWORD A;B-C*D ', @:)
bwipe! bwipe!
endfunc endfunc
func Test_illegal_address() func Test_remove_char_in_cmdline()
call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc ef', @:)
call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abcdef', @:)
call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx')
call assert_equal('"abc ghi', @:)
call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx')
call assert_equal('"def', @:)
endfunc
func Test_illegal_address1()
new new
2;'( 2;'(
2;') 2;')
quit quit
endfunc endfunc
func Test_illegal_address2()
call writefile(['c', 'x', ' x', '.', '1;y'], 'Xtest.vim')
new
source Xtest.vim
" Trigger calling validate_cursor()
diffsp Xtest.vim
quit!
bwipe!
call delete('Xtest.vim')
endfunc

View File

@ -212,6 +212,7 @@ func Test_diffoff()
call setline(1, ['One', '', 'Two', 'Three']) call setline(1, ['One', '', 'Two', 'Three'])
diffthis diffthis
redraw redraw
call assert_notequal(normattr, screenattr(1, 1))
diffoff! diffoff!
redraw redraw
call assert_equal(normattr, screenattr(1, 1)) call assert_equal(normattr, screenattr(1, 1))
@ -219,6 +220,42 @@ func Test_diffoff()
bwipe! bwipe!
endfunc endfunc
func Test_diffoff_hidden()
set diffopt=filler,foldcolumn:0
e! one
call setline(1, ['Two', 'Three'])
let normattr = screenattr(1, 1)
diffthis
botright vert new two
call setline(1, ['One', 'Four'])
diffthis
redraw
call assert_notequal(normattr, screenattr(1, 1))
set hidden
close
redraw
" diffing with hidden buffer two
call assert_notequal(normattr, screenattr(1, 1))
diffoff
redraw
call assert_equal(normattr, screenattr(1, 1))
diffthis
redraw
" still diffing with hidden buffer two
call assert_notequal(normattr, screenattr(1, 1))
diffoff!
redraw
call assert_equal(normattr, screenattr(1, 1))
diffthis
redraw
" no longer diffing with hidden buffer two
call assert_equal(normattr, screenattr(1, 1))
bwipe!
bwipe!
set hidden& diffopt&
endfunc
func Test_setting_cursor() func Test_setting_cursor()
new Xtest1 new Xtest1
put =range(1,90) put =range(1,90)

View File

@ -89,17 +89,8 @@ func s:doc_config_teardown()
endif endif
endfunc endfunc
func s:get_cmd_compl_list(cmd) func s:get_help_compl_list(cmd)
let list = [] return getcompletion(a:cmd, 'help')
let str = ''
for cnt in range(1, 999)
call feedkeys(a:cmd . repeat("\<Tab>", cnt) . "'\<C-B>let str='\<CR>", 'tx')
if str ==# a:cmd[1:]
break
endif
call add(list, str)
endfor
return list
endfunc endfunc
func Test_help_complete() func Test_help_complete()
@ -111,49 +102,49 @@ func Test_help_complete()
if has('multi_lang') if has('multi_lang')
set helplang= set helplang=
endif endif
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(['h test-col', 'h test-char'], list) call assert_equal(['test-col', 'test-char'], list)
if has('multi_lang') if has('multi_lang')
" 'helplang=ab' and help file lang is 'en' " 'helplang=ab' and help file lang is 'en'
set helplang=ab set helplang=ab
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(['h test-col', 'h test-char'], list) call assert_equal(['test-col', 'test-char'], list)
" 'helplang=' and help file lang is 'en' and 'ab' " 'helplang=' and help file lang is 'en' and 'ab'
set rtp+=Xdir1/doc-ab set rtp+=Xdir1/doc-ab
set helplang= set helplang=
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(sort(['h test-col@en', 'h test-col@ab', call assert_equal(sort(['test-col@en', 'test-col@ab',
\ 'h test-char@en', 'h test-char@ab']), sort(list)) \ 'test-char@en', 'test-char@ab']), sort(list))
" 'helplang=ab' and help file lang is 'en' and 'ab' " 'helplang=ab' and help file lang is 'en' and 'ab'
set helplang=ab set helplang=ab
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(sort(['h test-col', 'h test-col@en', call assert_equal(sort(['test-col', 'test-col@en',
\ 'h test-char', 'h test-char@en']), sort(list)) \ 'test-char', 'test-char@en']), sort(list))
" 'helplang=' and help file lang is 'en', 'ab' and 'ja' " 'helplang=' and help file lang is 'en', 'ab' and 'ja'
set rtp+=Xdir1/doc-ja set rtp+=Xdir1/doc-ja
set helplang= set helplang=
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(sort(['h test-col@en', 'h test-col@ab', call assert_equal(sort(['test-col@en', 'test-col@ab',
\ 'h test-col@ja', 'h test-char@en', \ 'test-col@ja', 'test-char@en',
\ 'h test-char@ab', 'h test-char@ja']), sort(list)) \ 'test-char@ab', 'test-char@ja']), sort(list))
" 'helplang=ab' and help file lang is 'en', 'ab' and 'ja' " 'helplang=ab' and help file lang is 'en', 'ab' and 'ja'
set helplang=ab set helplang=ab
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(sort(['h test-col', 'h test-col@en', call assert_equal(sort(['test-col', 'test-col@en',
\ 'h test-col@ja', 'h test-char', \ 'test-col@ja', 'test-char',
\ 'h test-char@en', 'h test-char@ja']), sort(list)) \ 'test-char@en', 'test-char@ja']), sort(list))
" 'helplang=ab,ja' and help file lang is 'en', 'ab' and 'ja' " 'helplang=ab,ja' and help file lang is 'en', 'ab' and 'ja'
set helplang=ab,ja set helplang=ab,ja
let list = s:get_cmd_compl_list(":h test") let list = s:get_help_compl_list("test")
call assert_equal(sort(['h test-col', 'h test-col@ja', call assert_equal(sort(['test-col', 'test-col@ja',
\ 'h test-col@en', 'h test-char', \ 'test-col@en', 'test-char',
\ 'h test-char@ja', 'h test-char@en']), sort(list)) \ 'test-char@ja', 'test-char@en']), sort(list))
endif endif
catch catch
call assert_exception('X') call assert_exception('X')

View File

@ -0,0 +1,155 @@
" Test for :mksession, :mkview and :loadview in latin1 encoding
scriptencoding latin1
if !has('multi_byte') || !has('mksession')
finish
endif
func Test_mksession()
tabnew
let wrap_save = &wrap
set sessionoptions=buffers splitbelow fileencoding=latin1
call setline(1, [
\ 'start:',
\ 'no multibyte chAracter',
\ ' one leaDing tab',
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
\ 'one ä multibyteCharacter',
\ 'aä Ä two multiByte characters',
\ 'Aäöü three mulTibyte characters'
\ ])
let tmpfile = 'Xtemp'
exec 'w! ' . tmpfile
/^start:
set wrap
vsplit
norm! j16|
split
norm! j16|
split
norm! j16|
split
norm! j8|
split
norm! j8|
split
norm! j16|
split
norm! j16|
split
norm! j16|
wincmd l
set nowrap
/^start:
norm! j16|3zl
split
norm! j016|3zl
split
norm! j016|3zl
split
norm! j08|3zl
split
norm! j08|3zl
split
norm! j016|3zl
split
norm! j016|3zl
split
norm! j016|3zl
split
call wincol()
mksession! Xtest_mks.out
let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"')
let expected = [
\ 'normal! 016|',
\ 'normal! 016|',
\ 'normal! 016|',
\ 'normal! 08|',
\ 'normal! 08|',
\ 'normal! 016|',
\ 'normal! 016|',
\ 'normal! 016|',
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
\ " normal! 08|",
\ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
\ " normal! 08|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|"
\ ]
call assert_equal(expected, li)
tabclose!
call delete('Xtest_mks.out')
call delete(tmpfile)
let &wrap = wrap_save
endfunc
func Test_mksession_winheight()
new
set winheight=10 winminheight=2
mksession! Xtest_mks.out
source Xtest_mks.out
call delete('Xtest_mks.out')
endfunc
" Verify that arglist is stored correctly to the session file.
func Test_mksession_arglist()
argdel *
next file1 file2 file3 file4
mksession! Xtest_mks.out
source Xtest_mks.out
call assert_equal(['file1', 'file2', 'file3', 'file4'], argv())
call delete('Xtest_mks.out')
argdel *
endfunc
func Test_mksession_one_buffer_two_windows()
edit Xtest1
new Xtest2
split
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let count1 = 0
let count2 = 0
let count2buf = 0
for line in lines
if line =~ 'edit \f*Xtest1$'
let count1 += 1
endif
if line =~ 'edit \f\{-}Xtest2'
let count2 += 1
endif
if line =~ 'buffer \f\{-}Xtest2'
let count2buf += 1
endif
endfor
call assert_equal(1, count1, 'Xtest1 count')
call assert_equal(2, count2, 'Xtest2 count')
call assert_equal(2, count2buf, 'Xtest2 buffer count')
close
bwipe!
call delete('Xtest_mks.out')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -0,0 +1,104 @@
" Test for :mksession, :mkview and :loadview in utf-8 encoding
set encoding=utf-8
scriptencoding utf-8
if !has('multi_byte') || !has('mksession')
finish
endif
func Test_mksession_utf8()
tabnew
let wrap_save = &wrap
set sessionoptions=buffers splitbelow fileencoding=utf-8
call setline(1, [
\ 'start:',
\ 'no multibyte chAracter',
\ ' one leaDing tab',
\ ' four leadinG spaces',
\ 'two consecutive tabs',
\ 'two tabs in one line',
\ 'one … multibyteCharacter',
\ 'a “b” two multiByte characters',
\ '“c”1€ three mulTibyte characters'
\ ])
let tmpfile = tempname()
exec 'w! ' . tmpfile
/^start:
set wrap
vsplit
norm! j16|
split
norm! j16|
split
norm! j16|
split
norm! j8|
split
norm! j8|
split
norm! j16|
split
norm! j16|
split
norm! j16|
wincmd l
set nowrap
/^start:
norm! j16|3zl
split
norm! j016|3zl
split
norm! j016|3zl
split
norm! j08|3zl
split
norm! j08|3zl
split
norm! j016|3zl
split
norm! j016|3zl
split
norm! j016|3zl
split
call wincol()
mksession! test_mks.out
let li = filter(readfile('test_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"')
let expected = [
\ 'normal! 016|',
\ 'normal! 016|',
\ 'normal! 016|',
\ 'normal! 08|',
\ 'normal! 08|',
\ 'normal! 016|',
\ 'normal! 016|',
\ 'normal! 016|',
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
\ " normal! 08|",
\ " exe 'normal! ' . s:c . '|zs' . 8 . '|'",
\ " normal! 08|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|",
\ " exe 'normal! ' . s:c . '|zs' . 16 . '|'",
\ " normal! 016|"
\ ]
call assert_equal(expected, li)
tabclose!
call delete('test_mks.out')
call delete(tmpfile)
let &wrap = wrap_save
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -13,6 +13,12 @@ function! Test_whichwrap()
set whichwrap+=h,l set whichwrap+=h,l
call assert_equal('b,s,h,l', &whichwrap) call assert_equal('b,s,h,l', &whichwrap)
set whichwrap=h,h
call assert_equal('h', &whichwrap)
set whichwrap=h,h,h
call assert_equal('h', &whichwrap)
set whichwrap& set whichwrap&
endfunction endfunction
@ -97,3 +103,13 @@ func Test_keymap_valid()
call assert_fails(":set kmp=trunc\x00name", "E544:") call assert_fails(":set kmp=trunc\x00name", "E544:")
call assert_fails(":set kmp=trunc\x00name", "trunc") call assert_fails(":set kmp=trunc\x00name", "trunc")
endfunc endfunc
func Test_complete()
" Trailing single backslash used to cause invalid memory access.
set complete=s\
new
call feedkeys("i\<C-N>\<Esc>", 'xt')
bwipe!
set complete&
endfun

View File

@ -7,10 +7,10 @@ func! ListMonths()
if g:setting != '' if g:setting != ''
exe ":set" g:setting exe ":set" g:setting
endif endif
let mth=copy(g:months) let mth = copy(g:months)
let entered = strcharpart(getline('.'),0,col('.')) let entered = strcharpart(getline('.'),0,col('.'))
if !empty(entered) if !empty(entered)
let mth=filter(mth, 'v:val=~"^".entered') let mth = filter(mth, 'v:val=~"^".entered')
endif endif
call complete(1, mth) call complete(1, mth)
return '' return ''
@ -468,7 +468,7 @@ endfunc
" auto-wrap text. " auto-wrap text.
func Test_completion_ctrl_e_without_autowrap() func Test_completion_ctrl_e_without_autowrap()
new new
let tw_save=&tw let tw_save = &tw
set tw=78 set tw=78
let li = [ let li = [
\ '" zzz', \ '" zzz',
@ -478,7 +478,7 @@ func Test_completion_ctrl_e_without_autowrap()
call feedkeys("A\<C-X>\<C-N>\<C-E>\<Esc>", "tx") call feedkeys("A\<C-X>\<C-N>\<C-E>\<Esc>", "tx")
call assert_equal(li, getline(1, '$')) call assert_equal(li, getline(1, '$'))
let &tw=tw_save let &tw = tw_save
q! q!
endfunc endfunc
@ -541,4 +541,33 @@ func Test_completion_comment_formatting()
bwipe! bwipe!
endfunc endfunc
function! DummyCompleteSix()
call complete(1, ['Hello', 'World'])
return ''
endfunction
" complete() correctly clears the list of autocomplete candidates
func Test_completion_clear_candidate_list()
new
%d
" select first entry from the completion popup
call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>", "tx")
call assert_equal('Hello', getline(1))
%d
" select second entry from the completion popup
call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>", "tx")
call assert_equal('World', getline(1))
%d
" select original text
call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>", "tx")
call assert_equal(' xxx', getline(1))
%d
" back at first entry from completion list
call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>\<C-N>", "tx")
call assert_equal('Hello', getline(1))
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -24,28 +24,34 @@ func Test_after_comes_later()
\ 'set guioptions+=M', \ 'set guioptions+=M',
\ 'let $HOME = "/does/not/exist"', \ 'let $HOME = "/does/not/exist"',
\ 'set loadplugins', \ 'set loadplugins',
\ 'set rtp=Xhere,Xafter', \ 'set rtp=Xhere,Xafter,Xanother',
\ 'set packpath=Xhere,Xafter', \ 'set packpath=Xhere,Xafter',
\ 'set nomore', \ 'set nomore',
\ 'let g:sequence = ""',
\ ] \ ]
let after = [ let after = [
\ 'redir! > Xtestout', \ 'redir! > Xtestout',
\ 'scriptnames', \ 'scriptnames',
\ 'redir END', \ 'redir END',
\ 'redir! > Xsequence',
\ 'echo g:sequence',
\ 'redir END',
\ 'quit', \ 'quit',
\ ] \ ]
call mkdir('Xhere/plugin', 'p') call mkdir('Xhere/plugin', 'p')
call writefile(['let done = 1'], 'Xhere/plugin/here.vim') call writefile(['let g:sequence .= "here "'], 'Xhere/plugin/here.vim')
call mkdir('Xanother/plugin', 'p')
call writefile(['let g:sequence .= "another "'], 'Xanother/plugin/another.vim')
call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p')
call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') call writefile(['let g:sequence .= "pack "'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim')
call mkdir('Xafter/plugin', 'p') call mkdir('Xafter/plugin', 'p')
call writefile(['let done = 1'], 'Xafter/plugin/later.vim') call writefile(['let g:sequence .= "after "'], 'Xafter/plugin/later.vim')
if RunVim(before, after, '') if RunVim(before, after, '')
let lines = readfile('Xtestout') let lines = readfile('Xtestout')
let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] let expected = ['Xbefore.vim', 'here.vim', 'another.vim', 'foo.vim', 'later.vim', 'Xafter.vim']
let found = [] let found = []
for line in lines for line in lines
for one in expected for one in expected
@ -57,9 +63,45 @@ func Test_after_comes_later()
call assert_equal(expected, found) call assert_equal(expected, found)
endif endif
call assert_equal('here another pack after', substitute(join(readfile('Xsequence', 1), ''), '\s\+$', '', ''))
call delete('Xtestout')
call delete('Xsequence')
call delete('Xhere', 'rf')
call delete('Xanother', 'rf')
call delete('Xafter', 'rf')
endfunc
func Test_pack_in_rtp_when_plugins_run()
if !has('packages')
return
endif
let before = [
\ 'set nocp viminfo+=nviminfo',
\ 'set guioptions+=M',
\ 'let $HOME = "/does/not/exist"',
\ 'set loadplugins',
\ 'set rtp=Xhere',
\ 'set packpath=Xhere',
\ 'set nomore',
\ ]
let after = [
\ 'quit',
\ ]
call mkdir('Xhere/plugin', 'p')
call writefile(['redir! > Xtestout', 'silent set runtimepath?', 'silent! call foo#Trigger()', 'redir END'], 'Xhere/plugin/here.vim')
call mkdir('Xhere/pack/foo/start/foobar/autoload', 'p')
call writefile(['function! foo#Trigger()', 'echo "autoloaded foo"', 'endfunction'], 'Xhere/pack/foo/start/foobar/autoload/foo.vim')
if RunVim(before, after, '')
let lines = filter(readfile('Xtestout'), '!empty(v:val)')
call assert_match('Xhere[/\\]pack[/\\]foo[/\\]start[/\\]foobar', get(lines, 0))
call assert_match('autoloaded foo', get(lines, 1))
endif
call delete('Xtestout') call delete('Xtestout')
call delete('Xhere', 'rf') call delete('Xhere', 'rf')
call delete('Xafter', 'rf')
endfunc endfunc
func Test_help_arg() func Test_help_arg()
@ -76,11 +118,11 @@ func Test_help_arg()
let found = [] let found = []
for line in lines for line in lines
if line =~ '-R.*Read-only mode' if line =~ '-R.*Read-only mode'
call add(found, 'Readonly mode') call add(found, 'Readonly mode')
endif endif
" Watch out for a second --version line in the Gnome version. " Watch out for a second --version line in the Gnome version.
if line =~ '--version.*Print version information and exit' if line =~ '--version.*Print version information'
call add(found, "--version") call add(found, "--version")
endif endif
endfor endfor
call assert_equal(['Readonly mode', '--version'], found) call assert_equal(['Readonly mode', '--version'], found)

View File

@ -50,7 +50,7 @@ func Test_syn_iskeyword()
setlocal isk-=_ setlocal isk-=_
call assert_equal('DLTD_BY', GetSyntaxItem('DLTD')) call assert_equal('DLTD_BY', GetSyntaxItem('DLTD'))
/\<D\k\+\>/:norm! ygn /\<D\k\+\>/:norm! ygn
let b2=@0 let b2 = @0
call assert_equal('DLTD', @0) call assert_equal('DLTD', @0)
syn iskeyword clear syn iskeyword clear
@ -76,3 +76,85 @@ func Test_syntax_after_reload()
call assert_true(exists('g:gotit')) call assert_true(exists('g:gotit'))
call delete('Xsomefile') call delete('Xsomefile')
endfunc endfunc
func Test_syntime()
if !has('profile')
return
endif
syntax on
syntime on
let a = execute('syntime report')
call assert_equal("\nNo Syntax items defined for this buffer", a)
view ../memfile_test.c
setfiletype cpp
redraw
let a = execute('syntime report')
call assert_match('^ TOTAL *COUNT *MATCH *SLOWEST *AVERAGE *NAME *PATTERN', a)
call assert_match(' \d*\.\d* \+[^0]\d* .* cppRawString ', a)
call assert_match(' \d*\.\d* \+[^0]\d* .* cppNumber ', a)
syntime off
syntime clear
let a = execute('syntime report')
call assert_match('^ TOTAL *COUNT *MATCH *SLOWEST *AVERAGE *NAME *PATTERN', a)
call assert_notmatch('.* cppRawString *', a)
call assert_notmatch('.* cppNumber*', a)
call assert_notmatch('[1-9]', a)
call assert_fails('syntime abc', 'E475')
syntax clear
let a = execute('syntime report')
call assert_equal("\nNo Syntax items defined for this buffer", a)
bd
endfunc
func Test_syntax_list()
syntax on
let a = execute('syntax list')
call assert_equal("\nNo Syntax items defined for this buffer", a)
view ../memfile_test.c
setfiletype c
let a = execute('syntax list')
call assert_match('cInclude*', a)
call assert_match('cDefine', a)
let a = execute('syntax list cDefine')
call assert_notmatch('cInclude*', a)
call assert_match('cDefine', a)
call assert_match(' links to Macro$', a)
call assert_fails('syntax list ABCD', 'E28:')
call assert_fails('syntax list @ABCD', 'E392:')
syntax clear
let a = execute('syntax list')
call assert_equal("\nNo Syntax items defined for this buffer", a)
bd
endfunc
func Test_syntax_completion()
call feedkeys(":syn \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"syn case clear cluster conceal enable include iskeyword keyword list manual match off on region reset spell sync', @:)
call feedkeys(":syn case \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"syn case ignore match', @:)
call feedkeys(":syn spell \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"syn spell default notoplevel toplevel', @:)
call feedkeys(":syn sync \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"syn sync ccomment clear fromstart linebreaks= linecont lines= match maxlines= minlines= region', @:)
call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_match('^"syn list Boolean Character ', @:)
call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx')
call assert_match('^"syn match Boolean Character ', @:)
endfunc

View File

@ -15,3 +15,23 @@ func Test_block_shift_multibyte()
call assert_equal(' ヹxxx', getline(2)) call assert_equal(' ヹxxx', getline(2))
q! q!
endfunc endfunc
func Test_Visual_ctrl_o()
new
call setline(1, ['one', 'two', 'three'])
call cursor(1,2)
set noshowmode
set tw=0
call feedkeys("\<c-v>jjlIa\<c-\>\<c-o>:set tw=88\<cr>\<esc>", 'tx')
call assert_equal(['oane', 'tawo', 'tahree'], getline(1, 3))
call assert_equal(88, &tw)
set tw&
bw!
endfu
func Test_Visual_vapo()
new
normal oxx
normal vapo
bwipe!
endfunc

View File

@ -8,6 +8,7 @@
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/main.h" #include "nvim/main.h"
#include "nvim/aucmd.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/event/rstream.h" #include "nvim/event/rstream.h"
@ -280,9 +281,9 @@ static void timer_cb(TimeWatcher *watcher, void *data)
/// Handle focus events. /// Handle focus events.
/// ///
/// If the upcoming sequence of bytes in the input stream matches either the /// If the upcoming sequence of bytes in the input stream matches the termcode
/// escape code for focus gained `<ESC>[I` or focus lost `<ESC>[O` then consume /// for "focus gained" or "focus lost", consume that sequence and schedule an
/// that sequence and push the appropriate event into the input queue /// event on the main loop.
/// ///
/// @param input the input stream /// @param input the input stream
/// @return true iff handle_focus_event consumed some input /// @return true iff handle_focus_event consumed some input
@ -294,11 +295,7 @@ static bool handle_focus_event(TermInput *input)
// Advance past the sequence // Advance past the sequence
bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
rbuffer_consumed(input->read_stream.buffer, 3); rbuffer_consumed(input->read_stream.buffer, 3);
if (focus_gained) { aucmd_schedule_focusgained(focus_gained);
enqueue_input(input, FOCUSGAINED_KEY, sizeof(FOCUSGAINED_KEY) - 1);
} else {
enqueue_input(input, FOCUSLOST_KEY, sizeof(FOCUSLOST_KEY) - 1);
}
return true; return true;
} }
return false; return false;

View File

@ -104,7 +104,9 @@ unibi_term *load_builtin_terminfo(const char * term)
return unibi_from_mem((const char *)interix_8colour_terminfo, return unibi_from_mem((const char *)interix_8colour_terminfo,
sizeof interix_8colour_terminfo); sizeof interix_8colour_terminfo);
} else if (terminfo_is_term_family(term, "iterm") } else if (terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iTerm.app")) { || terminfo_is_term_family(term, "iterm2")
|| terminfo_is_term_family(term, "iTerm.app")
|| terminfo_is_term_family(term, "iTerm2.app")) {
return unibi_from_mem((const char *)iterm_256colour_terminfo, return unibi_from_mem((const char *)iterm_256colour_terminfo,
sizeof iterm_256colour_terminfo); sizeof iterm_256colour_terminfo);
} else if (terminfo_is_term_family(term, "st")) { } else if (terminfo_is_term_family(term, "st")) {

View File

@ -52,6 +52,15 @@
#define LINUXSET0C "\x1b[?0c" #define LINUXSET0C "\x1b[?0c"
#define LINUXSET1C "\x1b[?1c" #define LINUXSET1C "\x1b[?1c"
#ifdef NVIM_UNIBI_HAS_VAR_FROM
#define UNIBI_SET_NUM_VAR(var, num) \
do { \
(var) = unibi_var_from_num((num)); \
} while (0)
#else
#define UNIBI_SET_NUM_VAR(var, num) (var).i = (num);
#endif
// Per the commentary in terminfo, only a minus sign is a true suffix // Per the commentary in terminfo, only a minus sign is a true suffix
// separator. // separator.
bool terminfo_is_term_family(const char *term, const char *family) bool terminfo_is_term_family(const char *term, const char *family)
@ -234,9 +243,9 @@ static void terminfo_start(UI *ui)
unibi_out(ui, unibi_keypad_xmit); unibi_out(ui, unibi_keypad_xmit);
unibi_out(ui, unibi_clear_screen); unibi_out(ui, unibi_clear_screen);
// Enable bracketed paste // Enable bracketed paste
unibi_out(ui, data->unibi_ext.enable_bracketed_paste); unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
// Enable focus reporting // Enable focus reporting
unibi_out(ui, data->unibi_ext.enable_focus_reporting); unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);
uv_loop_init(&data->write_loop); uv_loop_init(&data->write_loop);
if (data->out_isatty) { if (data->out_isatty) {
uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0);
@ -263,9 +272,9 @@ static void terminfo_stop(UI *ui)
unibi_out(ui, unibi_keypad_local); unibi_out(ui, unibi_keypad_local);
unibi_out(ui, unibi_exit_ca_mode); unibi_out(ui, unibi_exit_ca_mode);
// Disable bracketed paste // Disable bracketed paste
unibi_out(ui, data->unibi_ext.disable_bracketed_paste); unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste);
// Disable focus reporting // Disable focus reporting
unibi_out(ui, data->unibi_ext.disable_focus_reporting); unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting);
flush_buf(ui, true); flush_buf(ui, true);
uv_tty_reset_mode(); uv_tty_reset_mode();
uv_close((uv_handle_t *)&data->output_handle, NULL); uv_close((uv_handle_t *)&data->output_handle, NULL);
@ -279,7 +288,7 @@ static void terminfo_stop(UI *ui)
static void tui_terminal_start(UI *ui) static void tui_terminal_start(UI *ui)
{ {
TUIData *data = ui->data; TUIData *data = ui->data;
data->print_attrs = EMPTY_ATTRS; data->print_attrs = HLATTRS_INIT;
ugrid_init(&data->grid); ugrid_init(&data->grid);
terminfo_start(ui); terminfo_start(ui);
update_size(ui); update_size(ui);
@ -391,15 +400,15 @@ static void update_attrs(UI *ui, HlAttrs attrs)
if (unibi_get_str(data->ut, unibi_set_attributes)) { if (unibi_get_str(data->ut, unibi_set_attributes)) {
if (attrs.bold || attrs.reverse || attrs.underline || attrs.undercurl) { if (attrs.bold || attrs.reverse || attrs.underline || attrs.undercurl) {
data->params[0].i = 0; // standout UNIBI_SET_NUM_VAR(data->params[0], 0); // standout
data->params[1].i = attrs.underline || attrs.undercurl; UNIBI_SET_NUM_VAR(data->params[1], attrs.underline || attrs.undercurl);
data->params[2].i = attrs.reverse; UNIBI_SET_NUM_VAR(data->params[2], attrs.reverse);
data->params[3].i = 0; // blink UNIBI_SET_NUM_VAR(data->params[3], 0); // blink
data->params[4].i = 0; // dim UNIBI_SET_NUM_VAR(data->params[4], 0); // dim
data->params[5].i = attrs.bold; UNIBI_SET_NUM_VAR(data->params[5], attrs.bold);
data->params[6].i = 0; // blank UNIBI_SET_NUM_VAR(data->params[6], 0); // blank
data->params[7].i = 0; // protect UNIBI_SET_NUM_VAR(data->params[7], 0); // protect
data->params[8].i = 0; // alternate character set UNIBI_SET_NUM_VAR(data->params[8], 0); // alternate character set
unibi_out(ui, unibi_set_attributes); unibi_out(ui, unibi_set_attributes);
} else if (!data->default_attr) { } else if (!data->default_attr) {
unibi_out(ui, unibi_exit_attribute_mode); unibi_out(ui, unibi_exit_attribute_mode);
@ -423,26 +432,26 @@ static void update_attrs(UI *ui, HlAttrs attrs)
} }
if (ui->rgb) { if (ui->rgb) {
if (fg != -1) { if (fg != -1) {
data->params[0].i = (fg >> 16) & 0xff; // red UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red
data->params[1].i = (fg >> 8) & 0xff; // green UNIBI_SET_NUM_VAR(data->params[1], (fg >> 8) & 0xff); // green
data->params[2].i = fg & 0xff; // blue UNIBI_SET_NUM_VAR(data->params[2], fg & 0xff); // blue
unibi_out(ui, data->unibi_ext.set_rgb_foreground); unibi_out_ext(ui, data->unibi_ext.set_rgb_foreground);
} }
if (bg != -1) { if (bg != -1) {
data->params[0].i = (bg >> 16) & 0xff; // red UNIBI_SET_NUM_VAR(data->params[0], (bg >> 16) & 0xff); // red
data->params[1].i = (bg >> 8) & 0xff; // green UNIBI_SET_NUM_VAR(data->params[1], (bg >> 8) & 0xff); // green
data->params[2].i = bg & 0xff; // blue UNIBI_SET_NUM_VAR(data->params[2], bg & 0xff); // blue
unibi_out(ui, data->unibi_ext.set_rgb_background); unibi_out_ext(ui, data->unibi_ext.set_rgb_background);
} }
} else { } else {
if (fg != -1) { if (fg != -1) {
data->params[0].i = fg; UNIBI_SET_NUM_VAR(data->params[0], fg);
unibi_out(ui, unibi_set_a_foreground); unibi_out(ui, unibi_set_a_foreground);
} }
if (bg != -1) { if (bg != -1) {
data->params[0].i = bg; UNIBI_SET_NUM_VAR(data->params[0], bg);
unibi_out(ui, unibi_set_a_background); unibi_out(ui, unibi_set_a_background);
} }
} }
@ -558,7 +567,7 @@ static void cursor_goto(UI *ui, int row, int col)
unibi_out(ui, unibi_cursor_left); unibi_out(ui, unibi_cursor_left);
} }
} else { } else {
data->params[0].i = n; UNIBI_SET_NUM_VAR(data->params[0], n);
unibi_out(ui, unibi_parm_left_cursor); unibi_out(ui, unibi_parm_left_cursor);
} }
ugrid_goto(grid, row, col); ugrid_goto(grid, row, col);
@ -570,7 +579,7 @@ static void cursor_goto(UI *ui, int row, int col)
unibi_out(ui, unibi_cursor_right); unibi_out(ui, unibi_cursor_right);
} }
} else { } else {
data->params[0].i = n; UNIBI_SET_NUM_VAR(data->params[0], n);
unibi_out(ui, unibi_parm_right_cursor); unibi_out(ui, unibi_parm_right_cursor);
} }
ugrid_goto(grid, row, col); ugrid_goto(grid, row, col);
@ -585,7 +594,7 @@ static void cursor_goto(UI *ui, int row, int col)
unibi_out(ui, unibi_cursor_down); unibi_out(ui, unibi_cursor_down);
} }
} else { } else {
data->params[0].i = n; UNIBI_SET_NUM_VAR(data->params[0], n);
unibi_out(ui, unibi_parm_down_cursor); unibi_out(ui, unibi_parm_down_cursor);
} }
ugrid_goto(grid, row, col); ugrid_goto(grid, row, col);
@ -597,7 +606,7 @@ static void cursor_goto(UI *ui, int row, int col)
unibi_out(ui, unibi_cursor_up); unibi_out(ui, unibi_cursor_up);
} }
} else { } else {
data->params[0].i = n; UNIBI_SET_NUM_VAR(data->params[0], n);
unibi_out(ui, unibi_parm_up_cursor); unibi_out(ui, unibi_parm_up_cursor);
} }
ugrid_goto(grid, row, col); ugrid_goto(grid, row, col);
@ -619,7 +628,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right)
if (grid->bg == -1 && right == ui->width -1) { if (grid->bg == -1 && right == ui->width -1) {
// Background is set to the default color and the right edge matches the // Background is set to the default color and the right edge matches the
// screen end, try to use terminal codes for clearing the requested area. // screen end, try to use terminal codes for clearing the requested area.
HlAttrs clear_attrs = EMPTY_ATTRS; HlAttrs clear_attrs = HLATTRS_INIT;
clear_attrs.foreground = grid->fg; clear_attrs.foreground = grid->fg;
clear_attrs.background = grid->bg; clear_attrs.background = grid->bg;
update_attrs(ui, clear_attrs); update_attrs(ui, clear_attrs);
@ -675,19 +684,19 @@ static void set_scroll_region(UI *ui)
TUIData *data = ui->data; TUIData *data = ui->data;
UGrid *grid = &data->grid; UGrid *grid = &data->grid;
data->params[0].i = grid->top; UNIBI_SET_NUM_VAR(data->params[0], grid->top);
data->params[1].i = grid->bot; UNIBI_SET_NUM_VAR(data->params[1], grid->bot);
unibi_out(ui, unibi_change_scroll_region); unibi_out(ui, unibi_change_scroll_region);
if (grid->left != 0 || grid->right != ui->width - 1) { if (grid->left != 0 || grid->right != ui->width - 1) {
unibi_out(ui, data->unibi_ext.enable_lr_margin); unibi_out_ext(ui, data->unibi_ext.enable_lr_margin);
if (data->can_set_lr_margin) { if (data->can_set_lr_margin) {
data->params[0].i = grid->left; UNIBI_SET_NUM_VAR(data->params[0], grid->left);
data->params[1].i = grid->right; UNIBI_SET_NUM_VAR(data->params[1], grid->right);
unibi_out(ui, unibi_set_lr_margin); unibi_out(ui, unibi_set_lr_margin);
} else { } else {
data->params[0].i = grid->left; UNIBI_SET_NUM_VAR(data->params[0], grid->left);
unibi_out(ui, unibi_set_left_margin_parm); unibi_out(ui, unibi_set_left_margin_parm);
data->params[0].i = grid->right; UNIBI_SET_NUM_VAR(data->params[0], grid->right);
unibi_out(ui, unibi_set_right_margin_parm); unibi_out(ui, unibi_set_right_margin_parm);
} }
} }
@ -700,24 +709,24 @@ static void reset_scroll_region(UI *ui)
UGrid *grid = &data->grid; UGrid *grid = &data->grid;
if (0 <= data->unibi_ext.reset_scroll_region) { if (0 <= data->unibi_ext.reset_scroll_region) {
unibi_out(ui, data->unibi_ext.reset_scroll_region); unibi_out_ext(ui, data->unibi_ext.reset_scroll_region);
} else { } else {
data->params[0].i = 0; UNIBI_SET_NUM_VAR(data->params[0], 0);
data->params[1].i = ui->height - 1; UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1);
unibi_out(ui, unibi_change_scroll_region); unibi_out(ui, unibi_change_scroll_region);
} }
if (grid->left != 0 || grid->right != ui->width - 1) { if (grid->left != 0 || grid->right != ui->width - 1) {
if (data->can_set_lr_margin) { if (data->can_set_lr_margin) {
data->params[0].i = 0; UNIBI_SET_NUM_VAR(data->params[0], 0);
data->params[1].i = ui->width - 1; UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1);
unibi_out(ui, unibi_set_lr_margin); unibi_out(ui, unibi_set_lr_margin);
} else { } else {
data->params[0].i = 0; UNIBI_SET_NUM_VAR(data->params[0], 0);
unibi_out(ui, unibi_set_left_margin_parm); unibi_out(ui, unibi_set_left_margin_parm);
data->params[0].i = ui->width - 1; UNIBI_SET_NUM_VAR(data->params[0], ui->width - 1);
unibi_out(ui, unibi_set_right_margin_parm); unibi_out(ui, unibi_set_right_margin_parm);
} }
unibi_out(ui, data->unibi_ext.disable_lr_margin); unibi_out_ext(ui, data->unibi_ext.disable_lr_margin);
} }
unibi_goto(ui, grid->row, grid->col); unibi_goto(ui, grid->row, grid->col);
} }
@ -728,9 +737,9 @@ static void tui_resize(UI *ui, Integer width, Integer height)
ugrid_resize(&data->grid, (int)width, (int)height); ugrid_resize(&data->grid, (int)width, (int)height);
if (!got_winch) { // Try to resize the terminal window. if (!got_winch) { // Try to resize the terminal window.
data->params[0].i = (int)height; UNIBI_SET_NUM_VAR(data->params[0], (int)height);
data->params[1].i = (int)width; UNIBI_SET_NUM_VAR(data->params[1], (int)width);
unibi_out(ui, data->unibi_ext.resize_screen); unibi_out_ext(ui, data->unibi_ext.resize_screen);
// DECSLPP does not reset the scroll region. // DECSLPP does not reset the scroll region.
if (data->scroll_region_is_full_screen) { if (data->scroll_region_is_full_screen) {
reset_scroll_region(ui); reset_scroll_region(ui);
@ -836,7 +845,7 @@ static void tui_mouse_on(UI *ui)
{ {
TUIData *data = ui->data; TUIData *data = ui->data;
if (!data->mouse_enabled) { if (!data->mouse_enabled) {
unibi_out(ui, data->unibi_ext.enable_mouse); unibi_out_ext(ui, data->unibi_ext.enable_mouse);
data->mouse_enabled = true; data->mouse_enabled = true;
} }
} }
@ -845,7 +854,7 @@ static void tui_mouse_off(UI *ui)
{ {
TUIData *data = ui->data; TUIData *data = ui->data;
if (data->mouse_enabled) { if (data->mouse_enabled) {
unibi_out(ui, data->unibi_ext.disable_mouse); unibi_out_ext(ui, data->unibi_ext.disable_mouse);
data->mouse_enabled = false; data->mouse_enabled = false;
} }
} }
@ -863,8 +872,8 @@ static void tui_set_mode(UI *ui, ModeShape mode)
int attr = syn_id2attr(c.id); int attr = syn_id2attr(c.id);
if (attr > 0) { if (attr > 0) {
attrentry_T *aep = syn_cterm_attr2entry(attr); attrentry_T *aep = syn_cterm_attr2entry(attr);
data->params[0].i = aep->rgb_bg_color; UNIBI_SET_NUM_VAR(data->params[0], aep->rgb_bg_color);
unibi_out(ui, data->unibi_ext.set_cursor_color); unibi_out_ext(ui, data->unibi_ext.set_cursor_color);
} }
} }
@ -874,8 +883,8 @@ static void tui_set_mode(UI *ui, ModeShape mode)
case SHAPE_VER: shape = 5; break; case SHAPE_VER: shape = 5; break;
default: WLOG("Unknown shape value %d", shape); break; default: WLOG("Unknown shape value %d", shape); break;
} }
data->params[0].i = shape + (int)(c.blinkon == 0); UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0));
unibi_out(ui, data->unibi_ext.set_cursor_style); unibi_out_ext(ui, data->unibi_ext.set_cursor_style);
} }
/// @param mode editor mode /// @param mode editor mode
@ -917,7 +926,7 @@ static void tui_scroll(UI *ui, Integer count)
cursor_goto(ui, grid->top, grid->left); cursor_goto(ui, grid->top, grid->left);
// also set default color attributes or some terminals can become funny // also set default color attributes or some terminals can become funny
if (scroll_clears_to_current_colour) { if (scroll_clears_to_current_colour) {
HlAttrs clear_attrs = EMPTY_ATTRS; HlAttrs clear_attrs = HLATTRS_INIT;
clear_attrs.foreground = grid->fg; clear_attrs.foreground = grid->fg;
clear_attrs.background = grid->bg; clear_attrs.background = grid->bg;
update_attrs(ui, clear_attrs); update_attrs(ui, clear_attrs);
@ -927,14 +936,14 @@ static void tui_scroll(UI *ui, Integer count)
if (count == 1) { if (count == 1) {
unibi_out(ui, unibi_delete_line); unibi_out(ui, unibi_delete_line);
} else { } else {
data->params[0].i = (int)count; UNIBI_SET_NUM_VAR(data->params[0], (int)count);
unibi_out(ui, unibi_parm_delete_line); unibi_out(ui, unibi_parm_delete_line);
} }
} else { } else {
if (count == -1) { if (count == -1) {
unibi_out(ui, unibi_insert_line); unibi_out(ui, unibi_insert_line);
} else { } else {
data->params[0].i = -(int)count; UNIBI_SET_NUM_VAR(data->params[0], -(int)count);
unibi_out(ui, unibi_parm_insert_line); unibi_out(ui, unibi_parm_insert_line);
} }
} }
@ -1177,30 +1186,33 @@ end:
static void unibi_goto(UI *ui, int row, int col) static void unibi_goto(UI *ui, int row, int col)
{ {
TUIData *data = ui->data; TUIData *data = ui->data;
data->params[0].i = row; UNIBI_SET_NUM_VAR(data->params[0], row);
data->params[1].i = col; UNIBI_SET_NUM_VAR(data->params[1], col);
unibi_out(ui, unibi_cursor_address); unibi_out(ui, unibi_cursor_address);
} }
#define UNIBI_OUT(fn) \
do { \
TUIData *data = ui->data; \
const char *str = NULL; \
if (unibi_index >= 0) { \
str = fn(data->ut, (unsigned)unibi_index); \
} \
if (str) { \
unibi_var_t vars[26 + 26]; \
memset(&vars, 0, sizeof(vars)); \
unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); \
} \
} while (0)
static void unibi_out(UI *ui, int unibi_index) static void unibi_out(UI *ui, int unibi_index)
{ {
TUIData *data = ui->data; UNIBI_OUT(unibi_get_str);
const char *str = NULL;
if (unibi_index >= 0) {
if (unibi_index < unibi_string_begin_) {
str = unibi_get_ext_str(data->ut, (unsigned)unibi_index);
} else {
str = unibi_get_str(data->ut, (unsigned)unibi_index);
}
}
if (str) {
unibi_var_t vars[26 + 26] = {{0}};
unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL);
}
} }
static void unibi_out_ext(UI *ui, int unibi_index)
{
UNIBI_OUT(unibi_get_ext_str);
}
#undef UNIBI_OUT
static void out(void *ctx, const char *str, size_t len) static void out(void *ctx, const char *str, size_t len)
{ {
@ -1261,7 +1273,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
bool gnome = terminfo_is_term_family(term, "gnome") bool gnome = terminfo_is_term_family(term, "gnome")
|| terminfo_is_term_family(term, "vte"); || terminfo_is_term_family(term, "vte");
bool iterm = terminfo_is_term_family(term, "iterm") bool iterm = terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iTerm.app"); || terminfo_is_term_family(term, "iterm2")
|| terminfo_is_term_family(term, "iTerm.app")
|| terminfo_is_term_family(term, "iTerm2.app");
// None of the following work over SSH; see :help TERM . // None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env; bool iterm_pretending_xterm = xterm && iterm_env;
bool konsole_pretending_xterm = xterm && konsole; bool konsole_pretending_xterm = xterm && konsole;
@ -1444,7 +1458,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// teminfo entries. See // teminfo entries. See
// https://github.com/gnachman/iTerm2/pull/92 for more. // https://github.com/gnachman/iTerm2/pull/92 for more.
// xterm even has an extended version that has a vertical bar. // xterm even has an extended version that has a vertical bar.
if (true_xterm // per xterm ctlseqs doco (since version 282) if (!konsole && (true_xterm // per xterm ctlseqs doco (since version 282)
// per MinTTY 0.4.3-1 release notes from 2009 // per MinTTY 0.4.3-1 release notes from 2009
|| putty || putty
// per https://bugzilla.gnome.org/show_bug.cgi?id=720821 // per https://bugzilla.gnome.org/show_bug.cgi?id=720821
@ -1459,7 +1473,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// Allows forcing the use of DECSCUSR on linux type terminals, such as // Allows forcing the use of DECSCUSR on linux type terminals, such as
// console-terminal-emulator from the nosh toolset, which does indeed // console-terminal-emulator from the nosh toolset, which does indeed
// implement the xterm extension: // implement the xterm extension:
|| (linuxvt && (xterm_version || (vte_version > 0) || colorterm))) { || (linuxvt && (xterm_version || (vte_version > 0) || colorterm)))) {
data->unibi_ext.set_cursor_style = data->unibi_ext.set_cursor_style =
(int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q"); (int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q");
if (-1 == data->unibi_ext.reset_cursor_style) { if (-1 == data->unibi_ext.reset_cursor_style) {
@ -1533,7 +1547,9 @@ static void augment_terminfo(TUIData *data, const char *term,
bool screen = terminfo_is_term_family(term, "screen"); bool screen = terminfo_is_term_family(term, "screen");
bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX"); bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
bool iterm = terminfo_is_term_family(term, "iterm") bool iterm = terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iTerm.app"); || terminfo_is_term_family(term, "iterm2")
|| terminfo_is_term_family(term, "iTerm.app")
|| terminfo_is_term_family(term, "iTerm2.app");
// None of the following work over SSH; see :help TERM . // None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env; bool iterm_pretending_xterm = xterm && iterm_env;

View File

@ -16,7 +16,7 @@
void ugrid_init(UGrid *grid) void ugrid_init(UGrid *grid)
{ {
grid->attrs = EMPTY_ATTRS; grid->attrs = HLATTRS_INIT;
grid->fg = grid->bg = -1; grid->fg = grid->bg = -1;
grid->cells = NULL; grid->cells = NULL;
} }
@ -118,7 +118,7 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size)
static void clear_region(UGrid *grid, int top, int bot, int left, int right) static void clear_region(UGrid *grid, int top, int bot, int left, int right)
{ {
HlAttrs clear_attrs = EMPTY_ATTRS; HlAttrs clear_attrs = HLATTRS_INIT;
clear_attrs.foreground = grid->fg; clear_attrs.foreground = grid->fg;
clear_attrs.background = grid->bg; clear_attrs.background = grid->bg;
UGRID_FOREACH_CELL(grid, top, bot, left, right, { UGRID_FOREACH_CELL(grid, top, bot, left, right, {

View File

@ -21,8 +21,6 @@ struct ugrid {
UCell **cells; UCell **cells;
}; };
#define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 })
#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ #define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \
do { \ do { \
for (int row = top; row <= bot; row++) { \ for (int row = top; row <= bot; row++) { \

View File

@ -71,10 +71,10 @@ static char uilog_last_event[1024] = { 0 };
uilog_seen++; \ uilog_seen++; \
} else { \ } else { \
if (uilog_seen > 0) { \ if (uilog_seen > 0) { \
do_log(DEBUG_LOG_LEVEL, "ui", 0, true, \ do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, \
"%s (+%zu times...)", uilog_last_event, uilog_seen); \ "%s (+%zu times...)", uilog_last_event, uilog_seen); \
} \ } \
DLOG("ui: " STR(funname)); \ do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, STR(funname)); \
uilog_seen = 0; \ uilog_seen = 0; \
xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \ xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \
} \ } \
@ -166,6 +166,90 @@ void ui_event(char *name, Array args)
} }
} }
/// Converts an attrentry_T into an HlAttrs
///
/// @param[in] aep data to convert
/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*'
HlAttrs attrentry2hlattrs(const attrentry_T *aep, bool use_rgb)
{
assert(aep);
HlAttrs attrs = HLATTRS_INIT;
int mask = 0;
mask = use_rgb ? aep->rgb_ae_attr : aep->cterm_ae_attr;
attrs.bold = mask & HL_BOLD;
attrs.underline = mask & HL_UNDERLINE;
attrs.undercurl = mask & HL_UNDERCURL;
attrs.italic = mask & HL_ITALIC;
attrs.reverse = mask & (HL_INVERSE | HL_STANDOUT);
if (use_rgb) {
if (aep->rgb_fg_color != -1) {
attrs.foreground = aep->rgb_fg_color;
}
if (aep->rgb_bg_color != -1) {
attrs.background = aep->rgb_bg_color;
}
if (aep->rgb_sp_color != -1) {
attrs.special = aep->rgb_sp_color;
}
} else {
if (cterm_normal_fg_color != aep->cterm_fg_color) {
attrs.foreground = aep->cterm_fg_color - 1;
}
if (cterm_normal_bg_color != aep->cterm_bg_color) {
attrs.background = aep->cterm_bg_color - 1;
}
}
return attrs;
}
Dictionary hlattrs2dict(HlAttrs attrs)
{
Dictionary hl = ARRAY_DICT_INIT;
if (attrs.bold) {
PUT(hl, "bold", BOOLEAN_OBJ(true));
}
if (attrs.underline) {
PUT(hl, "underline", BOOLEAN_OBJ(true));
}
if (attrs.undercurl) {
PUT(hl, "undercurl", BOOLEAN_OBJ(true));
}
if (attrs.italic) {
PUT(hl, "italic", BOOLEAN_OBJ(true));
}
if (attrs.reverse) {
PUT(hl, "reverse", BOOLEAN_OBJ(true));
}
if (attrs.foreground != -1) {
PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground));
}
if (attrs.background != -1) {
PUT(hl, "background", INTEGER_OBJ(attrs.background));
}
if (attrs.special != -1) {
PUT(hl, "special", INTEGER_OBJ(attrs.special));
}
return hl;
}
void ui_refresh(void) void ui_refresh(void)
{ {
if (!ui_active()) { if (!ui_active()) {
@ -405,54 +489,20 @@ void ui_flush(void)
static void set_highlight_args(int attr_code) static void set_highlight_args(int attr_code)
{ {
HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 }; HlAttrs rgb_attrs = HLATTRS_INIT;
HlAttrs cterm_attrs = rgb_attrs; HlAttrs cterm_attrs = rgb_attrs;
if (attr_code == HL_NORMAL) { if (attr_code == HL_NORMAL) {
goto end; goto end;
} }
int rgb_mask = 0;
int cterm_mask = 0;
attrentry_T *aep = syn_cterm_attr2entry(attr_code); attrentry_T *aep = syn_cterm_attr2entry(attr_code);
if (!aep) { if (!aep) {
goto end; goto end;
} }
rgb_mask = aep->rgb_ae_attr; rgb_attrs = attrentry2hlattrs(aep, true);
cterm_mask = aep->cterm_ae_attr; cterm_attrs = attrentry2hlattrs(aep, false);
rgb_attrs.bold = rgb_mask & HL_BOLD;
rgb_attrs.underline = rgb_mask & HL_UNDERLINE;
rgb_attrs.undercurl = rgb_mask & HL_UNDERCURL;
rgb_attrs.italic = rgb_mask & HL_ITALIC;
rgb_attrs.reverse = rgb_mask & (HL_INVERSE | HL_STANDOUT);
cterm_attrs.bold = cterm_mask & HL_BOLD;
cterm_attrs.underline = cterm_mask & HL_UNDERLINE;
cterm_attrs.undercurl = cterm_mask & HL_UNDERCURL;
cterm_attrs.italic = cterm_mask & HL_ITALIC;
cterm_attrs.reverse = cterm_mask & (HL_INVERSE | HL_STANDOUT);
if (aep->rgb_fg_color != normal_fg) {
rgb_attrs.foreground = aep->rgb_fg_color;
}
if (aep->rgb_bg_color != normal_bg) {
rgb_attrs.background = aep->rgb_bg_color;
}
if (aep->rgb_sp_color != normal_sp) {
rgb_attrs.special = aep->rgb_sp_color;
}
if (cterm_normal_fg_color != aep->cterm_fg_color) {
cterm_attrs.foreground = aep->cterm_fg_color - 1;
}
if (cterm_normal_bg_color != aep->cterm_bg_color) {
cterm_attrs.background = aep->cterm_bg_color - 1;
}
end: end:
UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs)); UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs));

View File

@ -21,6 +21,9 @@ typedef struct {
int foreground, background, special; int foreground, background, special;
} HlAttrs; } HlAttrs;
#define HLATTRS_INIT \
((HlAttrs){ false, false, false, false, false, -1, -1, -1 })
typedef struct ui_t UI; typedef struct ui_t UI;
struct ui_t { struct ui_t {

View File

@ -77,6 +77,157 @@ static char *features[] = {
// clang-format off // clang-format off
static const int included_patches[] = { static const int included_patches[] = {
// 1026,
1025,
1024,
// 1023,
// 1022,
// 1021,
// 1020,
// 1019,
// 1018,
// 1017,
// 1016,
// 1015,
// 1014,
// 1013,
// 1012,
// 1011,
// 1010,
// 1009,
// 1008,
// 1007,
// 1006,
// 1005,
// 1004,
// 1003,
// 1002,
// 1001,
// 1000,
// 999,
// 998,
// 997,
// 996,
// 995,
// 994,
// 993,
// 992,
// 991,
// 990,
// 989,
// 988,
// 987,
// 986,
// 985,
// 984,
// 983,
// 982,
// 981,
// 980,
// 979,
// 978,
// 977,
// 976,
// 975,
// 974,
// 973,
// 972,
// 971,
// 970,
// 969,
// 968,
// 967,
// 966,
// 965,
// 964,
// 963,
// 962,
// 961,
// 960,
// 959,
// 958,
// 957,
// 956,
// 955,
// 954,
// 953,
// 952,
// 951,
// 950,
// 949,
// 948,
// 947,
// 946,
// 945,
// 944,
// 943,
// 942,
// 941,
// 940,
// 939,
// 938,
// 937,
// 936,
// 935,
// 934,
// 933,
// 932,
// 931,
// 930,
// 929,
// 928,
// 927,
// 926,
// 925,
// 924,
// 923,
// 922,
// 921,
// 920,
// 919,
// 918,
// 917,
// 916,
// 915,
// 914,
// 913,
// 912,
// 911,
// 910,
// 909,
// 908,
// 907,
// 906,
// 905,
// 904,
// 903,
// 902,
// 901,
// 900,
// 899,
// 898,
// 897,
// 896,
// 895,
// 894,
// 893,
// 892,
// 891,
// 890,
// 889,
// 888,
// 887,
// 886,
// 885,
// 884,
// 883,
// 882,
// 881,
// 880,
// 879,
// 878,
// 877,
// 876,
// 875, // 875,
// 874, // 874,
// 873, // 873,
@ -272,7 +423,7 @@ static const int included_patches[] = {
// 683, // 683,
// 682, // 682,
// 681, // 681,
// 680, 680,
679, 679,
678, 678,
// 677, // 677,
@ -340,7 +491,7 @@ static const int included_patches[] = {
// 615, // 615,
614, 614,
// 613, // 613,
// 612, 612,
// 611, // 611,
// 610, // 610,
// 609, // 609,
@ -515,7 +666,7 @@ static const int included_patches[] = {
// 440, // 440,
// 439, // 439,
// 438, // 438,
// 437, 437,
// 436, // 436,
// 435, // 435,
// 434, // 434,
@ -619,16 +770,16 @@ static const int included_patches[] = {
// 336, // 336,
// 335, // 335,
// 334, // 334,
// 333, 333,
// 332, // 332,
331, 331,
// 330, 330,
// 329, // 329,
// 328, 328,
// 327, 327,
// 326, 326,
// 325, 325,
// 324, 324,
// 323, // 323,
322, 322,
// 321, // 321,
@ -644,24 +795,24 @@ static const int included_patches[] = {
311, 311,
// 310, // 310,
// 309, // 309,
// 308, 308,
307, 307,
// 306, // 306,
// 305, 305,
// 304, // 304,
// 303, // 303,
// 302, // 302, NA
// 301, // 301,
// 300, 300,
// 299, // 299,
// 298, // 298,
297, 297,
// 296, // 296,
// 295, // 295,
// 294, 294,
// 293, // 293,
// 292, // 292,
// 291, 291,
290, 290,
// 289, // 289,
// 288 NA // 288 NA
@ -670,7 +821,7 @@ static const int included_patches[] = {
// 285 NA // 285 NA
// 284 NA // 284 NA
// 283, // 283,
// 282, 282,
// 281 NA // 281 NA
280, 280,
// 279 NA // 279 NA
@ -694,18 +845,18 @@ static const int included_patches[] = {
// 261, // 261,
// 260 NA // 260 NA
259, 259,
// 258, 258,
// 257 NA // 257 NA
// 256, // 256,
// 255, // 255,
// 254, // 254,
// 253, 253,
// 252, // 252,
// 251, // 251,
250, 250,
// 249 NA // 249 NA
// 248, // 248,
// 247, 247,
// 246 NA // 246 NA
// 245, // 245,
// 244, // 244,
@ -743,7 +894,7 @@ static const int included_patches[] = {
// 212, // 212,
// 211 NA // 211 NA
// 210, // 210,
// 209, 209,
208, 208,
// 207, // 207,
// 206, // 206,
@ -764,14 +915,14 @@ static const int included_patches[] = {
// 191 NA // 191 NA
190, 190,
// 189, // 189,
// 188, 188,
// 187 NA // 187 NA
// 186, 186,
// 185, // 185,
// 184, // 184,
// 183, // 183,
// 182, 182,
// 181, 181,
// 180, // 180,
179, 179,
178, 178,
@ -788,29 +939,29 @@ static const int included_patches[] = {
167, 167,
// 166, // 166,
165, 165,
// 164, 164,
// 163 NA // 163 NA
// 162 NA // 162 NA
// 161 NA // 161 NA
// 160, // 160,
159, 159,
158, 158,
// 157, 157,
156, 156,
// 155, 155,
// 154, // 154,
// 153, // 153,
// 152 NA // 152 NA
// 151, // 151,
150, 150,
149, 149,
// 148, 148,
147, 147,
146, 146,
// 145 NA // 145 NA
// 144 NA // 144 NA
143, 143,
// 142, 142,
// 141, // 141,
// 140, // 140,
// 139 NA // 139 NA
@ -820,14 +971,14 @@ static const int included_patches[] = {
135, 135,
134, 134,
133, 133,
// 132, 132,
// 131, 131,
// 130 NA // 130 NA
// 129 NA // 129 NA
128, 128,
127, 127,
126, 126,
// 125, 125,
124, 124,
// 123 NA // 123 NA
// 122 NA // 122 NA
@ -840,7 +991,7 @@ static const int included_patches[] = {
// 115 NA // 115 NA
// 114 NA // 114 NA
// 113 NA // 113 NA
// 112, 112,
111, 111,
110, 110,
// 109 NA // 109 NA
@ -860,8 +1011,8 @@ static const int included_patches[] = {
// 95 NA // 95 NA
// 94 NA // 94 NA
// 93 NA // 93 NA
// 92, 92,
// 91, 91,
90, 90,
// 89 NA // 89 NA
88, 88,
@ -1089,13 +1240,7 @@ static void list_features(void)
msg_putchar('\n'); msg_putchar('\n');
} }
} else { } else {
while (msg_col % width) { msg_putchar(' ');
int old_msg_col = msg_col;
msg_putchar(' ');
if (old_msg_col == msg_col) {
break; // XXX: Avoid infinite loop.
}
}
} }
} else { } else {
if (msg_col > 0) { if (msg_col > 0) {
@ -1103,7 +1248,7 @@ static void list_features(void)
} }
} }
} }
MSG_PUTS("For differences from Vim, see :help vim-differences\n\n"); MSG_PUTS("See \":help feature-compile\"\n\n");
} }
void list_version(void) void list_version(void)
@ -1144,7 +1289,7 @@ void list_version(void)
} }
#endif // ifdef HAVE_PATHDEF #endif // ifdef HAVE_PATHDEF
version_msg(_("\n\nOptional features included (+) or not (-): ")); version_msg(_("\n\nFeatures: "));
list_features(); list_features();
@ -1216,7 +1361,6 @@ void intro_message(int colon)
static char *(lines[]) = { static char *(lines[]) = {
N_(NVIM_VERSION_LONG), N_(NVIM_VERSION_LONG),
"", "",
N_("by al."),
N_("Nvim is open source and freely distributable"), N_("Nvim is open source and freely distributable"),
N_("https://neovim.io/community"), N_("https://neovim.io/community"),
"", "",

View File

@ -5515,11 +5515,14 @@ void restore_buffer(bufref_T *save_curbuf)
} }
// Add match to the match list of window 'wp'. The pattern 'pat' will be /// Add match to the match list of window 'wp'. The pattern 'pat' will be
// highlighted with the group 'grp' with priority 'prio'. /// highlighted with the group 'grp' with priority 'prio'.
// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). /// Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
// If no particular ID is desired, -1 must be specified for 'id'. ///
// Return ID of added match, -1 on failure. /// @param[in] id a desired ID 'id' can be specified
/// (greater than or equal to 1). -1 must be specified if no
/// particular ID is desired
/// @return ID of added match, -1 on failure.
int match_add(win_T *wp, const char *const grp, const char *const pat, int match_add(win_T *wp, const char *const grp, const char *const pat,
int prio, int id, list_T *pos_list, int prio, int id, list_T *pos_list,
const char *const conceal_char) const char *const conceal_char)
@ -5697,10 +5700,9 @@ fail:
return -1; return -1;
} }
/*
* Delete match with ID 'id' in the match list of window 'wp'. /// Delete match with ID 'id' in the match list of window 'wp'.
* Print error messages if 'perr' is TRUE. /// Print error messages if 'perr' is TRUE.
*/
int match_delete(win_T *wp, int id, int perr) int match_delete(win_T *wp, int id, int perr)
{ {
matchitem_T *cur = wp->w_match_head; matchitem_T *cur = wp->w_match_head;

View File

@ -17,4 +17,4 @@ ignore = {
} }
-- Ignore whitespace issues in converted Vim legacy tests. -- Ignore whitespace issues in converted Vim legacy tests.
files["functional/legacy"] = {ignore = { "611", "612", "613", "621" }} --files["functional/legacy"] = {ignore = { "611", "612", "613", "621" }}

View File

@ -0,0 +1,103 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, nvim = helpers.clear, helpers.nvim
local Screen = require('test.functional.ui.screen')
local eq, eval = helpers.eq, helpers.eval
local command = helpers.command
local meths = helpers.meths
describe('highlight api',function()
local expected_rgb = {
background = Screen.colors.Yellow,
foreground = Screen.colors.Red,
special = Screen.colors.Blue,
bold = true,
}
local expected_cterm = {
background = 10,
underline = true,
}
local expected_rgb2 = {
background = Screen.colors.Yellow,
foreground = Screen.colors.Red,
special = Screen.colors.Blue,
bold = true,
italic = true,
reverse = true,
undercurl = true,
underline = true,
}
before_each(function()
clear()
command("hi NewHighlight cterm=underline ctermbg=green guifg=red guibg=yellow guisp=blue gui=bold")
end)
it("nvim_get_hl_by_id", function()
local hl_id = eval("hlID('NewHighlight')")
eq(expected_cterm, nvim("get_hl_by_id", hl_id, false))
hl_id = eval("hlID('NewHighlight')")
-- Test valid id.
eq(expected_rgb, nvim("get_hl_by_id", hl_id, true))
-- Test invalid id.
local err, emsg = pcall(meths.get_hl_by_id, 30000, false)
eq(false, err)
eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*'))
-- Test all highlight properties.
command('hi NewHighlight gui=underline,bold,undercurl,italic,reverse')
eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true))
-- Test nil argument.
err, emsg = pcall(meths.get_hl_by_id, { nil }, false)
eq(false, err)
eq('Wrong type for argument 1, expecting Integer',
string.match(emsg, 'Wrong.*'))
-- Test 0 argument.
err, emsg = pcall(meths.get_hl_by_id, 0, false)
eq(false, err)
eq('Invalid highlight id: 0',
string.match(emsg, 'Invalid.*'))
-- Test -1 argument.
err, emsg = pcall(meths.get_hl_by_id, -1, false)
eq(false, err)
eq('Invalid highlight id: -1',
string.match(emsg, 'Invalid.*'))
end)
it("nvim_get_hl_by_name", function()
local expected_normal = { background = Screen.colors.Yellow,
foreground = Screen.colors.Red }
-- Test `Normal` default values.
eq({}, nvim("get_hl_by_name", 'Normal', true))
eq(expected_cterm, nvim("get_hl_by_name", 'NewHighlight', false))
eq(expected_rgb, nvim("get_hl_by_name", 'NewHighlight', true))
-- Test `Normal` modified values.
command('hi Normal guifg=red guibg=yellow')
eq(expected_normal, nvim("get_hl_by_name", 'Normal', true))
-- Test invalid name.
local err, emsg = pcall(meths.get_hl_by_name , 'unknown_highlight', false)
eq(false, err)
eq('Invalid highlight name: unknown_highlight',
string.match(emsg, 'Invalid.*'))
-- Test nil argument.
err, emsg = pcall(meths.get_hl_by_name , { nil }, false)
eq(false, err)
eq('Wrong type for argument 1, expecting String',
string.match(emsg, 'Wrong.*'))
-- Test empty string argument.
err, emsg = pcall(meths.get_hl_by_name , '', false)
eq(false, err)
eq('Invalid highlight name: ',
string.match(emsg, 'Invalid.*'))
end)
end)

View File

@ -20,6 +20,22 @@ describe('server -> client', function()
cid = nvim('get_api_info')[1] cid = nvim('get_api_info')[1]
end) end)
it('handles unexpected closed stream while preparing RPC response', function()
source([[
let g:_nvim_args = [v:progpath, '--embed', '-n', '-u', 'NONE', '-i', 'NONE', ]
let ch1 = jobstart(g:_nvim_args, {'rpc': v:true})
let child1_ch = rpcrequest(ch1, "nvim_get_api_info")[0]
call rpcnotify(ch1, 'nvim_eval', 'rpcrequest('.child1_ch.', "nvim_get_api_info")')
let ch2 = jobstart(g:_nvim_args, {'rpc': v:true})
let child2_ch = rpcrequest(ch2, "nvim_get_api_info")[0]
call rpcnotify(ch2, 'nvim_eval', 'rpcrequest('.child2_ch.', "nvim_get_api_info")')
call jobstop(ch1)
]])
eq(2, eval("1+1")) -- Still alive?
end)
describe('simple call', function() describe('simple call', function()
it('works', function() it('works', function()
local function on_setup() local function on_setup()
@ -141,7 +157,7 @@ describe('server -> client', function()
end) end)
end) end)
describe('when the client is a recursive vim instance', function() describe('recursive (child) nvim client', function()
if os.getenv("TRAVIS") and helpers.os_name() == "osx" then if os.getenv("TRAVIS") and helpers.os_name() == "osx" then
-- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86. -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86.
pending("[Hangs on Travis macOS. #5002]", function() end) pending("[Hangs on Travis macOS. #5002]", function() end)
@ -155,7 +171,7 @@ describe('server -> client', function()
after_each(function() command('call rpcstop(vim)') end) after_each(function() command('call rpcstop(vim)') end)
it('can send/recieve notifications and make requests', function() it('can send/receive notifications and make requests', function()
nvim('command', "call rpcnotify(vim, 'vim_set_current_line', 'SOME TEXT')") nvim('command', "call rpcnotify(vim, 'vim_set_current_line', 'SOME TEXT')")
-- Wait for the notification to complete. -- Wait for the notification to complete.
@ -188,7 +204,7 @@ describe('server -> client', function()
end) end)
end) end)
describe('when using jobstart', function() describe('jobstart()', function()
local jobid local jobid
before_each(function() before_each(function()
local channel = nvim('get_api_info')[1] local channel = nvim('get_api_info')[1]
@ -227,7 +243,7 @@ describe('server -> client', function()
end) end)
end) end)
describe('when connecting to another nvim instance', function() describe('connecting to another (peer) nvim', function()
local function connect_test(server, mode, address) local function connect_test(server, mode, address)
local serverpid = funcs.getpid() local serverpid = funcs.getpid()
local client = spawn(nvim_argv) local client = spawn(nvim_argv)
@ -256,7 +272,7 @@ describe('server -> client', function()
client:close() client:close()
end end
it('over a named pipe', function() it('via named pipe', function()
local server = spawn(nvim_argv) local server = spawn(nvim_argv)
set_session(server) set_session(server)
local address = funcs.serverlist()[1] local address = funcs.serverlist()[1]
@ -265,7 +281,7 @@ describe('server -> client', function()
connect_test(server, 'pipe', address) connect_test(server, 'pipe', address)
end) end)
it('to an ip adress', function() it('via ip address', function()
local server = spawn(nvim_argv) local server = spawn(nvim_argv)
set_session(server) set_session(server)
local address = funcs.serverstart("127.0.0.1:") local address = funcs.serverstart("127.0.0.1:")
@ -273,7 +289,7 @@ describe('server -> client', function()
connect_test(server, 'tcp', address) connect_test(server, 'tcp', address)
end) end)
it('to a hostname', function() it('via hostname', function()
local server = spawn(nvim_argv) local server = spawn(nvim_argv)
set_session(server) set_session(server)
local address = funcs.serverstart("localhost:") local address = funcs.serverstart("localhost:")

View File

@ -329,24 +329,92 @@ describe('api', function()
} }
eq({ { {mode='n', blocking=false}, eq({ { {mode='n', blocking=false},
13, 13,
{mode='n', blocking=false}, -- TODO: should be blocked=true {mode='n', blocking=false}, -- TODO: should be blocked=true ?
1 }, 1 },
NIL}, meths.call_atomic(req)) NIL}, meths.call_atomic(req))
eq({mode='r', blocking=true}, nvim("get_mode")) eq({mode='r', blocking=true}, nvim("get_mode"))
end) end)
-- TODO: bug #6166
it("during insert-mode map-pending, returns blocking=true #6166", function() it("during insert-mode map-pending, returns blocking=true #6166", function()
command("inoremap xx foo") command("inoremap xx foo")
nvim("input", "ix") nvim("input", "ix")
eq({mode='i', blocking=true}, nvim("get_mode")) eq({mode='i', blocking=true}, nvim("get_mode"))
end) end)
-- TODO: bug #6166
it("during normal-mode gU, returns blocking=false #6166", function() it("during normal-mode gU, returns blocking=false #6166", function()
nvim("input", "gu") nvim("input", "gu")
eq({mode='no', blocking=false}, nvim("get_mode")) eq({mode='no', blocking=false}, nvim("get_mode"))
end) end)
end) end)
describe('RPC (K_EVENT) #6166', function()
it('does not complete ("interrupt") normal-mode operator-pending', function()
helpers.insert([[
FIRST LINE
SECOND LINE]])
nvim('input', 'gg')
nvim('input', 'gu')
-- Make any RPC request (can be non-async: op-pending does not block).
nvim('get_current_buf')
-- Buffer should not change.
helpers.expect([[
FIRST LINE
SECOND LINE]])
-- Now send input to complete the operator.
nvim('input', 'j')
helpers.expect([[
first line
second line]])
end)
it('does not complete ("interrupt") `d` #3732', function()
local screen = Screen.new(20, 4)
screen:attach()
command('set listchars=eol:$')
command('set list')
feed('ia<cr>b<cr>c<cr><Esc>kkk')
feed('d')
-- Make any RPC request (can be non-async: op-pending does not block).
nvim('get_current_buf')
screen:expect([[
^a$ |
b$ |
c$ |
|
]])
end)
it('does not complete ("interrupt") normal-mode map-pending', function()
command("nnoremap dd :let g:foo='it worked...'<CR>")
helpers.insert([[
FIRST LINE
SECOND LINE]])
nvim('input', 'gg')
nvim('input', 'd')
-- Make any RPC request (must be async, because map-pending blocks).
nvim('get_api_info')
-- Send input to complete the mapping.
nvim('input', 'd')
helpers.expect([[
FIRST LINE
SECOND LINE]])
eq('it worked...', helpers.eval('g:foo'))
end)
it('does not complete ("interrupt") insert-mode map-pending', function()
command('inoremap xx foo')
command('set timeoutlen=9999')
helpers.insert([[
FIRST LINE
SECOND LINE]])
nvim('input', 'ix')
-- Make any RPC request (must be async, because map-pending blocks).
nvim('get_api_info')
-- Send input to complete the mapping.
nvim('input', 'x')
helpers.expect([[
FIRST LINE
SECOND LINfooE]])
end)
end)
describe('nvim_replace_termcodes', function() describe('nvim_replace_termcodes', function()
it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function() it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true)) eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true))

View File

@ -57,7 +57,9 @@ describe('TermClose event', function()
command('call jobstop(g:test_job)') command('call jobstop(g:test_job)')
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end) retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end)
local duration = os.time() - start local duration = os.time() - start
eq(4, duration) -- nvim starts sending kill after 2*KILL_TIMEOUT_MS
helpers.ok(4 <= duration)
helpers.ok(duration <= 7) -- <= 4 + delta because of slow CI
end) end)
it('reports the correct <abuf>', function() it('reports the correct <abuf>', function()

View File

@ -4,6 +4,8 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local feed_command, expect, eq, eval = helpers.feed_command, helpers.expect, helpers.eq, helpers.eval local feed_command, expect, eq, eval = helpers.feed_command, helpers.expect, helpers.eq, helpers.eval
local command = helpers.command
local meths = helpers.meths
local function basic_register_test(noblock) local function basic_register_test(noblock)
insert("some words") insert("some words")
@ -80,15 +82,73 @@ local function basic_register_test(noblock)
expect("two and three and one") expect("two and three and one")
end end
describe('the unnamed register', function() describe('clipboard', function()
before_each(clear) before_each(clear)
it('works without provider', function()
it('unnamed register works without provider', function()
eq('"', eval('v:register')) eq('"', eval('v:register'))
basic_register_test() basic_register_test()
end) end)
it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184',
function()
local screen = Screen.new(72, 4)
screen:attach()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
screen:expect([[
^ |
~ |
~ |
clipboard: No provider. Try ":CheckHealth" or ":h clipboard". |
]], nil, {{bold = true, foreground = Screen.colors.Blue}})
end)
it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184',
function()
local screen = Screen.new(72, 4)
screen:attach()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | bogus_cmd | redir END')
screen:expect([[
~ |
clipboard: No provider. Try ":CheckHealth" or ":h clipboard". |
E492: Not an editor command: bogus_cmd | redir END |
Press ENTER or type command to continue^ |
]], nil, {{bold = true, foreground = Screen.colors.Blue}})
end)
it('invalid g:clipboard shows hint if :redir is not active', function()
command("let g:clipboard = 'bogus'")
eq('', eval('provider#clipboard#Executable()'))
eq('clipboard: invalid g:clipboard', eval('provider#clipboard#Error()'))
local screen = Screen.new(72, 4)
screen:attach()
command("let g:clipboard = 'bogus'")
-- Explicit clipboard attempt, should show a hint message.
feed_command('let @+="foo"')
screen:expect([[
^ |
~ |
~ |
clipboard: No provider. Try ":CheckHealth" or ":h clipboard". |
]], nil, {{bold = true, foreground = Screen.colors.Blue}})
end)
it('valid g:clipboard', function()
-- provider#clipboard#Executable() only checks the structure.
meths.set_var('clipboard', {
['name'] = 'clippy!',
['copy'] = { ['+'] = 'any command', ['*'] = 'some other' },
['paste'] = { ['+'] = 'any command', ['*'] = 'some other' },
})
eq('clippy!', eval('provider#clipboard#Executable()'))
eq('', eval('provider#clipboard#Error()'))
end)
end) end)
describe('clipboard usage', function() describe('clipboard', function()
local function reset(...) local function reset(...)
clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...) clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...)
end end
@ -98,7 +158,36 @@ describe('clipboard usage', function()
feed_command('call getreg("*")') -- force load of provider feed_command('call getreg("*")') -- force load of provider
end) end)
it('has independent "* and unnamed registers per default', function() it('`:redir @+>` invokes clipboard once-per-message', function()
eq(0, eval("g:clip_called_set"))
feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
-- Assuming CONTRIBUTING.md has >100 lines.
assert(eval("g:clip_called_set") > 100)
end)
it('`:redir @">` does NOT invoke clipboard', function()
-- :redir to a non-clipboard register, with `:set clipboard=unnamed` does
-- NOT propagate to the clipboard. This is consistent with Vim.
command("set clipboard=unnamedplus")
eq(0, eval("g:clip_called_set"))
feed_command('redir @"> | :silent echo system("cat CONTRIBUTING.md") | redir END')
eq(0, eval("g:clip_called_set"))
end)
it('`:redir @+>|bogus_cmd|redir END` must not recurse #7184',
function()
local screen = Screen.new(72, 4)
screen:attach()
feed_command('redir @+> | bogus_cmd | redir END')
screen:expect([[
^ |
~ |
~ |
E492: Not an editor command: bogus_cmd | redir END |
]], nil, {{bold = true, foreground = Screen.colors.Blue}})
end)
it('has independent "* and unnamed registers by default', function()
insert("some words") insert("some words")
feed('^"*dwdw"*P') feed('^"*dwdw"*P')
expect('some ') expect('some ')
@ -139,7 +228,7 @@ describe('clipboard usage', function()
eq({'some\ntext', '\nvery binary\n'}, eval("getreg('*', 1, 1)")) eq({'some\ntext', '\nvery binary\n'}, eval("getreg('*', 1, 1)"))
end) end)
it('support autodectection of regtype', function() it('autodetects regtype', function()
feed_command("let g:test_clip['*'] = ['linewise stuff','']") feed_command("let g:test_clip['*'] = ['linewise stuff','']")
feed_command("let g:test_clip['+'] = ['charwise','stuff']") feed_command("let g:test_clip['+'] = ['charwise','stuff']")
eq("V", eval("getregtype('*')")) eq("V", eval("getregtype('*')"))
@ -169,7 +258,7 @@ describe('clipboard usage', function()
eq({{' much', 'ktext', ''}, 'b'}, eval("g:test_clip['+']")) eq({{' much', 'ktext', ''}, 'b'}, eval("g:test_clip['+']"))
end) end)
it('supports setreg', function() it('supports setreg()', function()
feed_command('call setreg("*", "setted\\ntext", "c")') feed_command('call setreg("*", "setted\\ntext", "c")')
feed_command('call setreg("+", "explicitly\\nlines", "l")') feed_command('call setreg("+", "explicitly\\nlines", "l")')
feed('"+P"*p') feed('"+P"*p')
@ -187,7 +276,7 @@ describe('clipboard usage', function()
]]) ]])
end) end)
it('supports let @+ (issue #1427)', function() it('supports :let @+ (issue #1427)', function()
feed_command("let @+ = 'some'") feed_command("let @+ = 'some'")
feed_command("let @* = ' other stuff'") feed_command("let @* = ' other stuff'")
eq({{'some'}, 'v'}, eval("g:test_clip['+']")) eq({{'some'}, 'v'}, eval("g:test_clip['+']"))
@ -303,9 +392,16 @@ describe('clipboard usage', function()
eq('---', eval('getreg("*")')) eq('---', eval('getreg("*")'))
end) end)
it('works in the cmdline window', function()
feed('q:itext<esc>yy')
eq({{'text', ''}, 'V'}, eval("g:test_clip['*']"))
command("let g:test_clip['*'] = [['star'], 'c']")
feed('p')
eq('textstar', meths.get_current_line())
end)
end) end)
describe('with clipboard=unnamedplus', function() describe('clipboard=unnamedplus', function()
before_each(function() before_each(function()
feed_command('set clipboard=unnamedplus') feed_command('set clipboard=unnamedplus')
end) end)
@ -349,6 +445,7 @@ describe('clipboard usage', function()
really unnamed really unnamed
the plus]]) the plus]])
end) end)
it('is updated on global changes', function() it('is updated on global changes', function()
insert([[ insert([[
text text

Some files were not shown because too many files have changed in this diff Show More