diff options
106 files changed, 3330 insertions, 2416 deletions
diff --git a/.ci/common/build.sh b/.ci/common/build.sh index f635ee4960..06bdab707f 100644 --- a/.ci/common/build.sh +++ b/.ci/common/build.sh @@ -5,6 +5,9 @@ build_deps() { if [[ "${BUILD_MINGW}" == ON ]]; then DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_MINGW}" fi + if [[ "${FUNCTIONALTEST}" == "functionaltest-lua" ]]; then + DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} -DUSE_BUNDLED_LUA=ON" + fi rm -rf "${DEPS_BUILD_DIR}" diff --git a/.ci/common/test.sh b/.ci/common/test.sh index c1bbd8dc9a..8c32b63ab2 100644 --- a/.ci/common/test.sh +++ b/.ci/common/test.sh @@ -53,7 +53,7 @@ run_unittests() { } run_functionaltests() { - if ! ${MAKE_CMD} -C "${BUILD_DIR}" functionaltest; then + if ! ${MAKE_CMD} -C "${BUILD_DIR}" ${FUNCTIONALTEST}; then asan_check "${LOG_DIR}" valgrind_check "${LOG_DIR}" exit 1 diff --git a/.travis.yml b/.travis.yml index 10219690b3..985a5c5381 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,8 @@ env: # if the tests were successful, but don't have this information # available in before_cache (which is run before after_success). - SUCCESS_MARKER="$BUILD_DIR/.tests_successful" + # default target name for functional tests + - FUNCTIONALTEST=functionaltest matrix: include: @@ -61,6 +63,9 @@ matrix: - os: linux compiler: gcc-5 - os: linux + compiler: gcc-5 + env: FUNCTIONALTEST=functionaltest-lua + - os: linux # Travis creates a cache per compiler. # Set a different value here to store 32-bit # dependencies in a separate cache. diff --git a/CMakeLists.txt b/CMakeLists.txt index acd9dc1865..5798a74369 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 2.8.7) project(nvim) +if(POLICY CMP0059) + cmake_policy(SET CMP0059 OLD) # Needed until cmake 2.8.12. #4389 +endif() + # Point CMake at any custom modules we may ship list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") @@ -55,7 +59,7 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # version string, else it is combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) set(NVIM_VERSION_MINOR 1) -set(NVIM_VERSION_PATCH 3) +set(NVIM_VERSION_PATCH 4) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) @@ -206,6 +210,12 @@ if(MSVC) else() add_definitions(-Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99) + + # On FreeBSD 64 math.h uses unguarded C11 extension, which taints clang + # 3.4.1 used there. + if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + add_definitions(-Wno-c11-extensions) + endif() endif() if(MINGW) @@ -384,6 +394,7 @@ message(STATUS "Using the Lua interpreter ${LUA_PRG}.") # Setup busted. find_program(BUSTED_PRG busted) +find_program(BUSTED_LUA_PRG busted-lua) if(NOT BUSTED_OUTPUT_TYPE) set(BUSTED_OUTPUT_TYPE "utfTerminal") endif() @@ -495,6 +506,20 @@ if(BUSTED_PRG) DEPENDS ${BENCHMARK_PREREQS}) endif() +if(BUSTED_LUA_PRG) + add_custom_target(functionaltest-lua + COMMAND ${CMAKE_COMMAND} + -DBUSTED_PRG=${BUSTED_LUA_PRG} + -DNVIM_PRG=$<TARGET_FILE:nvim> + -DWORKING_DIR=${CMAKE_CURRENT_SOURCE_DIR} + -DBUSTED_OUTPUT_TYPE=${BUSTED_OUTPUT_TYPE} + -DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test + -DBUILD_DIR=${CMAKE_BINARY_DIR} + -DTEST_TYPE=functional + -P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake + DEPENDS ${FUNCTIONALTEST_PREREQS}) +endif() + if(LUACHECK_PRG) add_custom_target(testlint COMMAND ${CMAKE_COMMAND} diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 4abcc4eae1..e0185844ad 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,9 +1,15 @@ - Neovim version: -- Operating system: -- Terminal emulator: +- [ ] Vim behaves differently? + - Vim version: +- Operating system/version: +- Terminal name/version: +- `$TERM`: ### Actual behaviour ### Expected behaviour -### Steps to reproduce +### Steps to reproduce using `nvim -u NONE` + +1. `nvim -u NONE` +2. @@ -45,6 +45,11 @@ ifneq (,$(USE_BUNDLED_DEPS)) BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED_DEPS) endif +ifneq (,$(findstring functionaltest-lua,$(MAKECMDGOALS))) + BUNDLED_LUA_CMAKE_FLAG := -DUSE_BUNDLED_LUA=ON + $(shell [ -x .deps/usr/bin/lua ] || rm build/.ran-*) +endif + # For use where we want to make sure only a single job is run. This does issue # a warning, but we need to keep SCRIPTS argument. SINGLE_MAKE = export MAKEFLAGS= ; $(MAKE) @@ -74,7 +79,7 @@ build/.ran-third-party-cmake: ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),) mkdir -p .deps cd .deps && \ - cmake -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) \ + cmake -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) $(BUNDLED_LUA_CMAKE_FLAG) \ $(DEPS_CMAKE_FLAGS) ../third-party endif mkdir -p build @@ -86,6 +91,9 @@ oldtest: | nvim functionaltest: | nvim +$(BUILD_CMD) -C build functionaltest +functionaltest-lua: | nvim + +$(BUILD_CMD) -C build functionaltest-lua + testlint: | nvim $(BUILD_CMD) -C build testlint @@ -2552,7 +2552,7 @@ def CheckBraces(filename, clean_lines, linenum, error): # If should always have a brace for blockstart in ('if', 'while', 'for'): - if Match(r'\s*{0}[^{{]*$'.format(blockstart), line): + if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line): pos = line.find(blockstart) pos = line.find('(', pos) if pos > 0: diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 49663d7e5a..0dfcc424e2 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -11,6 +11,8 @@ catch /E145:/ " Ignore the error in restricted mode endtry +" Load man page {page} from {section} +" call man#get_page([{section}, ]{page}) function man#get_page(...) abort let invoked_from_man = (&filetype ==# 'man') @@ -20,21 +22,14 @@ function man#get_page(...) abort elseif a:0 > 2 echoerr 'too many arguments' return - elseif a:0 == 2 - let [page, sect] = [a:2, 0 + a:1] - elseif type(1) == type(a:1) - let [page, sect] = ['<cword>', a:1] - else - let [page, sect] = [a:1, ''] endif - if page == '<cword>' - let page = expand('<cword>') - endif + let sect = get(a:000, 0) + let page = get(a:000, 1, sect) let [page, sect] = s:parse_page_and_section(sect, page) - if 0 + sect > 0 && s:find_page(sect, page) == 0 + if !empty(sect) && s:find_page(sect, page) == 0 let sect = '' endif @@ -54,9 +49,9 @@ function man#get_page(...) abort let thiswin = winnr() wincmd b if winnr() > 1 - exe "norm! " . thiswin . "\<C-W>w" + exec thiswin . 'wincmd w' while 1 - if &filetype == 'man' + if &filetype ==# 'man' break endif wincmd w @@ -80,11 +75,11 @@ function man#get_page(...) abort endif silent exec 'r!/usr/bin/man '.s:cmd(sect, page).' | col -b' " Remove blank lines from top and bottom. - while getline(1) =~ '^\s*$' - silent keepjumps norm! gg"_dd + while getline(1) =~# '^\s*$' + silent keepjumps 1delete _ endwhile - while getline('$') =~ '^\s*$' - silent keepjumps norm! G"_dd + while getline('$') =~# '^\s*$' + silent keepjumps $delete _ endwhile setlocal nomodified setlocal filetype=man @@ -118,15 +113,11 @@ endfunction " Expects a string like 'access' or 'access(2)'. function s:parse_page_and_section(sect, str) abort try - let save_isk = &iskeyword - setlocal iskeyword-=(,) - let page = substitute(a:str, '(*\(\k\+\).*', '\1', '') - let sect = substitute(a:str, '\(\k\+\)(\([^()]*\)).*', '\2', '') - if sect == page || -1 == match(sect, '^[0-9 ]\+$') + let [page, sect] = matchlist(a:str, '\v\C([-.[:alnum:]_]+)%(\(([-.[:alnum:]_]+)\))?')[1:2] + if empty(sect) let sect = a:sect endif catch - let &l:iskeyword = save_isk echoerr 'man.vim: failed to parse: "'.a:str.'"' endtry @@ -134,7 +125,7 @@ function s:parse_page_and_section(sect, str) abort endfunction function s:cmd(sect, page) abort - if 0 + a:sect > 0 + if !empty(a:sect) return s:man_sect_arg.' '.a:sect.' '.a:page endif return a:page @@ -142,10 +133,5 @@ endfunction function s:find_page(sect, page) abort let where = system('/usr/bin/man '.s:man_find_arg.' '.s:cmd(a:sect, a:page)) - if where !~ "^/" - if matchstr(where, " [^ ]*$") !~ "^ /" - return 0 - endif - endif - return 1 + return (where =~# '^ */') endfunction diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim index e6022922fe..2bb7ec5b02 100644 --- a/runtime/autoload/msgpack.vim +++ b/runtime/autoload/msgpack.vim @@ -395,7 +395,8 @@ endfunction "" " Dump floating-point value. function s:msgpack_dump_float(v) abort - return string(type(a:v) == type({}) ? a:v._VAL : a:v) + return substitute(string(type(a:v) == type({}) ? a:v._VAL : a:v), + \'\V\^\(-\)\?str2float(''\(inf\|nan\)'')\$', '\1\2', '') endfunction "" diff --git a/runtime/autoload/provider/python.vim b/runtime/autoload/provider/python.vim index b769895357..cb9d5c5296 100644 --- a/runtime/autoload/provider/python.vim +++ b/runtime/autoload/provider/python.vim @@ -24,12 +24,10 @@ if s:prog == '' finish endif -let s:plugin_path = expand('<sfile>:p:h').'/script_host.py' - " The Python provider plugin will run in a separate instance of the Python " host. call remote#host#RegisterClone('legacy-python-provider', 'python') -call remote#host#RegisterPlugin('legacy-python-provider', s:plugin_path, []) +call remote#host#RegisterPlugin('legacy-python-provider', 'script_host.py', []) function! provider#python#Call(method, args) if s:err != '' diff --git a/runtime/autoload/provider/python3.vim b/runtime/autoload/provider/python3.vim index 2952f76b40..f4a751e7a2 100644 --- a/runtime/autoload/provider/python3.vim +++ b/runtime/autoload/provider/python3.vim @@ -24,12 +24,10 @@ if s:prog == '' finish endif -let s:plugin_path = expand('<sfile>:p:h').'/script_host.py' - " The Python3 provider plugin will run in a separate instance of the Python3 " host. call remote#host#RegisterClone('legacy-python3-provider', 'python3') -call remote#host#RegisterPlugin('legacy-python3-provider', s:plugin_path, []) +call remote#host#RegisterPlugin('legacy-python3-provider', 'script_host.py', []) function! provider#python3#Call(method, args) if s:err != '' diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 022ef19914..05815a4896 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -5,6 +5,32 @@ endif let s:loaded_pythonx_provider = 1 +function! provider#pythonx#Require(host) abort + let ver = (a:host.orig_name ==# 'python') ? 2 : 3 + + " Python host arguments + let args = ['-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()'] + + " Collect registered Python plugins into args + let python_plugins = remote#host#PluginsForHost(a:host.name) + for plugin in python_plugins + call add(args, plugin.path) + endfor + + try + let channel_id = rpcstart((ver == '2' ? + \ provider#python#Prog() : provider#python3#Prog()), args) + if rpcrequest(channel_id, 'poll') == 'ok' + return channel_id + endif + catch + echomsg v:throwpoint + echomsg v:exception + endtry + throw remote#host#LoadErrorForHost(a:host.orig_name, + \ '$NVIM_PYTHON_LOG_FILE') +endfunction + function! provider#pythonx#Detect(major_ver) abort let host_var = (a:major_ver == 2) ? \ 'g:python_host_prog' : 'g:python3_host_prog' diff --git a/runtime/autoload/provider/ruby.vim b/runtime/autoload/provider/ruby.vim new file mode 100644 index 0000000000..aad8c09d28 --- /dev/null +++ b/runtime/autoload/provider/ruby.vim @@ -0,0 +1,34 @@ +" The Ruby provider helper +if exists('s:loaded_ruby_provider') + finish +endif + +let s:loaded_ruby_provider = 1 + +function! provider#ruby#Require(host) abort + " Collect registered Ruby plugins into args + let args = [] + let ruby_plugins = remote#host#PluginsForHost(a:host.name) + + for plugin in ruby_plugins + call add(args, plugin.path) + endfor + + try + let channel_id = rpcstart(provider#ruby#Prog(), args) + + if rpcrequest(channel_id, 'poll') == 'ok' + return channel_id + endif + catch + echomsg v:throwpoint + echomsg v:exception + endtry + + throw remote#host#LoadErrorForHost(a:host.orig_name, + \ '$NVIM_RUBY_LOG_FILE') +endfunction + +function! provider#ruby#Prog() abort + return 'neovim-ruby-host' +endfunction diff --git a/runtime/autoload/provider/script_host.py b/runtime/autoload/provider/script_host.py deleted file mode 100644 index 416b4070bb..0000000000 --- a/runtime/autoload/provider/script_host.py +++ /dev/null @@ -1,247 +0,0 @@ -"""Legacy python/python3-vim emulation.""" -import imp -import io -import logging -import os -import sys - -import neovim - -__all__ = ('ScriptHost',) - - -logger = logging.getLogger(__name__) -debug, info, warn = (logger.debug, logger.info, logger.warn,) - -IS_PYTHON3 = sys.version_info >= (3, 0) - -if IS_PYTHON3: - basestring = str - - if sys.version_info >= (3, 4): - from importlib.machinery import PathFinder - - -@neovim.plugin -class ScriptHost(object): - - """Provides an environment for running python plugins created for Vim.""" - - def __init__(self, nvim): - """Initialize the legacy python-vim environment.""" - self.setup(nvim) - # context where all code will run - self.module = imp.new_module('__main__') - nvim.script_context = self.module - # it seems some plugins assume 'sys' is already imported, so do it now - exec('import sys', self.module.__dict__) - self.legacy_vim = nvim.with_hook(LegacyEvalHook()) - sys.modules['vim'] = self.legacy_vim - - def setup(self, nvim): - """Setup import hooks and global streams. - - This will add import hooks for importing modules from runtime - directories and patch the sys module so 'print' calls will be - forwarded to Nvim. - """ - self.nvim = nvim - info('install import hook/path') - self.hook = path_hook(nvim) - sys.path_hooks.append(self.hook) - nvim.VIM_SPECIAL_PATH = '_vim_path_' - sys.path.append(nvim.VIM_SPECIAL_PATH) - info('redirect sys.stdout and sys.stderr') - self.saved_stdout = sys.stdout - self.saved_stderr = sys.stderr - sys.stdout = RedirectStream(lambda data: nvim.out_write(data)) - sys.stderr = RedirectStream(lambda data: nvim.err_write(data)) - - def teardown(self): - """Restore state modified from the `setup` call.""" - for plugin in self.installed_plugins: - if hasattr(plugin, 'on_teardown'): - plugin.teardown() - nvim = self.nvim - info('uninstall import hook/path') - sys.path.remove(nvim.VIM_SPECIAL_PATH) - sys.path_hooks.remove(self.hook) - info('restore sys.stdout and sys.stderr') - sys.stdout = self.saved_stdout - sys.stderr = self.saved_stderr - - @neovim.rpc_export('python_execute', sync=True) - def python_execute(self, script, range_start, range_stop): - """Handle the `python` ex command.""" - self._set_current_range(range_start, range_stop) - exec(script, self.module.__dict__) - - @neovim.rpc_export('python_execute_file', sync=True) - def python_execute_file(self, file_path, range_start, range_stop): - """Handle the `pyfile` ex command.""" - self._set_current_range(range_start, range_stop) - with open(file_path) as f: - script = compile(f.read(), file_path, 'exec') - exec(script, self.module.__dict__) - - @neovim.rpc_export('python_do_range', sync=True) - def python_do_range(self, start, stop, code): - """Handle the `pydo` ex command.""" - self._set_current_range(start, stop) - nvim = self.nvim - start -= 1 - stop -= 1 - fname = '_vim_pydo' - - # define the function - function_def = 'def %s(line, linenr):\n %s' % (fname, code,) - exec(function_def, self.module.__dict__) - # get the function - function = self.module.__dict__[fname] - while start <= stop: - # Process batches of 5000 to avoid the overhead of making multiple - # API calls for every line. Assuming an average line length of 100 - # bytes, approximately 488 kilobytes will be transferred per batch, - # which can be done very quickly in a single API call. - sstart = start - sstop = min(start + 5000, stop) - lines = nvim.current.buffer.get_line_slice(sstart, sstop, True, - True) - - exception = None - newlines = [] - linenr = sstart + 1 - for i, line in enumerate(lines): - result = function(line, linenr) - if result is None: - # Update earlier lines, and skip to the next - if newlines: - end = sstart + len(newlines) - 1 - nvim.current.buffer.set_line_slice(sstart, end, - True, True, - newlines) - sstart += len(newlines) + 1 - newlines = [] - pass - elif isinstance(result, basestring): - newlines.append(result) - else: - exception = TypeError('pydo should return a string ' + - 'or None, found %s instead' - % result.__class__.__name__) - break - linenr += 1 - - start = sstop + 1 - if newlines: - end = sstart + len(newlines) - 1 - nvim.current.buffer.set_line_slice(sstart, end, True, True, - newlines) - if exception: - raise exception - # delete the function - del self.module.__dict__[fname] - - @neovim.rpc_export('python_eval', sync=True) - def python_eval(self, expr): - """Handle the `pyeval` vim function.""" - return eval(expr, self.module.__dict__) - - def _set_current_range(self, start, stop): - current = self.legacy_vim.current - current.range = current.buffer.range(start, stop) - - -class RedirectStream(io.IOBase): - def __init__(self, redirect_handler): - self.redirect_handler = redirect_handler - - def write(self, data): - self.redirect_handler(data) - - def writelines(self, seq): - self.redirect_handler('\n'.join(seq)) - - -class LegacyEvalHook(neovim.SessionHook): - - """Injects legacy `vim.eval` behavior to a Nvim instance.""" - - def __init__(self): - super(LegacyEvalHook, self).__init__(from_nvim=self._string_eval) - - def _string_eval(self, obj, session, method, kind): - if method == 'vim_eval': - if IS_PYTHON3: - if isinstance(obj, (int, float)): - return str(obj) - elif isinstance(obj, (int, long, float)): - return str(obj) - return obj - - -# This was copied/adapted from nvim-python help -def path_hook(nvim): - def _get_paths(): - return discover_runtime_directories(nvim) - - def _find_module(fullname, oldtail, path): - idx = oldtail.find('.') - if idx > 0: - name = oldtail[:idx] - tail = oldtail[idx+1:] - fmr = imp.find_module(name, path) - module = imp.find_module(fullname[:-len(oldtail)] + name, *fmr) - return _find_module(fullname, tail, module.__path__) - else: - return imp.find_module(fullname, path) - - class VimModuleLoader(object): - def __init__(self, module): - self.module = module - - def load_module(self, fullname, path=None): - # Check sys.modules, required for reload (see PEP302). - if fullname in sys.modules: - return sys.modules[fullname] - return imp.load_module(fullname, *self.module) - - class VimPathFinder(object): - @staticmethod - def find_module(fullname, path=None): - "Method for Python 2.7 and 3.3." - try: - return VimModuleLoader( - _find_module(fullname, fullname, path or _get_paths())) - except ImportError: - return None - - @staticmethod - def find_spec(fullname, path=None, target=None): - "Method for Python 3.4+." - return PathFinder.find_spec(fullname, path or _get_paths(), target) - - def hook(path): - if path == nvim.VIM_SPECIAL_PATH: - return VimPathFinder - else: - raise ImportError - - return hook - - -def discover_runtime_directories(nvim): - rv = [] - for path in nvim.list_runtime_paths(): - if not os.path.exists(path): - continue - path1 = os.path.join(path, 'pythonx') - if IS_PYTHON3: - path2 = os.path.join(path, 'python3') - else: - path2 = os.path.join(path, 'python2') - if os.path.exists(path1): - rv.append(path1) - if os.path.exists(path2): - rv.append(path2) - return rv diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index 24497b10c2..1aead649a0 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -2,6 +2,7 @@ let s:hosts = {} let s:plugin_patterns = {} let s:remote_plugins_manifest = fnamemodify(expand($MYVIMRC, 1), ':h') \.'/.'.fnamemodify($MYVIMRC, ':t').'-rplugin~' +let s:plugins_for_host = {} " Register a host by associating it with a factory(funcref) @@ -35,6 +36,9 @@ endfunction " Get a host channel, bootstrapping it if necessary function! remote#host#Require(name) abort + if empty(s:plugins_for_host) + call remote#host#LoadRemotePlugins() + endif if !has_key(s:hosts, a:name) throw 'No host named "'.a:name.'" is registered' endif @@ -123,6 +127,13 @@ function! remote#host#LoadRemotePlugins() abort endfunction +function! remote#host#LoadRemotePluginsEvent(event, pattern) abort + autocmd! nvim-rplugin + call remote#host#LoadRemotePlugins() + execute 'silent doautocmd' a:event a:pattern +endfunction + + function! s:RegistrationCommands(host) abort " Register a temporary host clone for discovering specs let host_id = a:host.'-registration-clone' @@ -138,7 +149,9 @@ function! s:RegistrationCommands(host) abort endfor let channel = remote#host#Require(host_id) let lines = [] + let registered = [] for path in paths + unlet! specs let specs = rpcrequest(channel, 'specs', path) if type(specs) != type([]) " host didn't return a spec list, indicates a failure while loading a @@ -151,9 +164,10 @@ function! s:RegistrationCommands(host) abort call add(lines, " \\ ".string(spec).",") endfor call add(lines, " \\ ])") + call add(registered, path) endfor echomsg printf("remote/host: %s host registered plugins %s", - \ a:host, string(map(copy(paths), "fnamemodify(v:val, ':t')"))) + \ a:host, string(map(registered, "fnamemodify(v:val, ':t')"))) " Delete the temporary host clone call rpcstop(s:hosts[host_id].channel) @@ -163,7 +177,7 @@ function! s:RegistrationCommands(host) abort endfunction -function! s:UpdateRemotePlugins() abort +function! remote#host#UpdateRemotePlugins() abort let commands = [] let hosts = keys(s:hosts) for host in hosts @@ -185,10 +199,6 @@ function! s:UpdateRemotePlugins() abort endfunction -command! UpdateRemotePlugins call s:UpdateRemotePlugins() - - -let s:plugins_for_host = {} function! remote#host#PluginsForHost(host) abort if !has_key(s:plugins_for_host, a:host) let s:plugins_for_host[a:host] = [] @@ -197,40 +207,25 @@ function! remote#host#PluginsForHost(host) abort endfunction -" Registration of standard hosts - -" Python/Python3 {{{ -function! s:RequirePythonHost(host) abort - let ver = (a:host.orig_name ==# 'python') ? 2 : 3 - - " Python host arguments - let args = ['-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()'] - - " Collect registered Python plugins into args - let python_plugins = remote#host#PluginsForHost(a:host.name) - for plugin in python_plugins - call add(args, plugin.path) - endfor - - try - let channel_id = rpcstart((ver == '2' ? - \ provider#python#Prog() : provider#python3#Prog()), args) - if rpcrequest(channel_id, 'poll') == 'ok' - return channel_id - endif - catch - echomsg v:throwpoint - echomsg v:exception - endtry - throw 'Failed to load '. a:host.orig_name . ' host. '. +function! remote#host#LoadErrorForHost(host, log) abort + return 'Failed to load '. a:host . ' host. '. \ 'You can try to see what happened '. \ 'by starting Neovim with the environment variable '. - \ '$NVIM_PYTHON_LOG_FILE set to a file and opening '. - \ 'the generated log file. Also, the host stderr will be available '. + \ a:log . ' set to a file and opening the generated '. + \ 'log file. Also, the host stderr will be available '. \ 'in Neovim log, so it may contain useful information. '. \ 'See also ~/.nvimlog.' endfunction -call remote#host#Register('python', '*.py', function('s:RequirePythonHost')) -call remote#host#Register('python3', '*.py', function('s:RequirePythonHost')) -" }}} + +" Registration of standard hosts + +" Python/Python3 +call remote#host#Register('python', '*', + \ function('provider#pythonx#Require')) +call remote#host#Register('python3', '*', + \ function('provider#pythonx#Require')) + +" Ruby +call remote#host#Register('ruby', '*.rb', + \ function('provider#ruby#Require')) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index e17281821c..ec5818e16f 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -308,6 +308,8 @@ Name triggered by ~ |InsertCharPre| when a character was typed in Insert mode, before inserting it +|TextYankPost| when some text is yanked or deleted + |TextChanged| after a change was made to the text in Normal mode |TextChangedI| after a change was made to the text in Insert mode @@ -722,6 +724,18 @@ InsertCharPre When a character is typed in Insert mode, It is not allowed to change the text |textlock|. The event is not triggered when 'paste' is set. + *TextYankPost* +TextYankPost Just after a |yank| or |deleting| command, but not + if the black hole register |quote_| is used nor + for |setreg()|. Pattern must be * because its + meaning may change in the future. + Sets these |v:event| keys: + operator + regcontents + regname + regtype + Recursion is ignored. + It is not allowed to change the text |textlock|. *InsertEnter* InsertEnter Just before starting Insert mode. Also for Replace mode and Virtual Replace mode. The diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c478a217c1..f3192e108b 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1391,6 +1391,22 @@ v:errors Errors found by assert functions, such as |assert_true()|. < If v:errors is set to anything but a list it is made an empty list by the assert function. + *v:event* *event-variable* +v:event Dictionary of event data for the current |autocommand|. The + available keys differ per event type and are specified at the + documentation for each |event|. The possible keys are: + operator The operation performed. Unlike + |v:operator|, it is set also for an Ex + mode command. For instance, |:yank| is + translated to "|y|". + regcontents Text stored in the register as a + |readfile()|-style list of lines. + regname Requested register (e.g "x" for "xyy) + or the empty string for an unnamed + operation. + regtype Type of register as returned by + |getregtype()|. + *v:exception* *exception-variable* v:exception The value of the exception most recently caught and not finished. See also |v:throwpoint| and |throw-variables|. @@ -1791,8 +1807,10 @@ cursor( {lnum}, {col} [, {off}]) cursor( {list}) Number move cursor to position in {list} deepcopy( {expr} [, {noref}]) any make a full copy of {expr} delete( {fname}) Number delete file {fname} -dictwatcheradd({dict}, {pattern}, {callback}) Start watching a dictionary -dictwatcherdel({dict}, {pattern}, {callback}) Stop watching a dictionary +dictwatcheradd( {dict}, {pattern}, {callback}) + Start watching a dictionary +dictwatcherdel( {dict}, {pattern}, {callback}) + Stop watching a dictionary did_filetype() Number TRUE if FileType autocommand event used diff_filler( {lnum}) Number diff filler lines about {lnum} diff_hlID( {lnum}, {col}) Number diff highlighting at {lnum}/{col} @@ -1812,7 +1830,7 @@ feedkeys( {string} [, {mode}]) Number add key sequence to typeahead buffer filereadable( {file}) Number TRUE if {file} is a readable file filewritable( {file}) Number TRUE if {file} is a writable file filter( {expr}, {string}) List/Dict remove items from {expr} where - {string} is 0 + {string} is 0 finddir( {name}[, {path}[, {count}]]) String find directory {name} in {path} findfile( {name}[, {path}[, {count}]]) @@ -1894,20 +1912,22 @@ inputdialog( {p} [, {t} [, {c}]]) String like input() but in a GUI dialog inputlist( {textlist}) Number let the user pick from a choice list inputrestore() Number restore typeahead inputsave() Number save and clear typeahead -inputsecret( {prompt} [, {text}]) String like input() but hiding the text -insert( {list}, {item} [, {idx}]) List insert {item} in {list} [before {idx}] +inputsecret( {prompt} [, {text}]) + String like input() but hiding the text +insert( {list}, {item} [, {idx}]) + List insert {item} in {list} [before {idx}] invert( {expr}) Number bitwise invert isdirectory( {directory}) Number TRUE if {directory} is a directory islocked( {expr}) Number TRUE if {expr} is locked items( {dict}) List key-value pairs in {dict} -jobclose({job}[, {stream}]) Number Closes a job stream(s) -jobpid({job}) Number Returns pid of a job. -jobresize({job}, {width}, {height}) +jobclose( {job}[, {stream}]) Number Closes a job stream(s) +jobpid( {job}) Number Returns pid of a job. +jobresize( {job}, {width}, {height}) Number Resize {job}'s pseudo terminal window -jobsend({job}, {data}) Number Writes {data} to {job}'s stdin -jobstart({cmd}[, {opts}]) Number Spawns {cmd} as a job -jobstop({job}) Number Stops a job -jobwait({ids}[, {timeout}]) Number Wait for a set of jobs +jobsend( {job}, {data}) Number Writes {data} to {job}'s stdin +jobstart( {cmd}[, {opts}]) Number Spawns {cmd} as a job +jobstop( {job}) Number Stops a job +jobwait( {ids}[, {timeout}]) Number Wait for a set of jobs join( {list} [, {sep}]) String join {list} items into one String keys( {dict}) List keys in {dict} len( {expr}) Number the length of {expr} @@ -1977,12 +1997,12 @@ repeat( {expr}, {count}) String repeat {expr} {count} times resolve( {filename}) String get filename a shortcut points to reverse( {list}) List reverse {list} in-place round( {expr}) Float round off {expr} -rpcnotify({channel}, {event}[, {args}...]) +rpcnotify( {channel}, {event}[, {args}...]) Sends a |msgpack-rpc| notification to {channel} -rpcrequest({channel}, {method}[, {args}...]) +rpcrequest( {channel}, {method}[, {args}...]) Sends a |msgpack-rpc| request to {channel} -rpcstart({prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel -rpcstop({channel}) Closes a |msgpack-rpc| {channel} +rpcstart( {prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel +rpcstop( {channel}) Closes a |msgpack-rpc| {channel} screenattr( {row}, {col}) Number attribute at screen position screenchar( {row}, {col}) Number character at screen position screencol() Number current cursor column @@ -6243,12 +6263,22 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number, {expr} type result ~ String 'string' Number 123 - Float 123.123456 or 1.123456e8 - Funcref function('name') + Float 123.123456 or 1.123456e8 or + `str2float('inf')` + Funcref `function('name')` List [item, item] Dictionary {key: value, key: value} Note that in String values the ' character is doubled. Also see |strtrans()|. + Note 2: Output format is mostly compatible with YAML, except + for infinite and NaN floating-point values representations + which use |str2float()|. Strings are also dumped literally, + only single quote is escaped, which does not allow using YAML + for parsing back binary strings (including text when + 'encoding' is not UTF-8). |eval()| should always work for + strings and floats though and this is the only official + method, use |msgpackdump()| or |json_encode()| if you need to + share data with other application. *strlen()* strlen({expr}) The result is a Number, which is the length of the String diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index ced303947b..dbc87a8676 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5538,6 +5538,9 @@ A jump table for the options with a short description can be found at |Q_op|. c don't give |ins-completion-menu| messages. For example, "-- XXX completion (YYY)", "match 1 of 2", "The only match", "Pattern not found", "Back at original", etc. + q use "recording" instead of "recording @a" + F don't give the file info when editing a file, like `:silent` + was used for the command This gives you the opportunity to avoid that a change between buffers requires you to hit <Enter>, but still gives as useful a message as diff --git a/runtime/doc/os_dos.txt b/runtime/doc/os_dos.txt deleted file mode 100644 index 1601d65ffd..0000000000 --- a/runtime/doc/os_dos.txt +++ /dev/null @@ -1,279 +0,0 @@ -*os_dos.txt* For Vim version 7.4. Last change: 2006 Mar 30 - - - VIM REFERENCE MANUAL by Bram Moolenaar - - - *dos* *DOS* -This file documents some particularities of the Win32 -version of Vim. Also see |os_win32.txt|. - -1. File locations |dos-locations| -2. Using backslashes |dos-backslash| -3. Standard mappings |dos-standard-mappings| -4. Screen output and colors |dos-colors| -5. File formats |dos-file-formats| -6. :cd command |dos-:cd| -7. Interrupting |dos-CTRL-Break| -8. Temp files |dos-temp-files| -9. Shell option default |dos-shell| - -============================================================================== -1. File locations *dos-locations* - -If you keep the Vim executable in the directory that contains the help and -syntax subdirectories, there is no need to do anything special for Vim to -work. No registry entries or environment variables need to be set. Just make -sure that the directory is in your search path, or use a shortcut on the -desktop. - -Your vimrc files ("_vimrc" and "_gvimrc") are normally located one directory -up from the runtime files. If you want to put them somewhere else, set the -environment variable $VIM to the directory where you keep them. Example: > - set VIM=C:\user\piet -Will find "c:\user\piet\_vimrc". -Note: This would only be needed when the computer is used by several people. -Otherwise it's simpler to keep your _vimrc file in the default place. - -If you move the executable to another location, you also need to set the $VIM -environment variable. The runtime files will be found in "$VIM/vim{version}". -Example: > - set VIM=E:\vim -Will find the version 5.4 runtime files in "e:\vim\vim54". -Note: This is _not_ recommended. The preferred way is to keep the executable -in the runtime directory. - -If you move your executable AND want to put your "_vimrc" and "_gvimrc" files -somewhere else, you must set $VIM to where you vimrc files are, and set -$VIMRUNTIME to the runtime files. Example: > - set VIM=C:\usr\piet - set VIMRUNTIME=E:\vim\vim54 -Will find "c:\user\piet\_vimrc" and the runtime files in "e:\vim\vim54". - -See |$VIM| and |$VIMRUNTIME| for more information. - -You can set environment variables for each user separately under -"Start/Settings/Control Panel->System", or through the properties in the menu -of "My Computer", under the Environment Tab. - -============================================================================== -2. Using backslashes *dos-backslash* - -Using backslashes in file names can be a problem. Vi halves the number of -backslashes for some commands. Vim is a bit more tolerant and does not remove -backslashes from a file name, so ":e c:\foo\bar" works as expected. But when -a backslash occurs before a special character (space, comma, backslash, etc.), -Vim removes the backslash. Use slashes to avoid problems: ":e c:/foo/bar" -works fine. Vim replaces the slashes with backslashes internally to avoid -problems with some MS-DOS programs and Win32 programs. - -When you prefer to use forward slashes, set the 'shellslash' option. Vim will -then replace backslashes with forward slashes when expanding file names. This -is especially useful when using a Unix-like 'shell'. - -============================================================================== -3. Standard mappings *dos-standard-mappings* - -The mappings for CTRL-PageUp and CTRL-PageDown have been removed, they now -jump to the next or previous tab page |<C-PageUp>| |<C-PageDown>| - -If you want them to move to the first and last screen line you can use these -mappings: - -key key code Normal/Visual mode Insert mode ~ -CTRL-PageUp <M-N><M-C-D> H <C-O>H -CTRL-PageDown <M-N>v L$ <C-O>L<C-O>$ - -Additionally, these keys are available for copy/cut/paste. -In the Win32 version, they also use the clipboard. - -Shift-Insert paste text (from clipboard) *<S-Insert>* -CTRL-Insert copy Visual text (to clipboard) *<C-Insert>* -CTRL-Del cut Visual text (to clipboard) *<C-Del>* -Shift-Del cut Visual text (to clipboard) *<S-Del>* - -These mappings accomplish this (Win32 version of Vim): - -key key code Normal Visual Insert ~ -Shift-Insert <M-N><M-T> "*P "-d"*P <C-R><C-O>* -CTRL-Insert <M-N><M-U> "*y -Shift-Del <M-N><M-W> "*d -CTRL-Del <M-N><M-X> "*d - -Or these mappings (non-Win32 version of Vim): - -key key code Normal Visual Insert ~ -Shift-Insert <M-N><M-T> P "-dP <C-R><C-O>" -CTRL-Insert <M-N><M-U> y -Shift-Del <M-N><M-W> d -CTRL-Del <M-N><M-X> d - -When the clipboard is supported, the "* register is used. - -============================================================================== -4. Screen output and colors *dos-colors* - -The default output method for the screen is to use bios calls. This works -right away on most systems. You do not need ansi.sys. You can use ":mode" to -set the current screen mode. See |:mode|. - -To change the screen colors that Vim uses, you can use the |:highlight| -command. The Normal highlight group specifies the colors Vim uses for normal -text. For example, to get grey text on a blue background: > - :hi Normal ctermbg=Blue ctermfg=grey -See |highlight-groups| for other groups that are available. - -A DOS console does not support attributes like bold and underlining. You can -set the color used in five modes with nine terminal options. Note that this -is not necessary since you can set the color directly with the ":highlight" -command; these options are for backward compatibility with older Vim versions. -The |'highlight'| option specifies which of the five modes is used for which -action. > - - :set t_mr=^V^[\|xxm start of invert mode - :set t_md=^V^[\|xxm start of bold mode - :set t_me=^V^[\|xxm back to normal text - - :set t_so=^V^[\|xxm start of standout mode - :set t_se=^V^[\|xxm back to normal text - - :set t_us=^V^[\|xxm start of underline mode - :set t_ue=^V^[\|xxm back to normal text - - :set t_ZH=^V^[\|xxm start of italics mode - :set t_ZR=^V^[\|xxm back to normal text - -^V is CTRL-V -^[ is <Esc> -You must replace xx with a decimal code, which is the foreground color number -and background color number added together: - -COLOR FOREGROUND BACKGROUND ~ -Black 0 0 -DarkBlue 1 16 -DarkGreen 2 32 -DarkCyan 3 48 -DarkRed 4 64 -DarkMagenta 5 80 -Brown, DarkYellow 6 96 -LightGray 7 112 -DarkGray 8 128 * -Blue, LightBlue 9 144 * -Green, LightGreen 10 160 * -Cyan, LightCyan 11 176 * -Red, LightRed 12 192 * -Magenta, LightMagenta 13 208 * -Yellow, LightYellow 14 224 * -White 15 240 * - -* Depending on the display mode, the color codes above 128 may not be - available, and code 128 will make the text blink. - -When you use 0, the color is reset to the one used when you started Vim -(usually 7, lightgray on black, but you can override this. If you have -overridden the default colors in a command prompt, you may need to adjust -some of the highlight colors in your vimrc---see below). -This is the default for t_me. - -The defaults for the various highlight modes are: - t_mr 112 reverse mode: Black text (0) on LightGray (112) - t_md 15 bold mode: White text (15) on Black (0) - t_me 0 normal mode (revert to default) - - t_so 31 standout mode: White (15) text on DarkBlue (16) - t_se 0 standout mode end (revert to default) - - t_czh 225 italic mode: DarkBlue text (1) on Yellow (224) - t_czr 0 italic mode end (revert to default) - - t_us 67 underline mode: DarkCyan text (3) on DarkRed (64) - t_ue 0 underline mode end (revert to default) - -These colors were chosen because they also look good when using an inverted -display, but you can change them to your liking. - -Example: > - :set t_mr=^V^[\|97m " start of invert mode: DarkBlue (1) on Brown (96) - :set t_md=^V^[\|67m " start of bold mode: DarkCyan (3) on DarkRed (64) - :set t_me=^V^[\|112m " back to normal mode: Black (0) on LightGray (112) - - :set t_so=^V^[\|37m " start of standout mode: DarkMagenta (5) on DarkGreen - (32) - :set t_se=^V^[\|112m " back to normal mode: Black (0) on LightGray (112) - -============================================================================== -5. File formats *dos-file-formats* - -If the 'fileformat' option is set to "dos" (which is the default), Vim accepts -a single <NL> or a <CR><NL> pair for end-of-line (<EOL>). When writing a -file, Vim uses <CR><NL>. Thus, if you edit a file and write it, Vim replaces -<NL> with <CR><NL>. - -If the 'fileformat' option is set to "unix", Vim uses a single <NL> for <EOL> -and shows <CR> as ^M. - -You can use Vim to replace <NL> with <CR><NL> by reading in any mode and -writing in Dos mode (":se ff=dos"). -You can use Vim to replace <CR><NL> with <NL> by reading in Dos mode and -writing in Unix mode (":se ff=unix"). - -Vim sets 'fileformat' automatically when 'fileformats' is not empty (which is -the default), so you don't really have to worry about what you are doing. - |'fileformat'| |'fileformats'| - -If you want to edit a script file or a binary file, you should set the -'binary' option before loading the file. Script files and binary files may -contain single <NL> characters which Vim would replace with <CR><NL>. You can -set 'binary' automatically by starting Vim with the "-b" (binary) option. - -============================================================================== -6. :cd command *dos-:cd* - -The ":cd" command recognizes the drive specifier and changes the current -drive. Use ":cd c:" to make drive C the active drive. Use ":cd d:\foo" to go -to the directory "foo" in the root of drive D. Vim also recognizes UNC names -if the system supports them; e.g., ":cd \\server\share\dir". |:cd| - -============================================================================== -7. Interrupting *dos-CTRL-Break* - -Use CTRL-Break instead of CTRL-C to interrupt searches. Vim does not detect -the CTRL-C until it tries to read a key. - -============================================================================== -8. Temp files *dos-temp-files* - -Vim uses standard Windows functions to obtain a temporary file name (for -filtering). The first of these directories that exists and in which Vim can -create a file is used: - $TMP - $TEMP - current directory - -============================================================================== -9. Shell option default *dos-shell* - -The default for the 'sh' ('shell') option is "cmd.exe" on Windows. -If SHELL is defined, Vim uses SHELL instead, and if SHELL is not defined -but COMSPEC is, Vim uses COMSPEC. Vim starts external commands with -"<shell> /c <command_name>". Typing CTRL-Z starts a new command -subshell. Return to Vim with "exit". |'shell'| |CTRL-Z| - -If you are running a third-party shell, you may need to set the -|'shellcmdflag'| ('shcf') and |'shellquote'| ('shq') or |'shellxquote'| -('sxq') options. Unfortunately, this also depends on the version of Vim used. -For example, with the MKS Korn shell or with bash, the values of the options -on Win32 should be: - -'shellcmdflag' -c -'shellquote' (empty) -'shellxquote' " - -For Win32, this starts the shell as: - <shell> -c "command name >file" - -When starting up, Vim checks for the presence of "sh" anywhere in the 'shell' -option. If it is present, Vim sets the 'shellcmdflag' and 'shellquote' or -'shellxquote' options will be set as described above. - - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 21b5eef811..b2e935eb3f 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -109,6 +109,12 @@ q{0-9a-zA-Z"} Record typed characters into register {0-9a-zA-Z"} while executing a register, and it doesn't work inside a mapping and |:normal|. + Note: If the register being used for recording is also + used for |y| and |p| the result is most likely not + what is expected, because the put will paste the + recorded macro and the yank will overwrite the + recorded macro. + q Stops recording. Implementation note: The 'q' that stops recording is not stored in the register, unless it was the result diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index be108d4633..37593aef43 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -380,6 +380,8 @@ accordingly. Vim proceeds in this order: Places for your personal initializations: Unix $XDG_CONFIG_HOME/nvim/init.vim (default for $XDG_CONFIG_HOME is ~/.config) + Windows $XDG_CONFIG_HOME/nvim/init.vim + (default for $XDG_CONFIG_HOME is ~/AppData/Local) The files are searched in the order specified above and only the first one that is found is read. diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 86fcf0cc2f..5aecf33557 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -37,9 +37,10 @@ for you), you can edit it this way: > If you don't have a vimrc file yet, see |init.vim| to find out where you can create a vimrc file. -For Unix and Macintosh this file is always used and is recommended: +This file is always used and is recommended: - ~/.config/nvim/init.vim ~ + ~/.config/nvim/init.vim (Unix and OSX) ~ + ~/AppData/Local/nvim/init.vim (Windows) ~ The vimrc file can contain all the commands that you type after a colon. The most simple ones are for setting options. For example, if you want Vim to diff --git a/runtime/doc/usr_06.txt b/runtime/doc/usr_06.txt index 1cb3eb8673..b4b495ff9f 100644 --- a/runtime/doc/usr_06.txt +++ b/runtime/doc/usr_06.txt @@ -152,7 +152,7 @@ You could also write your own color scheme. This is how you do it: directory. For Unix, this should work: > !mkdir -p ~/.config/nvim/colors - !cp $VIMRUNTIME/colors/morning.vim ~/.vim/colors/mine.vim + !cp $VIMRUNTIME/colors/morning.vim ~/.config/nvim/colors/mine.vim < This is done from Vim, because it knows the value of $VIMRUNTIME. diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index ff37466a14..3f53e3f507 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -222,6 +222,10 @@ g8 Print the hex values of the bytes used in the modified, but can be forced with "!". See |termopen()| and |nvim-terminal-emulator| for more information. + To switch to terminal mode automatically: +> + autocmd BufEnter term://* startinsert +< *:!cmd* *:!* *E34* :!{cmd} Execute {cmd} with 'shell'. See also |:terminal|. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index d3768409f5..17e16911bc 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -21,10 +21,10 @@ these differences. ============================================================================== 1. Configuration *nvim-configuration* -- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for storing +- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for storing configuration. - Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files. -- Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent +- Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent session information. ============================================================================== @@ -73,54 +73,56 @@ are always available and may be used simultaneously in separate plugins. The |nvim-python|). |mkdir()| behaviour changed: -1. Assuming /tmp/foo does not exist and /tmp can be written to +1. Assuming /tmp/foo does not exist and /tmp can be written to mkdir('/tmp/foo/bar', 'p', 0700) will create both /tmp/foo and /tmp/foo/bar with 0700 permissions. Vim mkdir will create /tmp/foo with 0755. -2. If you try to create an existing directory with `'p'` (e.g. mkdir('/', +2. If you try to create an existing directory with `'p'` (e.g. mkdir('/', 'p')) mkdir() will silently exit. In Vim this was an error. 3. mkdir() error messages now include strerror() text when mkdir fails. 'encoding' cannot be changed after startup. |string()| and |:echo| behaviour changed: -1. No maximum recursion depth limit is applied to nested container +1. No maximum recursion depth limit is applied to nested container structures. -2. |string()| fails immediately on nested containers, not when recursion limit +2. |string()| fails immediately on nested containers, not when recursion limit was exceeded. 2. When |:echo| encounters duplicate containers like > let l = [] echo [l, l] < - it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is + it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is only used for recursive containers. -3. |:echo| printing nested containers adds "@level" after "..." designating - the level at which recursive container was printed: |:echo-self-refer|. - Same thing applies to |string()| (though it uses construct like - "{E724@level}"), but this is not reliable because |string()| continues to +3. |:echo| printing nested containers adds "@level" after "..." designating + the level at which recursive container was printed: |:echo-self-refer|. + Same thing applies to |string()| (though it uses construct like + "{E724@level}"), but this is not reliable because |string()| continues to error out. +4. Stringifyed infinite and NaN values now use |str2float()| and can be evaled + back. -Viminfo text files were replaced with binary (messagepack) ShaDa files. +Viminfo text files were replaced with binary (messagepack) ShaDa files. Additional differences: - |shada-c| has no effect. - |shada-s| now limits size of every item and not just registers. -- When reading ShaDa files items are merged according to the timestamp. +- When reading ShaDa files items are merged according to the timestamp. |shada-merging| -- 'viminfo' option got renamed to 'shada'. Old option is kept as an alias for +- 'viminfo' option got renamed to 'shada'. Old option is kept as an alias for compatibility reasons. -- |:wviminfo| was renamed to |:wshada|, |:rviminfo| to |:rshada|. Old +- |:wviminfo| was renamed to |:wshada|, |:rviminfo| to |:rshada|. Old commands are still kept. - |:oldfiles| supports !. -- When writing (|:wshada| without bang or at exit) it merges much more data, - and does this according to the timestamp. Vim merges only marks. +- When writing (|:wshada| without bang or at exit) it merges much more data, + and does this according to the timestamp. Vim merges only marks. |shada-merging| -- ShaDa file format was designed with forward and backward compatibility in +- ShaDa file format was designed with forward and backward compatibility in mind. |shada-compatibility| -- Some errors make ShaDa code keep temporary file in-place for user to decide - what to do with it. Vim deletes temporary file in these cases. +- Some errors make ShaDa code keep temporary file in-place for user to decide + what to do with it. Vim deletes temporary file in these cases. |shada-error-handling| -- Vim keeps no timestamps at all, neither in viminfo file nor in the instance +- Vim keeps no timestamps at all, neither in viminfo file nor in the instance itself. - ShaDa file keeps search direction (|v:searchforward|), viminfo does not. @@ -139,8 +141,8 @@ Meta (alt) chords are recognized (even in the terminal). Note: Meta chords are case-sensitive (<M-a> is distinguished from <M-A>). -Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants (even in -the terminal). Specifically, the following are known to work: +Some `CTRL-SHIFT-...` key chords are distinguished from `CTRL-...` variants +(even in the terminal). Specifically, the following are known to work: <C-Tab>, <C-S-Tab> <C-BS>, <C-S-BS> <C-Enter>, <C-S-Enter> diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index eee171b7da..29c8aaf808 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -706,7 +706,7 @@ can also get to them with the buffer list commands, like ":bnext". *:bufdo* :[range]bufdo[!] {cmd} Execute {cmd} in each buffer in the buffer list or if - [range[ is given only for buffers for which their + [range] is given only for buffers for which their buffer name is in the [range]. It works like doing this: > :bfirst diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 133a28e626..36cd70f203 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -24,11 +24,11 @@ setlocal buftype=nofile noswapfile setlocal nomodifiable readonly bufhidden=hide nobuflisted tabstop=8 if !exists("g:no_plugin_maps") && !exists("g:no_man_maps") - nnoremap <silent> <buffer> <C-]> :call man#get_page(v:count)<CR> + nnoremap <silent> <buffer> <C-]> :call man#get_page(v:count, expand('<cword>'))<CR> nnoremap <silent> <buffer> <C-T> :call man#pop_page()<CR> nnoremap <silent> <nowait><buffer> q <C-W>c if &keywordprg !=# ':Man' - nnoremap <silent> <buffer> K :call man#get_page(v:count)<CR> + nnoremap <silent> <buffer> K :call man#get_page(v:count, expand('<cword>'))<CR> endif endif diff --git a/runtime/macros/less.bat b/runtime/macros/less.bat index bbe619bc92..7395a70003 100644 --- a/runtime/macros/less.bat +++ b/runtime/macros/less.bat @@ -4,7 +4,7 @@ rem Read stdin if no arguments were given. rem Written by Ken Takata. if "%1"=="" ( - vim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" - + nvim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" - ) else ( - vim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" %* + nvim --cmd "let no_plugin_maps = 1" -c "runtime! macros/less.vim" %* ) diff --git a/runtime/macros/less.sh b/runtime/macros/less.sh index e29958f7ad..125162f10a 100755 --- a/runtime/macros/less.sh +++ b/runtime/macros/less.sh @@ -8,9 +8,9 @@ if test -t 1; then echo "Missing filename" 1>&2 exit fi - vim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' - + nvim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' - else - vim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' "$@" + nvim --cmd 'let no_plugin_maps = 1' -c 'runtime! macros/less.vim' "$@" fi else # Output is not a terminal, cat arguments or stdin diff --git a/runtime/plugin/rplugin.vim b/runtime/plugin/rplugin.vim index 879775ff0e..b4b03032b3 100644 --- a/runtime/plugin/rplugin.vim +++ b/runtime/plugin/rplugin.vim @@ -1,5 +1,16 @@ -if exists('g:loaded_remote_plugins') || &cp +if exists('g:loaded_remote_plugins') finish endif let g:loaded_remote_plugins = 1 -call remote#host#LoadRemotePlugins() + +command! UpdateRemotePlugins call remote#host#UpdateRemotePlugins() + +augroup nvim-rplugin + autocmd! + autocmd FuncUndefined * + \ call remote#host#LoadRemotePluginsEvent( + \ 'FuncUndefined', expand('<amatch>')) + autocmd CmdUndefined * + \ call remote#host#LoadRemotePluginsEvent( + \ 'CmdUndefined', expand('<amatch>')) +augroup END diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 7612a2ada0..4dc24a8cdf 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -65,7 +65,7 @@ get_vim_sources() { if [[ ! -d ${VIM_SOURCE_DIR} ]]; then echo "Cloning Vim sources into '${VIM_SOURCE_DIR}'." - git clone --depth=1000 https://github.com/vim/vim.git "${VIM_SOURCE_DIR}" + git clone https://github.com/vim/vim.git "${VIM_SOURCE_DIR}" cd "${VIM_SOURCE_DIR}" else if [[ ! -d "${VIM_SOURCE_DIR}/.git" ]]; then @@ -243,7 +243,7 @@ list_vim_patches() { local patch_number="${vim_tag:5}" # Remove prefix like "v7.4." # Tagged Vim patch, check version.c: is_missing="$(sed -n '/static int included_patches/,/}/p' "${NEOVIM_SOURCE_DIR}/src/nvim/version.c" | - grep -x -e "[[:space:]]*//[[:space:]]${patch_number} NA" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")" + grep -x -e "[[:space:]]*//[[:space:]]${patch_number} NA.*" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")" vim_commit="${vim_tag#v}" else # Untagged Vim patch (e.g. runtime updates), check the Neovim git log: diff --git a/src/.clang-format b/src/.clang-format index 35e545ac4b..5a910ff34b 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -1,4 +1,4 @@ -BasedOnStyle: llvm +BasedOnStyle: Google Language: Cpp ColumnLimit: 80 IndentWidth: 2 @@ -10,3 +10,9 @@ AlignEscapedNewlinesLeft: false AllowShortFunctionsOnASingleLine: false SpacesBeforeTrailingComments: 2 PenaltyReturnTypeOnItsOwnLine: 200 +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +BinPackParameters: false +BreakBeforeBinaryOperators: true +ContinuationIndentWidth: 4 diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index ea5125e4e7..487b554d6d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -60,6 +60,11 @@ file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c file(GLOB_RECURSE NEOVIM_HEADERS *.h) file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) +# Sort file lists to ensure generated files are created in the same order from +# build to build. +list(SORT NEOVIM_SOURCES) +list(SORT NEOVIM_HEADERS) + foreach(sfile ${NEOVIM_SOURCES}) get_filename_component(f ${sfile} NAME) if(${f} MATCHES "^(regexp_nfa.c)$") @@ -72,11 +77,9 @@ list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) # Handle legacy files that don't yet pass -Wconversion. set(CONV_SOURCES buffer.c - charset.c diff.c edit.c eval.c - ex_cmds2.c ex_cmds.c ex_docmd.c ex_getln.c @@ -87,8 +90,6 @@ set(CONV_SOURCES message.c misc1.c ops.c - path.c - quickfix.c regexp.c screen.c search.c diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index fa4b8e5f7d..c25a9789c5 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -45,14 +45,22 @@ Integer buffer_line_count(Buffer buffer, Error *err) /// Gets a buffer line /// +/// @deprecated use buffer_get_lines instead. +/// for positive indices (including 0) use +/// "buffer_get_lines(buffer, index, index+1, true)" +/// for negative indices use +/// "buffer_get_lines(buffer, index-1, index, true)" +/// /// @param buffer The buffer handle /// @param index The line index /// @param[out] err Details of an error that may have occurred /// @return The line string String buffer_get_line(Buffer buffer, Integer index, Error *err) { - String rv = {.size = 0}; - Array slice = buffer_get_line_slice(buffer, index, index, true, true, err); + String rv = { .size = 0 }; + + index = convert_index(index); + Array slice = buffer_get_lines(buffer, index, index+1, true, err); if (!err->set && slice.size) { rv = slice.items[0].data.string; @@ -65,6 +73,12 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// Sets a buffer line /// +/// @deprecated use buffer_set_lines instead. +/// for positive indices use +/// "buffer_set_lines(buffer, index, index+1, true, [line])" +/// for negative indices use +/// "buffer_set_lines(buffer, index-1, index, true, [line])" +/// /// @param buffer The buffer handle /// @param index The line index /// @param line The new line. @@ -72,23 +86,34 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) { Object l = STRING_OBJ(line); - Array array = {.items = &l, .size = 1}; - buffer_set_line_slice(buffer, index, index, true, true, array, err); + Array array = { .items = &l, .size = 1 }; + index = convert_index(index); + buffer_set_lines(buffer, index, index+1, true, array, err); } /// Deletes a buffer line /// +/// @deprecated use buffer_set_lines instead. +/// for positive indices use +/// "buffer_set_lines(buffer, index, index+1, true, [])" +/// for negative indices use +/// "buffer_set_lines(buffer, index-1, index, true, [])" /// @param buffer The buffer handle /// @param index The line index /// @param[out] err Details of an error that may have occurred void buffer_del_line(Buffer buffer, Integer index, Error *err) { Array array = ARRAY_DICT_INIT; - buffer_set_line_slice(buffer, index, index, true, true, array, err); + index = convert_index(index); + buffer_set_lines(buffer, index, index+1, true, array, err); } /// Retrieves a line range from the buffer /// +/// @deprecated use buffer_get_lines(buffer, newstart, newend, false) +/// where newstart = start + int(not include_start) - int(start < 0) +/// newend = end + int(include_end) - int(end < 0) +/// int(bool) = 1 if bool is true else 0 /// @param buffer The buffer handle /// @param start The first line index /// @param end The last line index @@ -103,16 +128,48 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Boolean include_end, Error *err) { + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + return buffer_get_lines(buffer, start , end, false, err); +} + + +/// Retrieves a line range from the buffer +/// +/// Indexing is zero-based, end-exclusive. Negative indices are interpreted +/// as length+1+index, i e -1 refers to the index past the end. So to get the +/// last element set start=-2 and end=-1. +/// +/// Out-of-bounds indices are clamped to the nearest valid value, unless +/// `strict_indexing` is set. +/// +/// @param buffer The buffer handle +/// @param start The first line index +/// @param end The last line index (exclusive) +/// @param strict_indexing whether out-of-bounds should be an error. +/// @param[out] err Details of an error that may have occurred +/// @return An array of lines +ArrayOf(String) buffer_get_lines(Buffer buffer, + Integer start, + Integer end, + Boolean strict_indexing, + Error *err) +{ Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf || !inbounds(buf, start)) { + if (!buf) { return rv; } - start = normalize_index(buf, start) + (include_start ? 0 : 1); - include_end = include_end || (end >= buf->b_ml.ml_line_count); - end = normalize_index(buf, end) + (include_end ? 1 : 0); + bool oob = false; + start = normalize_index(buf, start, &oob); + end = normalize_index(buf, end, &oob); + + if (strict_indexing && oob) { + api_set_error(err, Validation, _("Index out of bounds")); + return rv; + } if (start >= end) { // Return 0-length array @@ -152,8 +209,14 @@ end: return rv; } + /// Replaces a line range on the buffer /// +/// @deprecated use buffer_set_lines(buffer, newstart, newend, false, lines) +/// where newstart = start + int(not include_start) + int(start < 0) +/// newend = end + int(include_end) + int(end < 0) +/// int(bool) = 1 if bool is true else 0 +/// /// @param buffer The buffer handle /// @param start The first line index /// @param end The last line index @@ -170,20 +233,52 @@ void buffer_set_line_slice(Buffer buffer, ArrayOf(String) replacement, Error *err) { + start = convert_index(start) + !include_start; + end = convert_index(end) + include_end; + buffer_set_lines(buffer, start, end, false, replacement, err); +} + + +/// Replaces line range on the buffer +/// +/// Indexing is zero-based, end-exclusive. Negative indices are interpreted +/// as length+1+index, i e -1 refers to the index past the end. So to change +/// or delete the last element set start=-2 and end=-1. +/// +/// To insert lines at a given index, set both start and end to the same index. +/// To delete a range of lines, set replacement to an empty array. +/// +/// Out-of-bounds indices are clamped to the nearest valid value, unless +/// `strict_indexing` is set. +/// +/// @param buffer The buffer handle +/// @param start The first line index +/// @param end The last line index (exclusive) +/// @param strict_indexing whether out-of-bounds should be an error. +/// @param replacement An array of lines to use as replacement +/// @param[out] err Details of an error that may have occurred +void buffer_set_lines(Buffer buffer, + Integer start, + Integer end, + Boolean strict_indexing, + ArrayOf(String) replacement, + Error *err) +{ buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { return; } - if (!inbounds(buf, start)) { + bool oob = false; + start = normalize_index(buf, start, &oob); + end = normalize_index(buf, end, &oob); + + if (strict_indexing && oob) { api_set_error(err, Validation, _("Index out of bounds")); return; } - start = normalize_index(buf, start) + (include_start ? 0 : 1); - include_end = include_end || (end >= buf->b_ml.ml_line_count); - end = normalize_index(buf, end) + (include_end ? 1 : 0); if (start > end) { api_set_error(err, @@ -457,6 +552,8 @@ Boolean buffer_is_valid(Buffer buffer) /// Inserts a sequence of lines to a buffer at a certain index /// +/// @deprecated use buffer_set_lines(buffer, lnum, lnum, true, lines) +/// /// @param buffer The buffer handle /// @param lnum Insert the lines after `lnum`. If negative, it will append /// to the end of the buffer. @@ -467,8 +564,9 @@ void buffer_insert(Buffer buffer, ArrayOf(String) lines, Error *err) { - bool end_start = lnum < 0; - buffer_set_line_slice(buffer, lnum, lnum, !end_start, end_start, lines, err); + // "lnum" will be the index of the line after inserting, + // no matter if it is negative or not + buffer_set_lines(buffer, lnum, lnum, true, lines, err); } /// Return a tuple (row,col) representing the position of the named mark @@ -632,20 +730,26 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) } // Normalizes 0-based indexes to buffer line numbers -static int64_t normalize_index(buf_T *buf, int64_t index) +static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) { + int64_t line_count = buf->b_ml.ml_line_count; // Fix if < 0 - index = index < 0 ? buf->b_ml.ml_line_count + index : index; + index = index < 0 ? line_count + index +1 : index; + + // Check for oob + if (index > line_count) { + *oob = true; + index = line_count; + } else if (index < 0) { + *oob = true; + index = 0; + } // Convert the index to a vim line number index++; - // Fix if > line_count - index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index; return index; } -// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds. -static bool inbounds(buf_T *buf, int64_t index) +static int64_t convert_index(int64_t index) { - linenr_T nlines = buf->b_ml.ml_line_count; - return index >= -nlines && index < nlines; + return index < 0 ? index - 1 : index; } diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index aa4a8d8332..8d891effae 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -83,6 +83,7 @@ return { 'TermResponse', -- after setting "v:termresponse" 'TextChanged', -- text was modified 'TextChangedI', -- text was modified in Insert mode + 'TextYankPost', -- after a yank or delete was done (y, d, c) 'User', -- user defined autocommand 'VimEnter', -- after starting Vim 'VimLeave', -- before exiting Vim diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 62ab7495da..c514c4378e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -141,14 +141,21 @@ open_buffer ( /* mark cursor position as being invalid */ curwin->w_valid = 0; - if (curbuf->b_ffname != NULL - ) { + if (curbuf->b_ffname != NULL) { + int old_msg_silent = msg_silent; + if (shortmess(SHM_FILEINFO)) { + msg_silent = 1; + } + retval = readfile(curbuf->b_ffname, curbuf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_NEW); - /* Help buffer is filtered. */ - if (curbuf->b_help) + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, + flags | READ_NEW); + msg_silent = old_msg_silent; + + // Help buffer is filtered. + if (curbuf->b_help) { fix_help_buffer(); + } } else if (read_stdin) { int save_bin = curbuf->b_p_bin; linenr_T line_count; @@ -257,17 +264,16 @@ open_buffer ( return retval; } -/* - * Return TRUE if "buf" points to a valid buffer (in the buffer list). - */ -int buf_valid(buf_T *buf) +/// Check that "buf" points to a valid buffer (in the buffer list). +bool buf_valid(buf_T *buf) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { FOR_ALL_BUFFERS(bp) { if (bp == buf) { - return TRUE; + return true; } } - return FALSE; + return false; } /* @@ -2055,16 +2061,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, } -/* - * Return true when "wip" has 'diff' set and the diff is only for another tab - * page. That's because a diff is local to a tab page. - */ +/// Check that "wip" has 'diff' set and the diff is only for another tab page. +/// That's because a diff is local to a tab page. static bool wininfo_other_tab_diff(wininfo_T *wip) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (wip->wi_opt.wo_diff) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - /* return false when it's a window in the current tab page, thus - * the buffer was in diff mode here */ + // return false when it's a window in the current tab page, thus + // the buffer was in diff mode here if (wip->wi_win == wp) { return false; } @@ -2421,52 +2426,62 @@ void buflist_altfpos(win_T *win) buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE); } -/* - * Return TRUE if 'ffname' is not the same file as current file. - * Fname must have a full path (expanded by path_get_absolute_path()). - */ -int otherfile(char_u *ffname) +/// Check that "ffname" is not the same file as current file. +/// Fname must have a full path (expanded by path_get_absolute_path()). +/// +/// @param ffname full path name to check +bool otherfile(char_u *ffname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return otherfile_buf(curbuf, ffname, NULL, false); } -static int otherfile_buf(buf_T *buf, char_u *ffname, - FileID *file_id_p, bool file_id_valid) +/// Check that "ffname" is not the same file as the file loaded in "buf". +/// Fname must have a full path (expanded by path_get_absolute_path()). +/// +/// @param buf buffer to check +/// @param ffname full path name to check +/// @param file_id_p information about the file at "ffname". +/// @param file_id_valid whether a valid "file_id_p" was passed in. +static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, + bool file_id_valid) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* no name is different */ + // no name is different if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) { - return TRUE; + return true; } if (fnamecmp(ffname, buf->b_ffname) == 0) { - return FALSE; + return false; } { FileID file_id; - /* If no struct stat given, get it now */ + // If no struct stat given, get it now if (file_id_p == NULL) { file_id_p = &file_id; file_id_valid = os_fileid((char *)ffname, file_id_p); } if (!file_id_valid) { // file_id not valid, assume files are different. - return TRUE; + return true; } - /* Use dev/ino to check if the files are the same, even when the names - * are different (possible with links). Still need to compare the - * name above, for when the file doesn't exist yet. - * Problem: The dev/ino changes when a file is deleted (and created - * again) and remains the same when renamed/moved. We don't want to - * stat() each buffer each time, that would be too slow. Get the - * dev/ino again when they appear to match, but not when they appear - * to be different: Could skip a buffer when it's actually the same - * file. */ + // Use dev/ino to check if the files are the same, even when the names + // are different (possible with links). Still need to compare the + // name above, for when the file doesn't exist yet. + // Problem: The dev/ino changes when a file is deleted (and created + // again) and remains the same when renamed/moved. We don't want to + // stat() each buffer each time, that would be too slow. Get the + // dev/ino again when they appear to match, but not when they appear + // to be different: Could skip a buffer when it's actually the same + // file. if (buf_same_file_id(buf, file_id_p)) { buf_set_file_id(buf); - if (buf_same_file_id(buf, file_id_p)) - return FALSE; + if (buf_same_file_id(buf, file_id_p)) { + return false; + } } } - return TRUE; + return true; } // Set file_id for a buffer. @@ -2483,11 +2498,14 @@ void buf_set_file_id(buf_T *buf) } } -// return TRUE if file_id in buffer "buf" matches with "file_id". +/// Check that file_id in buffer "buf" matches with "file_id". +/// +/// @param buf buffer +/// @param file_id file id static bool buf_same_file_id(buf_T *buf, FileID *file_id) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return buf->file_id_valid - && os_fileid_equal(&(buf->file_id), file_id); + return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id); } /* @@ -2770,23 +2788,28 @@ void maketitle(void) resettitle(); } -/* - * Used for title and icon: Check if "str" differs from "*last". Set "*last" - * from "str" if it does. - * Return TRUE when "*last" changed. - */ -static int ti_change(char_u *str, char_u **last) +/// Used for title and icon: Check if "str" differs from "*last". Set "*last" +/// from "str" if it does by freeing the old value of "*last" and duplicating +/// "str". +/// +/// @param str desired title string +/// @param[in,out] last current title string +// +/// @return true when "*last" changed. +static bool ti_change(char_u *str, char_u **last) + FUNC_ATTR_WARN_UNUSED_RESULT { if ((str == NULL) != (*last == NULL) || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) { xfree(*last); - if (str == NULL) + if (str == NULL) { *last = NULL; - else + } else { *last = vim_strsave(str); - return TRUE; + } + return true; } - return FALSE; + return false; } /* @@ -3014,7 +3037,7 @@ int build_stl_str_hl( && item[groupitem[groupdepth]].minwid == 0) { bool has_normal_items = false; for (long n = groupitem[groupdepth] + 1; n < curitem; n++) { - if (item[n].type == Normal) { + if (item[n].type == Normal || item[n].type == Highlight) { has_normal_items = true; break; } @@ -3922,26 +3945,29 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) : (int)(above * 100L / (above + below))); } -/* - * Append (file 2 of 8) to "buf[buflen]", if editing more than one file. - * Return TRUE if it was appended. - */ -static int -append_arg_number ( - win_T *wp, - char_u *buf, - int buflen, - int add_file /* Add "file" before the arg number */ -) +/// Append (file 2 of 8) to "buf[buflen]", if editing more than one file. +/// +/// @param wp window whose buffers to check +/// @param[in,out] buf string buffer to add the text to +/// @param buflen length of the string buffer +/// @param add_file if true, add "file" before the arg number +/// +/// @return true if it was appended. +static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) + FUNC_ATTR_NONNULL_ALL { - char_u *p; + // Nothing to do + if (ARGCOUNT <= 1) { + return false; + } + + char_u *p = buf + STRLEN(buf); // go to the end of the buffer - if (ARGCOUNT <= 1) /* nothing to do */ - return FALSE; + // Early out if the string is getting too long + if (p - buf + 35 >= buflen) { + return false; + } - p = buf + STRLEN(buf); /* go to the end of the buffer */ - if (p - buf + 35 >= buflen) /* getting too long */ - return FALSE; *p++ = ' '; *p++ = '('; if (add_file) { @@ -3949,9 +3975,10 @@ append_arg_number ( p += 5; } vim_snprintf((char *)p, (size_t)(buflen - (p - buf)), - wp->w_arg_idx_invalid ? "(%d) of %d)" - : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); - return TRUE; + wp->w_arg_idx_invalid + ? "(%d) of %d)" + : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); + return true; } /* @@ -4585,11 +4612,16 @@ char_u *buf_spname(buf_T *buf) return NULL; } -/* - * Find a window for buffer "buf". - * If found true is returned and "wp" and "tp" are set to the window and tabpage. - * If not found false is returned. - */ +/// Find a window for buffer "buf". +/// If found true is returned and "wp" and "tp" are set to +/// the window and tabpage. +/// If not found, false is returned. +/// +/// @param buf buffer to find a window for +/// @param[out] wp stores the found window +/// @param[out] tp stores the found tabpage +/// +/// @return true if a window was found for the buffer. bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) { *wp = NULL; @@ -5103,50 +5135,54 @@ void set_buflisted(int on) } } -/* - * Read the file for "buf" again and check if the contents changed. - * Return TRUE if it changed or this could not be checked. - */ -int buf_contents_changed(buf_T *buf) +/// Read the file for "buf" again and check if the contents changed. +/// Return true if it changed or this could not be checked. +/// +/// @param buf buffer to check +/// +/// @return true if the buffer's contents have changed +bool buf_contents_changed(buf_T *buf) + FUNC_ATTR_NONNULL_ALL { - buf_T *newbuf; - int differ = TRUE; - linenr_T lnum; - aco_save_T aco; - exarg_T ea; + bool differ = true; - /* Allocate a buffer without putting it in the buffer list. */ - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); - if (newbuf == NULL) - return TRUE; + // Allocate a buffer without putting it in the buffer list. + buf_T *newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); + if (newbuf == NULL) { + return true; + } - /* Force the 'fileencoding' and 'fileformat' to be equal. */ + // Force the 'fileencoding' and 'fileformat' to be equal. + exarg_T ea; prep_exarg(&ea, buf); - /* set curwin/curbuf to buf and save a few things */ + // set curwin/curbuf to buf and save a few things + aco_save_T aco; aucmd_prepbuf(&aco, newbuf); if (ml_open(curbuf) == OK && readfile(buf->b_ffname, buf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, - &ea, READ_NEW | READ_DUMMY) == OK) { - /* compare the two files line by line */ + (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, + &ea, READ_NEW | READ_DUMMY) == OK) { + // compare the two files line by line if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) { - differ = FALSE; - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) - if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) { - differ = TRUE; + differ = false; + for (linenr_T lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { + if (STRCMP(ml_get_buf(buf, lnum, false), ml_get(lnum)) != 0) { + differ = true; break; } + } } } xfree(ea.cmd); - /* restore curwin/curbuf and a few other things */ + // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); - if (curbuf != newbuf) /* safety check */ - wipe_buffer(newbuf, FALSE); + if (curbuf != newbuf) { // safety check + wipe_buffer(newbuf, false); + } return differ; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 936a14b903..86e63eb52c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -533,9 +533,9 @@ struct file_buffer { /* * Character table, only used in charset.c for 'iskeyword' - * 32 bytes of 8 bits: 1 bit per character 0-255. + * bitset with 4*64=256 bits: 1 bit per character 0-255. */ - char_u b_chartab[32]; + uint64_t b_chartab[4]; /* Table used for mappings local to a buffer. */ mapblock_T *(b_maphash[256]); diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 4e329b5cd8..83e2aaa6e6 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -32,16 +32,16 @@ #endif -static int chartab_initialized = FALSE; +static bool chartab_initialized = false; -// b_chartab[] is an array of 32 bytes, each bit representing one of the +// b_chartab[] is an array with 256 bits, each bit representing one of the // characters 0-255. #define SET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] |= (1 << ((c) & 0x7)) + (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] &= ~(1 << ((c) & 0x7)) + (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 3] & (1 << ((c) & 0x7))) + ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) /// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword /// characters for current buffer. @@ -69,12 +69,12 @@ static int chartab_initialized = FALSE; /// an error, OK otherwise. int init_chartab(void) { - return buf_init_chartab(curbuf, TRUE); + return buf_init_chartab(curbuf, true); } /// Helper for init_chartab /// -/// @param global FALSE: only set buf->b_chartab[] +/// @param global false: only set buf->b_chartab[] /// /// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has /// an error, OK otherwise. @@ -84,13 +84,13 @@ int buf_init_chartab(buf_T *buf, int global) int c2; char_u *p; int i; - int tilde; - int do_isalpha; + bool tilde; + bool do_isalpha; if (global) { // Set the default size for printable characters: // From <Space> to '~' is 1 (printable), others are 2 (not printable). - // This also inits all 'isident' and 'isfname' flags to FALSE. + // This also inits all 'isident' and 'isfname' flags to false. c = 0; while (c < ' ') { @@ -133,7 +133,7 @@ int buf_init_chartab(buf_T *buf, int global) } } - // Init word char flags all to FALSE + // Init word char flags all to false memset(buf->b_chartab, 0, (size_t)32); if (enc_dbcs != 0) { @@ -169,11 +169,11 @@ int buf_init_chartab(buf_T *buf, int global) } while (*p) { - tilde = FALSE; - do_isalpha = FALSE; + tilde = false; + do_isalpha = false; if ((*p == '^') && (p[1] != NUL)) { - tilde = TRUE; + tilde = true; ++p; } @@ -212,7 +212,7 @@ int buf_init_chartab(buf_T *buf, int global) // standard function isalpha(). This takes care of locale for // single-byte characters). if (c == '@') { - do_isalpha = TRUE; + do_isalpha = true; c = 1; c2 = 255; } else { @@ -231,7 +231,7 @@ int buf_init_chartab(buf_T *buf, int global) if (i == 0) { // (re)set ID flag if (tilde) { - chartab[c] &= ~CT_ID_CHAR; + chartab[c] &= (uint8_t)~CT_ID_CHAR; } else { chartab[c] |= CT_ID_CHAR; } @@ -244,18 +244,18 @@ int buf_init_chartab(buf_T *buf, int global) || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) && !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) { if (tilde) { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) - + ((dy_flags & DY_UHEX) ? 4 : 2); - chartab[c] &= ~CT_PRINT_CHAR; + chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + + ((dy_flags & DY_UHEX) ? 4 : 2)); + chartab[c] &= (uint8_t)~CT_PRINT_CHAR; } else { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) + 1; + chartab[c] = (uint8_t)((chartab[c] & ~CT_CELL_MASK) + 1); chartab[c] |= CT_PRINT_CHAR; } } } else if (i == 2) { // (re)set fname flag if (tilde) { - chartab[c] &= ~CT_FNAME_CHAR; + chartab[c] &= (uint8_t)~CT_FNAME_CHAR; } else { chartab[c] |= CT_FNAME_CHAR; } @@ -280,7 +280,7 @@ int buf_init_chartab(buf_T *buf, int global) } } } - chartab_initialized = TRUE; + chartab_initialized = true; return OK; } @@ -333,7 +333,8 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET { char_u *res; char_u *p; - int l, c; + int c; + size_t l; char_u hexbuf[11]; if (has_mbyte) { @@ -343,7 +344,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET p = s; while (*p != NUL) { - if ((l = (*mb_ptr2len)(p)) > 1) { + if ((l = (size_t)(*mb_ptr2len)(p)) > 1) { c = (*mb_ptr2char)(p); p += l; @@ -354,7 +355,7 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET len += STRLEN(hexbuf); } } else { - l = byte2cells(*p++); + l = (size_t)byte2cells(*p++); if (l > 0) { len += l; @@ -366,14 +367,14 @@ char_u *transstr(char_u *s) FUNC_ATTR_NONNULL_RET } res = xmallocz(len); } else { - res = xmallocz(vim_strsize(s)); + res = xmallocz((size_t)vim_strsize(s)); } *res = NUL; p = s; while (*p != NUL) { - if (has_mbyte && ((l = (*mb_ptr2len)(p)) > 1)) { + if (has_mbyte && ((l = (size_t)(*mb_ptr2len)(p)) > 1)) { c = (*mb_ptr2char)(p); if (vim_isprintc(c)) { @@ -477,9 +478,9 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) i += (*mb_ptr2len)(STR_PTR(i)); } else { if (buf == NULL) { - GA_CHAR(i) = TOLOWER_LOC(GA_CHAR(i)); + GA_CHAR(i) = (char_u)TOLOWER_LOC(GA_CHAR(i)); } else { - buf[i] = TOLOWER_LOC(buf[i]); + buf[i] = (char_u)TOLOWER_LOC(buf[i]); } ++i; } @@ -493,7 +494,7 @@ char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) // Catch 22: chartab[] can't be initialized before the options are // initialized, and initializing options may cause transchar() to be called! -// When chartab_initialized == FALSE don't use chartab[]. +// When chartab_initialized == false don't use chartab[]. // Does NOT work for multi-byte characters, c must be <= 255. // Also doesn't work for the first byte of a multi-byte, "c" must be a // character! @@ -518,7 +519,7 @@ char_u* transchar(int c) if ((!chartab_initialized && (((c >= ' ') && (c <= '~')) || F_ischar(c))) || ((c < 256) && vim_isprintc_strict(c))) { // printable character - transchar_buf[i] = c; + transchar_buf[i] = (char_u)c; transchar_buf[i + 1] = NUL; } else { transchar_nonprint(transchar_buf + i, c); @@ -564,7 +565,7 @@ void transchar_nonprint(char_u *buf, int c) // 0x00 - 0x1f and 0x7f buf[0] = '^'; // DEL displayed as ^? - buf[1] = c ^ 0x40; + buf[1] = (char_u)(c ^ 0x40); buf[2] = NUL; } else if (enc_utf8 && (c >= 0x80)) { @@ -572,12 +573,12 @@ void transchar_nonprint(char_u *buf, int c) } else if ((c >= ' ' + 0x80) && (c <= '~' + 0x80)) { // 0xa0 - 0xfe buf[0] = '|'; - buf[1] = c - 0x80; + buf[1] = (char_u)(c - 0x80); buf[2] = NUL; } else { // 0x80 - 0x9f and 0xff buf[0] = '~'; - buf[1] = (c - 0x80) ^ 0x40; + buf[1] = (char_u)((c - 0x80) ^ 0x40); buf[2] = NUL; } } @@ -592,11 +593,11 @@ void transchar_hex(char_u *buf, int c) buf[0] = '<'; if (c > 255) { - buf[++i] = nr2hex((unsigned)c >> 12); - buf[++i] = nr2hex((unsigned)c >> 8); + buf[++i] = (char_u)nr2hex((unsigned)c >> 12); + buf[++i] = (char_u)nr2hex((unsigned)c >> 8); } - buf[++i] = nr2hex((unsigned)c >> 4); - buf[++i] = nr2hex((unsigned)c); + buf[++i] = (char_u)(nr2hex((unsigned)c >> 4)); + buf[++i] = (char_u)(nr2hex((unsigned)c)); buf[++i] = '>'; buf[++i] = NUL; } @@ -734,9 +735,8 @@ int vim_strnsize(char_u *s, int len) /// @return Number of characters. #define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \ - int ts; \ - ts = (buf)->b_p_ts; \ - return (int)(ts - (col % ts)); \ + const int ts = (int) (buf)->b_p_ts; \ + return (ts - (int)(col % ts)); \ } else { \ return ptr2cells(p); \ } @@ -1137,7 +1137,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) int n; if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) { - n = wp->w_buffer->b_p_ts; + n = (int)wp->w_buffer->b_p_ts; return n - (col % n); } n = ptr2cells(s); @@ -1205,11 +1205,11 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, char_u *line; // start of the line int incr; int head; - int ts = wp->w_buffer->b_p_ts; + int ts = (int)wp->w_buffer->b_p_ts; int c; vcol = 0; - line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col == MAXCOL) { // continue until the NUL @@ -1329,7 +1329,7 @@ colnr_T getvcol_nolist(pos_T *posp) int list_save = curwin->w_p_list; colnr_T vcol; - curwin->w_p_list = FALSE; + curwin->w_p_list = false; getvcol(curwin, posp, NULL, &vcol, NULL); curwin->w_p_list = list_save; return vcol; @@ -1358,7 +1358,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, endadd = 0; // Cannot put the cursor on part of a wide character. - ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col < (colnr_T)STRLEN(ptr)) { int c = (*mb_ptr2char)(ptr + pos->col); @@ -1595,7 +1595,7 @@ bool vim_islower(int c) if (c >= 0x100) { if (has_mbyte) { - return iswlower(c); + return iswlower((wint_t)c); } // islower() can't handle these chars and may crash @@ -1626,7 +1626,7 @@ bool vim_isupper(int c) if (c >= 0x100) { if (has_mbyte) { - return iswupper(c); + return iswupper((wint_t)c); } // isupper() can't handle these chars and may crash @@ -1653,7 +1653,7 @@ int vim_toupper(int c) if (c >= 0x100) { if (has_mbyte) { - return towupper(c); + return (int)towupper((wint_t)c); } // toupper() can't handle these chars and may crash @@ -1680,7 +1680,7 @@ int vim_tolower(int c) if (c >= 0x100) { if (has_mbyte) { - return towlower(c); + return (int)towlower((wint_t)c); } // tolower() can't handle these chars and may crash diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0be8b3c514..1149ca1e62 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -819,8 +819,8 @@ static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff) (diff_flags & DIFF_IWHITE) ? "-b " : "", (diff_flags & DIFF_ICASE) ? "-i " : "", tmp_orig, tmp_new); - append_redir(cmd, (int)len, p_srr, tmp_diff); - block_autocmds(); /* Avoid ShellCmdPost stuff */ + append_redir(cmd, len, p_srr, tmp_diff); + block_autocmds(); // Avoid ShellCmdPost stuff (void)call_shell( cmd, kShellOptFilter | kShellOptSilent | kShellOptDoOut, diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 6313ea2e81..614a5d43be 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -165,10 +165,6 @@ static int compl_restarting = FALSE; /* don't insert match */ * FALSE the word to be completed must be located. */ static int compl_started = FALSE; -/* Set when doing something for completion that may call edit() recursively, - * which is not allowed. */ -static int compl_busy = FALSE; - static int compl_matches = 0; static char_u *compl_pattern = NULL; static int compl_direction = FORWARD; @@ -887,7 +883,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_T: // Make indent one shiftwidth greater. if (s->c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { - if (has_compl_option(false)) { + if (check_compl_option(false)) { insert_do_complete(s); } break; @@ -1102,7 +1098,7 @@ static int insert_handle_key(InsertState *s) case Ctrl_K: // digraph or keyword completion if (ctrl_x_mode == CTRL_X_DICTIONARY) { - if (has_compl_option(true)) { + if (check_compl_option(true)) { insert_do_complete(s); } break; @@ -1264,30 +1260,28 @@ static void insert_do_cindent(InsertState *s) } } -/* - * edit(): Start inserting text. - * - * "cmdchar" can be: - * 'i' normal insert command - * 'a' normal append command - * 'R' replace command - * 'r' "r<CR>" command: insert one <CR>. Note: count can be > 1, for redo, - * but still only one <CR> is inserted. The <Esc> is not used for redo. - * 'g' "gI" command. - * 'V' "gR" command for Virtual Replace mode. - * 'v' "gr" command for single character Virtual Replace mode. - * - * This function is not called recursively. For CTRL-O commands, it returns - * and lets the caller handle the Normal-mode command. - * - * Return TRUE if a CTRL-O command caused the return (insert mode pending). - */ -int -edit ( - int cmdchar, - int startln, /* if set, insert at start of line */ - long count -) +/// edit(): Start inserting text. +/// +/// "cmdchar" can be: +/// 'i' normal insert command +/// 'a' normal append command +/// 'R' replace command +/// 'r' "r<CR>" command: insert one <CR>. +/// Note: count can be > 1, for redo, but still only one <CR> is inserted. +/// <Esc> is not used for redo. +/// 'g' "gI" command. +/// 'V' "gR" command for Virtual Replace mode. +/// 'v' "gr" command for single character Virtual Replace mode. +/// +/// This function is not called recursively. For CTRL-O commands, it returns +/// and lets the caller handle the Normal-mode command. +/// +/// @param cmdchar command that started the insert +/// @param startln if true, insert at start of line +/// @param count repeat count for the command +/// +/// @return true if a CTRL-O command caused the return (insert mode pending). +bool edit(int cmdchar, bool startln, long count) { if (curbuf->terminal) { if (ex_normal_busy) { @@ -1795,33 +1789,37 @@ void backspace_until_column(int col) } } -/* - * Like del_char(), but make sure not to go before column "limit_col". - * Only matters when there are composing characters. - * Return TRUE when something was deleted. - */ -static int del_char_after_col(int limit_col) +/// Like del_char(), but make sure not to go before column "limit_col". +/// Only matters when there are composing characters. +/// +/// @param limit_col only delete the character if it is after this column +// +/// @return true when something was deleted. +static bool del_char_after_col(int limit_col) { if (enc_utf8 && limit_col >= 0) { colnr_T ecol = curwin->w_cursor.col + 1; - /* Make sure the cursor is at the start of a character, but - * skip forward again when going too far back because of a - * composing character. */ + // Make sure the cursor is at the start of a character, but + // skip forward again when going too far back because of a + // composing character. mb_adjust_cursor(); while (curwin->w_cursor.col < (colnr_T)limit_col) { int l = utf_ptr2len(get_cursor_pos_ptr()); - if (l == 0) /* end of line */ + if (l == 0) { // end of line break; + } curwin->w_cursor.col += l; } - if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) - return FALSE; - del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE); - } else - (void)del_char(FALSE); - return TRUE; + if (*get_cursor_pos_ptr() == NUL || curwin->w_cursor.col == ecol) { + return false; + } + del_bytes((long)(ecol - curwin->w_cursor.col), false, true); + } else { + del_char(false); + } + return true; } /* @@ -1846,47 +1844,48 @@ static void ins_ctrl_x(void) } } -/* - * Return TRUE if the 'dict' or 'tsr' option can be used. - */ -static int has_compl_option(int dict_opt) +/// Check that the "dict" or "tsr" option can be used. +/// +/// @param dict_opt check "dict" when true, "tsr" when false. +static bool check_compl_option(bool dict_opt) { - if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL - && !curwin->w_p_spell - ) + if (dict_opt + ? (*curbuf->b_p_dict == NUL && *p_dict == NUL && !curwin->w_p_spell) : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) { ctrl_x_mode = 0; edit_submode = NULL; msg_attr(dict_opt ? (char_u *)_("'dictionary' option is empty") - : (char_u *)_("'thesaurus' option is empty"), - hl_attr(HLF_E)); + : (char_u *)_("'thesaurus' option is empty"), hl_attr(HLF_E)); if (emsg_silent == 0) { vim_beep(BO_COMPL); setcursor(); ui_flush(); os_delay(2000L, false); } - return FALSE; + return false; } - return TRUE; + return true; } -/* - * Is the character 'c' a valid key to go to or keep us in CTRL-X mode? - * This depends on the current mode. - */ -int vim_is_ctrl_x_key(int c) +/// Check that the character "c" a valid key to go to or keep us in CTRL-X mode? +/// This depends on the current mode. +/// +/// @param c character to check +bool vim_is_ctrl_x_key(int c) + FUNC_ATTR_WARN_UNUSED_RESULT { - /* Always allow ^R - let it's results then be checked */ - if (c == Ctrl_R) - return TRUE; + // Always allow ^R - let its results then be checked + if (c == Ctrl_R) { + return true; + } - /* Accept <PageUp> and <PageDown> if the popup menu is visible. */ - if (ins_compl_pum_key(c)) - return TRUE; + // Accept <PageUp> and <PageDown> if the popup menu is visible. + if (ins_compl_pum_key(c)) { + return true; + } switch (ctrl_x_mode) { - case 0: /* Not in any CTRL-X mode */ + case 0: // Not in any CTRL-X mode return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; case CTRL_X_NOT_DEFINED_YET: return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E @@ -1924,35 +1923,37 @@ int vim_is_ctrl_x_key(int c) return (c == Ctrl_P || c == Ctrl_N); } EMSG(_(e_internal)); - return FALSE; + return false; } -/* - * Return TRUE when character "c" is part of the item currently being - * completed. Used to decide whether to abandon complete mode when the menu - * is visible. - */ -static int ins_compl_accept_char(int c) +/// Check that character "c" is part of the item currently being +/// completed. Used to decide whether to abandon complete mode when the menu +/// is visible. +/// +/// @param c character to check +static bool ins_compl_accept_char(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - if (ctrl_x_mode & CTRL_X_WANT_IDENT) - /* When expanding an identifier only accept identifier chars. */ + if (ctrl_x_mode & CTRL_X_WANT_IDENT) { + // When expanding an identifier only accept identifier chars. return vim_isIDc(c); + } switch (ctrl_x_mode) { case CTRL_X_FILES: - /* When expanding file name only accept file name chars. But not - * path separators, so that "proto/<Tab>" expands files in - * "proto", not "proto/" as a whole */ + // When expanding file name only accept file name chars. But not + // path separators, so that "proto/<Tab>" expands files in + // "proto", not "proto/" as a whole return vim_isfilec(c) && !vim_ispathsep(c); case CTRL_X_CMDLINE: case CTRL_X_OMNI: - /* Command line and Omni completion can work with just about any - * printable character, but do stop at white space. */ + // Command line and Omni completion can work with just about any + // printable character, but do stop at white space. return vim_isprintc(c) && !ascii_iswhite(c); case CTRL_X_WHOLE_LINE: - /* For while line completion a space can be part of the line. */ + // For while line completion a space can be part of the line. return vim_isprintc(c); } return vim_iswordc(c); @@ -2197,15 +2198,19 @@ ins_compl_add ( return OK; } -/* - * Return TRUE if "str[len]" matches with match->cp_str, considering - * match->cp_icase. - */ -static int ins_compl_equal(compl_T *match, char_u *str, int len) +/// Check that "str[len]" matches with "match->cp_str", considering +/// "match->cp_icase". +/// +/// @param match completion match +/// @param str character string to check +/// @param len lenth of "str" +static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (match->cp_icase) - return STRNICMP(match->cp_str, str, (size_t)len) == 0; - return STRNCMP(match->cp_str, str, (size_t)len) == 0; + if (match->cp_icase) { + return STRNICMP(match->cp_str, str, len) == 0; + } + return STRNCMP(match->cp_str, str, len) == 0; } /* @@ -2353,13 +2358,13 @@ void set_completion(colnr_T startcol, list_T *list) int save_w_wrow = curwin->w_wrow; compl_curr_match = compl_first_match; - if (compl_no_insert) { + if (compl_no_insert || compl_no_select) { ins_complete(K_DOWN, false); - } else { - ins_complete(Ctrl_N, false); if (compl_no_select) { - ins_complete(Ctrl_P, false); + ins_complete(K_UP, false); } + } else { + ins_complete(Ctrl_N, false); } // Lazily show the popup menu, unless we got interrupted. @@ -2403,44 +2408,33 @@ static void ins_compl_del_pum(void) } } -/* - * Return TRUE if the popup menu should be displayed. - */ -static int pum_wanted(void) +/// Check if the popup menu should be displayed. +static bool pum_wanted(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* 'completeopt' must contain "menu" or "menuone" */ - if (vim_strchr(p_cot, 'm') == NULL) - return FALSE; - - /* The display looks bad on a B&W display. */ - if (t_colors < 8 - ) - return FALSE; - return TRUE; + // "completeopt" must contain "menu" or "menuone" + return vim_strchr(p_cot, 'm') != NULL; } -/* - * Return TRUE if there are two or more matches to be shown in the popup menu. - * One if 'completopt' contains "menuone". - */ -static int pum_enough_matches(void) +/// Check that there are two or more matches to be shown in the popup menu. +/// One if "completopt" contains "menuone". +static bool pum_enough_matches(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - compl_T *compl; - int i; - - /* Don't display the popup menu if there are no matches or there is only - * one (ignoring the original text). */ - compl = compl_first_match; - i = 0; + // Don't display the popup menu if there are no matches or there is only + // one (ignoring the original text). + compl_T *comp = compl_first_match; + int i = 0; do { - if (compl == NULL - || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) + if (comp == NULL || ((comp->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) { break; - compl = compl->cp_next; - } while (compl != compl_first_match); + } + comp = comp->cp_next; + } while (comp != compl_first_match); - if (strstr((char *)p_cot, "menuone") != NULL) + if (strstr((char *)p_cot, "menuone") != NULL) { return i >= 1; + } return i >= 2; } @@ -2871,10 +2865,9 @@ static void ins_compl_clear(void) set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); } -/* - * Return TRUE when Insert completion is active. - */ -int ins_compl_active(void) +/// Check that Insert completion is active. +bool ins_compl_active(void) + FUNC_ATTR_PURE { return compl_started; } @@ -2918,14 +2911,13 @@ static int ins_compl_bs(void) return NUL; } -/* - * Return TRUE when we need to find matches again, ins_compl_restart() is to - * be called. - */ -static int ins_compl_need_restart(void) +/// Check that we need to find matches again, ins_compl_restart() is to +/// be called. +static bool ins_compl_need_restart(void) + FUNC_ATTR_PURE { - /* Return TRUE if we didn't complete finding matches or when the - * 'completefunc' returned "always" in the "refresh" dictionary item. */ + // Return true if we didn't complete finding matches or when the + // "completefunc" returned "always" in the "refresh" dictionary item. return compl_was_interrupted || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) && compl_opt_refresh_always); @@ -3073,16 +3065,16 @@ static void ins_compl_addfrommatch(void) ins_compl_addleader(c); } -/* - * Prepare for Insert mode completion, or stop it. - * Called just after typing a character in Insert mode. - * Returns TRUE when the character is not to be inserted; - */ -static int ins_compl_prep(int c) +/// Prepare for Insert mode completion, or stop it. +/// Called just after typing a character in Insert mode. +/// +/// @param c character that was typed +/// +/// @return true when the character is not to be inserted; +static bool ins_compl_prep(int c) { - char_u *ptr; - int want_cindent; - int retval = FALSE; + char_u *ptr; + bool retval = false; /* Forget any previous 'special' messages if this is actually * a ^X mode key - bar ^R, in which case we wait to see what it gives us. @@ -3092,8 +3084,10 @@ static int ins_compl_prep(int c) /* Ignore end of Select mode mapping and mouse scroll buttons. */ if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT) + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT + || c == K_FOCUSGAINED || c == K_FOCUSLOST) { return retval; + } /* Set "compl_get_longest" when finding the first matches. */ if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET @@ -3247,11 +3241,9 @@ static int ins_compl_prep(int c) ins_compl_fixRedoBufForLeader(ptr); } - want_cindent = (can_cindent && cindent_on()); - /* - * When completing whole lines: fix indent for 'cindent'. - * Otherwise, break line if it's too long. - */ + bool want_cindent = (can_cindent && cindent_on()); + // When completing whole lines: fix indent for 'cindent'. + // Otherwise, break line if it's too long. if (compl_cont_mode == CTRL_X_WHOLE_LINE) { /* re-indent the current line */ if (want_cindent) { @@ -3271,22 +3263,24 @@ static int ins_compl_prep(int c) inc_cursor(); } - /* If the popup menu is displayed pressing CTRL-Y means accepting - * the selection without inserting anything. When - * compl_enter_selects is set the Enter key does the same. */ + // If the popup menu is displayed pressing CTRL-Y means accepting + // the selection without inserting anything. When + // compl_enter_selects is set the Enter key does the same. if ((c == Ctrl_Y || (compl_enter_selects && (c == CAR || c == K_KENTER || c == NL))) - && pum_visible()) - retval = TRUE; + && pum_visible()) { + retval = true; + } /* CTRL-E means completion is Ended, go back to the typed text. */ if (c == Ctrl_E) { ins_compl_delete(); - if (compl_leader != NULL) + if (compl_leader != NULL) { ins_bytes(compl_leader + ins_compl_len()); - else if (compl_first_match != NULL) + } else if (compl_first_match != NULL) { ins_bytes(compl_orig_text + ins_compl_len()); - retval = TRUE; + } + retval = true; } auto_format(FALSE, TRUE); @@ -4245,22 +4239,22 @@ void ins_compl_check_keys(int frequency) static int ins_compl_key2dir(int c) { if (c == Ctrl_P || c == Ctrl_L - || (pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP - || c == K_S_UP || c == K_UP))) + || c == K_PAGEUP || c == K_KPAGEUP + || c == K_S_UP || c == K_UP) { return BACKWARD; + } return FORWARD; } -/* - * Return TRUE for keys that are used for completion only when the popup menu - * is visible. - */ -static int ins_compl_pum_key(int c) +/// Check that "c" is a valid completion key only while the popup menu is shown +/// +/// @param c character to check +static bool ins_compl_pum_key(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP - || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == - K_S_DOWN - || c == K_UP || c == K_DOWN); + || c == K_PAGEDOWN || c == K_KPAGEDOWN + || c == K_S_DOWN || c == K_UP || c == K_DOWN); } /* @@ -4280,11 +4274,12 @@ static int ins_compl_key2count(int c) return 1; } -/* - * Return TRUE if completion with "c" should insert the match, FALSE if only - * to change the currently selected completion. - */ -static int ins_compl_use_match(int c) +/// Check that completion with "c" should insert the match, false if only +/// to change the currently selected completion. +/// +/// @param c character to check +static bool ins_compl_use_match(int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { switch (c) { case K_UP: @@ -4295,9 +4290,9 @@ static int ins_compl_use_match(int c) case K_PAGEUP: case K_KPAGEUP: case K_S_UP: - return FALSE; + return false; } - return TRUE; + return true; } /* @@ -6313,18 +6308,22 @@ char_u *get_last_insert_save(void) return s; } -/* - * Check the word in front of the cursor for an abbreviation. - * Called when the non-id character "c" has been entered. - * When an abbreviation is recognized it is removed from the text and - * the replacement string is inserted in typebuf.tb_buf[], followed by "c". - */ -static int echeck_abbr(int c) +/// Check the word in front of the cursor for an abbreviation. +/// Called when the non-id character "c" has been entered. +/// When an abbreviation is recognized it is removed from the text and +/// the replacement string is inserted in typebuf.tb_buf[], followed by "c". +/// +/// @param c character +/// +/// @return true if the word is a known abbreviation. +static bool echeck_abbr(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - /* Don't check for abbreviation in paste mode, when disabled and just - * after moving around with cursor keys. */ - if (p_paste || no_abbr || arrow_used) - return FALSE; + // Don't check for abbreviation in paste mode, when disabled and just + // after moving around with cursor keys. + if (p_paste || no_abbr || arrow_used) { + return false; + } return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col, curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); @@ -6560,13 +6559,11 @@ static void replace_do_bs(int limit_col) (void)del_char_after_col(limit_col); } -/* - * Return TRUE if C-indenting is on. - */ -static int cindent_on(void) { - return !p_paste && (curbuf->b_p_cin - || *curbuf->b_p_inde != NUL - ); +/// Check that C-indenting is on. +static bool cindent_on(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL); } /* @@ -6596,32 +6593,33 @@ void fix_indent(void) { do_c_expr_indent(); } -/* - * return TRUE if 'cinkeys' contains the key "keytyped", - * when == '*': Only if key is preceded with '*' (indent before insert) - * when == '!': Only if key is preceded with '!' (don't insert) - * when == ' ': Only if key is not preceded with '*'(indent afterwards) - * - * "keytyped" can have a few special values: - * KEY_OPEN_FORW - * KEY_OPEN_BACK - * KEY_COMPLETE just finished completion. - * - * If line_is_empty is TRUE accept keys with '0' before them. - */ -int in_cinkeys(int keytyped, int when, int line_is_empty) +/// Check that "cinkeys" contains the key "keytyped", +/// when == '*': Only if key is preceded with '*' (indent before insert) +/// when == '!': Only if key is preceded with '!' (don't insert) +/// when == ' ': Only if key is not preceded with '*' (indent afterwards) +/// +/// "keytyped" can have a few special values: +/// KEY_OPEN_FORW : +/// KEY_OPEN_BACK : +/// KEY_COMPLETE : Just finished completion. +/// +/// @param keytyped key that was typed +/// @param when condition on when to perform the check +/// @param line_is_empty when true, accept keys with '0' before them. +bool in_cinkeys(int keytyped, int when, bool line_is_empty) { - char_u *look; + char_u *look; int try_match; int try_match_word; - char_u *p; - char_u *line; + char_u *p; + char_u *line; int icase; int i; - if (keytyped == NUL) - /* Can happen with CTRL-Y and CTRL-E on a short line. */ - return FALSE; + if (keytyped == NUL) { + // Can happen with CTRL-Y and CTRL-E on a short line. + return false; + } if (*curbuf->b_p_inde != NUL) look = curbuf->b_p_indk; /* 'indentexpr' set: use 'indentkeys' */ @@ -6637,68 +6635,64 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) case '!': try_match = (*look == '!'); break; default: try_match = (*look != '*'); break; } - if (*look == '*' || *look == '!') - ++look; + if (*look == '*' || *look == '!') { + look++; + } - /* - * If there is a '0', only accept a match if the line is empty. - * But may still match when typing last char of a word. - */ + // If there is a '0', only accept a match if the line is empty. + // But may still match when typing last char of a word. if (*look == '0') { try_match_word = try_match; - if (!line_is_empty) - try_match = FALSE; - ++look; - } else - try_match_word = FALSE; + if (!line_is_empty) { + try_match = false; + } + look++; + } else { + try_match_word = false; + } - /* - * does it look like a control character? - */ - if (*look == '^' - && look[1] >= '?' && look[1] <= '_' - ) { - if (try_match && keytyped == Ctrl_chr(look[1])) - return TRUE; + // Does it look like a control character? + if (*look == '^' && look[1] >= '?' && look[1] <= '_') { + if (try_match && keytyped == Ctrl_chr(look[1])) { + return true; + } look += 2; - } - /* - * 'o' means "o" command, open forward. - * 'O' means "O" command, open backward. - */ - else if (*look == 'o') { - if (try_match && keytyped == KEY_OPEN_FORW) - return TRUE; - ++look; + + // 'o' means "o" command, open forward. + // 'O' means "O" command, open backward. + } else if (*look == 'o') { + if (try_match && keytyped == KEY_OPEN_FORW) { + return true; + } + look++; } else if (*look == 'O') { - if (try_match && keytyped == KEY_OPEN_BACK) - return TRUE; - ++look; - } - /* - * 'e' means to check for "else" at start of line and just before the - * cursor. - */ - else if (*look == 'e') { + if (try_match && keytyped == KEY_OPEN_BACK) { + return true; + } + look++; + + // 'e' means to check for "else" at start of line and just before the + // cursor. + } else if (*look == 'e') { if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { p = get_cursor_line_ptr(); if (skipwhite(p) == p + curwin->w_cursor.col - 4 && - STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) - return TRUE; + STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) { + return true; + } } - ++look; - } - /* - * ':' only causes an indent if it is at the end of a label or case - * statement, or when it was before typing the ':' (to fix - * class::method for C++). - */ - else if (*look == ':') { + look++; + + // ':' only causes an indent if it is at the end of a label or case + // statement, or when it was before typing the ':' (to fix + // class::method for C++). + } else if (*look == ':') { if (try_match && keytyped == ':') { p = get_cursor_line_ptr(); - if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) - return TRUE; - /* Need to get the line again after cin_islabel(). */ + if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) { + return true; + } + // Need to get the line again after cin_islabel(). p = get_cursor_line_ptr(); if (curwin->w_cursor.col > 2 && p[curwin->w_cursor.col - 1] == ':' @@ -6708,28 +6702,27 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) || cin_islabel()); p = get_cursor_line_ptr(); p[curwin->w_cursor.col - 1] = ':'; - if (i) - return TRUE; + if (i) { + return true; + } } } - ++look; - } - /* - * Is it a key in <>, maybe? - */ - else if (*look == '<') { + look++; + + // Is it a key in <>, maybe? + } else if (*look == '<') { if (try_match) { - /* - * make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, - * <:> and <!> so that people can re-indent on o, O, e, 0, <, - * >, *, : and ! keys if they really really want to. - */ + // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, + // <:> and <!> so that people can re-indent on o, O, e, 0, <, + // >, *, : and ! keys if they really really want to. if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL - && keytyped == look[1]) - return TRUE; + && keytyped == look[1]) { + return true; + } - if (keytyped == get_special_key_code(look + 1)) - return TRUE; + if (keytyped == get_special_key_code(look + 1)) { + return true; + } } while (*look && *look != '>') look++; @@ -6800,18 +6793,18 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) (int)(curwin->w_cursor.col - (p - look))) match = FALSE; } - if (match) - return TRUE; + if (match) { + return true; + } } look = p; - } - /* - * ok, it's a boring generic character. - */ - else { - if (try_match && *look == keytyped) - return TRUE; - ++look; + + // Ok, it's a boring generic character. + } else { + if (try_match && *look == keytyped) { + return true; + } + look++; } /* @@ -6819,7 +6812,7 @@ int in_cinkeys(int keytyped, int when, int line_is_empty) */ look = skip_to_option_part(look); } - return FALSE; + return false; } /* @@ -7046,27 +7039,24 @@ static void ins_ctrl_hat(void) status_redraw_curbuf(); } -/* - * Handle ESC in insert mode. - * Returns TRUE when leaving insert mode, FALSE when going to repeat the - * insert. - */ -static int -ins_esc ( - long *count, - int cmdchar, - int nomove /* don't move cursor */ -) +/// Handle ESC in insert mode. +/// +/// @param[in,out] count repeat count of the insert command +/// @param cmdchar command that started the insert +/// @param nomove when true, don't move the cursor +/// +/// @return true when leaving insert mode, false when repeating the insert. +static bool ins_esc(long *count, int cmdchar, bool nomove) + FUNC_ATTR_NONNULL_ARG(1) { - int temp; - static int disabled_redraw = FALSE; + static bool disabled_redraw = false; check_spell_redraw(); - temp = curwin->w_cursor.col; + int temp = curwin->w_cursor.col; if (disabled_redraw) { - --RedrawingDisabled; - disabled_redraw = FALSE; + RedrawingDisabled--; + disabled_redraw = false; } if (!arrow_used) { /* @@ -7096,9 +7086,10 @@ ins_esc ( if (cmdchar == 'r' || cmdchar == 'v') { stuffRedoReadbuff(ESC_STR); // No ESC in redo buffer } - ++RedrawingDisabled; - disabled_redraw = TRUE; - return FALSE; /* repeat the insert */ + RedrawingDisabled++; + disabled_redraw = true; + // Repeat the insert + return false; } stop_insert(&curwin->w_cursor, TRUE, nomove); undisplay_dollar(); @@ -7148,16 +7139,15 @@ ins_esc ( setmouse(); ui_cursor_shape(); /* may show different cursor shape */ - /* - * When recording or for CTRL-O, need to display the new mode. - * Otherwise remove the mode message. - */ - if (Recording || restart_edit != NUL) + // When recording or for CTRL-O, need to display the new mode. + // Otherwise remove the mode message. + if (Recording || restart_edit != NUL) { showmode(); - else if (p_smd) + } else if (p_smd) { MSG(""); - - return TRUE; /* exit Insert mode */ + } + // Exit Insert mode + return true; } /* @@ -7195,14 +7185,16 @@ static void ins_ctrl_(void) showmode(); } -/* - * If 'keymodel' contains "startsel", may start selection. - * Returns TRUE when a CTRL-O and other keys stuffed. - */ -static int ins_start_select(int c) +/// If 'keymodel' contains "startsel", may start selection. +/// +/// @param c character to check +// +/// @return true when a CTRL-O and other keys stuffed. +static bool ins_start_select(int c) + FUNC_ATTR_WARN_UNUSED_RESULT { if (!km_startsel) { - return FALSE; + return false; } switch (c) { case K_KHOME: @@ -7213,32 +7205,27 @@ static int ins_start_select(int c) case K_KPAGEDOWN: if (!(mod_mask & MOD_MASK_SHIFT)) break; - /* FALLTHROUGH */ + // FALLTHROUGH case K_S_LEFT: case K_S_RIGHT: case K_S_UP: case K_S_DOWN: case K_S_END: case K_S_HOME: - /* Start selection right away, the cursor can move with - * CTRL-O when beyond the end of the line. */ + // Start selection right away, the cursor can move with + // CTRL-O when beyond the end of the line. start_selection(); - /* Execute the key in (insert) Select mode. */ + // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4]; - - buf[0] = K_SPECIAL; - buf[1] = KS_MODIFIER; - buf[2] = mod_mask; - buf[3] = NUL; + char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); - return TRUE; + return true; } - return FALSE; + return false; } /* @@ -7362,18 +7349,23 @@ static void ins_bs_one(colnr_T *vcolp) (void)del_char(FALSE); } -/* - * Handle Backspace, delete-word and delete-line in Insert mode. - * Return TRUE when backspace was actually used. - */ -static int ins_bs(int c, int mode, int *inserted_space_p) +/// Handle Backspace, delete-word and delete-line in Insert mode. +/// +/// @param c charcter that was typed +/// @param mode backspace mode to use +/// @param[in,out] inserted_space_p whether a space was the last +// character inserted +/// +/// @return true when backspace was actually used. +static bool ins_bs(int c, int mode, int *inserted_space_p) + FUNC_ATTR_NONNULL_ARG(3) { linenr_T lnum; int cc; int temp = 0; /* init for GCC */ colnr_T save_col; colnr_T mincol; - int did_backspace = FALSE; + bool did_backspace = false; int in_indent; int oldState; int cpc[MAX_MCO]; /* composing characters */ @@ -7399,28 +7391,29 @@ static int ins_bs(int c, int mode, int *inserted_space_p) return false; } - if (stop_arrow() == FAIL) - return FALSE; + if (stop_arrow() == FAIL) { + return false; + } in_indent = inindent(0); - if (in_indent) - can_cindent = FALSE; - end_comment_pending = NUL; /* After BS, don't auto-end comment */ - if (revins_on) /* put cursor after last inserted char */ + if (in_indent) { + can_cindent = false; + } + end_comment_pending = NUL; // After BS, don't auto-end comment + if (revins_on) { // put cursor after last inserted char inc_cursor(); - - /* Virtualedit: - * BACKSPACE_CHAR eats a virtual space - * BACKSPACE_WORD eats all coladd - * BACKSPACE_LINE eats all coladd and keeps going - */ + } + // Virtualedit: + // BACKSPACE_CHAR eats a virtual space + // BACKSPACE_WORD eats all coladd + // BACKSPACE_LINE eats all coladd and keeps going if (curwin->w_cursor.coladd > 0) { if (mode == BACKSPACE_CHAR) { - --curwin->w_cursor.coladd; - return TRUE; + curwin->w_cursor.coladd--; + return true; } if (mode == BACKSPACE_WORD) { curwin->w_cursor.coladd = 0; - return TRUE; + return true; } curwin->w_cursor.coladd = 0; } @@ -7432,10 +7425,10 @@ static int ins_bs(int c, int mode, int *inserted_space_p) lnum = Insstart.lnum; if (curwin->w_cursor.lnum == lnum || revins_on) { if (u_save((linenr_T)(curwin->w_cursor.lnum - 2), - (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { - return FALSE; + (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { + return false; } - --Insstart.lnum; + Insstart.lnum--; Insstart.col = MAXCOL; } /* @@ -7677,12 +7670,12 @@ static int ins_bs(int c, int mode, int *inserted_space_p) if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1) dollar_vcol = curwin->w_virtcol; - /* When deleting a char the cursor line must never be in a closed fold. - * E.g., when 'foldmethod' is indent and deleting the first non-white - * char before a Tab. */ - if (did_backspace) + // When deleting a char the cursor line must never be in a closed fold. + // E.g., when 'foldmethod' is indent and deleting the first non-white + // char before a Tab. + if (did_backspace) { foldOpenCursor(); - + } return did_backspace; } @@ -8001,35 +7994,37 @@ static void ins_pagedown(void) } } -/* - * Handle TAB in Insert or Replace mode. - * Return TRUE when the TAB needs to be inserted like a normal character. - */ -static int ins_tab(void) +/// Handle TAB in Insert or Replace mode. +/// +/// @return true when the TAB needs to be inserted like a normal character. +static bool ins_tab(void) + FUNC_ATTR_WARN_UNUSED_RESULT { - int ind; int i; int temp; - if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) + if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) { Insstart_blank_vcol = get_nolist_virtcol(); - if (echeck_abbr(TAB + ABBR_OFF)) - return FALSE; + } + if (echeck_abbr(TAB + ABBR_OFF)) { + return false; + } - ind = inindent(0); - if (ind) - can_cindent = FALSE; + int ind = inindent(0); + if (ind) { + can_cindent = false; + } - /* - * When nothing special, insert TAB like a normal character - */ + // When nothing special, insert TAB like a normal character if (!curbuf->b_p_et && !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf)) - && get_sts_value() == 0) - return TRUE; + && get_sts_value() == 0) { + return true; + } - if (stop_arrow() == FAIL) - return TRUE; + if (stop_arrow() == FAIL) { + return true; + } did_ai = FALSE; did_si = FALSE; @@ -8037,12 +8032,13 @@ static int ins_tab(void) can_si_back = FALSE; AppendToRedobuff((char_u *)"\t"); - if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */ + if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); - else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */ + } else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set temp = get_sts_value(); - else /* otherwise use 'tabstop' */ + } else { // otherwise use "tabstop" temp = (int)curbuf->b_p_ts; + } temp -= get_nolist_virtcol() % temp; /* @@ -8182,21 +8178,20 @@ static int ins_tab(void) curwin->w_p_list = save_list; } - return FALSE; + return false; } -/* - * Handle CR or NL in insert mode. - * Return TRUE when it can't undo. - */ -static int ins_eol(int c) +/// Handle CR or NL in insert mode. +/// +/// @return true when it can't undo. +static bool ins_eol(int c) { - int i; - - if (echeck_abbr(c + ABBR_OFF)) - return FALSE; - if (stop_arrow() == FAIL) - return TRUE; + if (echeck_abbr(c + ABBR_OFF)) { + return false; + } + if (stop_arrow() == FAIL) { + return true; + } undisplay_dollar(); /* @@ -8229,9 +8224,9 @@ static int ins_eol(int c) curwin->w_cursor.col += (colnr_T)STRLEN(get_cursor_pos_ptr()); AppendToRedobuff(NL_STR); - i = open_line(FORWARD, - has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : - 0, old_indent); + bool i = open_line(FORWARD, + has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0, + old_indent); old_indent = 0; can_cindent = TRUE; /* When inserting a line the cursor line must never be in a closed fold. */ diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 72f2b984b0..5477e79cad 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -160,6 +160,7 @@ static char *e_listdictarg = N_( static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); +static char *e_strreq = N_("E114: String required"); static char *e_toomanyarg = N_("E118: Too many arguments for function: %s"); static char *e_dictkey = N_("E716: Key not present in Dictionary: %s"); static char *e_funcexts = N_( @@ -379,6 +380,7 @@ static struct vimvar { { VV_NAME("option_type", VAR_STRING), VV_RO }, { VV_NAME("errors", VAR_LIST), 0 }, { VV_NAME("msgpack_types", VAR_DICT), VV_RO }, + { VV_NAME("event", VAR_DICT), VV_RO }, }; /* shorthand */ @@ -546,6 +548,10 @@ void eval_init(void) set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + + dict_T *v_event = dict_alloc(); + v_event->dv_lock = VAR_FIXED; + set_vim_var_dict(VV_EVENT, v_event); set_vim_var_list(VV_ERRORS, list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); @@ -4011,12 +4017,22 @@ eval6 ( * When either side is a float the result is a float. */ if (use_float) { - if (op == '*') + if (op == '*') { f1 = f1 * f2; - else if (op == '/') { - /* We rely on the floating point library to handle divide - * by zero to result in "inf" and not a crash. */ - f1 = f2 != 0 ? f1 / f2 : INFINITY; + } else if (op == '/') { + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 + ? ( +#ifdef NAN + f1 == 0 + ? NAN + : +#endif + (f1 > 0 + ? INFINITY + : -INFINITY) + ) + : f1 / f2); } else { EMSG(_("E804: Cannot use '%' with Float")); return FAIL; @@ -6008,6 +6024,27 @@ static void rettv_dict_alloc(typval_T *rettv) ++d->dv_refcount; } +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void dict_clear(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + size_t todo = d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + dictitem_free(HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + todo--; + } + } + + hash_unlock(&d->dv_hashtab); +} + /* * Unreference a Dictionary: decrement the reference count and free it when it @@ -6249,6 +6286,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list) return OK; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen +void dict_set_keys_readonly(dict_T *dict) + FUNC_ATTR_NONNULL_ALL +{ + size_t todo = dict->dv_hashtab.ht_used; + for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { + if (HASHITEM_EMPTY(hi)) { + continue; + } + todo--; + HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + } +} + /* * Get the number of items in a Dictionary. */ @@ -6849,9 +6904,25 @@ vim_to_msgpack_error_ret: \ #define CONV_FLOAT(flt) \ do { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", (flt)); \ - ga_concat(gap, (char_u *) numbuf); \ + const float_T flt_ = (flt); \ + switch (fpclassify(flt_)) { \ + case FP_NAN: { \ + ga_concat(gap, (char_u *) "str2float('nan')"); \ + break; \ + } \ + case FP_INFINITE: { \ + if (flt_ < 0) { \ + ga_append(gap, '-'); \ + } \ + ga_concat(gap, (char_u *) "str2float('inf')"); \ + break; \ + } \ + default: { \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", flt_); \ + ga_concat(gap, (char_u *) numbuf); \ + } \ + } \ } while (0) #define CONV_FUNC(fun) \ @@ -7291,7 +7362,7 @@ static struct fst { { "pathshorten", 1, 1, f_pathshorten }, { "pow", 2, 2, f_pow }, { "prevnonblank", 1, 1, f_prevnonblank }, - { "printf", 2, 19, f_printf }, + { "printf", 2, MAX_FUNC_ARGS, f_printf }, { "pumvisible", 0, 0, f_pumvisible }, { "py3eval", 1, 1, f_py3eval }, { "pyeval", 1, 1, f_pyeval }, @@ -7305,8 +7376,8 @@ static struct fst { { "resolve", 1, 1, f_resolve }, { "reverse", 1, 1, f_reverse }, { "round", 1, 1, f_round }, - { "rpcnotify", 2, 64, f_rpcnotify }, - { "rpcrequest", 2, 64, f_rpcrequest }, + { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify }, + { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest }, { "rpcstart", 1, 2, f_rpcstart }, { "rpcstop", 1, 1, f_rpcstop }, { "screenattr", 2, 2, f_screenattr }, @@ -10554,8 +10625,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) { char_u *strregname; int regname; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -10572,18 +10641,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv) if (regname == 0) regname = '"'; - buf[0] = NUL; - buf[1] = NUL; - switch (get_reg_type(regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; - case MBLOCK: - buf[0] = Ctrl_V; - sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1)); - break; - } + colnr_T reglen = 0; + char buf[NUMBUFLEN + 2]; + char_u reg_type = get_reg_type(regname, ®len); + format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf)); + rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)xstrdup(buf); } /* @@ -15229,43 +15293,51 @@ static void f_setline(typval_T *argvars, typval_T *rettv) /// replace its content or create a new one. /// @param[in] title_arg New list title. Defaults to caller function name. /// @param[out] rettv Return value: 0 in case of success, -1 otherwise. -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, - typval_T *title_arg, typval_T *rettv) - FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) +static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(2, 3) { - char_u *act; - int action = ' '; char_u *title = NULL; - + int action = ' '; rettv->vval.v_number = -1; - if (list_arg->v_type != VAR_LIST) + typval_T *list_arg = &args[0]; + if (list_arg->v_type != VAR_LIST) { EMSG(_(e_listreq)); - else { - list_T *l = list_arg->vval.v_list; + return; + } - if (action_arg->v_type != VAR_UNKNOWN) { - act = get_tv_string_chk(action_arg); - if (act == NULL) - return; /* type error; errmsg already given */ - if (*act == 'a' || *act == 'r') - action = *act; - } + typval_T *action_arg = &args[1]; + if (action_arg->v_type == VAR_UNKNOWN) { + // Option argument was not given. + goto skip_args; + } else if (action_arg->v_type != VAR_STRING) { + EMSG(_(e_strreq)); + return; + } + char_u *act = get_tv_string_chk(action_arg); + if (*act == 'a' || *act == 'r') { + action = *act; + } - if (title_arg->v_type == VAR_STRING) { - title = get_tv_string_chk(title_arg); - if (!title) { - return; // type error; errmsg already given - } - } + typval_T *title_arg = &args[2]; + if (title_arg->v_type == VAR_UNKNOWN) { + // Option argument was not given. + goto skip_args; + } + title = get_tv_string_chk(title_arg); + if (!title) { + // Type error. Error already printed by get_tv_string_chk(). + return; + } - if (!title) { - title = (char_u*)(wp ? "setloclist()" : "setqflist()"); - } +skip_args: + if (!title) { + title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + } - if (l && set_errorlist(wp, l, action, title) == OK) { - rettv->vval.v_number = 0; - } + list_T *l = list_arg->vval.v_list; + if (l && set_errorlist(wp, l, action, title) == OK) { + rettv->vval.v_number = 0; } } @@ -15280,7 +15352,7 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv) win = find_win_by_nr(&argvars[0], NULL); if (win != NULL) { - set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv); + set_qf_ll_list(win, &argvars[1], rettv); } } @@ -15417,7 +15489,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv) */ static void f_setqflist(typval_T *argvars, typval_T *rettv) { - set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv); + set_qf_ll_list(NULL, argvars, rettv); } /* @@ -18159,14 +18231,7 @@ void set_vim_var_dict(int idx, dict_T *val) if (val != NULL) { ++val->dv_refcount; // Set readonly - size_t todo = val->dv_hashtab.ht_used; - for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - --todo; - HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - } + dict_set_keys_readonly(val); } } @@ -21951,7 +22016,15 @@ repeat: } if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { + // vim_strsave_shellescape() needs a NUL terminated string. + c = (*fnamep)[*fnamelen]; + if (c != NUL) { + (*fnamep)[*fnamelen] = NUL; + } p = vim_strsave_shellescape(*fnamep, false, false); + if (c != NUL) { + (*fnamep)[*fnamelen] = c; + } xfree(*bufp); *bufp = *fnamep = p; *fnamelen = STRLEN(p); diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 79a1341d98..f51b0f4921 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -113,7 +113,8 @@ enum { VV_OPTION_TYPE, VV_ERRORS, VV_MSGPACK_TYPES, - VV_LEN, /* number of v: vars */ + VV_EVENT, + VV_LEN, // number of v: vars }; /// Maximum number of function arguments diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ad21e71c51..5ea5beb478 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1377,9 +1377,9 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) } } #endif - if (otmp != NULL) - append_redir(buf, (int)len, p_srr, otmp); - + if (otmp != NULL) { + append_redir(buf, len, p_srr, otmp); + } return buf; } @@ -1390,7 +1390,7 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) * The caller should make sure that there is enough room: * STRLEN(opt) + STRLEN(fname) + 3 */ -void append_redir(char_u *buf, int buflen, char_u *opt, char_u *fname) +void append_redir(char_u *buf, size_t buflen, char_u *opt, char_u *fname) { char_u *p; char_u *end; @@ -1506,8 +1506,11 @@ void ex_file(exarg_T *eap) if (rename_buffer(eap->arg) == FAIL) return; } - /* print full file name if :cd used */ - fileinfo(FALSE, FALSE, eap->forceit); + + if (!shortmess(SHM_FILEINFO)) { + // print full file name if :cd used + fileinfo(false, false, eap->forceit); + } } /* @@ -2483,7 +2486,9 @@ do_ecmd ( msg_scroll = msg_scroll_save; msg_scrolled_ign = TRUE; - fileinfo(FALSE, TRUE, FALSE); + if (!shortmess(SHM_FILEINFO)) { + fileinfo(false, true, false); + } msg_scrolled_ign = FALSE; } @@ -3017,7 +3022,7 @@ void do_sub(exarg_T *eap) // The number of lines joined is the number of lines in the range linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 // plus one extra line if not at the end of file. - + eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0; + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); if (joined_lines_count > 1) { do_join(joined_lines_count, FALSE, TRUE, FALSE, true); sub_nsubs = joined_lines_count - 1; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 47774f5a99..71ea170e1c 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -57,23 +57,23 @@ typedef struct scriptitem_S { char_u *sn_name; bool file_id_valid; FileID file_id; - int sn_prof_on; /* TRUE when script is/was profiled */ - int sn_pr_force; /* forceit: profile functions in this script */ - proftime_T sn_pr_child; /* time set when going into first child */ - int sn_pr_nest; /* nesting for sn_pr_child */ - /* profiling the script as a whole */ - int sn_pr_count; /* nr of times sourced */ - proftime_T sn_pr_total; /* time spent in script + children */ - proftime_T sn_pr_self; /* time spent in script itself */ - proftime_T sn_pr_start; /* time at script start */ - proftime_T sn_pr_children; /* time in children after script start */ - /* profiling the script per line */ - garray_T sn_prl_ga; /* things stored for every line */ - proftime_T sn_prl_start; /* start time for current line */ - proftime_T sn_prl_children; /* time spent in children for this line */ - proftime_T sn_prl_wait; /* wait start time for current line */ - int sn_prl_idx; /* index of line being timed; -1 if none */ - int sn_prl_execed; /* line being timed was executed */ + bool sn_prof_on; ///< true when script is/was profiled + int sn_pr_force; ///< forceit: profile functions in this script + proftime_T sn_pr_child; ///< time set when going into first child + int sn_pr_nest; ///< nesting for sn_pr_child + // profiling the script as a whole + int sn_pr_count; ///< nr of times sourced + proftime_T sn_pr_total; ///< time spent in script + children + proftime_T sn_pr_self; ///< time spent in script itself + proftime_T sn_pr_start; ///< time at script start + proftime_T sn_pr_children; ///< time in children after script start + // profiling the script per line + garray_T sn_prl_ga; ///< things stored for every line + proftime_T sn_prl_start; ///< start time for current line + proftime_T sn_prl_children; ///< time spent in children for this line + proftime_T sn_prl_wait; ///< wait start time for current line + linenr_T sn_prl_idx; ///< index of line being timed; -1 if none + int sn_prl_execed; ///< line being timed was executed } scriptitem_T; static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; @@ -81,9 +81,9 @@ static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; /* Struct used in sn_prl_ga for every line of a script. */ typedef struct sn_prl_S { - int snp_count; /* nr of times line was executed */ - proftime_T sn_prl_total; /* time spent in a line + children */ - proftime_T sn_prl_self; /* time spent in a line itself */ + int snp_count; ///< nr of times line was executed + proftime_T sn_prl_total; ///< time spent in a line + children + proftime_T sn_prl_self; ///< time spent in a line itself } sn_prl_T; /* @@ -93,18 +93,18 @@ typedef struct sn_prl_S { * sourcing can be done recursively. */ struct source_cookie { - FILE *fp; /* opened file for sourcing */ - char_u *nextline; /* if not NULL: line that was read ahead */ - int finished; /* ":finish" used */ + FILE *fp; ///< opened file for sourcing + char_u *nextline; ///< if not NULL: line that was read ahead + int finished; ///< ":finish" used #if defined(USE_CRNL) - int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */ - int error; /* TRUE if LF found after CR-LF */ + int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS + int error; ///< TRUE if LF found after CR-LF #endif - linenr_T breakpoint; /* next line with breakpoint or zero */ - char_u *fname; /* name of sourced file */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of sourced file */ - vimconv_T conv; /* type of conversion */ + linenr_T breakpoint; ///< next line with breakpoint or zero + char_u *fname; ///< name of sourced file + int dbg_tick; ///< debug_tick when breakpoint was set + int level; ///< top nesting level of sourced file + vimconv_T conv; ///< type of conversion }; # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) @@ -272,7 +272,7 @@ void do_debug(char_u *cmd) xfree(cmdline); } - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); } xfree(cmdline); @@ -281,7 +281,7 @@ void do_debug(char_u *cmd) redraw_all_later(NOT_VALID); need_wait_return = FALSE; msg_scroll = save_msg_scroll; - lines_left = Rows - 1; + lines_left = (int)(Rows - 1); State = save_State; did_emsg = save_did_emsg; cmd_silent = save_cmd_silent; @@ -392,12 +392,12 @@ int dbg_check_skipped(exarg_T *eap) * This is a grow-array of structs. */ struct debuggy { - int dbg_nr; /* breakpoint number */ - int dbg_type; /* DBG_FUNC or DBG_FILE */ - char_u *dbg_name; /* function or file name */ - regprog_T *dbg_prog; /* regexp program */ - linenr_T dbg_lnum; /* line number in function or file */ - int dbg_forceit; /* ! used */ + int dbg_nr; ///< breakpoint number + int dbg_type; ///< DBG_FUNC or DBG_FILE + char_u *dbg_name; ///< function or file name + regprog_T *dbg_prog; ///< regexp program + linenr_T dbg_lnum; ///< line number in function or file + int dbg_forceit; ///< ! used }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; @@ -563,13 +563,14 @@ void ex_breakdel(exarg_T *eap) } if (ascii_isdigit(*eap->arg)) { - /* ":breakdel {nr}" */ - nr = atol((char *)eap->arg); - for (int i = 0; i < gap->ga_len; ++i) + // ":breakdel {nr}" + nr = atoi((char *)eap->arg); + for (int i = 0; i < gap->ga_len; ++i) { if (DEBUGGY(gap, i).dbg_nr == nr) { todel = i; break; } + } } else if (*eap->arg == '*') { todel = 0; del_all = TRUE; @@ -602,11 +603,13 @@ void ex_breakdel(exarg_T *eap) --gap->ga_len; if (todel < gap->ga_len) memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), - (gap->ga_len - todel) * sizeof(struct debuggy)); - if (eap->cmdidx == CMD_breakdel) + (size_t)(gap->ga_len - todel) * sizeof(struct debuggy)); + if (eap->cmdidx == CMD_breakdel) { ++debug_tick; - if (!del_all) + } + if (!del_all) { break; + } } /* If all breakpoints were removed clear the array. */ @@ -810,8 +813,8 @@ void ex_pydo3(exarg_T *eap) /* Command line expansion for :profile. */ static enum { - PEXP_SUBCMD, /* expand :profile sub-commands */ - PEXP_FUNC /* expand :profile func {funcname} */ + PEXP_SUBCMD, ///< expand :profile sub-commands + PEXP_FUNC ///< expand :profile func {funcname} } pexpand_what; static char *pexpand_cmds[] = { @@ -892,7 +895,7 @@ static void profile_reset(void) for (int id = 1; id <= script_items.ga_len; id++) { scriptitem_T *si = &SCRIPT_ITEM(id); if (si->sn_prof_on) { - si->sn_prof_on = 0; + si->sn_prof_on = false; si->sn_pr_force = 0; si->sn_pr_child = profile_zero(); si->sn_pr_nest = 0; @@ -949,7 +952,7 @@ static void profile_init(scriptitem_T *si) ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100); si->sn_prl_idx = -1; - si->sn_prof_on = TRUE; + si->sn_prof_on = true; si->sn_pr_nest = 0; } @@ -1255,7 +1258,7 @@ check_changed_any ( int save; int i; int bufnum = 0; - int bufcount = 0; + size_t bufcount = 0; int *bufnrs; FOR_ALL_BUFFERS(buf) { @@ -1520,7 +1523,7 @@ do_arglist ( didone = TRUE; xfree(ARGLIST[match].ae_fname); memmove(ARGLIST + match, ARGLIST + match + 1, - (ARGCOUNT - match - 1) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T)); --ALIST(curwin)->al_ga.ga_len; if (curwin->w_arg_idx > match) --curwin->w_arg_idx; @@ -1537,9 +1540,7 @@ do_arglist ( int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); ga_clear(&new_ga); - if (i == FAIL) - return FAIL; - if (exp_count == 0) { + if (i == FAIL || exp_count == 0) { EMSG(_(e_nomatch)); return FAIL; } @@ -1702,10 +1703,11 @@ void ex_argument(exarg_T *eap) { int i; - if (eap->addr_count > 0) - i = eap->line2 - 1; - else + if (eap->addr_count > 0) { + i = (int)eap->line2 - 1; + } else { i = curwin->w_arg_idx; + } do_argfile(eap, i); } @@ -1844,22 +1846,25 @@ void ex_argadd(exarg_T *eap) void ex_argdelete(exarg_T *eap) { if (eap->addr_count > 0) { - /* ":1,4argdel": Delete all arguments in the range. */ - if (eap->line2 > ARGCOUNT) + // ":1,4argdel": Delete all arguments in the range. + if (eap->line2 > ARGCOUNT) { eap->line2 = ARGCOUNT; - int n = eap->line2 - eap->line1 + 1; - if (*eap->arg != NUL || n <= 0) + } + linenr_T n = eap->line2 - eap->line1 + 1; + if (*eap->arg != NUL || n <= 0) { EMSG(_(e_invarg)); - else { - for (int i = eap->line1; i <= eap->line2; ++i) + } else { + for (linenr_T i = eap->line1; i <= eap->line2; ++i) { xfree(ARGLIST[i - 1].ae_fname); + } memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, - (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T))); - ALIST(curwin)->al_ga.ga_len -= n; - if (curwin->w_arg_idx >= eap->line2) - curwin->w_arg_idx -= n; - else if (curwin->w_arg_idx > eap->line1) - curwin->w_arg_idx = eap->line1; + (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T)); + ALIST(curwin)->al_ga.ga_len -= (int)n; + if (curwin->w_arg_idx >= eap->line2) { + curwin->w_arg_idx -= (int)n; + } else if (curwin->w_arg_idx > eap->line1) { + curwin->w_arg_idx = (int)eap->line1; + } } } else if (*eap->arg == NUL) EMSG(_(e_argreq)); @@ -1909,7 +1914,7 @@ void ex_listdo(exarg_T *eap) } break; case CMD_argdo: - i = eap->line1 - 1; + i = (int)eap->line1 - 1; break; default: break; @@ -1942,10 +1947,11 @@ void ex_listdo(exarg_T *eap) ex_cc(eap); buf = curbuf; - i = eap->line1 - 1; + i = (int)eap->line1 - 1; if (eap->addr_count <= 0) { // Default to all quickfix/location list entries. - eap->line2 = qf_size; + assert(qf_size < MAXLNUM); + eap->line2 = (linenr_T)qf_size; } } } else { @@ -2098,7 +2104,7 @@ alist_add_list ( after = ARGCOUNT; if (after < ARGCOUNT) memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), - (ARGCOUNT - after) * sizeof(aentry_T)); + (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); for (int i = 0; i < count; ++i) { ARGLIST[after + i].ae_fname = files[i]; ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); @@ -2571,7 +2577,7 @@ do_source ( while (script_items.ga_len < current_SID) { ++script_items.ga_len; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; - SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; + SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; } si = &SCRIPT_ITEM(current_SID); si->sn_name = fname_exp; @@ -3386,8 +3392,8 @@ static void script_host_execute(char *name, exarg_T *eap) // script list_append_string(args, script ? script : eap->arg, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3403,16 +3409,16 @@ static void script_host_execute_file(char *name, exarg_T *eap) // filename list_append_string(args, buffer, -1); // current range - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { list_T *args = list_alloc(); - list_append_number(args, eap->line1); - list_append_number(args, eap->line2); + list_append_number(args, (int)eap->line1); + list_append_number(args, (int)eap->line2); list_append_string(args, eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3a24f194c1..a7e98e7f04 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6290,10 +6290,8 @@ void ex_splitview(exarg_T *eap) if (eap->cmdidx == CMD_tabedit || eap->cmdidx == CMD_tabfind || eap->cmdidx == CMD_tabnew) { - if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab - : eap->addr_count == 0 ? 0 - : (int)eap->line2 + 1) != FAIL) { - apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf); + if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 + ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) { do_exedit(eap, old_curwin); apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf); diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index bf67047ae8..a1e54e74a6 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -378,7 +378,7 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should char_u *p, *val; if (type == ET_ERROR) { - *should_free = FALSE; + *should_free = true; mesg = ((struct msglist *)value)->throw_msg; if (cmdname != NULL && *cmdname != NUL) { size_t cmdlen = STRLEN(cmdname); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 39bff9b2ad..cffda1ca55 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1130,7 +1130,7 @@ static int command_line_handle_key(CommandLineState *s) if (!mouse_has(MOUSE_COMMAND)) { return command_line_not_changed(s); // Ignore mouse } - cmdline_paste(0, true, true); + cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true); redrawcmd(); return command_line_changed(s); @@ -2424,20 +2424,17 @@ void restore_cmdline_alloc(char_u *p) xfree(p); } -/* - * paste a yank register into the command line. - * used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -static int -cmdline_paste ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* remove trailing CR */ -) +/// Paste a yank register into the command line. +/// Used by CTRL-R command in command-line mode. +/// insert_reg() can't be used here, because special characters from the +/// register contents will be interpreted as commands. +/// +/// @param regname Register name. +/// @param literally Insert text literally instead of "as typed". +/// @param remcr When true, remove trailing CR. +/// +/// @returns FAIL for failure, OK otherwise +static bool cmdline_paste(int regname, bool literally, bool remcr) { long i; char_u *arg; diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 6c135ef47b..70ab4ced75 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -762,6 +762,10 @@ void clearFolding(win_T *win) */ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) { + if (compl_busy) { + return; + } + fold_T *fp; if (wp->w_buffer->terminal) { return; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 69e65c3208..40b5718071 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -214,6 +214,10 @@ EXTERN int compl_length INIT(= 0); * stop looking for matches. */ EXTERN int compl_interrupted INIT(= FALSE); +// Set when doing something for completion that may call edit() recursively, +// which is not allowed. Also used to disable folding during completion +EXTERN int compl_busy INIT(= false); + /* List of flags for method of completion. */ EXTERN int compl_cont_status INIT(= 0); # define CONT_ADDING 1 /* "normal" or "adding" expansion */ @@ -918,8 +922,8 @@ EXTERN int KeyTyped; // TRUE if user typed current char EXTERN int KeyStuffed; // TRUE if current char from stuffbuf EXTERN int maptick INIT(= 0); // tick for each non-mapped char -EXTERN char_u chartab[256]; /* table used in charset.c; See - init_chartab() for explanation */ +EXTERN uint8_t chartab[256]; // table used in charset.c; See + // init_chartab() for explanation EXTERN int must_redraw INIT(= 0); /* type of redraw necessary */ EXTERN int skip_redraw INIT(= FALSE); /* skip redraw once */ diff --git a/src/nvim/main.c b/src/nvim/main.c index a2aca65001..5b5c8a22aa 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -317,14 +317,16 @@ int main(int argc, char **argv) } // open terminals when opening files that start with term:// - do_cmdline_cmd("autocmd BufReadCmd term://* " +#define PROTO "term://" + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " ":call termopen( " // Capture the command string "matchstr(expand(\"<amatch>\"), " - "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " // capture the working directory "{'cwd': get(matchlist(expand(\"<amatch>\"), " - "'\\c\\mterm://\\(.\\{-}\\)//'), 1, '')})"); + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"); +#undef PROTO /* Execute --cmd arguments. */ exe_pre_commands(¶ms); diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 8cf5642a80..6599db787f 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -79,8 +79,6 @@ static size_t total_mem_used = 0; /// total memory used for memfiles /// - NULL, on failure. memfile_T *mf_open(char_u *fname, int flags) { - off_t size; - memfile_T *mfp = xmalloc(sizeof(memfile_T)); if (fname == NULL) { // no file, use memory only @@ -88,11 +86,9 @@ memfile_T *mf_open(char_u *fname, int flags) mfp->mf_ffname = NULL; mfp->mf_fd = -1; } else { // try to open the file - mf_do_open(mfp, fname, flags); - - if (mfp->mf_fd < 0) { // fail if file could not be opened + if (!mf_do_open(mfp, fname, flags)) { xfree(mfp); - return NULL; + return NULL; // fail if file could not be opened } } @@ -115,6 +111,8 @@ memfile_T *mf_open(char_u *fname, int flags) } } + off_t size; + // When recovering, the actual block size will be retrieved from block 0 // in ml_recover(). The size used here may be wrong, therefore mf_blocknr_max // must be rounded up. @@ -171,13 +169,12 @@ memfile_T *mf_open(char_u *fname, int flags) /// FAIL If file could not be opened. int mf_open_file(memfile_T *mfp, char_u *fname) { - mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); // try to open the file - - if (mfp->mf_fd < 0) - return FAIL; + if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) { + mfp->mf_dirty = true; + return OK; + } - mfp->mf_dirty = true; - return OK; + return FAIL; } /// Close a memory file and optionally delete the associated file. @@ -185,28 +182,28 @@ int mf_open_file(memfile_T *mfp, char_u *fname) /// @param del_file Whether to delete associated file. void mf_close(memfile_T *mfp, bool del_file) { - bhdr_T *hp, *nextp; - if (mfp == NULL) { // safety check return; } if (mfp->mf_fd >= 0 && close(mfp->mf_fd) < 0) { EMSG(_(e_swapclose)); } - if (del_file && mfp->mf_fname != NULL) + if (del_file && mfp->mf_fname != NULL) { os_remove((char *)mfp->mf_fname); + } + // free entries in used list - for (hp = mfp->mf_used_first; hp != NULL; hp = nextp) { + for (bhdr_T *hp = mfp->mf_used_first, *nextp; hp != NULL; hp = nextp) { total_mem_used -= hp->bh_page_count * mfp->mf_page_size; nextp = hp->bh_next; mf_free_bhdr(hp); } - while (mfp->mf_free_first != NULL) // free entries in free list + while (mfp->mf_free_first != NULL) { // free entries in free list xfree(mf_rem_free(mfp)); + } mf_hash_free(&mfp->mf_hash); mf_hash_free_all(&mfp->mf_trans); // free hashtable and its items - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); + mf_free_fnames(mfp); xfree(mfp); } @@ -216,28 +213,28 @@ void mf_close(memfile_T *mfp, bool del_file) void mf_close_file(buf_T *buf, bool getlines) { memfile_T *mfp = buf->b_ml.ml_mfp; - if (mfp == NULL || mfp->mf_fd < 0) // nothing to close + if (mfp == NULL || mfp->mf_fd < 0) { // nothing to close return; + } if (getlines) { // get all blocks in memory by accessing all lines (clumsy!) mf_dont_release = true; - for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum) { (void)ml_get_buf(buf, lnum, false); + } mf_dont_release = false; // TODO(elmart): should check if all blocks are really in core } - if (close(mfp->mf_fd) < 0) // close the file + if (close(mfp->mf_fd) < 0) { // close the file EMSG(_(e_swapclose)); + } mfp->mf_fd = -1; if (mfp->mf_fname != NULL) { os_remove((char *)mfp->mf_fname); // delete the swap file - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); - mfp->mf_fname = NULL; - mfp->mf_ffname = NULL; + mf_free_fnames(mfp); } } @@ -390,11 +387,11 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) /// Signal block as no longer used (may put it in the free list). void mf_free(memfile_T *mfp, bhdr_T *hp) { - xfree(hp->bh_data); // free data + xfree(hp->bh_data); // free data mf_rem_hash(mfp, hp); // get *hp out of the hash list mf_rem_used(mfp, hp); // get *hp out of the used list if (hp->bh_bnum < 0) { - xfree(hp); // don't want negative numbers in free list + xfree(hp); // don't want negative numbers in free list mfp->mf_neg_count--; } else { mf_ins_free(mfp, hp); // put *hp in the free list @@ -475,10 +472,11 @@ int mf_sync(memfile_T *mfp, int flags) /// These are blocks that need to be written to a newly created swapfile. void mf_set_dirty(memfile_T *mfp) { - bhdr_T *hp; - for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) - if (hp->bh_bnum > 0) + for (bhdr_T *hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) { + if (hp->bh_bnum > 0) { hp->bh_flags |= BH_DIRTY; + } + } mfp->mf_dirty = true; } @@ -506,10 +504,11 @@ static void mf_ins_used(memfile_T *mfp, bhdr_T *hp) hp->bh_next = mfp->mf_used_first; mfp->mf_used_first = hp; hp->bh_prev = NULL; - if (hp->bh_next == NULL) // list was empty, adjust last pointer + if (hp->bh_next == NULL) { // list was empty, adjust last pointer mfp->mf_used_last = hp; - else + } else { hp->bh_next->bh_prev = hp; + } mfp->mf_used_count += hp->bh_page_count; total_mem_used += hp->bh_page_count * mfp->mf_page_size; } @@ -615,9 +614,10 @@ bool mf_release_all(void) FOR_ALL_BUFFERS(buf) { memfile_T *mfp = buf->b_ml.ml_mfp; if (mfp != NULL) { - // If no swap file yet, may open one. - if (mfp->mf_fd < 0 && buf->b_may_swap) + // If no swap file yet, try to open one. + if (mfp->mf_fd < 0 && buf->b_may_swap) { ml_open_file(buf); + } // Flush as many blocks as possible, only if there is a swapfile. if (mfp->mf_fd >= 0) { @@ -752,7 +752,8 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) else page_count = hp2->bh_page_count; size = page_size * page_count; - if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL) { + void *data = (hp2 == NULL) ? hp->bh_data : hp2->bh_data; + if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) { /// Avoid repeating the error message, this mostly happens when the /// disk is full. We give the message again only after a successful /// write or when hitting a key. We keep on trying, in case some @@ -773,20 +774,6 @@ static int mf_write(memfile_T *mfp, bhdr_T *hp) return OK; } -/// Write block to memfile's file. -/// -/// @return OK On success. -/// FAIL On failure. -static int mf_write_block(memfile_T *mfp, bhdr_T *hp, - off_t offset, unsigned size) -{ - void *data = hp->bh_data; - int result = OK; - if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size) - result = FAIL; - return result; -} - /// Make block number positive and add it to the translation list. /// /// @return OK On success. @@ -856,13 +843,23 @@ blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr) return new_bnum; } -/// Set full file name of memfile's swapfile, out of simple file name and some -/// other considerations. +/// Frees mf_fname and mf_ffname. +void mf_free_fnames(memfile_T *mfp) +{ + xfree(mfp->mf_fname); + xfree(mfp->mf_ffname); + mfp->mf_fname = NULL; + mfp->mf_ffname = NULL; +} + +/// Set the simple file name and the full file name of memfile's swapfile, out +/// of simple file name and some other considerations. /// /// Only called when creating or renaming the swapfile. Either way it's a new /// name so we must work out the full path name. -void mf_set_ffname(memfile_T *mfp) +void mf_set_fnames(memfile_T *mfp, char_u *fname) { + mfp->mf_fname = fname; mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false); } @@ -878,7 +875,7 @@ void mf_fullname(memfile_T *mfp) } } -/// Return TRUE if there are any translations pending for memfile. +/// Return true if there are any translations pending for memfile. bool mf_need_trans(memfile_T *mfp) { return mfp->mf_fname != NULL && mfp->mf_neg_count > 0; @@ -889,11 +886,11 @@ bool mf_need_trans(memfile_T *mfp) /// "fname" must be in allocated memory, and is consumed (also when error). /// /// @param flags Flags for open(). -static void mf_do_open(memfile_T *mfp, char_u *fname, int flags) +/// @return A bool indicating success of the `open` call. +static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags) { // fname cannot be NameBuff, because it must have been allocated. - mfp->mf_fname = fname; - mf_set_ffname(mfp); + mf_set_fnames(mfp, fname); /// Extra security check: When creating a swap file it really shouldn't /// exist yet. If there is a symbolic link, this is most likely an attack. @@ -904,26 +901,26 @@ static void mf_do_open(memfile_T *mfp, char_u *fname, int flags) EMSG(_("E300: Swap file already exists (symlink attack?)")); } else { // try to open the file - flags |= O_NOFOLLOW; - mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags); + mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags | O_NOFOLLOW); } // If the file cannot be opened, use memory only if (mfp->mf_fd < 0) { - xfree(mfp->mf_fname); - xfree(mfp->mf_ffname); - mfp->mf_fname = NULL; - mfp->mf_ffname = NULL; - } else { + mf_free_fnames(mfp); + return false; + } + #ifdef HAVE_FD_CLOEXEC - int fdflags = fcntl(mfp->mf_fd, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + int fdflags = fcntl(mfp->mf_fd, F_GETFD); + if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) { + fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); + } #endif #ifdef HAVE_SELINUX - mch_copy_sec(fname, mfp->mf_fname); + mch_copy_sec(fname, mfp->mf_fname); #endif - } + + return true; } // @@ -948,20 +945,21 @@ static void mf_hash_init(mf_hashtab_T *mht) /// The hash table must not be used again without another mf_hash_init() call. static void mf_hash_free(mf_hashtab_T *mht) { - if (mht->mht_buckets != mht->mht_small_buckets) + if (mht->mht_buckets != mht->mht_small_buckets) { xfree(mht->mht_buckets); + } } /// Free the array of a hash table and all the items it contains. static void mf_hash_free_all(mf_hashtab_T *mht) { - mf_hashitem_T *next; - - for (size_t idx = 0; idx <= mht->mht_mask; idx++) + for (size_t idx = 0; idx <= mht->mht_mask; idx++) { + mf_hashitem_T *next; for (mf_hashitem_T *mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next) { next = mhi->mhi_next; xfree(mhi); } + } mf_hash_free(mht); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f58b2ac38f..b568279d7d 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -426,10 +426,8 @@ void ml_setname(buf_T *buf) /* try to rename the swap file */ if (vim_rename(mfp->mf_fname, fname) == 0) { success = TRUE; - xfree(mfp->mf_fname); - mfp->mf_fname = fname; - xfree(mfp->mf_ffname); - mf_set_ffname(mfp); + mf_free_fnames(mfp); + mf_set_fnames(mfp, fname); ml_upd_block0(buf, UB_SAME_DIR); break; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e064d34e09..2895816b8f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2344,9 +2344,12 @@ do_mouse ( if (mouse_row == 0 && firstwin->w_winrow > 0) { if (is_drag) { if (in_tab_line) { - tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose - ? 9999 - : tab_page_click_defs[mouse_col].tabnr - 1); + if (tab_page_click_defs[mouse_col].type == kStlClickTabClose) { + tabpage_move(9999); + } else { + int tabnr = tab_page_click_defs[mouse_col].tabnr; + tabpage_move(tabnr < tabpage_index(curtab) ? tabnr - 1 : tabnr); + } } return false; } @@ -4096,6 +4099,7 @@ dozet: case 't': scroll_cursor_top(0, true); redraw_later(VALID); + set_fraction(curwin); break; /* "z." and "zz": put cursor in middle of screen */ @@ -4104,6 +4108,7 @@ dozet: case 'z': scroll_cursor_halfway(true); redraw_later(VALID); + set_fraction(curwin); break; /* "z^", "z-" and "zb": put cursor at bottom of screen */ @@ -4124,6 +4129,7 @@ dozet: case 'b': scroll_cursor_bot(0, true); redraw_later(VALID); + set_fraction(curwin); break; /* "zH" - scroll screen right half-page */ diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b1adc85e1d..5f48fdbf3d 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -19,6 +19,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" +#include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" @@ -835,12 +836,13 @@ int do_record(int c) yankreg_T *old_y_previous; int retval; - if (Recording == FALSE) { /* start recording */ - /* registers 0-9, a-z and " are allowed */ - if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) + if (Recording == false) { + // start recording + // registers 0-9, a-z and " are allowed + if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) { retval = FAIL; - else { - Recording = TRUE; + } else { + Recording = c; showmode(); regname = c; retval = OK; @@ -1259,21 +1261,18 @@ get_spec_reg ( return FALSE; } -/* - * Paste a yank register into the command line. - * Only for non-special registers. - * Used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -int -cmdline_paste_reg ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* don't add trailing CR */ -) +/// Paste a yank register into the command line. +/// Only for non-special registers. +/// Used by CTRL-R command in command-line mode +/// insert_reg() can't be used here, because special characters from the +/// register contents will be interpreted as commands. +/// +/// @param regname Register name. +/// @param literally Insert text literally instead of "as typed". +/// @param remcr When true, don't add CR characters. +/// +/// @returns FAIL for failure, OK otherwise +bool cmdline_paste_reg(int regname, bool literally, bool remcr) { long i; @@ -1284,13 +1283,9 @@ cmdline_paste_reg ( for (i = 0; i < reg->y_size; i++) { cmdline_paste_str(reg->y_array[i], literally); - /* Insert ^M between lines and after last line if type is MLINE. - * Don't do this when "remcr" is TRUE and the next line is empty. */ - if (reg->y_type == MLINE - || (i < reg->y_size - 1 - && !(remcr - && i == reg->y_size - 2 - && *reg->y_array[i + 1] == NUL))) { + // Insert ^M between lines and after last line if type is MLINE. + // Don't do this when "remcr" is true. + if ((reg->y_type == MLINE || i < reg->y_size - 1) && !remcr) { cmdline_paste_str((char_u *)"\r", literally); } @@ -1409,8 +1404,9 @@ int op_delete(oparg_T *oap) op_yank_reg(oap, false, reg, false); } - if(oap->regname == 0) { + if (oap->regname == 0) { set_clipboard(0, reg); + yank_do_autocmd(oap, reg); } } @@ -2309,6 +2305,8 @@ bool op_yank(oparg_T *oap, bool message) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); set_clipboard(oap->regname, reg); + yank_do_autocmd(oap, reg); + return true; } @@ -2524,6 +2522,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx) *pnew = NUL; } +/// Execute autocommands for TextYankPost. +/// +/// @param oap Operator arguments. +/// @param reg The yank register used. +static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + + if (recursive || !has_event(EVENT_TEXTYANKPOST)) { + // No autocommand was defined + // or we yanked from this autocommand. + return; + } + + recursive = true; + + // set v:event to a dictionary with information about the yank + dict_T *dict = get_vim_var_dict(VV_EVENT); + + // the yanked text + list_T *list = list_alloc(); + for (linenr_T i = 0; i < reg->y_size; i++) { + list_append_string(list, reg->y_array[i], -1); + } + list->lv_lock = VAR_FIXED; + dict_add_list(dict, "regcontents", list); + + // the register type + char buf[NUMBUFLEN+2]; + format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); + dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + + // name of requested register or the empty string for an unnamed operation. + buf[0] = (char)oap->regname; + buf[1] = NUL; + dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + + // kind of operation (yank/delete/change) + buf[0] = get_op_char(oap->op_type); + buf[1] = NUL; + dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + + dict_set_keys_readonly(dict); + textlock++; + apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); + textlock--; + dict_clear(dict); + + recursive = false; +} + /* * Put contents of register "regname" into the text. @@ -4631,7 +4681,7 @@ theend: * Used for getregtype() * Returns MAUTO for error. */ -char_u get_reg_type(int regname, long *reglen) +char_u get_reg_type(int regname, colnr_T *reg_width) { switch (regname) { case '%': /* file name */ @@ -4654,13 +4704,45 @@ char_u get_reg_type(int regname, long *reglen) yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array != NULL) { - if (reglen != NULL && reg->y_type == MBLOCK) - *reglen = reg->y_width; + if (reg_width != NULL && reg->y_type == MBLOCK) { + *reg_width = reg->y_width; + } return reg->y_type; } return MAUTO; } +/// Format the register type as a string. +/// +/// @param reg_type The register type. +/// @param reg_width The width, only used if "reg_type" is MBLOCK. +/// @param[out] buf Buffer to store formatted string. The allocated size should +/// be at least NUMBUFLEN+2 to always fit the value. +/// @param buf_len The allocated size of the buffer. +void format_reg_type(char_u reg_type, colnr_T reg_width, + char* buf, size_t buf_len) + FUNC_ATTR_NONNULL_ALL +{ + assert(buf_len > 1); + switch (reg_type) { + case MLINE: + buf[0] = 'V'; + buf[1] = NUL; + break; + case MCHAR: + buf[0] = 'v'; + buf[1] = NUL; + break; + case MBLOCK: + snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1); + break; + case MAUTO: + buf[0] = NUL; + break; + } +} + + /// When `flags` has `kGRegList` return a list with text `s`. /// Otherwise just return `s`. /// diff --git a/src/nvim/option.c b/src/nvim/option.c index af7b272467..5efd71444a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -6167,16 +6167,14 @@ int has_format_option(int x) return vim_strchr(curbuf->b_p_fo, x) != NULL; } -/* - * Return TRUE if "x" is present in 'shortmess' option, or - * 'shortmess' contains 'a' and "x" is present in SHM_A. - */ -int shortmess(int x) +/// @returns true if "x" is present in 'shortmess' option, or +/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS. +bool shortmess(int x) { return p_shm != NULL && - ( vim_strchr(p_shm, x) != NULL - || (vim_strchr(p_shm, 'a') != NULL - && vim_strchr((char_u *)SHM_A, x) != NULL)); + (vim_strchr(p_shm, x) != NULL + || (vim_strchr(p_shm, 'a') != NULL + && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL)); } /* diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 10706a0753..8e74e5036e 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -151,26 +151,42 @@ #define COCU_ALL "nvic" /* flags for 'concealcursor' */ -/* characters for p_shm option: */ -#define SHM_RO 'r' /* readonly */ -#define SHM_MOD 'm' /* modified */ -#define SHM_FILE 'f' /* (file 1 of 2) */ -#define SHM_LAST 'i' /* last line incomplete */ -#define SHM_TEXT 'x' /* tx instead of textmode */ -#define SHM_LINES 'l' /* "L" instead of "lines" */ -#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */ -#define SHM_WRI 'w' /* "[w]" instead of "written" */ -#define SHM_A "rmfixlnw" /* represented by 'a' flag */ -#define SHM_WRITE 'W' /* don't use "written" at all */ -#define SHM_TRUNC 't' /* trunctate file messages */ -#define SHM_TRUNCALL 'T' /* trunctate all messages */ -#define SHM_OVER 'o' /* overwrite file messages */ -#define SHM_OVERALL 'O' /* overwrite more messages */ -#define SHM_SEARCH 's' /* no search hit bottom messages */ -#define SHM_ATTENTION 'A' /* no ATTENTION messages */ -#define SHM_INTRO 'I' /* intro messages */ -#define SHM_COMPLETIONMENU 'c' // completion menu messages -#define SHM_ALL "rmfixlnwaWtToOsAIc" /* all possible flags for 'shm' */ +/// characters for p_shm option: +enum { + SHM_RO = 'r', ///< Readonly. + SHM_MOD = 'm', ///< Modified. + SHM_FILE = 'f', ///< (file 1 of 2) + SHM_LAST = 'i', ///< Last line incomplete. + SHM_TEXT = 'x', ///< Tx instead of textmode. + SHM_LINES = 'l', ///< "L" instead of "lines". + SHM_NEW = 'n', ///< "[New]" instead of "[New file]". + SHM_WRI = 'w', ///< "[w]" instead of "written". + SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS. + SHM_WRITE = 'W', ///< Don't use "written" at all. + SHM_TRUNC = 't', ///< Trunctate file messages. + SHM_TRUNCALL = 'T', ///< Trunctate all messages. + SHM_OVER = 'o', ///< Overwrite file messages. + SHM_OVERALL = 'O', ///< Overwrite more messages. + SHM_SEARCH = 's', ///< No search hit bottom messages. + SHM_ATTENTION = 'A', ///< No ATTENTION messages. + SHM_INTRO = 'I', ///< Intro messages. + SHM_COMPLETIONMENU = 'c', ///< Completion menu messages. + SHM_RECORDING = 'q', ///< Short recording message. + SHM_FILEINFO = 'F', ///< No file info messages. +}; +/// Represented by 'a' flag. +#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + 0, \ +}) +/// All possible flags for 'shm'. +#define SHM_ALL ((char_u[]) { \ + SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \ + SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \ + SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \ + SHM_RECORDING, SHM_FILEINFO, \ + 0, \ +}) /* characters for p_go: */ #define GO_ASEL 'a' /* autoselect */ diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2e671653ed..34d8fde4f1 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -166,7 +166,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) // Glue together the given directory from $PATH with name and save into // buf. STRLCPY(buf, path, e - path + 1); - append_path((char *) buf, (const char *) name, (int)buf_len); + append_path((char *) buf, (const char *) name, buf_len); if (is_executable(buf)) { // Check if the caller asked for a copy of the path. diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index c9631a434c..81ceb919c4 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -22,9 +22,9 @@ static const char *xdg_env_vars[] = { static const char *const xdg_defaults[] = { #ifdef WIN32 // Windows - [kXDGConfigHome] = "$LOCALAPPDATA\\nvim\\config", - [kXDGDataHome] = "$LOCALAPPDATA\\nvim\\data", - [kXDGCacheHome] = "$LOCALAPPDATA\\nvim\\cache", + [kXDGConfigHome] = "$LOCALAPPDATA", + [kXDGDataHome] = "$LOCALAPPDATA", + [kXDGCacheHome] = "$TEMP", [kXDGRuntimeDir] = NULL, [kXDGConfigDirs] = NULL, [kXDGDataDirs] = NULL, @@ -66,12 +66,21 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) /// @param[in] idx XDG directory to use. /// /// @return [allocated] `{xdg_directory}/nvim` +/// +/// In WIN32 get_xdg_home(kXDGDataHome) returns `{xdg_directory}/nvim-data` to +/// avoid storing configuration and data files in the same path. static char *get_xdg_home(const XDGVarType idx) FUNC_ATTR_WARN_UNUSED_RESULT { char *dir = stdpaths_get_xdg_var(idx); if (dir) { +#if defined(WIN32) + dir = concat_fnames_realloc(dir, + (idx == kXDGDataHome ? "nvim-data" : "nvim"), + true); +#else dir = concat_fnames_realloc(dir, "nvim", true); +#endif } return dir; } diff --git a/src/nvim/path.c b/src/nvim/path.c index 5cd93ab811..22a3f96cfa 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -268,16 +268,13 @@ char_u *shorten_dir(char_u *str) */ bool dir_of_file_exists(char_u *fname) { - char_u *p; - int c; - bool retval; - - p = path_tail_with_sep(fname); - if (p == fname) + char_u *p = path_tail_with_sep(fname); + if (p == fname) { return true; - c = *p; + } + char_u c = *p; *p = NUL; - retval = os_isdir(fname); + bool retval = os_isdir(fname); *p = c; return retval; } @@ -539,15 +536,10 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff, int flags, bool didstar) FUNC_ATTR_NONNULL_ALL { - char_u *buf; - char_u *p, *s, *e; int start_len = gap->ga_len; - char_u *pat; - int starts_with_dot; - int matches; - int len; + size_t len; bool starstar = false; - static int stardepth = 0; /* depth for "**" expansion */ + static int stardepth = 0; // depth for "**" expansion /* Expanding "**" may take a long time, check for CTRL-C. */ if (stardepth > 0) { @@ -558,16 +550,14 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, // Make room for file name. When doing encoding conversion the actual // length may be quite a bit longer, thus use the maximum possible length. - buf = xmalloc(MAXPATHL); - - /* - * Find the first part in the path name that contains a wildcard. - * When EW_ICASE is set every letter is considered to be a wildcard. - * Copy it into "buf", including the preceding characters. - */ - p = buf; - s = buf; - e = NULL; + char_u *buf = xmalloc(MAXPATHL); + + // Find the first part in the path name that contains a wildcard. + // When EW_ICASE is set every letter is considered to be a wildcard. + // Copy it into "buf", including the preceding characters. + char_u *p = buf; + char_u *s = buf; + char_u *e = NULL; const char_u *path_end = path; while (*path_end != NUL) { /* May ignore a wildcard that has a backslash before it; it will @@ -588,7 +578,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, e = p; } if (has_mbyte) { - len = (*mb_ptr2len)(path_end); + len = (size_t)(*mb_ptr2len)(path_end); STRNCPY(p, path_end, len); p += len; path_end += len; @@ -613,9 +603,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (p[0] == '*' && p[1] == '*') starstar = true; - /* convert the file pattern to a regexp pattern */ - starts_with_dot = (*s == '.'); - pat = file_pat_to_reg_pat(s, e, NULL, FALSE); + // convert the file pattern to a regexp pattern + int starts_with_dot = (*s == '.'); + char_u *pat = file_pat_to_reg_pat(s, e, NULL, false); if (pat == NULL) { xfree(buf); return 0; @@ -646,9 +636,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, if (!didstar && stardepth < 100 && starstar && e - s == 2 && *path_end == '/') { STRCPY(s, path_end + 1); - ++stardepth; - (void)do_path_expand(gap, buf, (int)(s - buf), flags, true); - --stardepth; + stardepth++; + (void)do_path_expand(gap, buf, (size_t)(s - buf), flags, true); + stardepth--; } *s = NUL; @@ -703,10 +693,11 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, xfree(buf); vim_regfree(regmatch.regprog); - matches = gap->ga_len - start_len; - if (matches > 0) + size_t matches = (size_t)(gap->ga_len - start_len); + if (matches > 0) { qsort(((char_u **)gap->ga_data) + start_len, matches, - sizeof(char_u *), pstrcmp); + sizeof(char_u *), pstrcmp); + } return matches; } @@ -736,27 +727,24 @@ static int find_previous_pathsep(char_u *path, char_u **psep) */ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i) { - int candidate_len; - int other_path_len; - char_u **other_paths = (char_u **)gap->ga_data; - char_u *rival; + char_u **other_paths = (char_u **)gap->ga_data; for (int j = 0; j < gap->ga_len; j++) { - if (j == i) - continue; /* don't compare it with itself */ - - candidate_len = (int)STRLEN(maybe_unique); - other_path_len = (int)STRLEN(other_paths[j]); - if (other_path_len < candidate_len) - continue; /* it's different when it's shorter */ - - rival = other_paths[j] + other_path_len - candidate_len; + if (j == i) { + continue; // don't compare it with itself + } + size_t candidate_len = STRLEN(maybe_unique); + size_t other_path_len = STRLEN(other_paths[j]); + if (other_path_len < candidate_len) { + continue; // it's different when it's shorter + } + char_u *rival = other_paths[j] + other_path_len - candidate_len; if (fnamecmp(maybe_unique, rival) == 0 - && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) - return false; /* match */ + && (rival == other_paths[j] || vim_ispathsep(*(rival - 1)))) { + return false; // match + } } - - return true; /* no match found */ + return true; // no match found } /* @@ -770,12 +758,8 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i) */ static void expand_path_option(char_u *curdir, garray_T *gap) { - char_u *path_option = *curbuf->b_p_path == NUL - ? p_path : curbuf->b_p_path; - char_u *buf; - int len; - - buf = xmalloc(MAXPATHL); + char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; + char_u *buf = xmalloc(MAXPATHL); while (*path_option != NUL) { copy_option_part(&path_option, buf, MAXPATHL, " ,"); @@ -787,26 +771,27 @@ static void expand_path_option(char_u *curdir, garray_T *gap) if (curbuf->b_ffname == NULL) continue; char_u *p = path_tail(curbuf->b_ffname); - len = (int)(p - curbuf->b_ffname); - if (len + (int)STRLEN(buf) >= MAXPATHL) + size_t len = (size_t)(p - curbuf->b_ffname); + if (len + STRLEN(buf) >= MAXPATHL) { continue; - if (buf[1] == NUL) + } + if (buf[1] == NUL) { buf[len] = NUL; - else + } else { STRMOVE(buf + len, buf + 2); + } memmove(buf, curbuf->b_ffname, len); simplify_filename(buf); - } else if (buf[0] == NUL) - /* relative to current directory */ - STRCPY(buf, curdir); - else if (path_with_url((char *)buf)) - /* URL can't be used here */ - continue; - else if (!path_is_absolute_path(buf)) { - /* Expand relative path to their full path equivalent */ - len = (int)STRLEN(curdir); - if (len + (int)STRLEN(buf) + 3 > MAXPATHL) + } else if (buf[0] == NUL) { + STRCPY(buf, curdir); // relative to current directory + } else if (path_with_url((char *)buf)) { + continue; // URL can't be used here + } else if (!path_is_absolute_path(buf)) { + // Expand relative path to their full path equivalent + size_t len = STRLEN(curdir); + if (len + STRLEN(buf) + 3 > MAXPATHL) { continue; + } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); buf[len] = PATHSEP; @@ -860,31 +845,25 @@ static char_u *get_path_cutoff(char_u *fname, garray_T *gap) */ static void uniquefy_paths(garray_T *gap, char_u *pattern) { - int len; - char_u **fnames = (char_u **)gap->ga_data; + char_u **fnames = (char_u **)gap->ga_data; bool sort_again = false; - char_u *pat; - char_u *file_pattern; - char_u *curdir; regmatch_T regmatch; garray_T path_ga; - char_u **in_curdir = NULL; - char_u *short_name; + char_u **in_curdir = NULL; + char_u *short_name; ga_remove_duplicate_strings(gap); ga_init(&path_ga, (int)sizeof(char_u *), 1); - /* - * We need to prepend a '*' at the beginning of file_pattern so that the - * regex matches anywhere in the path. FIXME: is this valid for all - * possible patterns? - */ - len = (int)STRLEN(pattern); - file_pattern = xmalloc(len + 2); + // We need to prepend a '*' at the beginning of file_pattern so that the + // regex matches anywhere in the path. FIXME: is this valid for all + // possible patterns? + size_t len = STRLEN(pattern); + char_u *file_pattern = xmalloc(len + 2); file_pattern[0] = '*'; file_pattern[1] = NUL; STRCAT(file_pattern, pattern); - pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, TRUE); + char_u *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true); xfree(file_pattern); if (pat == NULL) return; @@ -895,11 +874,11 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) if (regmatch.regprog == NULL) return; - curdir = xmalloc(MAXPATHL); + char_u *curdir = xmalloc(MAXPATHL); os_dirname(curdir, MAXPATHL); expand_path_option(curdir, &path_ga); - in_curdir = xcalloc(gap->ga_len, sizeof(char_u *)); + in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { char_u *path = fnames[i]; @@ -908,7 +887,7 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) char_u *pathsep_p; char_u *path_cutoff; - len = (int)STRLEN(path); + len = STRLEN(path); is_in_curdir = fnamencmp(curdir, path, dir_end - path) == 0 && curdir[dir_end - path] == NUL; if (is_in_curdir) @@ -1113,9 +1092,8 @@ static bool has_special_wildchar(char_u *p) int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) { - int i; garray_T ga; - char_u *p; + char_u *p; static bool recursive = false; int add_pat; bool did_expand_in_path = false; @@ -1140,11 +1118,11 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, * avoids starting the shell for each argument separately. * For `=expr` do use the internal function. */ - for (i = 0; i < num_pat; i++) { + for (int i = 0; i < num_pat; i++) { if (has_special_wildchar(pat[i]) - && !(vim_backtick(pat[i]) && pat[i][1] == '=') - ) + && !(vim_backtick(pat[i]) && pat[i][1] == '=')) { return mch_expand_wildcards(num_pat, pat, num_file, file, flags); + } } #endif @@ -1155,7 +1133,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, */ ga_init(&ga, (int)sizeof(char_u *), 30); - for (i = 0; i < num_pat; ++i) { + for (int i = 0; i < num_pat; ++i) { add_pat = -1; p = pat[i]; @@ -1212,7 +1190,9 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, recursive = true; did_expand_in_path = true; } else { - add_pat = path_expand(&ga, p, flags); + size_t tmp_add_pat = path_expand(&ga, p, flags); + assert(tmp_add_pat <= INT_MAX); + add_pat = (int)tmp_add_pat; } } } @@ -1261,14 +1241,12 @@ static int expand_backtick( int flags /* EW_* flags */ ) { - char_u *p; - char_u *cmd; - char_u *buffer; + char_u *p; + char_u *buffer; int cnt = 0; - int i; - /* Create the command: lop off the backticks. */ - cmd = vim_strnsave(pat + 1, (int)STRLEN(pat) - 2); + // Create the command: lop off the backticks. + char_u *cmd = vim_strnsave(pat + 1, STRLEN(pat) - 2); if (*cmd == '=') /* `={expr}`: Expand expression */ buffer = eval_to_string(cmd + 1, &p, TRUE); @@ -1288,7 +1266,7 @@ static int expand_backtick( ++p; /* add an entry if it is not empty */ if (p > cmd) { - i = *p; + char_u i = *p; *p = NUL; addfile(gap, cmd, flags); *p = i; @@ -1541,9 +1519,8 @@ find_file_name_in_path ( char_u *rel_fname /* file we are searching relative to */ ) { - char_u *file_name; - int c; - char_u *tofree = NULL; + char_u *file_name; + char_u *tofree = NULL; if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) { tofree = eval_includeexpr(ptr, len); @@ -1554,8 +1531,8 @@ find_file_name_in_path ( } if (options & FNAME_EXP) { - file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, - TRUE, rel_fname); + file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS, true, + rel_fname); /* * If the file could not be found in a normal way, try applying @@ -1572,7 +1549,7 @@ find_file_name_in_path ( } } if (file_name == NULL && (options & FNAME_MESS)) { - c = ptr[len]; + char_u c = ptr[len]; ptr[len] = NUL; EMSG2(_("E447: Can't find file \"%s\" in path"), ptr); ptr[len] = c; @@ -1631,7 +1608,7 @@ bool vim_isAbsName(char_u *name) /// @param force is a flag to force expanding even if the path is absolute /// /// @return FAIL for failure, OK otherwise -int vim_FullName(const char *fname, char *buf, int len, bool force) +int vim_FullName(const char *fname, char *buf, size_t len, bool force) FUNC_ATTR_NONNULL_ARG(2) { int retval = OK; @@ -2017,14 +1994,12 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, */ int match_suffix(char_u *fname) { - int fnamelen, setsuflen; - char_u *setsuf; -#define MAXSUFLEN 30 /* maximum length of a file suffix */ +#define MAXSUFLEN 30 // maximum length of a file suffix char_u suf_buf[MAXSUFLEN]; - fnamelen = (int)STRLEN(fname); - setsuflen = 0; - for (setsuf = p_su; *setsuf; ) { + size_t fnamelen = STRLEN(fname); + size_t setsuflen = 0; + for (char_u *setsuf = p_su; *setsuf; ) { setsuflen = copy_option_part(&setsuf, suf_buf, MAXSUFLEN, ".,"); if (setsuflen == 0) { char_u *tail = path_tail(fname); @@ -2035,10 +2010,10 @@ int match_suffix(char_u *fname) break; } } else { - if (fnamelen >= setsuflen - && fnamencmp(suf_buf, fname + fnamelen - setsuflen, - (size_t)setsuflen) == 0) + if (fnamelen >= setsuflen && + fnamencmp(suf_buf, fname + fnamelen - setsuflen, setsuflen) == 0) { break; + } setsuflen = 0; } } @@ -2049,7 +2024,7 @@ int match_suffix(char_u *fname) /// /// @param directory Directory name, relative to current directory. /// @return `FAIL` for failure, `OK` for success. -int path_full_dir_name(char *directory, char *buffer, int len) +int path_full_dir_name(char *directory, char *buffer, size_t len) { int SUCCESS = 0; int retval = OK; @@ -2091,10 +2066,10 @@ int path_full_dir_name(char *directory, char *buffer, int len) // Append to_append to path with a slash in between. // Append to_append to path with a slash in between. -int append_path(char *path, const char *to_append, int max_len) +int append_path(char *path, const char *to_append, size_t max_len) { - int current_length = STRLEN(path); - int to_append_length = STRLEN(to_append); + size_t current_length = strlen(path); + size_t to_append_length = strlen(to_append); // Do not append empty strings. if (to_append_length == 0) { @@ -2129,12 +2104,14 @@ int append_path(char *path, const char *to_append, int max_len) /// Expand a given file to its absolute path. /// -/// @param fname The filename which should be expanded. -/// @param buf Buffer to store the absolute path of `fname`. -/// @param len Length of `buf`. -/// @param force Also expand when `fname` is already absolute. -/// @return `FAIL` for failure, `OK` for success. -static int path_get_absolute_path(const char_u *fname, char_u *buf, int len, int force) +/// @param fname filename which should be expanded. +/// @param buf buffer to store the absolute path of "fname". +/// @param len length of "buf". +/// @param force also expand when "fname" is already absolute. +/// +/// @return FAIL for failure, OK for success. +static int path_get_absolute_path(const char_u *fname, char_u *buf, + size_t len, int force) { char_u *p; *buf = NUL; diff --git a/src/nvim/pos.h b/src/nvim/pos.h index 7071df51e8..864f3fe866 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -2,7 +2,11 @@ #define NVIM_POS_H typedef long linenr_T; // line number type -typedef int colnr_T; // column number type + +/// Column number type +typedef int colnr_T; +/// Format used to print values which have colnr_T type +#define PRIdCOLNR "d" #define MAXLNUM 0x7fffffff // maximum (invalid) line number #define MAXCOL 0x7fffffff // maximum column number, 31 bits diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f3abf864fb..3bc6d46dd9 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -200,9 +200,8 @@ qf_init_ext ( char_u *pattern; char_u *fmtstr = NULL; int col = 0; - char_u use_viscol = FALSE; - int type = 0; - int valid; + bool use_viscol = false; + char_u type = 0; linenr_T buflnum = lnumfirst; long lnum = 0L; int enr = 0; @@ -220,16 +219,16 @@ qf_init_ext ( int i; int round; int idx = 0; - int multiline = FALSE; - int multiignore = FALSE; - int multiscan = FALSE; - int retval = -1; /* default: return error flag */ - char_u *directory = NULL; - char_u *currfile = NULL; - char_u *tail = NULL; - char_u *p_str = NULL; - listitem_T *p_li = NULL; - struct dir_stack_T *file_stack = NULL; + bool multiline = false; + bool multiignore = false; + bool multiscan = false; + int retval = -1; // default: return error flag + char_u *directory = NULL; + char_u *currfile = NULL; + char_u *tail = NULL; + char_u *p_str = NULL; + listitem_T *p_li = NULL; + struct dir_stack_T *file_stack = NULL; regmatch_T regmatch; static struct fmtpattern { char_u convchar; @@ -278,15 +277,16 @@ qf_init_ext ( /* * Get some space to modify the format string into. */ - i = 3 * FMT_PATTERNS + 4 * (int)STRLEN(efm); - for (round = FMT_PATTERNS; round > 0; ) - i += (int)STRLEN(fmt_pat[--round].pattern); + size_t fmtstr_size = 3 * FMT_PATTERNS + 4 * STRLEN(efm); + for (round = FMT_PATTERNS; round > 0; ) { + fmtstr_size += STRLEN(fmt_pat[--round].pattern); + } #ifdef COLON_IN_FILENAME - i += 12; /* "%f" can become twelve chars longer */ + fmtstr_size += 12; // "%f" can become twelve chars longer #else - i += 2; /* "%f" can become two chars longer */ + fmtstr_size += 2; // "%f" can become two chars longer #endif - fmtstr = xmalloc(i); + fmtstr = xmalloc(fmtstr_size); while (efm[0] != NUL) { /* @@ -530,11 +530,9 @@ qf_init_ext ( fmt_start = NULL; } - /* - * Try to match each part of 'errorformat' until we find a complete - * match or no match. - */ - valid = TRUE; + // Try to match each part of 'errorformat' until we find a complete + // match or no match. + bool valid = true; restofline: for (; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next) { idx = fmt_ptr->prefix; @@ -546,7 +544,7 @@ restofline: errmsg[0] = NUL; lnum = 0; col = 0; - use_viscol = FALSE; + use_viscol = false; enr = -1; type = 0; tail = NULL; @@ -555,25 +553,23 @@ restofline: int r = vim_regexec(®match, IObuff, (colnr_T)0); fmt_ptr->prog = regmatch.regprog; if (r) { - if ((idx == 'C' || idx == 'Z') && !multiline) + if ((idx == 'C' || idx == 'Z') && !multiline) { continue; - if (vim_strchr((char_u *)"EWI", idx) != NULL) - type = idx; - else + } + if (vim_strchr((char_u *)"EWI", idx) != NULL) { + type = (char_u)idx; + } else { type = 0; - /* - * Extract error message data from matched line. - * We check for an actual submatch, because "\[" and "\]" in - * the 'errorformat' may cause the wrong submatch to be used. - */ - if ((i = (int)fmt_ptr->addr[0]) > 0) { /* %f */ - int c; - - if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) + } + // Extract error message data from matched line. + // We check for an actual submatch, because "\[" and "\]" in + // the 'errorformat' may cause the wrong submatch to be used. + if ((i = (int)fmt_ptr->addr[0]) > 0) { // %f + if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) { continue; - - /* Expand ~/file and $HOME/file to full path. */ - c = *regmatch.endp[i]; + } + // Expand ~/file and $HOME/file to full path. + char_u c = *regmatch.endp[i]; *regmatch.endp[i] = NUL; expand_env(regmatch.startp[i], namebuf, CMDBUFFSIZE); *regmatch.endp[i] = c; @@ -629,14 +625,14 @@ restofline: col -= col % 8; } } - ++col; - use_viscol = TRUE; + col++; + use_viscol = true; } if ((i = (int)fmt_ptr->addr[8]) > 0) { /* %v */ if (regmatch.startp[i] == NULL) continue; col = (int)atol((char *)regmatch.startp[i]); - use_viscol = TRUE; + use_viscol = true; } if ((i = (int)fmt_ptr->addr[9]) > 0) { /* %s */ if (regmatch.startp[i] == NULL || regmatch.endp[i] == NULL) @@ -653,7 +649,7 @@ restofline: break; } } - multiscan = FALSE; + multiscan = false; if (fmt_ptr == NULL || idx == 'D' || idx == 'X') { if (fmt_ptr != NULL) { @@ -667,20 +663,21 @@ restofline: } else if (idx == 'X') /* leave directory */ directory = qf_pop_dir(&dir_stack); } - namebuf[0] = NUL; /* no match found, remove file name */ - lnum = 0; /* don't jump to this line */ - valid = FALSE; - STRCPY(errmsg, IObuff); /* copy whole line to error message */ - if (fmt_ptr == NULL) - multiline = multiignore = FALSE; + namebuf[0] = NUL; // no match found, remove file name + lnum = 0; // don't jump to this line + valid = false; + STRCPY(errmsg, IObuff); // copy whole line to error message + if (fmt_ptr == NULL) { + multiline = multiignore = false; + } } else if (fmt_ptr != NULL) { /* honor %> item */ if (fmt_ptr->conthere) fmt_start = fmt_ptr; if (vim_strchr((char_u *)"AEWI", idx) != NULL) { - multiline = TRUE; /* start of a multi-line message */ - multiignore = FALSE; /* reset continuation */ + multiline = true; // start of a multi-line message + multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { /* continuation of multi-line msg */ if (qfprev == NULL) @@ -702,15 +699,17 @@ restofline: qfprev->qf_viscol = use_viscol; if (!qfprev->qf_fnum) qfprev->qf_fnum = qf_get_fnum(directory, - *namebuf || directory ? namebuf - : currfile && valid ? currfile : 0); - if (idx == 'Z') - multiline = multiignore = FALSE; + *namebuf + || directory ? namebuf : currfile + && valid ? currfile : 0); + if (idx == 'Z') { + multiline = multiignore = false; + } line_breakcheck(); continue; } else if (vim_strchr((char_u *)"OPQ", idx) != NULL) { - /* global file names */ - valid = FALSE; + // global file names + valid = false; if (*namebuf == NUL || os_file_exists(namebuf)) { if (*namebuf && idx == 'P') currfile = qf_push_dir(namebuf, &file_stack); @@ -719,14 +718,15 @@ restofline: *namebuf = NUL; if (tail && *tail) { STRMOVE(IObuff, skipwhite(tail)); - multiscan = TRUE; + multiscan = true; goto restofline; } } } - if (fmt_ptr->flags == '-') { /* generally exclude this line */ - if (multiline) - multiignore = TRUE; /* also exclude continuation lines */ + if (fmt_ptr->flags == '-') { // generally exclude this line + if (multiline) { + multiignore = true; // also exclude continuation lines + } continue; } } @@ -867,26 +867,27 @@ void qf_free_all(win_T *wp) qf_free(qi, i); } -/* - * Add an entry to the end of the list of errors. - * Returns OK or FAIL. - */ -static int -qf_add_entry ( - qf_info_T *qi, /* quickfix list */ - qfline_T **prevp, /* nonnull pointer (to previously added entry or NULL) */ - char_u *dir, /* optional directory name */ - char_u *fname, /* file name or NULL */ - int bufnum, /* buffer number or zero */ - char_u *mesg, /* message */ - long lnum, /* line number */ - int col, /* column */ - int vis_col, /* using visual column */ - char_u *pattern, /* search pattern */ - int nr, /* error number */ - int type, /* type character */ - int valid /* valid entry */ -) +/// Add an entry to the end of the list of errors. +/// +/// @param qi quickfix list +/// @param prevp nonnull pointer (to previously added entry or NULL) +/// @param dir optional directory name +/// @param fname file name or NULL +/// @param bufnum buffer number or zero +/// @param mesg message +/// @param lnum line number +/// @param col column +/// @param vis_col using visual column +/// @param pattern search pattern +/// @param nr error number +/// @param type type character +/// @param valid valid entry +/// +/// @returns OK or FAIL. +static int qf_add_entry(qf_info_T *qi, qfline_T **prevp, char_u *dir, + char_u *fname, int bufnum, char_u *mesg, long lnum, + int col, char_u vis_col, char_u *pattern, int nr, + char_u type, char_u valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); @@ -1657,12 +1658,13 @@ win_found: * flag is present in 'shortmess'; But when not jumping, print the * whole message. */ i = msg_scroll; - if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) - msg_scroll = TRUE; - else if (!msg_scrolled && shortmess(SHM_OVERALL)) - msg_scroll = FALSE; - msg_attr_keep(IObuff, 0, TRUE); - msg_scroll = i; + if (curbuf == old_curbuf && curwin->w_cursor.lnum == old_lnum) { + msg_scroll = true; + } else if (!msg_scrolled && shortmess(SHM_OVERALL)) { + msg_scroll = false; + } + msg_attr_keep(IObuff, 0, true); + msg_scroll = (int)i; } } else { if (opened_window) @@ -1827,10 +1829,12 @@ void qf_age(exarg_T *eap) } } - if (eap->addr_count != 0) - count = eap->line2; - else + if (eap->addr_count != 0) { + assert(eap->line2 <= INT_MAX); + count = (int)eap->line2; + } else { count = 1; + } while (count--) { if (eap->cmdidx == CMD_colder || eap->cmdidx == CMD_lolder) { if (qi->qf_curlist == 0) { @@ -1882,6 +1886,8 @@ static void qf_free(qf_info_T *qi, int idx) --qi->qf_lists[idx].qf_count; } xfree(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_start = NULL; + qi->qf_lists[idx].qf_ptr = NULL; qi->qf_lists[idx].qf_title = NULL; qi->qf_lists[idx].qf_index = 0; } @@ -1948,7 +1954,7 @@ static char_u *qf_types(int c, int nr) p = (char_u *)""; else { cc[0] = ' '; - cc[1] = c; + cc[1] = (char_u)c; cc[2] = NUL; p = cc; } @@ -2036,12 +2042,13 @@ void ex_copen(exarg_T *eap) } } - if (eap->addr_count != 0) - height = eap->line2; - else + if (eap->addr_count != 0) { + assert(eap->line2 <= INT_MAX); + height = (int)eap->line2; + } else { height = QF_WINHEIGHT; - - reset_VIsual_and_resel(); /* stop Visual mode */ + } + reset_VIsual_and_resel(); // stop Visual mode /* * Find existing quickfix window, or open a new one. @@ -2299,13 +2306,15 @@ static void qf_fill_buffer(qf_info_T *qi) if (qfp->qf_fnum != 0 && (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL && errbuf->b_fname != NULL) { - if (qfp->qf_type == 1) /* :helpgrep */ - STRCPY(IObuff, path_tail(errbuf->b_fname)); - else - STRCPY(IObuff, errbuf->b_fname); + if (qfp->qf_type == 1) { // :helpgrep + STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff)); + } else { + STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff)); + } len = (int)STRLEN(IObuff); - } else + } else { len = 0; + } IObuff[len++] = '|'; if (qfp->qf_lnum > 0) { @@ -2432,7 +2441,7 @@ void ex_make(exarg_T *eap) { char_u *fname; char_u *cmd; - unsigned len; + size_t len; win_T *wp = NULL; qf_info_T *qi = &ql_info; int res; @@ -2473,9 +2482,10 @@ void ex_make(exarg_T *eap) /* * If 'shellpipe' empty: don't redirect to 'errorfile'. */ - len = (unsigned)STRLEN(p_shq) * 2 + (unsigned)STRLEN(eap->arg) + 1; - if (*p_sp != NUL) - len += (unsigned)STRLEN(p_sp) + (unsigned)STRLEN(fname) + 3; + len = STRLEN(p_shq) * 2 + STRLEN(eap->arg) + 1; + if (*p_sp != NUL) { + len += STRLEN(p_sp) + STRLEN(fname) + 3; + } cmd = xmalloc(len); sprintf((char *)cmd, "%s%s%s", (char *)p_shq, (char *)eap->arg, (char *)p_shq); @@ -2546,11 +2556,11 @@ static char_u *get_mef_name(void) /* Keep trying until the name doesn't exist yet. */ for (;; ) { - if (start == -1) - start = os_get_pid(); - else + if (start == -1) { + start = (int)os_get_pid(); + } else { off += 19; - + } name = xmalloc(STRLEN(p_mef) + 30); STRCPY(name, p_mef); sprintf((char *)name + (p - p_mef), "%d%d", start, off); @@ -2897,7 +2907,7 @@ void ex_vimgrep(exarg_T *eap) int found_match; buf_T *first_match_buf = NULL; time_t seconds = 0; - int save_mls; + long save_mls; char_u *save_ei = NULL; aco_save_T aco; int flags = 0; @@ -3454,18 +3464,12 @@ int get_errorlist(win_T *wp, list_T *list) */ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) { - listitem_T *li; - dict_T *d; - char_u *filename, *pattern, *text, *type; - int bufnum; - long lnum; - int col, nr; - int vcol; - qfline_T *prevp = NULL; - int valid, status; + listitem_T *li; + dict_T *d; + qfline_T *prevp = NULL; int retval = OK; - qf_info_T *qi = &ql_info; - int did_bufnr_emsg = FALSE; + qf_info_T *qi = &ql_info; + bool did_bufnr_emsg = false; if (wp != NULL) { qi = ll_get_or_alloc_list(wp); @@ -3492,21 +3496,22 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) if (d == NULL) continue; - filename = get_dict_string(d, (char_u *)"filename", TRUE); - bufnum = get_dict_number(d, (char_u *)"bufnr"); - lnum = get_dict_number(d, (char_u *)"lnum"); - col = get_dict_number(d, (char_u *)"col"); - vcol = get_dict_number(d, (char_u *)"vcol"); - nr = get_dict_number(d, (char_u *)"nr"); - type = get_dict_string(d, (char_u *)"type", TRUE); - pattern = get_dict_string(d, (char_u *)"pattern", TRUE); - text = get_dict_string(d, (char_u *)"text", TRUE); - if (text == NULL) + char_u *filename = get_dict_string(d, (char_u *)"filename", true); + int bufnum = (int)get_dict_number(d, (char_u *)"bufnr"); + long lnum = get_dict_number(d, (char_u *)"lnum"); + int col = (int)get_dict_number(d, (char_u *)"col"); + char_u vcol = (char_u)get_dict_number(d, (char_u *)"vcol"); + int nr = (int)get_dict_number(d, (char_u *)"nr"); + char_u *type = get_dict_string(d, (char_u *)"type", true); + char_u *pattern = get_dict_string(d, (char_u *)"pattern", true); + char_u *text = get_dict_string(d, (char_u *)"text", true); + if (text == NULL) { text = vim_strsave((char_u *)""); - - valid = TRUE; - if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) - valid = FALSE; + } + bool valid = true; + if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { + valid = false; + } /* Mark entries with non-existing buffer number as not valid. Give the * error message only once. */ @@ -3515,22 +3520,23 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title) did_bufnr_emsg = TRUE; EMSGN(_("E92: Buffer %" PRId64 " not found"), bufnum); } - valid = FALSE; + valid = false; bufnum = 0; } - status = qf_add_entry(qi, &prevp, - NULL, /* dir */ - filename, - bufnum, - text, - lnum, - col, - vcol, /* vis_col */ - pattern, /* search pattern */ - nr, - type == NULL ? NUL : *type, - valid); + int status = qf_add_entry(qi, + &prevp, + NULL, // dir + filename, + bufnum, + text, + lnum, + col, + vcol, // vis_col + pattern, // search pattern + nr, + (char_u)(type == NULL ? NUL : *type), + valid); xfree(filename); xfree(pattern); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index cd440fe8dc..1cc270023c 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6780,8 +6780,8 @@ int showmode(void) if (Recording && edit_submode == NULL /* otherwise it gets too long */ ) { - MSG_PUTS_ATTR(_("recording"), attr); - need_clear = TRUE; + recording_mode(attr); + need_clear = true; } mode_displayed = TRUE; @@ -6820,26 +6820,33 @@ static void msg_pos_mode(void) msg_row = Rows - 1; } -/* - * Delete mode message. Used when ESC is typed which is expected to end - * Insert mode (but Insert mode didn't end yet!). - * Caller should check "mode_displayed". - */ -void unshowmode(int force) +/// Delete mode message. Used when ESC is typed which is expected to end +/// Insert mode (but Insert mode didn't end yet!). +/// Caller should check "mode_displayed". +void unshowmode(bool force) { - /* - * Don't delete it right now, when not redrawing or inside a mapping. - */ - if (!redrawing() || (!force && char_avail() && !KeyTyped)) - redraw_cmdline = TRUE; /* delete mode later */ - else { + // Don't delete it right now, when not redrawing or inside a mapping. + if (!redrawing() || (!force && char_avail() && !KeyTyped)) { + redraw_cmdline = true; // delete mode later + } else { msg_pos_mode(); - if (Recording) - MSG_PUTS_ATTR(_("recording"), hl_attr(HLF_CM)); + if (Recording) { + recording_mode(hl_attr(HLF_CM)); + } msg_clr_eos(); } } +static void recording_mode(int attr) +{ + MSG_PUTS_ATTR(_("recording"), attr); + if (!shortmess(SHM_RECORDING)) { + char_u s[4]; + vim_snprintf((char *)s, ARRAY_SIZE(s), " @%c", Recording); + MSG_PUTS_ATTR(s, attr); + } +} + /* * Draw the tab pages line at the top of the Vim window. */ diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 478fa973a1..26f0a6c94b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5533,25 +5533,29 @@ char_u *get_syntax_name(expand_T *xp, int idx) } -/* - * Function called for expression evaluation: get syntax ID at file position. - */ -int -syn_get_id ( +// Function called for expression evaluation: get syntax ID at file position. +int syn_get_id( win_T *wp, long lnum, colnr_T col, - int trans, /* remove transparency */ - bool *spellp, /* return: can do spell checking */ - int keep_state /* keep state of char at "col" */ + int trans, // remove transparency + bool *spellp, // return: can do spell checking + int keep_state // keep state of char at "col" ) { - /* When the position is not after the current position and in the same - * line of the same buffer, need to restart parsing. */ + // When the position is not after the current position and in the same + // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum - || col < current_col) + || col < current_col) { syntax_start(wp, lnum); + } else if (wp->w_buffer == syn_buf + && lnum == current_lnum + && col > current_col) { + // next_match may not be correct when moving around, e.g. with the + // "skip" expression in searchpair() + next_match_idx = -1; + } (void)get_syntax_attr(col, spellp, keep_state); @@ -6391,7 +6395,7 @@ do_highlight ( HL_TABLE()[idx].sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; - { + if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { if (t_colors < 16) diff --git a/src/nvim/tempfile.c b/src/nvim/tempfile.c index a218c03fdb..afe926b2ef 100644 --- a/src/nvim/tempfile.c +++ b/src/nvim/tempfile.c @@ -32,8 +32,9 @@ static void vim_maketempdir(void) char_u path[TEMP_FILE_PATH_MAXLEN]; for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); ++i) { // Expand environment variables, leave room for "/nvimXXXXXX/999999999" + // Skip the directory check if the expansion fails. expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); - if (!os_isdir(template)) { // directory doesn't exist + if (template[0] == '$' || !os_isdir(template)) { continue; } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 42fd81f643..63a7e20880 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -411,6 +411,10 @@ static int terminal_execute(VimState *state, int key) apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); break; + // Temporary fix until paste events gets implemented + case K_PASTE: + break; + case K_LEFTMOUSE: case K_LEFTDRAG: case K_LEFTRELEASE: diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 63ca4cf6c4..ec236e72b4 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -32,7 +32,6 @@ SCRIPTS := \ test69.out \ test73.out \ test79.out \ - test88.out \ test_listlbr.out \ test_breakindent.out \ test_close_count.out \ diff --git a/src/nvim/testdir/test88.in b/src/nvim/testdir/test88.in deleted file mode 100644 index 9e43f703e9..0000000000 --- a/src/nvim/testdir/test88.in +++ /dev/null @@ -1,99 +0,0 @@ -vim: set ft=vim - -Tests for correct display (cursor column position) with +conceal and -tabulators. - -STARTTEST -:so small.vim -:if !has('conceal') - e! test.ok - wq! test.out -:endif -:" Conceal settings. -:set conceallevel=2 -:set concealcursor=nc -:syntax match test /|/ conceal -:" Save current cursor position. Only works in <expr> mode, can't be used -:" with :normal because it moves the cursor to the command line. Thanks to ZyX -:" <zyx.vim@gmail.com> for the idea to use an <expr> mapping. -:let positions = [] -:nnoremap <expr> GG ":let positions += ['".screenrow().":".screencol()."']\n" -:" Start test. -/^start: -:normal ztj -GGk -:" We should end up in the same column when running these commands on the two -:" lines. -:normal ft -GGk -:normal $ -GGk -:normal 0j -GGk -:normal ft -GGk -:normal $ -GGk -:normal 0j0j -GGk -:" Same for next test block. -:normal ft -GGk -:normal $ -GGk -:normal 0j -GGk -:normal ft -GGk -:normal $ -GGk -:normal 0j0j -GGk -:" And check W with multiple tabs and conceals in a line. -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:normal 0j -GGk -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:set lbr -:normal $ -GGk -:set list listchars=tab:>- -:normal 0 -GGk -:normal W -GGk -:normal W -GGk -:normal W -GGk -:normal $ -GGk -:" Display result. -:call append('$', 'end:') -:call append('$', positions) -:/^end/,$wq! test.out -ENDTEST - -start: -.concealed. text -|concealed| text - - .concealed. text - |concealed| text - -.a. .b. .c. .d. -|a| |b| |c| |d| diff --git a/src/nvim/testdir/test88.ok b/src/nvim/testdir/test88.ok deleted file mode 100644 index 12949f274a..0000000000 --- a/src/nvim/testdir/test88.ok +++ /dev/null @@ -1,29 +0,0 @@ -end: -2:1 -2:17 -2:20 -3:1 -3:17 -3:20 -5:8 -5:25 -5:28 -6:8 -6:25 -6:28 -8:1 -8:9 -8:17 -8:25 -8:27 -9:1 -9:9 -9:17 -9:25 -9:26 -9:26 -9:1 -9:9 -9:17 -9:25 -9:26 diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 4a8a24d79d..7d9b2c7c3b 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1782,7 +1782,13 @@ void undo_time(long step, int sec, int file, int absolute) /* "target" is the node below which we want to be. * Init "closest" to a value we can't reach. */ if (absolute) { - target = step; + if (step == 0) { + // target 0 does not exist, got to 1 and above it. + target = 1; + above = true; + } else { + target = step; + } closest = -1; } else { /* When doing computations with time_t subtract starttime, because diff --git a/src/nvim/version.c b/src/nvim/version.c index efd3c230a7..ebf21d162f 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,6 +69,14 @@ static char *features[] = { // clang-format off static int included_patches[] = { + 1654, + 1652, + 1643, + 1641, + 1574, + 1570, + 1511, + 1425, 1366, // 1219 NA @@ -149,7 +157,7 @@ static int included_patches[] = { // 1144 NA // 1143, // 1142, - // 1141, + 1141, // 1140, // 1139 NA // 1138 NA @@ -178,7 +186,7 @@ static int included_patches[] = { // 1115 NA // 1114, // 1113, - // 1112, + 1112, // 1111, // 1110, // 1109 NA @@ -297,7 +305,7 @@ static int included_patches[] = { // 996 NA // 995 NA // 994 NA - // 993, + // 993 NA // 992 NA 991, // 990 NA @@ -316,12 +324,12 @@ static int included_patches[] = { 977, // 976 NA 975, - // 974, - // 973, + 974, + 973, 972, // 971 NA // 970 NA - // 969, + // 969 NA // 968 NA // 967 NA // 966 NA @@ -329,25 +337,25 @@ static int included_patches[] = { // 964 NA 963, // 962 NA - // 961, + 961, // 960 NA // 959 NA - // 958, + 958, // 957, - // 956, + // 956 NA 955, // 954 NA 953, - // 952, + 952, // 951, 950, 949, // 948 NA - // 947, + // 947 NA 946, 945, 944, - // 943, + // 943 NA // 942, // 941, // 940 NA @@ -365,7 +373,7 @@ static int included_patches[] = { // 928 NA // 927 NA 926, - // 925, + 925, // 924 NA // 923 NA 922, @@ -376,17 +384,17 @@ static int included_patches[] = { // 917 NA 916, 915, - // 914, + // 914 NA // 913 NA 912, // 911 NA // 910 NA - // 909, + // 909 NA // 908 NA // 907 NA // 906 NA - // 905, - // 904, + // 905 NA + // 904 NA 903, // 902 NA 901, @@ -398,7 +406,7 @@ static int included_patches[] = { 895, // 894 NA 893, - // 892, + // 892 NA 891, // 890 NA // 889, @@ -433,7 +441,7 @@ static int included_patches[] = { // 860 NA 859, 858, - // 857, + 857, 856, // 855 NA // 854 NA diff --git a/src/nvim/window.c b/src/nvim/window.c index 39106a7b8d..1b8c953596 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -248,7 +248,7 @@ newwindow: /* First create a new tab with the window, then go back to * the old tab and close the window there. */ wp = curwin; - if (win_new_tabpage((int)Prenum) == OK + if (win_new_tabpage((int)Prenum, NULL) == OK && valid_tabpage(oldtab)) { newtab = curtab; goto_tabpage_tp(oldtab, TRUE, TRUE); @@ -2952,14 +2952,15 @@ void free_tabpage(tabpage_T *tp) xfree(tp); } -/* - * Create a new Tab page with one window. - * It will edit the current buffer, like after ":split". - * When "after" is 0 put it just after the current Tab page. - * Otherwise put it just before tab page "after". - * Return FAIL or OK. - */ -int win_new_tabpage(int after) +/// Create a new tabpage with one window. +/// +/// It will edit the current buffer, like after :split. +/// +/// @param after Put new tabpage after tabpage "after", or after the current +/// tabpage in case of 0. +/// @param filename Will be passed to apply_autocmds(). +/// @return Was the new tabpage created successfully? FAIL or OK. +int win_new_tabpage(int after, char_u *filename) { tabpage_T *tp = curtab; tabpage_T *newtp; @@ -2999,10 +3000,12 @@ int win_new_tabpage(int after) newtp->tp_topframe = topframe; last_status(FALSE); - redraw_all_later(CLEAR); - apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); + + apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); + apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); + apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); + return OK; } @@ -3023,7 +3026,7 @@ int may_open_tabpage(void) if (n != 0) { cmdmod.tab = 0; /* reset it to avoid doing it twice */ postponed_split_tab = 0; - return win_new_tabpage(n); + return win_new_tabpage(n, NULL); } return FAIL; } @@ -3047,9 +3050,11 @@ int make_tabpages(int maxcount) */ block_autocmds(); - for (todo = count - 1; todo > 0; --todo) - if (win_new_tabpage(0) == FAIL) + for (todo = count - 1; todo > 0; --todo) { + if (win_new_tabpage(0, NULL) == FAIL) { break; + } + } unblock_autocmds(); @@ -4619,10 +4624,8 @@ void win_drag_vsep_line(win_T *dragwin, int offset) #define FRACTION_MULT 16384L -/* - * Set wp->w_fraction for the current w_wrow and w_height. - */ -static void set_fraction(win_T *wp) +// Set wp->w_fraction for the current w_wrow and w_height. +void set_fraction(win_T *wp) { wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2) / (long)wp->w_height; diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index c924988d06..a15d489a1f 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,8 +1,8 @@ -- Sanity checks for buffer_* API calls via msgpack-rpc local helpers = require('test.functional.helpers') -local clear, nvim, buffer, curbuf, curwin, eq, ok = - helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, - helpers.eq, helpers.ok +local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer +local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq +local curbufmeths, ok = helpers.curbufmeths, helpers.ok describe('buffer_* functions', function() before_each(clear) @@ -35,10 +35,11 @@ describe('buffer_* functions', function() eq('', curbuf('get_line', 0)) end) - it('get_line: out-of-bounds returns empty string', function() + it('get_line: out-of-bounds is an error', function() curbuf('set_line', 0, 'line1.a') - eq('', curbuf('get_line', 1)) - eq('', curbuf('get_line', -2)) + eq(1, curbuf('line_count')) -- sanity + eq(false, pcall(curbuf, 'get_line', 1)) + eq(false, pcall(curbuf, 'get_line', -2)) end) it('set_line, del_line: out-of-bounds is an error', function() @@ -68,14 +69,16 @@ describe('buffer_* functions', function() eq({}, curbuf('get_line_slice', -4, -5, true, true)) end) - it('set_line_slice: out-of-bounds is an error', function() + it('set_line_slice: out-of-bounds extends past end', function() curbuf('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity eq({'c'}, curbuf('get_line_slice', -1, 4, true, true)) eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 5, true, true)) - eq(false, pcall(curbuf, 'set_line_slice', 4, 5, true, true, {'d'})) - eq(false, pcall(curbuf, 'set_line_slice', -4, -5, true, true, {'d'})) + curbuf('set_line_slice', 4, 5, true, true, {'d'}) + eq({'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) + curbuf('set_line_slice', -4, -5, true, true, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) end) it('works', function() @@ -101,6 +104,136 @@ describe('buffer_* functions', function() end) end) + describe('{get,set}_lines', function() + local get_lines, set_lines = curbufmeths.get_lines, curbufmeths.set_lines + local line_count = curbufmeths.line_count + + it('has correct line_count when inserting and deleting', function() + eq(1, line_count()) + set_lines(-1, -1, true, {'line'}) + eq(2, line_count()) + set_lines(-1, -1, true, {'line'}) + eq(3, line_count()) + set_lines(-2, -1, true, {}) + eq(2, line_count()) + set_lines(-2, -1, true, {}) + set_lines(-2, -1, true, {}) + -- There's always at least one line + eq(1, line_count()) + end) + + it('can get, set and delete a single line', function() + eq({''}, get_lines(0, 1, true)) + set_lines(0, 1, true, {'line1'}) + eq({'line1'}, get_lines(0, 1, true)) + set_lines(0, 1, true, {'line2'}) + eq({'line2'}, get_lines(0, 1, true)) + set_lines(0, 1, true, {}) + eq({''}, get_lines(0, 1, true)) + end) + + it('can get a single line with strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(1, line_count()) -- sanity + eq(false, pcall(get_lines, 1, 2, true)) + eq(false, pcall(get_lines, -3, -2, true)) + end) + + it('can get a single line with non-strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(1, line_count()) -- sanity + eq({}, get_lines(1, 2, false)) + eq({}, get_lines(-3, -2, false)) + end) + + it('can set and delete a single line with strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + eq(false, pcall(set_lines, 1, 2, true, {'line1.b'})) + eq(false, pcall(set_lines, -3, -2, true, {'line1.c'})) + eq({'line1.a'}, get_lines(0, -1, true)) + eq(false, pcall(set_lines, 1, 2, true, {})) + eq(false, pcall(set_lines, -3, -2, true, {})) + eq({'line1.a'}, get_lines(0, -1, true)) + end) + + it('can set and delete a single line with non-strict indexing', function() + set_lines(0, 1, true, {'line1.a'}) + set_lines(1, 2, false, {'line1.b'}) + set_lines(-4, -3, false, {'line1.c'}) + eq({'line1.c', 'line1.a', 'line1.b'}, get_lines(0, -1, true)) + set_lines(3, 4, false, {}) + set_lines(-5, -4, false, {}) + eq({'line1.c', 'line1.a', 'line1.b'}, get_lines(0, -1, true)) + end) + + it('can handle NULs', function() + set_lines(0, 1, true, {'ab\0cd'}) + eq({'ab\0cd'}, get_lines(0, -1, true)) + end) + + it('works with multiple lines', function() + eq({''}, get_lines(0, -1, true)) + -- Replace buffer + for _, mode in pairs({false, true}) do + set_lines(0, -1, mode, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, mode)) + eq({'b', 'c'}, get_lines(1, -1, mode)) + eq({'b'}, get_lines(1, 2, mode)) + eq({}, get_lines(1, 1, mode)) + eq({'a', 'b'}, get_lines(0, -2, mode)) + eq({'b'}, get_lines(1, -2, mode)) + eq({'b', 'c'}, get_lines(-3, -1, mode)) + set_lines(1, 2, mode, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'c'}, get_lines(0, -1, mode)) + set_lines(-2, -1, mode, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'a', 'b', 'c'}, + get_lines(0, -1, mode)) + set_lines(0, -4, mode, {}) + eq({'a', 'b', 'c'}, get_lines(0, -1, mode)) + set_lines(0, -1, mode, {}) + eq({''}, get_lines(0, -1, mode)) + end + end) + + it('can get line ranges with non-strict indexing', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq({}, get_lines(3, 4, false)) + eq({}, get_lines(3, 10, false)) + eq({}, get_lines(-5, -5, false)) + eq({}, get_lines(3, -1, false)) + eq({}, get_lines(-3, -4, false)) + end) + + it('can get line ranges with strict indexing', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq(false, pcall(get_lines, 3, 4, true)) + eq(false, pcall(get_lines, 3, 10, true)) + eq(false, pcall(get_lines, -5, -5, true)) + -- empty or inverted ranges are not errors + eq({}, get_lines(3, -1, true)) + eq({}, get_lines(-3, -4, true)) + end) + + it('set_line_slice: out-of-bounds can extend past end', function() + set_lines(0, -1, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, get_lines(0, -1, true)) --sanity + + eq({'c'}, get_lines(-2, 5, false)) + eq({'a', 'b', 'c'}, get_lines(0, 6, false)) + eq(false, pcall(set_lines, 4, 6, true, {'d'})) + set_lines(4, 6, false, {'d'}) + eq({'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) + eq(false, pcall(set_lines, -6, -6, true, {'e'})) + set_lines(-6, -6, false, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, get_lines(0, -1, true)) + end) + + end) + describe('{get,set}_var', function() it('works', function() curbuf('set_var', 'lua', {1, 2, {['3'] = 1}}) diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index c0099e44c4..1b33275803 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -165,8 +165,8 @@ describe('server -> client', function() eq('SOME TEXT', eval("rpcrequest(vim, 'buffer_get_line', "..buf..", 0)")) - -- Call get_line_slice(buf, range [0,0], includes start, includes end) - eq({'SOME TEXT'}, eval("rpcrequest(vim, 'buffer_get_line_slice', "..buf..", 0, 0, 1, 1)")) + -- Call get_lines(buf, range [0,0], strict_indexing) + eq({'SOME TEXT'}, eval("rpcrequest(vim, 'buffer_get_lines', "..buf..", 0, 1, 1)")) end) it('returns an error if the request failed', function() diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 9c9759adf6..99f67fe43a 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3,7 +3,7 @@ local helpers = require('test.functional.helpers') local Screen = require('test.functional.ui.screen') local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed -local os_is_windows = helpers.os_is_windows +local os_name = helpers.os_name describe('vim_* functions', function() before_each(clear) @@ -17,7 +17,7 @@ describe('vim_* functions', function() nvim('command', 'w') local f = io.open(fname) ok(f ~= nil) - if os_is_windows() then + if os_name() == 'windows' then eq('testing\r\napi\r\n', f:read('*a')) else eq('testing\napi\n', f:read('*a')) diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua new file mode 100644 index 0000000000..3c813abc2e --- /dev/null +++ b/test/functional/autocmd/autocmd_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local command = helpers.command +local eval = helpers.eval + +describe('autocmds:', function() + before_each(clear) + + it(':tabnew triggers events in the correct order', function() + local expected = { + 'WinLeave', + 'TabLeave', + 'TabNew', + 'WinEnter', + 'TabEnter', + 'BufLeave', + 'BufEnter' + } + command('let g:foo = []') + command('autocmd BufEnter * :call add(g:foo, "BufEnter")') + command('autocmd BufLeave * :call add(g:foo, "BufLeave")') + command('autocmd TabEnter * :call add(g:foo, "TabEnter")') + command('autocmd TabLeave * :call add(g:foo, "TabLeave")') + command('autocmd TabNew * :call add(g:foo, "TabNew")') + command('autocmd WinEnter * :call add(g:foo, "WinEnter")') + command('autocmd WinLeave * :call add(g:foo, "WinLeave")') + command('tabnew') + assert.same(expected, eval('g:foo')) + end) +end) diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua index 5ab504889b..aaf9db0a99 100644 --- a/test/functional/autocmd/tabnew_spec.lua +++ b/test/functional/autocmd/tabnew_spec.lua @@ -1,22 +1,28 @@ local helpers = require('test.functional.helpers') -local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq -describe('TabNew', function() - setup(clear) - describe('au TabNew', function() - describe('with * as <afile>', function() - it('matches when opening any new tab', function() - nvim('command', 'au! TabNew * echom "tabnew:".tabpagenr().":".bufnr("")') - eq("\ntabnew:2:1", nvim('command_output', 'tabnew')) - eq("\ntabnew:3:2\n\"test.x\" [New File]", nvim('command_output', 'tabnew test.x')) - end) - end) - describe('with FILE as <afile>', function() - it('matches when opening a new tab for FILE', function() - local tmp_path = nvim('eval', 'tempname()') - nvim('command', 'au! TabNew '..tmp_path..' echom "tabnew:match"') - eq("\ntabnew:4:3\ntabnew:match\n\""..tmp_path.."\" [New File]", nvim('command_output', 'tabnew '..tmp_path)) - end) - end) - end) +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval + +describe('autocmd TabNew', function() + before_each(clear) + + it('matches when opening any new tab', function() + command('autocmd! TabNew * let g:test = "tabnew:".tabpagenr().":".bufnr("")') + command('tabnew') + eq('tabnew:2:1', eval('g:test')) + command('tabnew test.x') + eq('tabnew:3:2', eval('g:test')) + end) + + it('matches when opening a new tab for FILE', function() + local tmp_path = helpers.funcs.tempname() + command('let g:test = "foo"') + command('autocmd! TabNew ' .. tmp_path .. ' let g:test = "bar"') + command('tabnew ' .. tmp_path ..'X') + eq('foo', eval('g:test')) + command('tabnew ' .. tmp_path) + eq('bar', eval('g:test')) + end) end) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua new file mode 100644 index 0000000000..0e46aa5641 --- /dev/null +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -0,0 +1,216 @@ +local helpers = require('test.functional.helpers') +local clear, eval, eq, insert = helpers.clear, helpers.eval, helpers.eq, helpers.insert +local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command +local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq + +describe('TextYankPost', function() + before_each(function() + clear() + + -- emulate the clipboard so system clipboard isn't affected + execute('let &rtp = "test/functional/fixtures,".&rtp') + + execute('let g:count = 0') + execute('autocmd TextYankPost * let g:event = copy(v:event)') + execute('autocmd TextYankPost * let g:count += 1') + + curbufmeths.set_lines(0, -1, true, { + 'foo\0bar', + 'baz text', + }) + end) + + it('is executed after yank and handles register types', function() + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + + -- v:event is cleared after the autocommand is done + eq({}, eval('v:event')) + + feed('+yw') + eq({ + operator = 'y', + regcontents = { 'baz ' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(2, eval('g:count')) + + feed('<c-v>eky') + eq({ + operator = 'y', + regcontents = { 'foo', 'baz' }, + regname = '', + regtype = "\0223" -- ^V + block width + }, eval('g:event')) + eq(3, eval('g:count')) + end) + + it('makes v:event immutable', function() + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + + execute('set debug=msg') + -- the regcontents should not be changed without copy. + local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])') + eq(status,false) + neq(nil, string.find(err, ':E742:')) + + -- can't mutate keys inside the autocommand + execute('autocmd! TextYankPost * let v:event.regcontents = 0') + status, err = pcall(command,'normal yy') + eq(status,false) + neq(nil, string.find(err, ':E46:')) + + -- can't add keys inside the autocommand + execute('autocmd! TextYankPost * let v:event.mykey = 0') + status, err = pcall(command,'normal yy') + eq(status,false) + neq(nil, string.find(err, ':E742:')) + end) + + it('is not invoked recursively', function() + execute('autocmd TextYankPost * normal "+yy') + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + eq({ 'foo\nbar' }, funcs.getreg('+',1,1)) + end) + + it('is executed after delete and change', function() + feed('dw') + eq({ + operator = 'd', + regcontents = { 'foo' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(1, eval('g:count')) + + feed('dd') + eq({ + operator = 'd', + regcontents = { '\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(2, eval('g:count')) + + feed('cwspam<esc>') + eq({ + operator = 'c', + regcontents = { 'baz' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(3, eval('g:count')) + end) + + it('is not executed after black-hole operation', function() + feed('"_dd') + eq(0, eval('g:count')) + + feed('"_cwgood<esc>') + eq(0, eval('g:count')) + + expect([[ + good text]]) + feed('"_yy') + eq(0, eval('g:count')) + + execute('delete _') + eq(0, eval('g:count')) + end) + + it('gives the correct register name', function() + feed('$"byiw') + eq({ + operator = 'y', + regcontents = { 'bar' }, + regname = 'b', + regtype = 'v' + }, eval('g:event')) + + feed('"*yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '*', + regtype = 'V' + }, eval('g:event')) + + execute("set clipboard=unnamed") + + -- regname still shows the name the user requested + feed('yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + + feed('"*yy') + eq({ + operator = 'y', + regcontents = { 'foo\nbar' }, + regname = '*', + regtype = 'V' + }, eval('g:event')) + end) + + it('works with Ex commands', function() + execute('1delete +') + eq({ + operator = 'd', + regcontents = { 'foo\nbar' }, + regname = '+', + regtype = 'V' + }, eval('g:event')) + eq(1, eval('g:count')) + + execute('yank') + eq({ + operator = 'y', + regcontents = { 'baz text' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(2, eval('g:count')) + + execute('normal yw') + eq({ + operator = 'y', + regcontents = { 'baz ' }, + regname = '', + regtype = 'v' + }, eval('g:event')) + eq(3, eval('g:count')) + + execute('normal! dd') + eq({ + operator = 'd', + regcontents = { 'baz text' }, + regname = '', + regtype = 'V' + }, eval('g:event')) + eq(4, eval('g:count')) + end) + +end) diff --git a/test/functional/eval/operators_spec.lua b/test/functional/eval/operators_spec.lua new file mode 100644 index 0000000000..bc9a17935c --- /dev/null +++ b/test/functional/eval/operators_spec.lua @@ -0,0 +1,28 @@ +local helpers = require('test.functional.helpers') +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear + +describe('Division operator', function() + before_each(clear) + + it('returns infinity on {positive}/0.0', function() + eq('str2float(\'inf\')', eval('string(1.0/0.0)')) + eq('str2float(\'inf\')', eval('string(1.0e-100/0.0)')) + eq('str2float(\'inf\')', eval('string(1.0e+100/0.0)')) + eq('str2float(\'inf\')', eval('string((1.0/0.0)/0.0)')) + end) + + it('returns -infinity on {negative}/0.0', function() + eq('-str2float(\'inf\')', eval('string((-1.0)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0e-100)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0e+100)/0.0)')) + eq('-str2float(\'inf\')', eval('string((-1.0/0.0)/0.0)')) + end) + + it('returns NaN on 0.0/0.0', function() + eq('str2float(\'nan\')', eval('string(0.0/0.0)')) + eq('str2float(\'nan\')', eval('string(-(0.0/0.0))')) + eq('str2float(\'nan\')', eval('string((-0.0)/0.0)')) + end) +end) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua new file mode 100644 index 0000000000..f7f5dca70a --- /dev/null +++ b/test/functional/eval/string_spec.lua @@ -0,0 +1,175 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local command = helpers.command +local meths = helpers.meths +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local redir_exec = helpers.redir_exec +local funcs = helpers.funcs +local write_file = helpers.write_file + +describe('string() function', function() + before_each(clear) + + describe('used to represent floating-point values', function() + it('dumps NaN values', function() + eq('str2float(\'nan\')', eval('string(str2float(\'nan\'))')) + end) + + it('dumps infinite values', function() + eq('str2float(\'inf\')', eval('string(str2float(\'inf\'))')) + eq('-str2float(\'inf\')', eval('string(str2float(\'-inf\'))')) + end) + + it('dumps regular values', function() + eq('1.5', funcs.string(1.5)) + eq('1.56e-20', funcs.string(1.56000e-020)) + eq('0.0', eval('string(0.0)')) + end) + + it('dumps values with at most six digits after the decimal point', + function() + eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020)) + eq('1.234568', funcs.string(1.23456789123456789123456789)) + end) + + it('dumps values with at most seven digits before the decimal point', + function() + eq('1234567.891235', funcs.string(1234567.89123456789123456789)) + eq('1.234568e7', funcs.string(12345678.9123456789123456789)) + end) + + it('dumps negative values', function() + eq('-1.5', funcs.string(-1.5)) + eq('-1.56e-20', funcs.string(-1.56000e-020)) + eq('-1.234568e-20', funcs.string(-1.23456789123456789123456789e-020)) + eq('-1.234568', funcs.string(-1.23456789123456789123456789)) + eq('-1234567.891235', funcs.string(-1234567.89123456789123456789)) + eq('-1.234568e7', funcs.string(-12345678.9123456789123456789)) + end) + end) + + describe('used to represent numbers', function() + it('dumps regular values', function() + eq('0', funcs.string(0)) + eq('-1', funcs.string(-1)) + eq('1', funcs.string(1)) + end) + + it('dumps large values', function() + eq('2147483647', funcs.string(2^31-1)) + eq('-2147483648', funcs.string(-2^31)) + end) + end) + + describe('used to represent strings', function() + it('dumps regular strings', function() + eq('\'test\'', funcs.string('test')) + end) + + it('dumps empty strings', function() + eq('\'\'', funcs.string('')) + end) + + it('dumps strings with \' inside', function() + eq('\'\'\'\'\'\'\'\'', funcs.string('\'\'\'')) + eq('\'a\'\'b\'\'\'\'\'', funcs.string('a\'b\'\'')) + eq('\'\'\'b\'\'\'\'d\'', funcs.string('\'b\'\'d')) + eq('\'a\'\'b\'\'c\'\'d\'', funcs.string('a\'b\'c\'d')) + end) + end) + + describe('used to represent funcrefs', function() + local fname = 'Xtest-functional-eval-string_spec-fref-script.vim' + + before_each(function() + write_file(fname, [[ + function Test1() + endfunction + + function s:Test2() + endfunction + + function g:Test3() + endfunction + + let g:Test2_f = function('s:Test2') + ]]) + command('source ' .. fname) + end) + + after_each(function() + os.remove(fname) + end) + + it('dumps references to built-in functions', function() + eq('function(\'function\')', eval('string(function("function"))')) + end) + + it('dumps references to user functions', function() + eq('function(\'Test1\')', eval('string(function("Test1"))')) + eq('function(\'g:Test3\')', eval('string(function("g:Test3"))')) + end) + + it('dumps references to script functions', function() + eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)')) + end) + end) + + describe('used to represent lists', function() + it('dumps empty list', function() + eq('[]', funcs.string({})) + end) + + it('dumps nested lists', function() + eq('[[[[[]]]]]', funcs.string({{{{{}}}}})) + end) + + it('dumps nested non-empty lists', function() + eq('[1, [[3, [[5], 4]], 2]]', funcs.string({1, {{3, {{5}, 4}}, 2}})) + end) + + it('errors when dumping recursive lists', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(l)')) + end) + + it('dumps recursive lists despite the error', function() + meths.set_var('l', {}) + eval('add(l, l)') + eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]', + redir_exec('echo string(l)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@1}]]', + redir_exec('echo string([l])')) + end) + end) + + describe('used to represent dictionaries', function() + it('dumps empty dictionary', function() + eq('{}', eval('string({})')) + end) + + it('dumps non-empty dictionary', function() + eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1})) + end) + + it('errors when dumping recursive dictionaries', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container', + exc_exec('echo string(d)')) + end) + + it('dumps recursive dictionaries despite the error', function() + meths.set_var('d', {d=1}) + eval('extend(d, {"d": d})') + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}', + redir_exec('echo string(d)')) + eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}', + redir_exec('echo string({"out": d})')) + end) + end) +end) diff --git a/test/functional/eval/vvar_event_spec.lua b/test/functional/eval/vvar_event_spec.lua new file mode 100644 index 0000000000..bbac86524f --- /dev/null +++ b/test/functional/eval/vvar_event_spec.lua @@ -0,0 +1,15 @@ +local helpers = require('test.functional.helpers') +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local command = helpers.command +describe('v:event', function() + before_each(clear) + it('is empty before any autocommand', function() + eq({}, eval('v:event')) + end) + + it('is immutable', function() + eq(false, pcall(command, 'let v:event = {}')) + eq(false, pcall(command, 'let v:event.mykey = {}')) + end) +end) + diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index 5fa8a58049..d9ec254aff 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -1,25 +1,59 @@ -// A simple implementation of a shell for testing -// `termopen([&sh, &shcf, '{cmd'}])` and `termopen([&sh])`. -// -// If launched with no arguments, prints "ready $ ", otherwise prints -// "ready $ {cmd}\n". - #include <stdio.h> #include <string.h> +#include <stdint.h> + +static void help(void) +{ + puts("A simple implementation of a shell for testing termopen()."); + puts(""); + puts("Usage:"); + puts(" shell-test --help"); + puts(" Prints this help to stdout."); + puts(" shell-test"); + puts(" shell-test EXE"); + puts(" Prints \"ready $ \" to stderr."); + puts(" shell-test EXE \"prog args...\""); + puts(" Prints \"ready $ prog args...\\n\" to stderr."); + puts(" shell-test REP {byte} \"line line line\""); + puts(" Prints \"{lnr}: line line line\\n\" to stdout {byte} times."); + puts(" I.e. for `shell-test REP ab \"test\"'"); + puts(" 0: test"); + puts(" ..."); + puts(" 96: test"); + puts(" will be printed because byte `a' is equal to 97."); +} int main(int argc, char **argv) { - fprintf(stderr, "ready $ "); + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + help(); + } - if (argc == 3) { - // argv should be {"terminal-test", "EXE", "prog args..."} - if (strcmp(argv[1], "EXE") != 0) { - fprintf(stderr, "first argument must be 'EXE'\n"); - return 2; + if (argc >= 2) { + if (strcmp(argv[1], "EXE") == 0) { + fprintf(stderr, "ready $ "); + if (argc >= 3) { + fprintf(stderr, "%s\n", argv[2]); + } + } else if (strcmp(argv[1], "REP") == 0) { + if (argc < 4) { + fprintf(stderr, "Not enough REP arguments\n"); + return 4; + } + uint8_t number = (uint8_t) *argv[2]; + for (uint8_t i = 0; i < number; i++) { + printf("%d: %s\n", (int) i, argv[3]); + } + } else { + fprintf(stderr, "Unknown first argument\n"); + return 3; } - - fprintf(stderr, "%s\n", argv[2]); + return 0; + } else if (argc == 1) { + fprintf(stderr, "ready $ "); + return 0; + } else { + fprintf(stderr, "Missing first argument\n"); + return 2; } - - return 0; } diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 1fba15c2c3..4e294029ab 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,5 +1,4 @@ require('coxpcall') -local ffi = require('ffi') local lfs = require('lfs') local assert = require('luassert') local Loop = require('nvim.loop') @@ -133,6 +132,22 @@ local function nvim_eval(expr) return request('vim_eval', expr) end +local os_name = (function() + local name = nil + return (function() + if not name then + if nvim_eval('has("win32")') == 1 then + name = 'windows' + elseif nvim_eval('has("macunix")') == 1 then + name = 'osx' + else + name = 'unix' + end + end + return name + end) +end)() + local function nvim_call(name, ...) return request('vim_call_function', name, {...}) end @@ -247,7 +262,7 @@ end local function source(code) local tmpname = os.tmpname() - if ffi.os == 'OSX' and string.match(tmpname, '^/tmp') then + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then tmpname = '/private'..tmpname end write_file(tmpname, code) @@ -305,7 +320,7 @@ local function curbuf_contents() -- previously sent keys are processed(vim_eval is a deferred function, and -- only processed after all input) wait() - return table.concat(curbuf('get_line_slice', 0, -1, true, true), '\n') + return table.concat(curbuf('get_lines', 0, -1, true), '\n') end local function curwin(method, ...) @@ -328,24 +343,18 @@ local function expect(contents) return eq(dedent(contents), curbuf_contents()) end -local function os_is_windows() - return nvim_eval('has("win32")') == 1 -end - local function rmdir(path) if lfs.attributes(path, 'mode') ~= 'directory' then return nil end for file in lfs.dir(path) do - if file == '.' or file == '..' then - goto continue - end - local ret, err = os.remove(path..'/'..file) - if not ret then - error('os.remove: '..err) - return nil + if file ~= '.' and file ~= '..' then + local ret, err = os.remove(path..'/'..file) + if not ret then + error('os.remove: '..err) + return nil + end end - ::continue:: end local ret, err = os.remove(path) if not ret then @@ -434,7 +443,7 @@ return { wait = wait, set_session = set_session, write_file = write_file, - os_is_windows = os_is_windows, + os_name = os_name, rmdir = rmdir, mkdir = lfs.mkdir, exc_exec = exc_exec, diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua index 3c264423ff..de080f4b43 100644 --- a/test/functional/legacy/036_regexp_character_classes_spec.lua +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -1,9 +1,9 @@ -- Test character classes in regexp using regexpengine 0, 1, 2. local helpers = require('test.functional.helpers') -local ffi = require('ffi') local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect local source, write_file = helpers.source, helpers.write_file +local os_name = helpers.os_name local function sixlines(text) local result = '' @@ -15,7 +15,7 @@ end local function diff(text, nodedent) local tmpname = os.tmpname() - if ffi.os == 'OSX' and string.match(tmpname, '^/tmp') then + if os_name() == 'osx' and string.match(tmpname, '^/tmp') then tmpname = '/private'..tmpname end execute('w! '..tmpname) @@ -30,7 +30,7 @@ local function diff(text, nodedent) end describe('character classes in regexp', function() - local ctrl1 = '\t\x0c\r' + local ctrl1 = '\t\012\r' local punct1 = " !\"#$%&'()#+'-./" local digits = '0123456789' local punct2 = ':;<=>?@' @@ -38,8 +38,8 @@ describe('character classes in regexp', function() local punct3 = '[\\]^_`' local lower = 'abcdefghiwxyz' local punct4 = '{|}~' - local ctrl2 = '\x7f\x80\x82\x90\x9b' - local iso_text = '\xa6\xb1\xbc\xc7\xd3\xe9' -- "¦±¼ÇÓé" in utf-8 + local ctrl2 = '\127\128\130\144\155' + local iso_text = '\166\177\188\199\211\233' -- "¦±¼ÇÓé" in utf-8 setup(function() -- The original test32.in file was not in utf-8 encoding and did also -- contain some control characters. We use lua escape sequences to write diff --git a/test/functional/legacy/039_visual_block_mode_commands_spec.lua b/test/functional/legacy/039_visual_block_mode_commands_spec.lua index 6e1879035b..7195d7d11d 100644 --- a/test/functional/legacy/039_visual_block_mode_commands_spec.lua +++ b/test/functional/legacy/039_visual_block_mode_commands_spec.lua @@ -187,7 +187,7 @@ describe('Visual block mode', function() 98<Nul>65 98<Nul>65]] expected = expected:gsub('<CR>', '\r') - expected = expected:gsub('<Nul>', '\x00') + expected = expected:gsub('<Nul>', '\000') expect(expected) end) diff --git a/test/functional/legacy/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua index ceb114b2a7..6db37bf1ff 100644 --- a/test/functional/legacy/061_undo_tree_spec.lua +++ b/test/functional/legacy/061_undo_tree_spec.lua @@ -191,7 +191,7 @@ describe('undo tree:', function() end) it('undo an expression-register', function() - local normal_commands = 'o1\x1ba2\x12=string(123)\n\x1b' + local normal_commands = 'o1\027a2\018=string(123)\n\027' write_file('Xtest.source', normal_commands) feed('oa<esc>') diff --git a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua index dc6df007e6..6b5ee60568 100644 --- a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua +++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua @@ -24,7 +24,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function() ]]) write_file('test83-tags2', '!_TAG_FILE_ENCODING cp932 //\n' .. - '\x82`\x82a\x82b Xtags2.txt /\x82`\x82a\x82b\n' + '\130`\130a\130b Xtags2.txt /\130`\130a\130b\n' ) -- The last file is very long but repetetive and can be generated on the -- fly. @@ -32,7 +32,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function() !_TAG_FILE_SORTED 1 // !_TAG_FILE_ENCODING cp932 // ]]) - local line = ' Xtags3.txt /\x82`\x82a\x82b\n' + local line = ' Xtags3.txt /\130`\130a\130b\n' for i = 1, 100 do text = text .. 'abc' .. i .. line end diff --git a/test/functional/legacy/088_conceal_tabs_spec.lua b/test/functional/legacy/088_conceal_tabs_spec.lua new file mode 100644 index 0000000000..c78f4e5c3e --- /dev/null +++ b/test/functional/legacy/088_conceal_tabs_spec.lua @@ -0,0 +1,96 @@ +-- Tests for correct display (cursor column position) with +conceal and +-- tabulators. + +local helpers = require('test.functional.helpers') +local feed, insert, clear, execute = + helpers.feed, helpers.insert, helpers.clear, helpers.execute + +local expect_pos = function(row, col) + return helpers.eq({row, col}, helpers.eval('[screenrow(), screencol()]')) +end + +describe('cursor and column position with conceal and tabulators', function() + setup(clear) + + it('are working', function() + insert([[ + start: + .concealed. text + |concealed| text + + .concealed. text + |concealed| text + + .a. .b. .c. .d. + |a| |b| |c| |d|]]) + + -- Conceal settings. + execute('set conceallevel=2') + execute('set concealcursor=nc') + execute('syntax match test /|/ conceal') + -- Start test. + execute('/^start:') + feed('ztj') + expect_pos(2, 1) + -- We should end up in the same column when running these commands on the + -- two lines. + feed('ft') + expect_pos(2, 17) + feed('$') + expect_pos(2, 20) + feed('0j') + expect_pos(3, 1) + feed('ft') + expect_pos(3, 17) + feed('$') + expect_pos(3, 20) + feed('j0j') + expect_pos(5, 8) + -- Same for next test block. + feed('ft') + expect_pos(5, 25) + feed('$') + expect_pos(5, 28) + feed('0j') + expect_pos(6, 8) + feed('ft') + expect_pos(6, 25) + feed('$') + expect_pos(6, 28) + feed('0j0j') + expect_pos(8, 1) + -- And check W with multiple tabs and conceals in a line. + feed('W') + expect_pos(8, 9) + feed('W') + expect_pos(8, 17) + feed('W') + expect_pos(8, 25) + feed('$') + expect_pos(8, 27) + feed('0j') + expect_pos(9, 1) + feed('W') + expect_pos(9, 9) + feed('W') + expect_pos(9, 17) + feed('W') + expect_pos(9, 25) + feed('$') + expect_pos(9, 26) + execute('set lbr') + feed('$') + expect_pos(9, 26) + execute('set list listchars=tab:>-') + feed('0') + expect_pos(9, 1) + feed('W') + expect_pos(9, 9) + feed('W') + expect_pos(9, 17) + feed('W') + expect_pos(9, 25) + feed('$') + expect_pos(9, 26) + end) +end) diff --git a/test/functional/legacy/100_lispwords_spec.lua b/test/functional/legacy/100_lispwords_spec.lua deleted file mode 100644 index 739a02f0b3..0000000000 --- a/test/functional/legacy/100_lispwords_spec.lua +++ /dev/null @@ -1,47 +0,0 @@ --- Tests for 'lispwords' setting being global-local - -local helpers = require('test.functional.helpers') -local source = helpers.source -local clear, expect = helpers.clear, helpers.expect - -describe('lispwords', function() - setup(clear) - - it('global-local', function() - source([[ - setglobal lispwords=foo,bar,baz - setlocal lispwords-=foo - setlocal lispwords+=quux - redir @A - echo "Testing 'lispwords' local value" - setglobal lispwords? - setlocal lispwords? - echo &lispwords - echo '' - redir end - setlocal lispwords< - redir @A - echo "Testing 'lispwords' value reset" - setglobal lispwords? - setlocal lispwords? - echo &lispwords - redir end - - 0put a - $d - ]]) - - -- Assert buffer contents. - expect([[ - - Testing 'lispwords' local value - lispwords=foo,bar,baz - lispwords=bar,baz,quux - bar,baz,quux - - Testing 'lispwords' value reset - lispwords=foo,bar,baz - lispwords=foo,bar,baz - foo,bar,baz]]) - end) -end) diff --git a/test/functional/legacy/105_filename_modifiers_spec.lua b/test/functional/legacy/105_filename_modifiers_spec.lua deleted file mode 100644 index 3413667022..0000000000 --- a/test/functional/legacy/105_filename_modifiers_spec.lua +++ /dev/null @@ -1,81 +0,0 @@ --- Test filename modifiers. - -local helpers = require('test.functional.helpers') -local clear = helpers.clear -local execute, expect = helpers.execute, helpers.expect - -describe('filename modifiers', function() - setup(clear) - - it('is working', function() - local tmpdir = helpers.nvim('eval', 'resolve("/tmp")') - - execute('cd ' .. tmpdir) - execute([=[set shell=sh]=]) - execute([=[set shellslash]=]) - execute([=[let tab="\t"]=]) - execute([=[command -nargs=1 Put :let expr=<q-args> | $put =expr.tab.strtrans(string(eval(expr)))]=]) - execute([=[let $HOME=fnamemodify('.', ':p:h:h:h')]=]) - execute([=[Put fnamemodify('.', ':p' )[-1:]]=]) - execute([=[Put fnamemodify('.', ':p:h' )[-1:]]=]) - execute([=[Put fnamemodify('test.out', ':p' )[-1:]]=]) - execute([=[Put fnamemodify('test.out', ':.' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':.' )]=]) - execute([=[Put fnamemodify('test.out', ':~' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':~' )]=]) - execute([=[Put fnamemodify('../testdir/a', ':t' )]=]) - execute([=[Put fnamemodify('.', ':p:t' )]=]) - execute([=[Put fnamemodify('test.out', ':p:t' )]=]) - execute([=[Put fnamemodify('test.out', ':p:e' )]=]) - execute([=[Put fnamemodify('test.out', ':p:t:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r:r' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':r:r:r' )]=]) - execute([=[Put substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '')]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:e' )]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')]=]) - execute([=[Put fnamemodify('abc.fb2.tar.gz', ':e:e:r' )]=]) - execute([=[Put fnamemodify('abc def', ':S' )]=]) - execute([=[Put fnamemodify('abc" "def', ':S' )]=]) - execute([=[Put fnamemodify('abc"%"def', ':S' )]=]) - execute([=[Put fnamemodify('abc'' ''def', ':S' )]=]) - execute([=[Put fnamemodify('abc''%''def', ':S' )]=]) - execute([=[Put fnamemodify("abc\ndef", ':S' )]=]) - execute([=[set shell=tcsh]=]) - execute([=[Put fnamemodify("abc\ndef", ':S' )]=]) - execute([=[1 delete _]=]) - - -- Assert buffer contents. - expect([=[ - fnamemodify('.', ':p' )[-1:] '/' - fnamemodify('.', ':p:h' )[-1:] 'p' - fnamemodify('test.out', ':p' )[-1:] 't' - fnamemodify('test.out', ':.' ) 'test.out' - fnamemodify('../testdir/a', ':.' ) '../testdir/a' - fnamemodify('test.out', ':~' ) 'test.out' - fnamemodify('../testdir/a', ':~' ) '../testdir/a' - fnamemodify('../testdir/a', ':t' ) 'a' - fnamemodify('.', ':p:t' ) '' - fnamemodify('test.out', ':p:t' ) 'test.out' - fnamemodify('test.out', ':p:e' ) 'out' - fnamemodify('test.out', ':p:t:e' ) 'out' - fnamemodify('abc.fb2.tar.gz', ':r' ) 'abc.fb2.tar' - fnamemodify('abc.fb2.tar.gz', ':r:r' ) 'abc.fb2' - fnamemodify('abc.fb2.tar.gz', ':r:r:r' ) 'abc' - substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '') ']=] .. tmpdir .. [=[/abc.fb2' - fnamemodify('abc.fb2.tar.gz', ':e' ) 'gz' - fnamemodify('abc.fb2.tar.gz', ':e:e' ) 'tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:e' ) 'fb2.tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:e:e') 'fb2.tar.gz' - fnamemodify('abc.fb2.tar.gz', ':e:e:r' ) 'tar' - fnamemodify('abc def', ':S' ) '''abc def''' - fnamemodify('abc" "def', ':S' ) '''abc" "def''' - fnamemodify('abc"%"def', ':S' ) '''abc"%"def''' - fnamemodify('abc'' ''def', ':S' ) '''abc''\'''' ''\''''def''' - fnamemodify('abc''%''def', ':S' ) '''abc''\''''%''\''''def''' - fnamemodify("abc\ndef", ':S' ) '''abc^@def''' - fnamemodify("abc\ndef", ':S' ) '''abc\^@def''']=]) - end) -end) diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index 9304e3b331..d1ed96cc2e 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -54,8 +54,8 @@ describe('eval', function() expect([[ ": type v; value: abc (['abc']), expr: abc (['abc']) - ": type V; value: abc]].."\x00 (['abc']), expr: abc\x00"..[[ (['abc']) - ": type V; value: abc]].."\r\x00 (['abc\r']), expr: abc\r\x00 (['abc\r"..[[']) + ": type V; value: abc]].."\000 (['abc']), expr: abc\000"..[[ (['abc']) + ": type V; value: abc]].."\r\000 (['abc\r']), expr: abc\r\000 (['abc\r"..[[']) =: type v; value: abc (['abc']), expr: "abc" (['"abc"'])]]) end) @@ -97,29 +97,29 @@ describe('eval', function() == =abcB= {{{2 setreg('c', 'abcC', 'l') - c: type V; value: abcC]].."\x00 (['abcC']), expr: abcC\x00"..[[ (['abcC']) + c: type V; value: abcC]].."\000 (['abcC']), expr: abcC\000"..[[ (['abcC']) == abcC == {{{2 setreg('d', 'abcD', 'V') - d: type V; value: abcD]].."\x00 (['abcD']), expr: abcD\x00"..[[ (['abcD']) + d: type V; value: abcD]].."\000 (['abcD']), expr: abcD\000"..[[ (['abcD']) == abcD == {{{2 setreg('e', 'abcE', 'b') - e: type ]]..'\x16'..[[4; value: abcE (['abcE']), expr: abcE (['abcE']) + e: type ]]..'\022'..[[4; value: abcE (['abcE']), expr: abcE (['abcE']) == =abcE= - {{{2 setreg('f', 'abcF', ']]..'\x16'..[[') - f: type ]]..'\x16'..[[4; value: abcF (['abcF']), expr: abcF (['abcF']) + {{{2 setreg('f', 'abcF', ']]..'\022'..[[') + f: type ]]..'\022'..[[4; value: abcF (['abcF']), expr: abcF (['abcF']) == =abcF= {{{2 setreg('g', 'abcG', 'b10') - g: type ]]..'\x16'..[[10; value: abcG (['abcG']), expr: abcG (['abcG']) + g: type ]]..'\022'..[[10; value: abcG (['abcG']), expr: abcG (['abcG']) == =abcG = - {{{2 setreg('h', 'abcH', ']]..'\x16'..[[10') - h: type ]]..'\x16'..[[10; value: abcH (['abcH']), expr: abcH (['abcH']) + {{{2 setreg('h', 'abcH', ']]..'\022'..[[10') + h: type ]]..'\022'..[[10; value: abcH (['abcH']), expr: abcH (['abcH']) == =abcH = {{{2 setreg('I', 'abcI') @@ -132,12 +132,12 @@ describe('eval', function() == =abcAabcAc= {{{2 setreg('A', 'abcAl', 'l') - A: type V; value: abcAabcAcabcAl]].."\x00 (['abcAabcAcabcAl']), expr: abcAabcAcabcAl\x00"..[[ (['abcAabcAcabcAl']) + A: type V; value: abcAabcAcabcAl]].."\000 (['abcAabcAcabcAl']), expr: abcAabcAcabcAl\000"..[[ (['abcAabcAcabcAl']) == abcAabcAcabcAl == {{{2 setreg('A', 'abcAc2', 'c') - A: type v; value: abcAabcAcabcAl]].."\x00abcAc2 (['abcAabcAcabcAl', 'abcAc2']), expr: abcAabcAcabcAl\x00"..[[abcAc2 (['abcAabcAcabcAl', 'abcAc2']) + A: type v; value: abcAabcAcabcAl]].."\000abcAc2 (['abcAabcAcabcAl', 'abcAc2']), expr: abcAabcAcabcAl\000"..[[abcAc2 (['abcAabcAcabcAl', 'abcAc2']) == =abcAabcAcabcAl abcAc2= @@ -146,50 +146,50 @@ describe('eval', function() == =abcBabcBc= {{{2 setreg('b', 'abcBb', 'ba') - b: type ]]..'\x16'..[[5; value: abcBabcBcabcBb (['abcBabcBcabcBb']), expr: abcBabcBcabcBb (['abcBabcBcabcBb']) + b: type ]]..'\022'..[[5; value: abcBabcBcabcBb (['abcBabcBcabcBb']), expr: abcBabcBcabcBb (['abcBabcBcabcBb']) == =abcBabcBcabcBb= {{{2 setreg('b', 'abcBc2', 'ca') - b: type v; value: abcBabcBcabcBb]].."\x00abcBc2 (['abcBabcBcabcBb', 'abcBc2']), expr: abcBabcBcabcBb\x00"..[[abcBc2 (['abcBabcBcabcBb', 'abcBc2']) + b: type v; value: abcBabcBcabcBb]].."\000abcBc2 (['abcBabcBcabcBb', 'abcBc2']), expr: abcBabcBcabcBb\000"..[[abcBc2 (['abcBabcBcabcBb', 'abcBc2']) == =abcBabcBcabcBb abcBc2= {{{2 setreg('b', 'abcBb2', 'b50a') - b: type ]].."\x1650; value: abcBabcBcabcBb\x00abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']), expr: abcBabcBcabcBb\x00"..[[abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']) + b: type ]].."\02250; value: abcBabcBcabcBb\000abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']), expr: abcBabcBcabcBb\000"..[[abcBc2abcBb2 (['abcBabcBcabcBb', 'abcBc2abcBb2']) == =abcBabcBcabcBb = abcBc2abcBb2 {{{2 setreg('C', 'abcCl', 'l') - C: type V; value: abcC]].."\x00abcCl\x00 (['abcC', 'abcCl']), expr: abcC\x00abcCl\x00"..[[ (['abcC', 'abcCl']) + C: type V; value: abcC]].."\000abcCl\000 (['abcC', 'abcCl']), expr: abcC\000abcCl\000"..[[ (['abcC', 'abcCl']) == abcC abcCl == {{{2 setreg('C', 'abcCc', 'c') - C: type v; value: abcC]].."\x00abcCl\x00abcCc (['abcC', 'abcCl', 'abcCc']), expr: abcC\x00abcCl\x00"..[[abcCc (['abcC', 'abcCl', 'abcCc']) + C: type v; value: abcC]].."\000abcCl\000abcCc (['abcC', 'abcCl', 'abcCc']), expr: abcC\000abcCl\000"..[[abcCc (['abcC', 'abcCl', 'abcCc']) == =abcC abcCl abcCc= {{{2 setreg('D', 'abcDb', 'b') - D: type ]].."\x165; value: abcD\x00abcDb (['abcD', 'abcDb']), expr: abcD\x00"..[[abcDb (['abcD', 'abcDb']) + D: type ]].."\0225; value: abcD\000abcDb (['abcD', 'abcDb']), expr: abcD\000"..[[abcDb (['abcD', 'abcDb']) == =abcD = abcDb {{{2 setreg('E', 'abcEb', 'b') - E: type ]].."\x165; value: abcE\x00abcEb (['abcE', 'abcEb']), expr: abcE\x00"..[[abcEb (['abcE', 'abcEb']) + E: type ]].."\0225; value: abcE\000abcEb (['abcE', 'abcEb']), expr: abcE\000"..[[abcEb (['abcE', 'abcEb']) == =abcE = abcEb {{{2 setreg('E', 'abcEl', 'l') - E: type V; value: abcE]].."\x00abcEb\x00abcEl\x00 (['abcE', 'abcEb', 'abcEl']), expr: abcE\x00abcEb\x00abcEl\x00"..[[ (['abcE', 'abcEb', 'abcEl']) + E: type V; value: abcE]].."\000abcEb\000abcEl\000 (['abcE', 'abcEb', 'abcEl']), expr: abcE\000abcEb\000abcEl\000"..[[ (['abcE', 'abcEb', 'abcEl']) == abcE abcEb abcEl == {{{2 setreg('F', 'abcFc', 'c') - F: type v; value: abcF]].."\x00abcFc (['abcF', 'abcFc']), expr: abcF\x00"..[[abcFc (['abcF', 'abcFc']) + F: type v; value: abcF]].."\000abcFc (['abcF', 'abcFc']), expr: abcF\000"..[[abcFc (['abcF', 'abcFc']) == =abcF abcFc=]]) @@ -219,36 +219,36 @@ describe('eval', function() execute([[call SetReg('F', "\n", 'b')]]) expect([[ - {{{2 setreg('A', ']]..'\x00'..[[') - A: type V; value: abcA2]].."\x00 (['abcA2']), expr: abcA2\x00"..[[ (['abcA2']) + {{{2 setreg('A', ']]..'\000'..[[') + A: type V; value: abcA2]].."\000 (['abcA2']), expr: abcA2\000"..[[ (['abcA2']) == abcA2 == - {{{2 setreg('B', ']]..'\x00'..[[', 'c') - B: type v; value: abcB2]].."\x00 (['abcB2', '']), expr: abcB2\x00"..[[ (['abcB2', '']) + {{{2 setreg('B', ']]..'\000'..[[', 'c') + B: type v; value: abcB2]].."\000 (['abcB2', '']), expr: abcB2\000"..[[ (['abcB2', '']) == =abcB2 = - {{{2 setreg('C', ']]..'\x00'..[[') - C: type V; value: abcC2]].."\x00\x00 (['abcC2', '']), expr: abcC2\x00\x00"..[[ (['abcC2', '']) + {{{2 setreg('C', ']]..'\000'..[[') + C: type V; value: abcC2]].."\000\000 (['abcC2', '']), expr: abcC2\000\000"..[[ (['abcC2', '']) == abcC2 == - {{{2 setreg('D', ']]..'\x00'..[[', 'l') - D: type V; value: abcD2]].."\x00\x00 (['abcD2', '']), expr: abcD2\x00\x00"..[[ (['abcD2', '']) + {{{2 setreg('D', ']]..'\000'..[[', 'l') + D: type V; value: abcD2]].."\000\000 (['abcD2', '']), expr: abcD2\000\000"..[[ (['abcD2', '']) == abcD2 == - {{{2 setreg('E', ']]..'\x00'..[[') - E: type V; value: abcE2]].."\x00\x00 (['abcE2', '']), expr: abcE2\x00\x00"..[[ (['abcE2', '']) + {{{2 setreg('E', ']]..'\000'..[[') + E: type V; value: abcE2]].."\000\000 (['abcE2', '']), expr: abcE2\000\000"..[[ (['abcE2', '']) == abcE2 == - {{{2 setreg('F', ']]..'\x00'..[[', 'b') - F: type ]].."\x160; value: abcF2\x00 (['abcF2', '']), expr: abcF2\x00"..[[ (['abcF2', '']) + {{{2 setreg('F', ']]..'\000'..[[', 'b') + F: type ]].."\0220; value: abcF2\000 (['abcF2', '']), expr: abcF2\000"..[[ (['abcF2', '']) == =abcF2= ]]) @@ -282,21 +282,21 @@ describe('eval', function() == =abcA3= {{{2 setreg('b', ['abcB3'], 'l') - b: type V; value: abcB3]].."\x00 (['abcB3']), expr: abcB3\x00"..[[ (['abcB3']) + b: type V; value: abcB3]].."\000 (['abcB3']), expr: abcB3\000"..[[ (['abcB3']) == abcB3 == {{{2 setreg('c', ['abcC3'], 'b') - c: type ]]..'\x16'..[[5; value: abcC3 (['abcC3']), expr: abcC3 (['abcC3']) + c: type ]]..'\022'..[[5; value: abcC3 (['abcC3']), expr: abcC3 (['abcC3']) == =abcC3= {{{2 setreg('d', ['abcD3']) - d: type V; value: abcD3]].."\x00 (['abcD3']), expr: abcD3\x00"..[[ (['abcD3']) + d: type V; value: abcD3]].."\000 (['abcD3']), expr: abcD3\000"..[[ (['abcD3']) == abcD3 == {{{2 setreg('e', [1, 2, 'abc', 3]) - e: type V; value: 1]].."\x002\x00abc\x003\x00 (['1', '2', 'abc', '3']), expr: 1\x002\x00abc\x003\x00"..[[ (['1', '2', 'abc', '3']) + e: type V; value: 1]].."\0002\000abc\0003\000 (['1', '2', 'abc', '3']), expr: 1\0002\000abc\0003\000"..[[ (['1', '2', 'abc', '3']) == 1 2 @@ -304,7 +304,7 @@ describe('eval', function() 3 == {{{2 setreg('f', [1, 2, 3]) - f: type V; value: 1]].."\x002\x003\x00 (['1', '2', '3']), expr: 1\x002\x003\x00"..[[ (['1', '2', '3']) + f: type V; value: 1]].."\0002\0003\000 (['1', '2', '3']), expr: 1\0002\0003\000"..[[ (['1', '2', '3']) == 1 2 @@ -312,49 +312,49 @@ describe('eval', function() == {{{1 Appending lists with setreg() {{{2 setreg('A', ['abcA3c'], 'c') - A: type v; value: abcA3]].."\x00abcA3c (['abcA3', 'abcA3c']), expr: abcA3\x00"..[[abcA3c (['abcA3', 'abcA3c']) + A: type v; value: abcA3]].."\000abcA3c (['abcA3', 'abcA3c']), expr: abcA3\000"..[[abcA3c (['abcA3', 'abcA3c']) == =abcA3 abcA3c= {{{2 setreg('b', ['abcB3l'], 'la') - b: type V; value: abcB3]].."\x00abcB3l\x00 (['abcB3', 'abcB3l']), expr: abcB3\x00abcB3l\x00"..[[ (['abcB3', 'abcB3l']) + b: type V; value: abcB3]].."\000abcB3l\000 (['abcB3', 'abcB3l']), expr: abcB3\000abcB3l\000"..[[ (['abcB3', 'abcB3l']) == abcB3 abcB3l == {{{2 setreg('C', ['abcC3b'], 'lb') - C: type ]].."\x166; value: abcC3\x00abcC3b (['abcC3', 'abcC3b']), expr: abcC3\x00"..[[abcC3b (['abcC3', 'abcC3b']) + C: type ]].."\0226; value: abcC3\000abcC3b (['abcC3', 'abcC3b']), expr: abcC3\000"..[[abcC3b (['abcC3', 'abcC3b']) == =abcC3 = abcC3b {{{2 setreg('D', ['abcD32']) - D: type V; value: abcD3]].."\x00abcD32\x00 (['abcD3', 'abcD32']), expr: abcD3\x00abcD32\x00"..[[ (['abcD3', 'abcD32']) + D: type V; value: abcD3]].."\000abcD32\000 (['abcD3', 'abcD32']), expr: abcD3\000abcD32\000"..[[ (['abcD3', 'abcD32']) == abcD3 abcD32 == {{{2 setreg('A', ['abcA32']) - A: type V; value: abcA3]].."\x00abcA3c\x00abcA32\x00 (['abcA3', 'abcA3c', 'abcA32']), expr: abcA3\x00abcA3c\x00abcA32\x00"..[[ (['abcA3', 'abcA3c', 'abcA32']) + A: type V; value: abcA3]].."\000abcA3c\000abcA32\000 (['abcA3', 'abcA3c', 'abcA32']), expr: abcA3\000abcA3c\000abcA32\000"..[[ (['abcA3', 'abcA3c', 'abcA32']) == abcA3 abcA3c abcA32 == {{{2 setreg('B', ['abcB3c'], 'c') - B: type v; value: abcB3]].."\x00abcB3l\x00abcB3c (['abcB3', 'abcB3l', 'abcB3c']), expr: abcB3\x00abcB3l\x00"..[[abcB3c (['abcB3', 'abcB3l', 'abcB3c']) + B: type v; value: abcB3]].."\000abcB3l\000abcB3c (['abcB3', 'abcB3l', 'abcB3c']), expr: abcB3\000abcB3l\000"..[[abcB3c (['abcB3', 'abcB3l', 'abcB3c']) == =abcB3 abcB3l abcB3c= {{{2 setreg('C', ['abcC3l'], 'l') - C: type V; value: abcC3]].."\x00abcC3b\x00abcC3l\x00 (['abcC3', 'abcC3b', 'abcC3l']), expr: abcC3\x00abcC3b\x00abcC3l\x00"..[[ (['abcC3', 'abcC3b', 'abcC3l']) + C: type V; value: abcC3]].."\000abcC3b\000abcC3l\000 (['abcC3', 'abcC3b', 'abcC3l']), expr: abcC3\000abcC3b\000abcC3l\000"..[[ (['abcC3', 'abcC3b', 'abcC3l']) == abcC3 abcC3b abcC3l == {{{2 setreg('D', ['abcD3b'], 'b') - D: type ]].."\x166; value: abcD3\x00abcD32\x00abcD3b (['abcD3', 'abcD32', 'abcD3b']), expr: abcD3\x00abcD32\x00"..[[abcD3b (['abcD3', 'abcD32', 'abcD3b']) + D: type ]].."\0226; value: abcD3\000abcD32\000abcD3b (['abcD3', 'abcD32', 'abcD3b']), expr: abcD3\000abcD32\000"..[[abcD3b (['abcD3', 'abcD32', 'abcD3b']) == =abcD3 = abcD32 @@ -370,50 +370,50 @@ describe('eval', function() expect( '\n'.. '{{{1 Appending lists with NL with setreg()\n'.. - "{{{2 setreg('A', ['\x00', 'abcA3l2'], 'l')\n".. - "A: type V; value: abcA3\x00abcA3c\x00abcA32\x00\x00\x00abcA3l2\x00 (['abcA3', 'abcA3c', 'abcA32', '\x00', 'abcA3l2']), expr: abcA3\x00abcA3c\x00abcA32\x00\x00\x00abcA3l2\x00 (['abcA3', 'abcA3c', 'abcA32', '\x00', 'abcA3l2'])\n".. + "{{{2 setreg('A', ['\000', 'abcA3l2'], 'l')\n".. + "A: type V; value: abcA3\000abcA3c\000abcA32\000\000\000abcA3l2\000 (['abcA3', 'abcA3c', 'abcA32', '\000', 'abcA3l2']), expr: abcA3\000abcA3c\000abcA32\000\000\000abcA3l2\000 (['abcA3', 'abcA3c', 'abcA32', '\000', 'abcA3l2'])\n".. '==\n'.. 'abcA3\n'.. 'abcA3c\n'.. 'abcA32\n'.. - '\x00\n'.. + '\000\n'.. 'abcA3l2\n'.. '==') execute('%delete') execute([=[call SetReg('B', ["\n", 'abcB3c2'], 'c')]=]) expect( '\n'.. - "{{{2 setreg('B', ['\x00', 'abcB3c2'], 'c')\n".. - "B: type v; value: abcB3\x00abcB3l\x00abcB3c\x00\x00\x00abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\x00', 'abcB3c2']), expr: abcB3\x00abcB3l\x00abcB3c\x00\x00\x00abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\x00', 'abcB3c2'])\n".. + "{{{2 setreg('B', ['\000', 'abcB3c2'], 'c')\n".. + "B: type v; value: abcB3\000abcB3l\000abcB3c\000\000\000abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\000', 'abcB3c2']), expr: abcB3\000abcB3l\000abcB3c\000\000\000abcB3c2 (['abcB3', 'abcB3l', 'abcB3c', '\000', 'abcB3c2'])\n".. '==\n'.. '=abcB3\n'.. 'abcB3l\n'.. 'abcB3c\n'.. - '\x00\n'.. + '\000\n'.. 'abcB3c2=') execute('%delete') execute([=[call SetReg('C', ["\n", 'abcC3b2'], 'b')]=]) expect( '\n'.. - "{{{2 setreg('C', ['\x00', 'abcC3b2'], 'b')\n".. - "C: type \x167; value: abcC3\x00abcC3b\x00abcC3l\x00\x00\x00abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\x00', 'abcC3b2']), expr: abcC3\x00abcC3b\x00abcC3l\x00\x00\x00abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\x00', 'abcC3b2'])\n".. + "{{{2 setreg('C', ['\000', 'abcC3b2'], 'b')\n".. + "C: type \0227; value: abcC3\000abcC3b\000abcC3l\000\000\000abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\000', 'abcC3b2']), expr: abcC3\000abcC3b\000abcC3l\000\000\000abcC3b2 (['abcC3', 'abcC3b', 'abcC3l', '\000', 'abcC3b2'])\n".. '==\n'.. '=abcC3 =\n'.. ' abcC3b\n'.. ' abcC3l\n'.. - ' \x00\n'.. + ' \000\n'.. ' abcC3b2') execute('%delete') execute([=[call SetReg('D', ["\n", 'abcD3b50'],'b50')]=]) expect( '\n'.. - "{{{2 setreg('D', ['\x00', 'abcD3b50'], 'b50')\n".. - "D: type \x1650; value: abcD3\x00abcD32\x00abcD3b\x00\x00\x00abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\x00', 'abcD3b50']), expr: abcD3\x00abcD32\x00abcD3b\x00\x00\x00abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\x00', 'abcD3b50'])\n".. + "{{{2 setreg('D', ['\000', 'abcD3b50'], 'b50')\n".. + "D: type \02250; value: abcD3\000abcD32\000abcD3b\000\000\000abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\000', 'abcD3b50']), expr: abcD3\000abcD32\000abcD3b\000\000\000abcD3b50 (['abcD3', 'abcD32', 'abcD3b', '\000', 'abcD3b50'])\n".. '==\n'.. '=abcD3 =\n'.. ' abcD32\n'.. ' abcD3b\n'.. - ' \x00\n'.. + ' \000\n'.. ' abcD3b50') end) @@ -425,14 +425,14 @@ describe('eval', function() execute([=[call SetReg('a', ['abcA4-0', "\n", "abcA4-2\n", "\nabcA4-3", "abcA4-4\nabcA4-4-2"])]=]) expect( '\n'.. - "{{{2 setreg('a', ['abcA4-0', '\x00', 'abcA4-2\x00', '\x00abcA4-3', 'abcA4-4\x00abcA4-4-2'])\n".. - "a: type V; value: abcA4-0\x00\x00\x00abcA4-2\x00\x00\x00abcA4-3\x00abcA4-4\x00abcA4-4-2\x00 (['abcA4-0', '\x00', 'abcA4-2\x00', '\x00abcA4-3', 'abcA4-4\x00abcA4-4-2']), expr: abcA4-0\x00\x00\x00abcA4-2\x00\x00\x00abcA4-3\x00abcA4-4\x00abcA4-4-2\x00 (['abcA4-0', '\x00', 'abcA4-2\x00', '\x00abcA4-3', 'abcA4-4\x00abcA4-4-2'])\n".. + "{{{2 setreg('a', ['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2'])\n".. + "a: type V; value: abcA4-0\000\000\000abcA4-2\000\000\000abcA4-3\000abcA4-4\000abcA4-4-2\000 (['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2']), expr: abcA4-0\000\000\000abcA4-2\000\000\000abcA4-3\000abcA4-4\000abcA4-4-2\000 (['abcA4-0', '\000', 'abcA4-2\000', '\000abcA4-3', 'abcA4-4\000abcA4-4-2'])\n".. '==\n'.. 'abcA4-0\n'.. - '\x00\n'.. - 'abcA4-2\x00\n'.. - '\x00abcA4-3\n'.. - 'abcA4-4\x00abcA4-4-2\n'.. + '\000\n'.. + 'abcA4-2\000\n'.. + '\000abcA4-3\n'.. + 'abcA4-4\000abcA4-4-2\n'.. '==') end) @@ -441,14 +441,14 @@ describe('eval', function() execute([=[call SetReg('b', ['abcB4c-0', "\n", "abcB4c-2\n", "\nabcB4c-3", "abcB4c-4\nabcB4c-4-2"], 'c')]=]) expect( '\n'.. - "{{{2 setreg('b', ['abcB4c-0', '\x00', 'abcB4c-2\x00', '\x00abcB4c-3', 'abcB4c-4\x00abcB4c-4-2'], 'c')\n".. - "b: type v; value: abcB4c-0\x00\x00\x00abcB4c-2\x00\x00\x00abcB4c-3\x00abcB4c-4\x00abcB4c-4-2 (['abcB4c-0', '\x00', 'abcB4c-2\x00', '\x00abcB4c-3', 'abcB4c-4\x00abcB4c-4-2']), expr: abcB4c-0\x00\x00\x00abcB4c-2\x00\x00\x00abcB4c-3\x00abcB4c-4\x00abcB4c-4-2 (['abcB4c-0', '\x00', 'abcB4c-2\x00', '\x00abcB4c-3', 'abcB4c-4\x00abcB4c-4-2'])\n".. + "{{{2 setreg('b', ['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2'], 'c')\n".. + "b: type v; value: abcB4c-0\000\000\000abcB4c-2\000\000\000abcB4c-3\000abcB4c-4\000abcB4c-4-2 (['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2']), expr: abcB4c-0\000\000\000abcB4c-2\000\000\000abcB4c-3\000abcB4c-4\000abcB4c-4-2 (['abcB4c-0', '\000', 'abcB4c-2\000', '\000abcB4c-3', 'abcB4c-4\000abcB4c-4-2'])\n".. '==\n'.. '=abcB4c-0\n'.. - '\x00\n'.. - 'abcB4c-2\x00\n'.. - '\x00abcB4c-3\n'.. - 'abcB4c-4\x00abcB4c-4-2=') + '\000\n'.. + 'abcB4c-2\000\n'.. + '\000abcB4c-3\n'.. + 'abcB4c-4\000abcB4c-4-2=') end) it('setting lists with NLs with setreg(), part 3', function() @@ -456,14 +456,14 @@ describe('eval', function() execute([=[call SetReg('c', ['abcC4l-0', "\n", "abcC4l-2\n", "\nabcC4l-3", "abcC4l-4\nabcC4l-4-2"], 'l')]=]) expect( '\n'.. - "{{{2 setreg('c', ['abcC4l-0', '\x00', 'abcC4l-2\x00', '\x00abcC4l-3', 'abcC4l-4\x00abcC4l-4-2'], 'l')\n".. - "c: type V; value: abcC4l-0\x00\x00\x00abcC4l-2\x00\x00\x00abcC4l-3\x00abcC4l-4\x00abcC4l-4-2\x00 (['abcC4l-0', '\x00', 'abcC4l-2\x00', '\x00abcC4l-3', 'abcC4l-4\x00abcC4l-4-2']), expr: abcC4l-0\x00\x00\x00abcC4l-2\x00\x00\x00abcC4l-3\x00abcC4l-4\x00abcC4l-4-2\x00 (['abcC4l-0', '\x00', 'abcC4l-2\x00', '\x00abcC4l-3', 'abcC4l-4\x00abcC4l-4-2'])\n".. + "{{{2 setreg('c', ['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2'], 'l')\n".. + "c: type V; value: abcC4l-0\000\000\000abcC4l-2\000\000\000abcC4l-3\000abcC4l-4\000abcC4l-4-2\000 (['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2']), expr: abcC4l-0\000\000\000abcC4l-2\000\000\000abcC4l-3\000abcC4l-4\000abcC4l-4-2\000 (['abcC4l-0', '\000', 'abcC4l-2\000', '\000abcC4l-3', 'abcC4l-4\000abcC4l-4-2'])\n".. '==\n'.. 'abcC4l-0\n'.. - '\x00\n'.. - 'abcC4l-2\x00\n'.. - '\x00abcC4l-3\n'.. - 'abcC4l-4\x00abcC4l-4-2\n'.. + '\000\n'.. + 'abcC4l-2\000\n'.. + '\000abcC4l-3\n'.. + 'abcC4l-4\000abcC4l-4-2\n'.. '==') end) it('setting lists with NLs with setreg(), part 4', function() @@ -471,28 +471,28 @@ describe('eval', function() execute([=[call SetReg('d', ['abcD4b-0', "\n", "abcD4b-2\n", "\nabcD4b-3", "abcD4b-4\nabcD4b-4-2"], 'b')]=]) expect( '\n'.. - "{{{2 setreg('d', ['abcD4b-0', '\x00', 'abcD4b-2\x00', '\x00abcD4b-3', 'abcD4b-4\x00abcD4b-4-2'], 'b')\n".. - "d: type \x1619; value: abcD4b-0\x00\x00\x00abcD4b-2\x00\x00\x00abcD4b-3\x00abcD4b-4\x00abcD4b-4-2 (['abcD4b-0', '\x00', 'abcD4b-2\x00', '\x00abcD4b-3', 'abcD4b-4\x00abcD4b-4-2']), expr: abcD4b-0\x00\x00\x00abcD4b-2\x00\x00\x00abcD4b-3\x00abcD4b-4\x00abcD4b-4-2 (['abcD4b-0', '\x00', 'abcD4b-2\x00', '\x00abcD4b-3', 'abcD4b-4\x00abcD4b-4-2'])\n".. + "{{{2 setreg('d', ['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2'], 'b')\n".. + "d: type \02219; value: abcD4b-0\000\000\000abcD4b-2\000\000\000abcD4b-3\000abcD4b-4\000abcD4b-4-2 (['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2']), expr: abcD4b-0\000\000\000abcD4b-2\000\000\000abcD4b-3\000abcD4b-4\000abcD4b-4-2 (['abcD4b-0', '\000', 'abcD4b-2\000', '\000abcD4b-3', 'abcD4b-4\000abcD4b-4-2'])\n".. '==\n'.. '=abcD4b-0 =\n'.. - ' \x00\n'.. - ' abcD4b-2\x00\n'.. - ' \x00abcD4b-3\n'.. - ' abcD4b-4\x00abcD4b-4-2') + ' \000\n'.. + ' abcD4b-2\000\n'.. + ' \000abcD4b-3\n'.. + ' abcD4b-4\000abcD4b-4-2') end) it('setting lists with NLs with setreg(), part 5', function() execute('so test_eval_setup.vim') execute([=[call SetReg('e', ['abcE4b10-0', "\n", "abcE4b10-2\n", "\nabcE4b10-3", "abcE4b10-4\nabcE4b10-4-2"], 'b10')]=]) expect( '\n'.. - "{{{2 setreg('e', ['abcE4b10-0', '\x00', 'abcE4b10-2\x00', '\x00abcE4b10-3', 'abcE4b10-4\x00abcE4b10-4-2'], 'b10')\n".. - "e: type \x1610; value: abcE4b10-0\x00\x00\x00abcE4b10-2\x00\x00\x00abcE4b10-3\x00abcE4b10-4\x00abcE4b10-4-2 (['abcE4b10-0', '\x00', 'abcE4b10-2\x00', '\x00abcE4b10-3', 'abcE4b10-4\x00abcE4b10-4-2']), expr: abcE4b10-0\x00\x00\x00abcE4b10-2\x00\x00\x00abcE4b10-3\x00abcE4b10-4\x00abcE4b10-4-2 (['abcE4b10-0', '\x00', 'abcE4b10-2\x00', '\x00abcE4b10-3', 'abcE4b10-4\x00abcE4b10-4-2'])\n".. + "{{{2 setreg('e', ['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2'], 'b10')\n".. + "e: type \02210; value: abcE4b10-0\000\000\000abcE4b10-2\000\000\000abcE4b10-3\000abcE4b10-4\000abcE4b10-4-2 (['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2']), expr: abcE4b10-0\000\000\000abcE4b10-2\000\000\000abcE4b10-3\000abcE4b10-4\000abcE4b10-4-2 (['abcE4b10-0', '\000', 'abcE4b10-2\000', '\000abcE4b10-3', 'abcE4b10-4\000abcE4b10-4-2'])\n".. '==\n'.. '=abcE4b10-0=\n'.. - ' \x00\n'.. - ' abcE4b10-2\x00\n'.. - ' \x00abcE4b10-3\n'.. - ' abcE4b10-4\x00abcE4b10-4-2') + ' \000\n'.. + ' abcE4b10-2\000\n'.. + ' \000abcE4b10-3\n'.. + ' abcE4b10-4\000abcE4b10-4-2') end) it('search and expressions', function() @@ -507,14 +507,14 @@ describe('eval', function() /: type v; value: abc/ (['abc/']), expr: abc/ (['abc/']) == =abc/= - {{{2 setreg('/', ['abc/]]..'\x00'..[[']) - /: type v; value: abc/]].."\x00 (['abc/\x00']), expr: abc/\x00 (['abc/\x00"..[[']) + {{{2 setreg('/', ['abc/]]..'\000'..[[']) + /: type v; value: abc/]].."\000 (['abc/\000']), expr: abc/\000 (['abc/\000"..[[']) == - =abc/]]..'\x00'..[[= + =abc/]]..'\000'..[[= {{{2 setreg('=', ['"abc/"']) =: type v; value: abc/ (['abc/']), expr: "abc/" (['"abc/"']) - {{{2 setreg('=', ['"abc/]]..'\x00'..[["']) - =: type v; value: abc/]].."\x00 (['abc/\x00"..[[']), expr: "abc/]]..'\x00'..[[" (['"abc/]]..'\x00'..[["'])]]) + {{{2 setreg('=', ['"abc/]]..'\000'..[["']) + =: type v; value: abc/]].."\000 (['abc/\000"..[[']), expr: "abc/]]..'\000'..[[" (['"abc/]]..'\000'..[["'])]]) end) if has_clipboard() then diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua new file mode 100644 index 0000000000..2a32aea127 --- /dev/null +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -0,0 +1,75 @@ +-- Test filename modifiers. + +local helpers = require('test.functional.helpers') +local clear, source = helpers.clear, helpers.source +local call, eq, nvim = helpers.call, helpers.eq, helpers.meths + +local function expected_empty() + eq({}, nvim.get_vvar('errors')) +end + +describe('filename modifiers', function() + before_each(function() + clear() + + source([=[ + func Test_fnamemodify() + let tmpdir = resolve('/tmp') + execute 'cd '. tmpdir + set shell=sh + set shellslash + let $HOME=fnamemodify('.', ':p:h:h:h') + call assert_equal('/', fnamemodify('.', ':p')[-1:]) + call assert_equal('p', fnamemodify('.', ':p:h')[-1:]) + call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) + call assert_equal('test.out', fnamemodify('test.out', ':.')) + call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':.')) + call assert_equal('test.out', fnamemodify('test.out', ':~')) + call assert_equal('../testdir/a', fnamemodify('../testdir/a', ':~')) + call assert_equal('a', fnamemodify('../testdir/a', ':t')) + call assert_equal('', fnamemodify('.', ':p:t')) + call assert_equal('test.out', fnamemodify('test.out', ':p:t')) + call assert_equal('out', fnamemodify('test.out', ':p:e')) + call assert_equal('out', fnamemodify('test.out', ':p:t:e')) + call assert_equal('abc.fb2.tar', fnamemodify('abc.fb2.tar.gz', ':r')) + call assert_equal('abc.fb2', fnamemodify('abc.fb2.tar.gz', ':r:r')) + call assert_equal('abc', fnamemodify('abc.fb2.tar.gz', ':r:r:r')) + call assert_equal(tmpdir .'/abc.fb2', substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(nvim/testdir/.*\)', '\1', '')) + call assert_equal('gz', fnamemodify('abc.fb2.tar.gz', ':e')) + call assert_equal('tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')) + call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) + call assert_equal('''abc def''', fnamemodify('abc def', ':S')) + call assert_equal('''abc" "def''', fnamemodify('abc" "def', ':S')) + call assert_equal('''abc"%"def''', fnamemodify('abc"%"def', ':S')) + call assert_equal('''abc''\'''' ''\''''def''', fnamemodify('abc'' ''def', ':S')) + call assert_equal('''abc''\''''%''\''''def''', fnamemodify('abc''%''def', ':S')) + new foo.txt + call assert_equal(expand('%:r:S'), shellescape(expand('%:r'))) + call assert_equal('foo,''foo'',foo.txt', join([expand('%:r'), expand('%:r:S'), expand('%')], ',')) + quit + + call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S')) + set shell=tcsh + call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + endfunc + + func Test_expand() + new + call assert_equal("", expand('%:S')) + quit + endfunc + ]=]) + end) + + it('is working', function() + call('Test_fnamemodify') + expected_empty() + end) + + it('works for :S in an unnamed buffer', function() + call('Test_expand') + expected_empty() + end) +end) diff --git a/test/functional/legacy/lispwords_spec.lua b/test/functional/legacy/lispwords_spec.lua new file mode 100644 index 0000000000..48df4de55e --- /dev/null +++ b/test/functional/legacy/lispwords_spec.lua @@ -0,0 +1,25 @@ +local helpers = require('test.functional.helpers') +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local execute = helpers.execute +local source = helpers.source + +describe('lispwords', function() + before_each(clear) + + it('should be set global-local',function() + source([[ + setglobal lispwords=foo,bar,baz + setlocal lispwords-=foo + setlocal lispwords+=quux]]) + eq('foo,bar,baz', eval('&g:lispwords')) + eq('bar,baz,quux', eval('&l:lispwords')) + eq('bar,baz,quux', eval('&lispwords')) + + execute('setlocal lispwords<') + eq('foo,bar,baz', eval('&g:lispwords')) + eq('foo,bar,baz', eval('&l:lispwords')) + eq('foo,bar,baz', eval('&lispwords')) + end) +end) diff --git a/test/functional/options/shortmess_spec.lua b/test/functional/options/shortmess_spec.lua new file mode 100644 index 0000000000..04a64d2943 --- /dev/null +++ b/test/functional/options/shortmess_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') +local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute + +describe("'shortmess'", function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + end) + + after_each(function() + screen:detach() + end) + + describe('"F" flag', function() + it('hides messages about the files read', function() + execute('e test') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "test" is a directory | + ]]) + execute('set shortmess=F') + execute('e test') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + :e test | + ]]) + end) + end) +end) diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua index 18ff0f5156..90cc2af9c0 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers') local eq, nvim_eval, nvim_command, exc_exec = helpers.eq, helpers.eval, helpers.command, helpers.exc_exec +local ok = helpers.ok local plugin_helpers = require('test.functional.plugin.helpers') local reset = plugin_helpers.reset @@ -21,10 +22,8 @@ describe('In autoload/msgpack.vim', function() end local nan = -(1.0/0.0-1.0/0.0) - local minus_nan = 1.0/0.0-1.0/0.0 local inf = 1.0/0.0 local minus_inf = -(1.0/0.0) - local has_minus_nan = tostring(nan) ~= tostring(minus_nan) describe('function msgpack#equal', function() local msgpack_eq = function(expected, a, b) @@ -160,9 +159,9 @@ describe('In autoload/msgpack.vim', function() it('compares raw floats correctly', function() msgpack_eq(1, '0.0', '0.0') msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '(1.0/0.0-1.0/0.0)') - if has_minus_nan then - msgpack_eq(0, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') - end + -- both (1.0/0.0-1.0/0.0) and -(1.0/0.0-1.0/0.0) now return + -- str2float('nan'). ref: @18d1ba3422d + msgpack_eq(1, '(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') msgpack_eq(1, '-(1.0/0.0-1.0/0.0)', '-(1.0/0.0-1.0/0.0)') msgpack_eq(1, '1.0/0.0', '1.0/0.0') msgpack_eq(1, '-(1.0/0.0)', '-(1.0/0.0)') @@ -178,10 +177,8 @@ describe('In autoload/msgpack.vim', function() it('compares float specials with raw floats correctly', function() msgpack_eq(1, sp('float', '0.0'), '0.0') msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') - if has_minus_nan then - msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') - msgpack_eq(0, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') - end + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') + msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '(1.0/0.0-1.0/0.0)') msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), '-(1.0/0.0-1.0/0.0)') msgpack_eq(1, sp('float', '1.0/0.0'), '1.0/0.0') msgpack_eq(1, sp('float', '-(1.0/0.0)'), '-(1.0/0.0)') @@ -207,10 +204,8 @@ describe('In autoload/msgpack.vim', function() msgpack_eq(0, sp('float', '0.0'), sp('float', '-(1.0/0.0)')) msgpack_eq(0, sp('float', '1.0/0.0'), sp('float', '-(1.0/0.0)')) msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0)')) - if has_minus_nan then - msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), - sp('float', '-(1.0/0.0-1.0/0.0)')) - end + msgpack_eq(1, sp('float', '(1.0/0.0-1.0/0.0)'), + sp('float', '-(1.0/0.0-1.0/0.0)')) msgpack_eq(1, sp('float', '-(1.0/0.0-1.0/0.0)'), sp('float', '-(1.0/0.0-1.0/0.0)')) msgpack_eq(0, sp('float', '(1.0/0.0-1.0/0.0)'), sp('float', '1.0/0.0')) @@ -392,9 +387,7 @@ describe('In autoload/msgpack.vim', function() string_eq('0.0', sp('float', '0.0')) string_eq('inf', sp('float', '(1.0/0.0)')) string_eq('-inf', sp('float', '-(1.0/0.0)')) - if has_minus_nan then - string_eq('-nan', sp('float', '(1.0/0.0-1.0/0.0)')) - end + string_eq('nan', sp('float', '(1.0/0.0-1.0/0.0)')) string_eq('nan', sp('float', '-(1.0/0.0-1.0/0.0)')) string_eq('FALSE', sp('boolean', '0')) string_eq('TRUE', sp('boolean', '1')) @@ -413,9 +406,7 @@ describe('In autoload/msgpack.vim', function() string_eq('0.0', '0.0') string_eq('inf', '(1.0/0.0)') string_eq('-inf', '-(1.0/0.0)') - if has_minus_nan then - string_eq('-nan', '(1.0/0.0-1.0/0.0)') - end + string_eq('nan', '(1.0/0.0-1.0/0.0)') string_eq('nan', '-(1.0/0.0-1.0/0.0)') end) end) @@ -547,8 +538,11 @@ describe('In autoload/msgpack.vim', function() end if expected_val_full == expected_val_full then eq(expected_val_full, nvim_eval('g:__val')) - else - eq(tostring(expected_val_full), tostring(nvim_eval('g:__val'))) + else -- NaN + local nvim_nan = tostring(nvim_eval('g:__val')) + -- -NaN is a hardware-specific detail, there's no need to test for it. + -- Accept ether 'nan' or '-nan' as the response. + ok(nvim_nan == 'nan' or nvim_nan == '-nan') end nvim_command('unlet g:__val') end @@ -615,7 +609,6 @@ describe('In autoload/msgpack.vim', function() eval_eq('float', inf, 'inf') eval_eq('float', minus_inf, '-inf') eval_eq('float', nan, 'nan') - eval_eq('float', minus_nan, '-nan') eval_eq('float', 1.0e10, '1.0e10') eval_eq('float', 1.0e10, '1.0e+10') eval_eq('float', -1.0e10, '-1.0e+10') diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 4100a30452..a3f617eeb0 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -2215,7 +2215,7 @@ describe('In plugin/shada.vim', function() describe('event BufWriteCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2271,7 +2271,7 @@ describe('In plugin/shada.vim', function() describe('event FileWriteCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2310,7 +2310,7 @@ describe('In plugin/shada.vim', function() describe('event FileAppendCmd', function() it('works', function() nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Jump with timestamp ' .. epoch .. ':', ' % Key________ Description Value', ' + n name \'A\'', @@ -2512,7 +2512,7 @@ describe('syntax/shada.vim', function() it('works', function() nvim_command('syntax on') nvim_command('setlocal syntax=shada') - curbuf('set_line_slice', 0, 0, true, true, { + curbuf('set_lines', 0, 1, true, { 'Header with timestamp ' .. epoch .. ':', ' % Key Value', ' + t "test"', diff --git a/test/functional/preload.lua b/test/functional/preload.lua index 5f34f7fa6e..1971ef77cc 100644 --- a/test/functional/preload.lua +++ b/test/functional/preload.lua @@ -1,5 +1,4 @@ -- Modules loaded here will not be cleared and reloaded by Busted. -- Busted started doing this to help provide more isolation. See issue #62 -- for more information about this. -local ffi = require('ffi') local helpers = require('test.functional.helpers') diff --git a/test/functional/server/server_spec.lua b/test/functional/server/server_spec.lua index 649e9dbabe..7f53522c08 100644 --- a/test/functional/server/server_spec.lua +++ b/test/functional/server/server_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers') local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths -local os_is_windows = helpers.os_is_windows +local os_name = helpers.os_name describe('serverstart(), serverstop()', function() before_each(clear) @@ -39,7 +39,7 @@ describe('serverstart(), serverstop()', function() eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - local servername = (os_is_windows() + local servername = (os_name() == 'windows' and [[\\.\pipe\Xtest-functional-server-server-pipe]] or 'Xtest-functional-server-server-socket') funcs.serverstart(servername) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua new file mode 100644 index 0000000000..dcc4a54610 --- /dev/null +++ b/test/functional/terminal/edit_spec.lua @@ -0,0 +1,75 @@ +local helpers = require('test.functional.helpers') +local screen = require('test.functional.ui.screen') + +local curbufmeths = helpers.curbufmeths +local curwinmeths = helpers.curwinmeths +local nvim_dir = helpers.nvim_dir +local command = helpers.command +local meths = helpers.meths +local clear = helpers.clear +local eq = helpers.eq + +describe(':edit term://*', function() + local get_screen = function(columns, lines) + local scr = screen.new(columns, lines) + scr:attach(false) + return scr + end + + before_each(function() + clear() + meths.set_option('shell', nvim_dir .. '/shell-test') + meths.set_option('shellcmdflag', 'EXE') + end) + + it('runs TermOpen event', function() + meths.set_var('termopen_runs', {}) + command('autocmd TermOpen * :call add(g:termopen_runs, expand("<amatch>"))') + command('edit term://') + local termopen_runs = meths.get_var('termopen_runs') + eq(1, #termopen_runs) + eq(termopen_runs[1], termopen_runs[1]:match('^term://.//%d+:$')) + end) + + it('runs TermOpen early enough to respect terminal_scrollback_buffer_size', function() + local columns, lines = 20, 4 + local scr = get_screen(columns, lines) + local rep = 'a' + meths.set_option('shellcmdflag', 'REP ' .. rep) + local rep_size = rep:byte() + local sb = 10 + local gsb = 20 + meths.set_var('terminal_scrollback_buffer_size', gsb) + command('autocmd TermOpen * :let b:terminal_scrollback_buffer_size = ' + .. tostring(sb)) + command('edit term://foobar') + local bufcontents = {} + local winheight = curwinmeths.get_height() + -- I have no idea why there is + 4 needed. But otherwise it works fine with + -- different scrollbacks. + local shift = -4 + local buf_cont_start = rep_size - 1 - sb - winheight - shift + local bufline = function(i) return ('%d: foobar'):format(i) end + for i = buf_cont_start,(rep_size - 1) do + bufcontents[#bufcontents + 1] = bufline(i) + end + bufcontents[#bufcontents + 1] = '' + bufcontents[#bufcontents + 1] = '[Process exited 0]' + -- Do not ask me why displayed screen is one line *before* buffer + -- contents: buffer starts with 87:, screen with 86:. + local exp_screen = '\n' + local did_cursor = false + for i = 0,(winheight - 1) do + local line = bufline(buf_cont_start + i - 1) + exp_screen = (exp_screen + .. (did_cursor and '' or '^') + .. line + .. (' '):rep(columns - #line) + .. '|\n') + did_cursor = true + end + exp_screen = exp_screen .. (' '):rep(columns) .. '|\n' + scr:expect(exp_screen) + eq(bufcontents, curbufmeths.get_lines(1, -1, true)) + end) +end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 838d05a6df..48376a344f 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -41,7 +41,7 @@ describe('tui', function() -- INSERT -- | -- TERMINAL -- | ]]) - feed('\x1b') + feed('\027') screen:expect([[ abc | test1 | @@ -57,7 +57,7 @@ describe('tui', function() local keys = 'dfghjkl' for c in keys:gmatch('.') do execute('nnoremap <a-'..c..'> ialt-'..c..'<cr><esc>') - feed('\x1b'..c) + feed('\027'..c) end screen:expect([[ alt-j | @@ -87,7 +87,7 @@ describe('tui', function() -- Example: for input ALT+j: -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê". -- * Nvim (after #3982) inserts "j". - feed('i\x1bj') + feed('i\027j') screen:expect([[ j{1: } | ~ | @@ -101,9 +101,9 @@ describe('tui', function() it('accepts ascii control sequences', function() feed('i') - feed('\x16\x07') -- ctrl+g - feed('\x16\x16') -- ctrl+v - feed('\x16\x0d') -- ctrl+m + feed('\022\007') -- ctrl+g + feed('\022\022') -- ctrl+v + feed('\022\013') -- ctrl+m screen:expect([[ {3:^G^V^M}{1: } | ~ | @@ -116,7 +116,7 @@ describe('tui', function() end) it('automatically sends <Paste> for bracketed paste sequences', function() - feed('i\x1b[200~') + feed('i\027[200~') screen:expect([[ {1: } | ~ | @@ -136,7 +136,7 @@ describe('tui', function() -- INSERT (paste) -- | -- TERMINAL -- | ]]) - feed('\x1b[201~') + feed('\027[201~') screen:expect([[ pasted from terminal{1: } | ~ | @@ -154,9 +154,9 @@ describe('tui', function() for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end - feed('i\x1b[200~') + feed('i\027[200~') feed(table.concat(t, '\n')) - feed('\x1b[201~') + feed('\027[201~') screen:expect([[ item 2997 | item 2998 | @@ -204,7 +204,7 @@ describe('tui focus event handling', function() end) it('can handle focus events in normal mode', function() - feed('\x1b[I') + feed('\027[I') screen:expect([[ {1: } | ~ | @@ -215,7 +215,7 @@ describe('tui focus event handling', function() -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ {1: } | ~ | @@ -230,7 +230,7 @@ describe('tui focus event handling', function() it('can handle focus events in insert mode', function() execute('set noshowmode') feed('i') - feed('\x1b[I') + feed('\027[I') screen:expect([[ {1: } | ~ | @@ -240,7 +240,7 @@ describe('tui focus event handling', function() gained | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ {1: } | ~ | @@ -254,7 +254,7 @@ describe('tui focus event handling', function() it('can handle focus events in cmdline mode', function() feed(':') - feed('\x1b[I') + feed('\027[I') screen:expect([[ | ~ | @@ -264,7 +264,7 @@ describe('tui focus event handling', function() g{1:a}ined | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ | ~ | @@ -281,7 +281,7 @@ describe('tui focus event handling', function() execute('set laststatus=0') execute('set noshowmode') execute('terminal') - feed('\x1b[I') + feed('\027[I') screen:expect([[ ready $ | [Process exited 0]{1: } | @@ -291,7 +291,7 @@ describe('tui focus event handling', function() gained | -- TERMINAL -- | ]]) - feed('\x1b[O') + feed('\027[O') screen:expect([[ ready $ | [Process exited 0]{1: } | diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 80f46326ee..b35e0cde69 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -219,12 +219,23 @@ function Screen:expect(expected, attr_ids, attr_ignore) local ids = attr_ids or self._default_attr_ids local ignore = attr_ignore or self._default_attr_ignore self:wait(function() + local actual_rows = {} for i = 1, self._height do - local expected_row = expected_rows[i] - local actual_row = self:_row_repr(self._rows[i], ids, ignore) - if expected_row ~= actual_row then - return 'Row '..tostring(i)..' didn\'t match.\nExpected: "'.. - expected_row..'"\nActual: "'..actual_row..'"' + actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore) + end + for i = 1, self._height do + if expected_rows[i] ~= actual_rows[i] then + local msg_expected_rows = {} + for j = 1, #expected_rows do + msg_expected_rows[j] = expected_rows[j] + end + msg_expected_rows[i] = '*' .. msg_expected_rows[i] + actual_rows[i] = '*' .. actual_rows[i] + return ( + 'Row ' .. tostring(i) .. ' didn\'t match.\n' + .. 'Expected:\n|' .. table.concat(msg_expected_rows, '|\n|') .. '|\n' + .. 'Actual:\n|' .. table.concat(actual_rows, '|\n|') .. '|' + ) end end end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 4bb9707cda..01b2bad059 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers') +local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq local execute, source, expect = helpers.execute, helpers.source, helpers.expect @@ -99,6 +100,18 @@ describe('completion', function() feed('o<C-r>=TestComplete()<CR><ESC>') eq('', eval('getline(3)')) end) + it('does not change modified state if noinsert', function() + execute('set completeopt+=menuone,noinsert') + execute('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) + it('does not change modified state if noselect', function() + execute('set completeopt+=menuone,noselect') + execute('setlocal nomodified') + feed('i<C-r>=TestComplete()<CR><ESC>') + eq(0, eval('&l:modified')) + end) end) describe("refresh:always", function() @@ -141,4 +154,62 @@ describe('completion', function() June]]) end) end) + + it('disables folding during completion', function () + execute("set foldmethod=indent") + feed('i<Tab>foo<CR><Tab>bar<Esc>ggA<C-x><C-l>') + eq(-1, eval('foldclosed(1)')) + end) + + it('popupmenu is not interrupted by events', function () + local screen = Screen.new(40, 8) + screen:attach() + screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}}) + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.Grey}, + [3] = {bold = true}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen}, + }) + + feed('ifoobar fooegg<cr>f<c-p>') + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar } | + {2:fooegg } | + ~ | + ~ | + ~ | + {3:-- }{4:match 1 of 2} | + ]]) + + eval('1 + 1') + -- popupmenu still visible + screen:expect([[ + foobar fooegg | + fooegg^ | + {1:foobar } | + {2:fooegg } | + ~ | + ~ | + ~ | + {3:-- }{4:match 1 of 2} | + ]]) + + feed('<c-p>') + -- Didn't restart completion: old matches still used + screen:expect([[ + foobar fooegg | + foobar^ | + {2:foobar } | + {1:fooegg } | + ~ | + ~ | + ~ | + {3:-- }{4:match 2 of 2} | + ]]) + end) + end) + diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua index 78e25297f2..30cb86f8d1 100644 --- a/test/functional/viml/errorlist_spec.lua +++ b/test/functional/viml/errorlist_spec.lua @@ -3,32 +3,44 @@ local helpers = require('test.functional.helpers') local clear = helpers.clear local command = helpers.command local eq = helpers.eq +local exc_exec = helpers.exc_exec local get_cur_win_var = helpers.curwinmeths.get_var --- local exc_exec = helpers.exc_exec describe('setqflist()', function() local setqflist = helpers.funcs.setqflist before_each(clear) + it('requires a list for {list}', function() + eq('Vim(call):E714: List required', exc_exec('call setqflist("foo")')) + eq('Vim(call):E714: List required', exc_exec('call setqflist(5)')) + eq('Vim(call):E714: List required', exc_exec('call setqflist({})')) + end) + + it('requires a string for {action}', function() + eq('Vim(call):E114: String required', exc_exec('call setqflist([], 5)')) + eq('Vim(call):E114: String required', exc_exec('call setqflist([], [])')) + eq('Vim(call):E114: String required', exc_exec('call setqflist([], {})')) + end) + it('sets w:quickfix_title', function() setqflist({''}, 'r', 'foo') command('copen') eq(':foo', get_cur_win_var('quickfix_title')) end) - it('expects a proper type for {title}', function() + it('requires string or number for {title}', function() command('copen') - setqflist({''}, 'r', '5') + setqflist({}, 'r', '5') eq(':5', get_cur_win_var('quickfix_title')) - setqflist({''}, 'r', 6) - eq(':setqflist()', get_cur_win_var('quickfix_title')) - -- local exc = exc_exec('call setqflist([""], "r", function("function"))') - -- eq('Vim(call):E729: using Funcref as a String', exc) - -- exc = exc_exec('call setqflist([""], "r", [])') - -- eq('Vim(call):E730: using List as a String', exc) - -- exc = exc_exec('call setqflist([""], "r", {})') - -- eq('Vim(call):E731: using Dictionary as a String', exc) + setqflist({}, 'r', 6) + eq(':6', get_cur_win_var('quickfix_title')) + local exc = exc_exec('call setqflist([], "r", function("function"))') + eq('Vim(call):E729: using Funcref as a String', exc) + exc = exc_exec('call setqflist([], "r", [])') + eq('Vim(call):E730: using List as a String', exc) + exc = exc_exec('call setqflist([], "r", {})') + eq('Vim(call):E731: using Dictionary as a String', exc) end) end) @@ -37,10 +49,22 @@ describe('setloclist()', function() before_each(clear) + it('requires a list for {list}', function() + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, "foo")')) + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, 5)')) + eq('Vim(call):E714: List required', exc_exec('call setloclist(0, {})')) + end) + + it('requires a string for {action}', function() + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], 5)')) + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], [])')) + eq('Vim(call):E114: String required', exc_exec('call setloclist(0, [], {})')) + end) + it('sets w:quickfix_title for the correct window', function() command('rightbelow vsplit') - setloclist(1, {''}, 'r', 'foo') - setloclist(2, {''}, 'r', 'bar') + setloclist(1, {}, 'r', 'foo') + setloclist(2, {}, 'r', 'bar') command('lopen') eq(':bar', get_cur_win_var('quickfix_title')) command('lclose | wincmd w | lopen') diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua new file mode 100644 index 0000000000..665f5d4467 --- /dev/null +++ b/test/functional/viml/function_spec.lua @@ -0,0 +1,29 @@ +local helpers = require('test.functional.helpers') + +local clear = helpers.clear +local eq = helpers.eq +local exc_exec = helpers.exc_exec + +describe('Up to MAX_FUNC_ARGS arguments are handled by', function() + local max_func_args = 20 -- from eval.h + local range = helpers.funcs.range + + before_each(clear) + + it('printf()', function() + local printf = helpers.funcs.printf + local rep = helpers.funcs['repeat'] + local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' + eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) + local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function printf', ret) + end) + + it('rpcnotify()', function() + local rpcnotify = helpers.funcs.rpcnotify + local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) + eq(1, ret) + ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) + end) +end) diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index a2e7bd91af..b7f82064d7 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -41,13 +41,13 @@ describe('buffer functions', function() describe('buf_valid', function() it('should view NULL as an invalid buffer', function() - eq(0, buffer.buf_valid(NULL)) + eq(false, buffer.buf_valid(NULL)) end) it('should view an open buffer as valid', function() local buf = buflist_new(path1, buffer.BLN_LISTED) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and hidden buffer as valid', function() @@ -55,7 +55,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, 0, 0) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and unloaded buffer as valid', function() @@ -63,7 +63,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0) - eq(1, buffer.buf_valid(buf)) + eq(true, buffer.buf_valid(buf)) end) it('should view a closed and wiped buffer as invalid', function() @@ -71,7 +71,7 @@ describe('buffer functions', function() close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0) - eq(0, buffer.buf_valid(buf)) + eq(false, buffer.buf_valid(buf)) end) end) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 08e76ceef0..6f5a5f44dc 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -21,6 +21,9 @@ option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED}) option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED}) option(USE_BUNDLED_LUAJIT "Use the bundled version of luajit." ${USE_BUNDLED}) option(USE_BUNDLED_LUAROCKS "Use the bundled version of luarocks." ${USE_BUNDLED}) +#XXX(tarruda): Lua is only used for debugging the functional test client, no +# build it unless explicitly requested +option(USE_BUNDLED_LUA "Use the bundled version of lua." OFF) option(USE_EXISTING_SRC_DIR "Skip download of deps sources in case of existing source directory." OFF) @@ -80,6 +83,9 @@ set(MSGPACK_SHA256 afda64ca445203bb7092372b822bae8b2539fdcebbfc3f753f393628c2bcf set(LUAJIT_URL https://github.com/neovim/deps/raw/master/opt/LuaJIT-2.0.4.tar.gz) set(LUAJIT_SHA256 620fa4eb12375021bef6e4f237cbd2dd5d49e56beb414bee052c746beef1807d) +set(LUA_URL https://github.com/lua/lua/archive/5.1.5.tar.gz) +set(LUA_SHA256 1cd642c4c39778306a14e62ccddace5c7a4fb2257b0b06f43bc81cf305c7415f) + set(LUAROCKS_URL https://github.com/keplerproject/luarocks/archive/5d8a16526573b36d5b22aa74866120c998466697.tar.gz) set(LUAROCKS_SHA256 cae709111c5701235770047dfd7169f66b82ae1c7b9b79207f9df0afb722bfd9) @@ -89,8 +95,8 @@ set(UNIBILIUM_SHA256 623af1099515e673abfd3cae5f2fa808a09ca55dda1c65a7b5c9424eb30 set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.18.tar.gz) set(LIBTERMKEY_SHA256 239746de41c845af52bb3c14055558f743292dd6c24ac26c2d6567a5a6093926) -set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/1b745d29d45623aa8d22a7b9288c7b0e331c7088.tar.gz) -set(LIBVTERM_SHA256 3fc75908256c0d158d6c2a32d39f34e86bfd26364f5404b7d9c03bb70cdc3611) +set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/a9c7c6fd20fa35e0ad3e0e98901ca12dfca9c25c.tar.gz) +set(LIBVTERM_SHA256 1a4272be91d9614dc183a503786df83b6584e4afaab7feaaa5409f841afbd796) set(JEMALLOC_URL https://github.com/jemalloc/jemalloc/releases/download/4.0.2/jemalloc-4.0.2.tar.bz2) set(JEMALLOC_SHA256 0d8a9c8a98adb6983e0ccb521d45d9db1656ef3e71d0b14fb333f2c8138f4611) @@ -119,6 +125,10 @@ if(USE_BUNDLED_LUAJIT) include(BuildLuajit) endif() +if(USE_BUNDLED_LUA AND NOT CMAKE_CROSSCOMPILING) + include(BuildLua) +endif() + if(USE_BUNDLED_LUAROCKS) include(BuildLuarocks) endif() diff --git a/third-party/cmake/BuildLua.cmake b/third-party/cmake/BuildLua.cmake new file mode 100644 index 0000000000..b24ef089de --- /dev/null +++ b/third-party/cmake/BuildLua.cmake @@ -0,0 +1,85 @@ +include(CMakeParseArguments) + +# BuildLua(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...) +# Reusable function to build lua, wraps ExternalProject_Add. +# Failing to pass a command argument will result in no command being run +function(BuildLua) + cmake_parse_arguments(_lua + "" + "" + "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND" + ${ARGN}) + + if(NOT _lua_CONFIGURE_COMMAND AND NOT _lua_BUILD_COMMAND + AND NOT _lua_INSTALL_COMMAND) + message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND") + endif() + + ExternalProject_Add(lua + PREFIX ${DEPS_BUILD_DIR} + URL ${LUA_URL} + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua + DOWNLOAD_COMMAND ${CMAKE_COMMAND} + -DPREFIX=${DEPS_BUILD_DIR} + -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/lua + -DURL=${LUA_URL} + -DEXPECTED_SHA256=${LUA_SHA256} + -DTARGET=lua + -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake + CONFIGURE_COMMAND "${_lua_CONFIGURE_COMMAND}" + BUILD_IN_SOURCE 1 + BUILD_COMMAND "${_lua_BUILD_COMMAND}" + INSTALL_COMMAND "${_lua_INSTALL_COMMAND}") +endfunction() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LUA_TARGET linux) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(LUA_TARGET macosx) +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(LUA_TARGET freebsd) +elseif(CMAKE_SYSTEM_NAME MATCHES "BSD") + set(CMAKE_LUA_TARGET bsd) +elseif(SYSTEM_NAME MATCHES "^MINGW") + set(CMAKE_LUA_TARGET mingw) +else() + if(UNIX) + set(LUA_TARGET posix) + else() + set(LUA_TARGET generic) + endif() +endif() + +set(LUA_CONFIGURE_COMMAND + sed -e "/^CC/s@gcc@${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}@" + -e "/^CFLAGS/s@-O2@-g3@" + -e "s@-lreadline@@g" + -e "s@-lhistory@@g" + -e "s@-lncurses@@g" + -i ${DEPS_BUILD_DIR}/src/lua/src/Makefile && + sed -e "/#define LUA_USE_READLINE/d" + -i ${DEPS_BUILD_DIR}/src/lua/src/luaconf.h) +set(LUA_BUILD_COMMAND + ${MAKE_PRG} ${LUA_TARGET}) +set(LUA_INSTALL_COMMAND + ${MAKE_PRG} INSTALL_TOP=${DEPS_INSTALL_DIR} install) + +message(STATUS "Lua target is ${LUA_TARGET}") + +BuildLua(CONFIGURE_COMMAND ${LUA_CONFIGURE_COMMAND} + BUILD_COMMAND ${LUA_BUILD_COMMAND} + INSTALL_COMMAND ${LUA_INSTALL_COMMAND}) +list(APPEND THIRD_PARTY_DEPS lua) +add_dependencies(lua busted) + +set(BUSTED ${DEPS_INSTALL_DIR}/bin/busted) +set(BUSTED_LUA ${BUSTED}-lua) + +add_custom_command(OUTPUT ${BUSTED_LUA} + COMMAND sed -e 's/jit//g' < ${BUSTED} > ${BUSTED_LUA} && chmod +x ${BUSTED_LUA} + DEPENDS lua) +add_custom_target(busted-lua + DEPENDS ${DEPS_INSTALL_DIR}/bin/busted-lua) + +list(APPEND THIRD_PARTY_DEPS busted-lua) |